diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 000000000..5b85299a6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,41 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + + + +## Summary + +One paragraph explanation of the feature. + +## Motivation + +Why are we doing this? What use cases does it support? What is the expected outcome? + +## Describe alternatives you've considered + +A clear and concise description of the alternative solutions you've considered. Be sure to explain why Atom's existing customizability isn't suitable for this feature. + +## Additional context + +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..293c66c18 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,46 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + + + +### Prerequisites + +* [ ] Put an X between the brackets on this line if you have done all of the following: + * Reproduced the problem in Safe Mode: https://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode + * Followed all applicable steps in the debugging guide: https://flight-manual.atom.io/hacking-atom/sections/debugging/ + * Checked the FAQs on the message board for common solutions: https://discuss.atom.io/c/faq + * Checked that your issue isn't already filed: https://github.com/issues?utf8=✓&q=is%3Aissue+user%3Aatom + * Checked that there is not already an Atom package that provides the described functionality: https://atom.io/packages + +### Description + +[Description of the issue] + +### Steps to Reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** [What you expect to happen] + +**Actual behavior:** [What actually happens] + +**Reproduces how often:** [What percentage of the time does it reproduce?] + +### Versions + +You can get this information from copy and pasting the output of `atom --version` and `apm --version` from the command line. Also, please include the OS and what version of the OS you're running. + +### Additional Information + +Any additional information, configuration or data that might be necessary to reproduce the issue. diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 000000000..39319ee90 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,17 @@ +# Configuration for lock-threads - https://github.com/dessant/lock-threads + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 180 +# Comment to post before locking. Set to `false` to disable +lockComment: > + This issue has been automatically locked since there has not been + any recent activity after it was closed. If you can still reproduce this issue in + [Safe Mode](https://flight-manual.atom.io/hacking-atom/sections/debugging/#using-safe-mode) + then please open a new issue and fill out + [the entire issue template](https://github.com/atom/atom/blob/master/ISSUE_TEMPLATE.md) + to ensure that we have enough information to address your issue. Thanks! +# Issues or pull requests with these labels will not be locked +exemptLabels: + - help-wanted +# Limit to only `issues` or `pulls` +only: issues diff --git a/.github/move.yml b/.github/move.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.pairs b/.pairs deleted file mode 100644 index 295531028..000000000 --- a/.pairs +++ /dev/null @@ -1,17 +0,0 @@ -pairs: - ns: Nathan Sobo; nathan - cj: Corey Johnson; cj - dg: David Graham; dgraham - ks: Kevin Sawicki; kevin - jc: Jerry Cheung; jerry - bl: Brian Lopez; brian - jp: Justin Palmer; justin - gt: Garen Torikian; garen - mc: Matt Colyer; mcolyer - bo: Ben Ogle; benogle - jr: Jason Rudolph; jasonrudolph - jl: Jessica Lord; jlord - dh: Daniel Hengeveld; danielh -email: - domain: github.com -#global: true diff --git a/.travis.yml b/.travis.yml index e127aa499..f5a6ae3a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ matrix: include: - os: linux dist: trusty - env: NODE_VERSION=6.9.4 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1 + env: NODE_VERSION=8.9.3 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1 sudo: required @@ -27,7 +27,7 @@ install: - source /tmp/.nvm/nvm.sh - nvm install $NODE_VERSION - nvm use --delete-prefix $NODE_VERSION - - npm install -g npm@5.3.0 + - npm install --global npm@6.2.0 - script/build --create-debian-package --create-rpm-package --compress-artifacts script: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dceaecddb..4d01f82df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -230,7 +230,7 @@ Atom Core and all packages can be developed locally. For instructions on how to * Use the imperative mood ("Move cursor to..." not "Moves cursor to...") * Limit the first line to 72 characters or less * Reference issues and pull requests liberally after the first line -* When only changing documentation, include `[ci skip]` in the commit description +* When only changing documentation, include `[ci skip]` in the commit title * Consider starting the commit message with an applicable emoji: * :art: `:art:` when improving the format/structure of the code * :racehorse: `:racehorse:` when improving performance diff --git a/README.md b/README.md index 8078c179b..ee936527d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Atom](https://cloud.githubusercontent.com/assets/72919/2874231/3af1db48-d3dd-11e3-98dc-6066f8bc766f.png) -[![macOS Build Status](https://circleci.com/gh/atom/atom/tree/master.svg?style=shield)](https://circleci.com/gh/atom/atom) [![Linux Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom) +[![Build status](https://github.visualstudio.com/Atom/_apis/build/status/Atom%20Production%20Branches?branch=master)](https://github.visualstudio.com/Atom/_build/latest?definitionId=32&branch=master) [![Linux Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom) [![Dependency Status](https://david-dm.org/atom/atom.svg)](https://david-dm.org/atom/atom) [![Join the Atom Community on Slack](https://atom-slack.herokuapp.com/badge.svg)](https://atom-slack.herokuapp.com) @@ -33,7 +33,7 @@ Atom will automatically update when a new release is available. ### Windows -Download the latest [Atom installer](https://github.com/atom/atom/releases/latest). AtomSetup.exe is 32-bit, AtomSetup-x64.exe for 64-bit systems. +Download the latest [Atom installer](https://github.com/atom/atom/releases/latest). `AtomSetup.exe` is 32-bit. For 64-bit systems, download `AtomSetup-x64.exe`. Atom will automatically update when a new release is available. @@ -65,11 +65,15 @@ repeat these steps to upgrade to future releases. ## Building -* [FreeBSD](./docs/build-instructions/freebsd.md) * [Linux](https://flight-manual.atom.io/hacking-atom/sections/hacking-on-atom-core/#platform-linux) * [macOS](https://flight-manual.atom.io/hacking-atom/sections/hacking-on-atom-core/#platform-mac) * [Windows](https://flight-manual.atom.io/hacking-atom/sections/hacking-on-atom-core/#platform-windows) +## Discussion + +* Discuss Atom on our [forums](https://discuss.atom.io/) +* Chat about Atom on our Slack team -- [instructions for joining](https://discuss.atom.io/t/join-us-on-slack/16638?source_topic_id=25406) + ## License [MIT](https://github.com/atom/atom/blob/master/LICENSE.md) diff --git a/apm/package-lock.json b/apm/package-lock.json new file mode 100644 index 000000000..4e984bce1 --- /dev/null +++ b/apm/package-lock.json @@ -0,0 +1,4516 @@ +{ + "name": "atom-bundled-apm", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "atom-package-manager": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/atom-package-manager/-/atom-package-manager-2.1.0.tgz", + "integrity": "sha512-980iBRWOH59dc3dUVyrTgugA6Bwb2+4q4FmjBf+JarYrrejo6bwqsWyRtp9avjt1Km3wT3di137E2zCPwP9/8A==", + "requires": { + "asar-require": "0.3.0", + "async": "~0.2.8", + "colors": "~0.6.1", + "first-mate": "6.2.0", + "fs-plus": "2.x", + "git-utils": "^4.0", + "hosted-git-info": "^2.1.4", + "keytar": "^4.0", + "mv": "2.0.0", + "ncp": "~0.5.1", + "node-gyp": "3.4.0", + "npm": "6.2.0", + "open": "0.0.5", + "plist": "git+https://github.com/nathansobo/node-plist.git#bd3a93387f1d4b2cff819b200870d35465796e77", + "q": "~0.9.7", + "read": "~1.0.5", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "season": "^6.0.2", + "semver": "^5.1.0", + "tar": "^2.2.1", + "temp": "^0.8.3", + "underscore-plus": "1.x", + "wordwrap": "0.0.2", + "wrench": "~1.5.1", + "yargs": "^3.23.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "array-index": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-index/-/array-index-1.0.0.tgz", + "integrity": "sha1-7FanSe4QPk4Ix5C5w1PfFgVbl/k=", + "requires": { + "debug": "^2.2.0", + "es6-symbol": "^3.0.2" + }, + "dependencies": { + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "asar": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/asar/-/asar-0.12.1.tgz", + "integrity": "sha1-35Q+jrXNdPvKBmPi10uPK3J7UI8=", + "requires": { + "chromium-pickle-js": "^0.1.0", + "commander": "^2.9.0", + "cuint": "^0.2.1", + "glob": "^6.0.4", + "minimatch": "^3.0.0", + "mkdirp": "^0.5.0", + "mksnapshot": "^0.3.0", + "tmp": "0.0.28" + } + }, + "asar-require": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/asar-require/-/asar-require-0.3.0.tgz", + "integrity": "sha1-R+TLRBSJSthplTbNDFjAySFRtFs=", + "requires": { + "asar": "0.12.1" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + }, + "chromium-pickle-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.1.0.tgz", + "integrity": "sha1-HUixB9ghJqLz4hHC6iX4A7pVGyE=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "coffee-script": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.9.0.tgz", + "integrity": "sha1-dJLLvD8DYcxdiGWv9yN1Uv8z4fc=" + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cson-parser": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.0.9.tgz", + "integrity": "sha1-t5/BuCp3V0NoDw7/uL+tMRNNrHQ=", + "requires": { + "coffee-script": "1.9.0" + } + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=" + }, + "d": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", + "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", + "requires": { + "es5-ext": "~0.10.2" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-zip": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.3.0.tgz", + "integrity": "sha1-rjvLfjTGWHmt/nfhnDD4ZgK0vbA=", + "requires": { + "binary": "^0.3.0", + "graceful-fs": "^4.1.3", + "mkpath": "^0.1.0", + "nopt": "^3.0.1", + "q": "^1.1.2", + "readable-stream": "^1.1.8", + "touch": "0.0.3" + }, + "dependencies": { + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emissary": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/emissary/-/emissary-1.3.3.tgz", + "integrity": "sha1-phjZLWgrIy0xER3DYlpd9mF5lgY=", + "requires": { + "es6-weak-map": "^0.1.2", + "mixto": "1.x", + "property-accessors": "^1.1", + "underscore-plus": "1.x" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "es5-ext": { + "version": "0.10.45", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + }, + "dependencies": { + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-iterator": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", + "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.5", + "es6-symbol": "~2.0.1" + } + }, + "es6-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", + "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.5" + } + }, + "es6-weak-map": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", + "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.6", + "es6-iterator": "~0.1.3", + "es6-symbol": "~2.0.1" + } + }, + "event-kit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz", + "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=", + "requires": { + "grim": "^1.2.1" + } + }, + "expand-template": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", + "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "first-mate": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/first-mate/-/first-mate-6.2.0.tgz", + "integrity": "sha1-lSnK5evqVkC03DxD7ViMWzUoVa8=", + "requires": { + "emissary": "^1", + "event-kit": "^1.0.0", + "fs-plus": "^2", + "grim": "^1.2.1", + "oniguruma": "^6.1.0", + "season": "^5.0.2", + "underscore-plus": "^1" + }, + "dependencies": { + "season": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/season/-/season-5.4.1.tgz", + "integrity": "sha1-S9baYVKn8tbwixQzzi2SBmmFPQ0=", + "requires": { + "cson-parser": "1.0.9", + "fs-plus": "2.x", + "optimist": "~0.4.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "fs-plus": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-2.10.1.tgz", + "integrity": "sha1-MgR4HXhAYR5jZOe2+wWMljJ8WqU=", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-utils": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/git-utils/-/git-utils-4.1.4.tgz", + "integrity": "sha1-uS0x9h/LTHNvSngxTeNuQbn8fWg=", + "requires": { + "fs-plus": "^2.1.0", + "nan": "^2.0.0" + } + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "grim": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/grim/-/grim-1.5.0.tgz", + "integrity": "sha1-sysI71Z88YUvgXWe2caLDXE5ajI=", + "requires": { + "emissary": "^1.2.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keytar": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.2.1.tgz", + "integrity": "sha1-igamV3/fY3PgqmsRInfmPex3/RI=", + "requires": { + "nan": "2.8.0", + "prebuild-install": "^2.4.1" + }, + "dependencies": { + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" + } + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "mime-db": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" + }, + "mime-types": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "~1.35.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mixto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz", + "integrity": "sha1-wyDvYbUvKJj1IuF9i7xtUG2EJbY=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mkpath": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz", + "integrity": "sha1-dVSm+Nhxg0zJe1RisSLEwSTW3pE=" + }, + "mksnapshot": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mksnapshot/-/mksnapshot-0.3.1.tgz", + "integrity": "sha1-JQHAVldDbXQs6Vik/5LHfkDdN+Y=", + "requires": { + "decompress-zip": "0.3.0", + "fs-extra": "0.26.7", + "request": "^2.79.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "mv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.0.0.tgz", + "integrity": "sha1-jn7CtRh8hHFNd8jpg7HtKdejOhs=", + "requires": { + "mkdirp": "~0.3.5", + "ncp": "~0.4.2", + "rimraf": "~2.2.6" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + }, + "ncp": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", + "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=" + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + } + } + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "ncp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.5.1.tgz", + "integrity": "sha1-dDmFMW49tFkoG1hxaehFc1oFQ58=" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-abi": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.3.tgz", + "integrity": "sha512-b656V5C0628gOOA2kwcpNA/bxdlqYF9FvxJ+qqVX0ctdXNVZpS8J6xEUYir3WAKc7U0BH/NRlSpNbGsy+azjeg==", + "requires": { + "semver": "^5.4.1" + } + }, + "node-gyp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.4.0.tgz", + "integrity": "sha1-3aVYOTs+y74kyea4cDxxGUxj+jY=", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3", + "osenv": "0", + "path-array": "^1.0.0", + "request": "2", + "rimraf": "2", + "semver": "2.x || 3.x || 4 || 5", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "gauge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.6.0.tgz", + "integrity": "sha1-01MBrRjpaQK0dR3LvkD0IYuUKkY=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-color": "^0.1.7", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "npmlog": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-3.1.2.tgz", + "integrity": "sha1-LUb6h0M3r5SYovErtD2NC+SjaHM=", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.6.0", + "set-blocking": "~2.0.0" + } + } + } + }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "npm": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.2.0.tgz", + "integrity": "sha512-GnlNsOnxwVJX4WSfyQY0gY3LnUX2cc46XU0eu1g+WSuZgDRUGmw8tuptitJu6byp0RWGT8ZEAKajblwdhQHN8A==", + "requires": { + "JSONStream": "^1.3.3", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "~1.2.0", + "archy": "~1.0.0", + "bin-links": "^1.1.2", + "bluebird": "~3.5.1", + "byte-size": "^4.0.3", + "cacache": "^11.0.2", + "call-limit": "~1.1.0", + "chownr": "~1.0.1", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.0", + "cmd-shim": "~2.0.2", + "columnify": "~1.5.4", + "config-chain": "~1.1.11", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.1.0", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.0.1", + "glob": "~7.1.2", + "graceful-fs": "~4.1.11", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.6.0", + "iferr": "^1.0.0", + "imurmurhash": "*", + "inflight": "~1.0.6", + "inherits": "~2.0.3", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^2.0.6", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^2.0.0", + "libnpmhook": "^4.0.1", + "libnpx": "^10.2.0", + "lock-verify": "^2.0.2", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^4.1.3", + "meant": "~1.0.1", + "mississippi": "^3.0.0", + "mkdirp": "~0.5.1", + "move-concurrently": "^1.0.1", + "node-gyp": "^3.7.0", + "nopt": "~4.0.1", + "normalize-package-data": "~2.4.0", + "npm-audit-report": "^1.3.1", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "~3.0.0", + "npm-lifecycle": "^2.0.3", + "npm-package-arg": "^6.1.0", + "npm-packlist": "~1.1.10", + "npm-pick-manifest": "^2.1.0", + "npm-profile": "^3.0.2", + "npm-registry-client": "^8.5.1", + "npm-registry-fetch": "^1.1.0", + "npm-user-validate": "~1.0.0", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "~1.4.3", + "osenv": "^0.1.5", + "pacote": "^8.1.6", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.1.0", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "~1.0.1", + "read-installed": "~4.0.3", + "read-package-json": "^2.0.13", + "read-package-tree": "^5.2.1", + "readable-stream": "^2.3.6", + "readdir-scoped-modules": "*", + "request": "^2.81.0", + "retry": "^0.12.0", + "rimraf": "~2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.5.0", + "sha": "~2.0.1", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.0", + "tar": "^4.4.4", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "~1.1.0", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.2", + "validate-npm-package-license": "^3.0.3", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.6.0", + "wrappy": "~1.0.2", + "write-file-atomic": "^2.3.0" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.3", + "bundled": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.4.1", + "bundled": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true + }, + "aws4": { + "version": "1.7.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.2", + "bundled": true, + "requires": { + "bluebird": "^3.5.0", + "cmd-shim": "^2.0.2", + "gentle-fs": "^2.0.0", + "graceful-fs": "^4.1.11", + "write-file-atomic": "^2.3.0" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.5.1", + "bundled": true + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.x.x" + } + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "4.0.3", + "bundled": true + }, + "cacache": { + "version": "11.0.2", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "figgy-pudding": "^3.1.0", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.0", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.0", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "ci-info": { + "version": "1.1.3", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.9", + "bundled": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.0", + "bundled": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "2.0.2", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.3.0", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.2", + "bundled": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "requires": { + "boom": "2.x.x" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es6-promise": { + "version": "4.2.4", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.1.0", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "4.0.1", + "bundled": true + }, + "gentle-fs": { + "version": "2.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.2", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "requires": { + "ajv": "^4.9.1", + "har-schema": "^1.0.5" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "bundled": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "hosted-git-info": { + "version": "2.6.0", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "bundled": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.0", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-ci": { + "version": "1.1.0", + "bundled": true, + "requires": { + "ci-info": "^1.0.0" + } + }, + "is-cidr": { + "version": "2.0.6", + "bundled": true, + "requires": { + "cidr-regex": "^2.0.8" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "libcipm": { + "version": "2.0.0", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^2.0.3", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^8.1.6", + "protoduck": "^5.0.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpmhook": { + "version": "4.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.1.0", + "npm-registry-fetch": "^3.0.0" + }, + "dependencies": { + "npm-registry-fetch": { + "version": "3.1.1", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "figgy-pudding": "^3.1.0", + "lru-cache": "^4.1.2", + "make-fetch-happen": "^4.0.0", + "npm-package-arg": "^6.0.0" + } + } + } + }, + "libnpx": { + "version": "10.2.0", + "bundled": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^11.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lock-verify": { + "version": "2.0.2", + "bundled": true, + "requires": { + "npm-package-arg": "^5.1.2 || 6", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true + }, + "lru-cache": { + "version": "4.1.3", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "version": "4.0.1", + "bundled": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "meant": { + "version": "1.0.1", + "bundled": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mime-db": { + "version": "1.33.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.18", + "bundled": true, + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.3", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "3.7.0", + "bundled": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": ">=2.9.0 <2.82.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "bundled": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "1.3.1", + "bundled": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.0", + "bundled": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "2.0.3", + "bundled": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.11", + "node-gyp": "^3.6.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.0" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true + }, + "npm-package-arg": { + "version": "6.1.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "^1.1.2 || 2", + "make-fetch-happen": "^2.5.0 || 3 || 4" + } + }, + "npm-registry-client": { + "version": "8.5.1", + "bundled": true, + "requires": { + "concat-stream": "^1.5.2", + "graceful-fs": "^4.1.6", + "normalize-package-data": "~1.0.1 || ^2.0.0", + "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "npmlog": "2 || ^3.1.0 || ^4.0.0", + "once": "^1.3.3", + "request": "^2.74.0", + "retry": "^0.10.0", + "safe-buffer": "^5.1.1", + "semver": "2 >=2.2.1 || 3.x || 4 || 5", + "slide": "^1.1.3", + "ssri": "^5.2.4" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-registry-fetch": { + "version": "1.1.0", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "figgy-pudding": "^2.0.1", + "lru-cache": "^4.1.2", + "make-fetch-happen": "^3.0.0", + "npm-package-arg": "^6.0.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "cacache": { + "version": "10.0.4", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + }, + "dependencies": { + "mississippi": { + "version": "2.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + } + } + }, + "figgy-pudding": { + "version": "2.0.1", + "bundled": true + }, + "make-fetch-happen": { + "version": "3.0.0", + "bundled": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^10.0.4", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.0", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^3.0.1", + "ssri": "^5.2.4" + } + }, + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ip": "^1.1.4", + "smart-buffer": "^1.0.13" + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "requires": { + "agent-base": "^4.1.0", + "socks": "^1.1.10" + } + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.4.3", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "8.1.6", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "cacache": "^11.0.2", + "get-stream": "^3.0.0", + "glob": "^7.1.2", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.10", + "npm-pick-manifest": "^2.1.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.5.0", + "ssri": "^6.0.0", + "tar": "^4.4.3", + "unique-filename": "^1.1.0", + "which": "^1.3.0" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true + }, + "pify": { + "version": "3.0.0", + "bundled": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "requires": { + "genfun": "^4.0.1" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.4.0", + "bundled": true + }, + "query-string": { + "version": "6.1.0", + "bundled": true, + "requires": { + "decode-uri-component": "^0.2.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.1", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "version": "2.0.13", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "slash": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.2.1", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "once": "^1.3.0", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "bundled": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "2.0.1", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "readable-stream": "^2.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "slash": { + "version": "1.0.0", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.0.1", + "bundled": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "requires": { + "hoek": "2.x.x" + } + }, + "socks": { + "version": "2.2.0", + "bundled": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "bundled": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "ssri": { + "version": "6.0.0", + "bundled": true + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringstream": { + "version": "0.0.6", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.4", + "bundled": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "tough-cookie": { + "version": "2.3.4", + "bundled": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.0", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "uuid": { + "version": "3.3.2", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.3", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.6.0", + "bundled": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.3.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true + }, + "yargs": { + "version": "11.0.0", + "bundled": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "bundled": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "oniguruma": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/oniguruma/-/oniguruma-6.2.1.tgz", + "integrity": "sha1-pQ7mlkKEStHSUmhaqxhxcbBuzgQ=", + "requires": { + "nan": "^2.0.9" + } + }, + "open": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz", + "integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw=" + }, + "optimist": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.4.0.tgz", + "integrity": "sha1-y47Dfy/jqphky2eidSUOfhliCiU=", + "requires": { + "wordwrap": "~0.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-array": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-array/-/path-array-1.0.1.tgz", + "integrity": "sha1-fi8PNfB6IBUSK4aLfqwOssT+wnE=", + "requires": { + "array-index": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "plist": { + "version": "git+https://github.com/nathansobo/node-plist.git#bd3a93387f1d4b2cff819b200870d35465796e77", + "from": "git+https://github.com/nathansobo/node-plist.git", + "requires": { + "xmlbuilder": "0.4.x", + "xmldom": "0.1.x" + } + }, + "prebuild-install": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz", + "integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.1.6", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "property-accessors": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/property-accessors/-/property-accessors-1.1.3.tgz", + "integrity": "sha1-Hd6EAkYxhlkJ7zBwM2VoDF+SixU=", + "requires": { + "es6-weak-map": "^0.1.2", + "mixto": "1.x" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "q": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/q/-/q-0.9.7.tgz", + "integrity": "sha1-TeLmyzspCIyeTLwDv51C+5bOL3U=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "^7.0.5" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "season": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/season/-/season-6.0.2.tgz", + "integrity": "sha1-naWPsd3SSCTXYhstxjpxI7UCF7Y=", + "requires": { + "cson-parser": "^1.3.0", + "fs-plus": "^3.0.0", + "yargs": "^3.23.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, + "cson-parser": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", + "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", + "requires": { + "coffee-script": "^1.10.0" + } + }, + "fs-plus": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.0.2.tgz", + "integrity": "sha1-a19Sp3EolMTd6f2PgfqMYN8EHz0=", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + } + } + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", + "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.1.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "temp": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "requires": { + "os-tmpdir": "^1.0.0", + "rimraf": "~2.2.6" + }, + "dependencies": { + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + } + } + }, + "tmp": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", + "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=", + "requires": { + "os-tmpdir": "~1.0.1" + } + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "touch": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz", + "integrity": "sha1-Ua7z1ElXHU8oel2Hyci0kYGg2x0=", + "requires": { + "nopt": "~1.0.10" + }, + "dependencies": { + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1" + } + } + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "^1.4.1" + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "underscore-plus": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.6.8.tgz", + "integrity": "sha512-88PrCeMKeAAC1L4xjSiiZ3Fg6kZOYrLpLGVPPeqKq/662DfQe/KTSKdSR/Q/tucKNnfW2MNAUGSCkDf8HmXC5Q==", + "requires": { + "underscore": "~1.8.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "wrench": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.9.tgz", + "integrity": "sha1-QRaRxjqbJTGxcAJnJ5veyiOyFCo=" + }, + "xmlbuilder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.3.tgz", + "integrity": "sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=" + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + } + } + } + } +} diff --git a/apm/package.json b/apm/package.json index 90093b3d4..bf16cca66 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.19.0" + "atom-package-manager": "2.1.0" } } diff --git a/appveyor.yml b/appveyor.yml index 0e5abaa83..7e5c07b10 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,6 +10,7 @@ branches: only: - master - /^[0-9.]+-releases$/ + - /^electron-[0-9.]+$/ platform: - x64 @@ -19,7 +20,7 @@ environment: global: ATOM_DEV_RESOURCE_PATH: c:\projects\atom TEST_JUNIT_XML_ROOT: c:\projects\junit-test-results - NODE_VERSION: 6.9.4 + NODE_VERSION: 8.9.3 matrix: - TASK: test @@ -35,21 +36,30 @@ install: - IF NOT EXIST %TEST_JUNIT_XML_ROOT% MKDIR %TEST_JUNIT_XML_ROOT% - SET PATH=C:\Program Files\Atom\resources\cli;%PATH% - ps: Install-Product node $env:NODE_VERSION $env:PLATFORM - - npm install -g npm@5.3.0 + - npm install --global npm@6.2.0 build_script: - CD %APPVEYOR_BUILD_FOLDER% - IF NOT EXIST C:\tmp MKDIR C:\tmp - SET SQUIRREL_TEMP=C:\tmp + - IF [%APPVEYOR_REPO_BRANCH:~-9%]==[-releases] SET IS_RELEASE_BRANCH=true + - IF [%APPVEYOR_REPO_BRANCH%]==[master] IF NOT DEFINED APPVEYOR_PULL_REQUEST_NUMBER SET IS_SIGNED_ZIP_BRANCH=true + - IF [%APPVEYOR_REPO_BRANCH:~0,9%]==[electron-] SET IS_SIGNED_ZIP_BRANCH=true - IF [%TASK%]==[installer] ( - IF [%APPVEYOR_REPO_BRANCH:~-9%]==[-releases] ( + IF [%IS_RELEASE_BRANCH%]==[true] ( + ECHO Building on release branch - Creating production artifacts && script\build.cmd --code-sign --compress-artifacts --create-windows-installer ) ELSE ( - ECHO Skipping installer and Atom build on non-release branch + IF [%IS_SIGNED_ZIP_BRANCH%]==[true] ( + ECHO Building on %APPVEYOR_REPO_BRANCH% branch - Creating signed zips && + script\build.cmd --code-sign --compress-artifacts + ) ELSE ( + ECHO Skipping installer build for non-release/non-master branch + ) ) ) ELSE ( - ECHO Skipping installer build on non-installer build matrix row && - script\build.cmd --code-sign --compress-artifacts + ECHO Test build only - Not creating artifacts && + script\build.cmd ) test_script: @@ -68,6 +78,12 @@ artifacts: name: atom-windows.zip - path: out\RELEASES name: RELEASES + - path: out\AtomSetup-x64.exe + name: AtomSetup-x64.exe + - path: out\atom-x64-windows.zip + name: atom-x64-windows.zip + - path: out\RELEASES-x64 + name: RELEASES-x64 - path: out\atom-*-delta.nupkg name: atom-delta.nupkg - path: out\atom-*-full.nupkg diff --git a/atom.sh b/atom.sh index b36938bc5..935204bfc 100755 --- a/atom.sh +++ b/atom.sh @@ -9,11 +9,20 @@ else exit 1 fi -if [ "$(basename $0)" == 'atom-beta' ]; then - BETA_VERSION=true -else - BETA_VERSION= -fi +case $(basename $0) in + atom-beta) + CHANNEL=beta + ;; + atom-nightly) + CHANNEL=nightly + ;; + atom-dev) + CHANNEL=dev + ;; + *) + CHANNEL=stable + ;; +esac export ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT=true @@ -53,6 +62,9 @@ if [ $REDIRECT_STDERR ]; then exec 2> /dev/null fi +ATOM_HOME="${ATOM_HOME:-$HOME/.atom}" +mkdir -p "$ATOM_HOME" + if [ $OS == 'Mac' ]; then if [ -L "$0" ]; then SCRIPT="$(readlink "$0")" @@ -67,10 +79,20 @@ if [ $OS == 'Mac' ]; then ATOM_APP_NAME="$(basename "$ATOM_APP")" fi - if [ -n "$BETA_VERSION" ]; then - ATOM_EXECUTABLE_NAME="Atom Beta" + if [ ! -z "${ATOM_APP_NAME}" ]; then + # If ATOM_APP_NAME is known, use it as the executable name + ATOM_EXECUTABLE_NAME="${ATOM_APP_NAME%.*}" else - ATOM_EXECUTABLE_NAME="Atom" + # Else choose it from the inferred channel name + if [ "$CHANNEL" == 'beta' ]; then + ATOM_EXECUTABLE_NAME="Atom Beta" + elif [ "$CHANNEL" == 'nightly' ]; then + ATOM_EXECUTABLE_NAME="Atom Nightly" + elif [ "$CHANNEL" == 'dev' ]; then + ATOM_EXECUTABLE_NAME="Atom Dev" + else + ATOM_EXECUTABLE_NAME="Atom" + fi fi if [ -z "${ATOM_PATH}" ]; then @@ -101,14 +123,20 @@ elif [ $OS == 'Linux' ]; then SCRIPT=$(readlink -f "$0") USR_DIRECTORY=$(readlink -f $(dirname $SCRIPT)/..) - if [ -n "$BETA_VERSION" ]; then - ATOM_PATH="$USR_DIRECTORY/share/atom-beta/atom" - else - ATOM_PATH="$USR_DIRECTORY/share/atom/atom" - fi - - ATOM_HOME="${ATOM_HOME:-$HOME/.atom}" - mkdir -p "$ATOM_HOME" + case $CHANNEL in + beta) + ATOM_PATH="$USR_DIRECTORY/share/atom-beta/atom" + ;; + nightly) + ATOM_PATH="$USR_DIRECTORY/share/atom-nightly/atom" + ;; + dev) + ATOM_PATH="$USR_DIRECTORY/share/atom-dev/atom" + ;; + *) + ATOM_PATH="$USR_DIRECTORY/share/atom/atom" + ;; + esac : ${TMPDIR:=/tmp} @@ -134,8 +162,20 @@ on_die() { } trap 'on_die' SIGQUIT SIGTERM -# If the wait flag is set, don't exit this process until Atom tells it to. +# If the wait flag is set, don't exit this process until Atom kills it. if [ $WAIT ]; then + WAIT_FIFO="$ATOM_HOME/.wait_fifo" + + if [ ! -p "$WAIT_FIFO" ]; then + rm -f "$WAIT_FIFO" + mkfifo "$WAIT_FIFO" + fi + + # Block endlessly by reading from a named pipe. + exec 2>/dev/null + read < "$WAIT_FIFO" + + # If the read completes for some reason, fall back to sleeping in a loop. while true; do sleep 1 done diff --git a/circle.yml b/circle.yml deleted file mode 100644 index b5791e7ad..000000000 --- a/circle.yml +++ /dev/null @@ -1,45 +0,0 @@ -machine: - environment: - XCODE_SCHEME: test - XCODE_WORKSPACE: test - XCODE_PROJECT: test - TEST_JUNIT_XML_ROOT: ${CIRCLE_TEST_REPORTS} - - xcode: - version: 7.3 - -general: - artifacts: - - out/atom-mac.zip - - out/atom-mac-symbols.zip - - docs/output/atom-api.json - -dependencies: - pre: - - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.3/install.sh | bash - - nvm install 6.9.4 - - nvm use 6.9.4 - - npm install -g npm@5.3.0 - - override: - - script/build --code-sign --compress-artifacts - - cache_directories: - - electron - - apm/node_modules - - script/node_modules - - node_modules - - ~/.atom/compile-cache - - ~/.atom/snapshot-cache - -test: - override: - - script/lint - - osascript -e 'tell application "System Events" to keystroke "x"' # clear screen saver - - caffeinate -s script/test # Run with caffeinate to prevent screen saver - -experimental: - notify: - branches: - only: - - master diff --git a/docs/README.md b/docs/README.md index c45e117e4..094bc6e5b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,7 +8,6 @@ Most of the Atom user and developer documentation is contained in the [Atom Flig Instructions for building Atom on various platforms from source. -* [FreeBSD](./build-instructions/freebsd.md) * Moved to [the Flight Manual](https://flight-manual.atom.io/hacking-atom/sections/hacking-on-atom-core/) * Linux * macOS diff --git a/docs/build-instructions/build-status.md b/docs/build-instructions/build-status.md index 9bc806e88..a6f7fdfd6 100644 --- a/docs/build-instructions/build-status.md +++ b/docs/build-instructions/build-status.md @@ -1,8 +1,8 @@ # Atom build status -| System | Travis | AppVeyor/Win | Circle/Mac | Dependencies | +| System | Travis | AppVeyor/Win | VSTS | Dependencies | |--------|--------|--------------|------------|--------------| -| [Atom](https://github.com/atom/atom) | [![Travis Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom) | [![Circle/Mac Build Status](https://circleci.com/gh/atom/atom.svg?style=shield)](https://circleci.com/gh/atom/atom) | [![Dependency Status](https://david-dm.org/atom/atom.svg)](https://david-dm.org/atom/atom) | +| [Atom](https://github.com/atom/atom) | [![Travis Build Status](https://travis-ci.org/atom/atom.svg?branch=master)](https://travis-ci.org/atom/atom) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/1tkktwh654w07eim?svg=true)](https://ci.appveyor.com/project/Atom/atom) | [![Build status](https://github.visualstudio.com/Atom/_apis/build/status/Atom%20Production%20Branches?branch=master)](https://github.visualstudio.com/Atom/_build/latest?definitionId=32&branch=master) | [![Dependency Status](https://david-dm.org/atom/atom.svg)](https://david-dm.org/atom/atom) | | [APM](https://github.com/atom/apm) | [![Travis Build Status](https://travis-ci.org/atom/apm.svg?branch=master)](https://travis-ci.org/atom/apm) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/j6ixw374a397ugkb/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/apm/branch/master) | | [![Dependency Status](https://david-dm.org/atom/apm.svg)](https://david-dm.org/atom/apm) | | [Electron](https://github.com/electron/electron) | [![Travis Build Status](https://travis-ci.org/electron/electron.svg?branch=master)](https://travis-ci.org/electron/electron) | [![AppVeyor/Wi Build Status](https://ci.appveyor.com/api/projects/status/kvxe4byi7jcxbe26/branch/master?svg=true)](https://ci.appveyor.com/project/Atom/electron) | | [![Dependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron) diff --git a/docs/build-instructions/freebsd.md b/docs/build-instructions/freebsd.md deleted file mode 100644 index ab07ff529..000000000 --- a/docs/build-instructions/freebsd.md +++ /dev/null @@ -1,19 +0,0 @@ -# FreeBSD - -FreeBSD -RELEASE 64-bit is the recommended platform. - -## Requirements - -* FreeBSD -* `pkg install node` -* `pkg install npm` -* `pkg install libgnome-keyring` -* `npm config set python /usr/local/bin/python2 -g` to ensure that gyp uses Python 2 - -## Instructions - -```sh -git clone https://github.com/atom/atom -cd atom -script/build -``` diff --git a/docs/focus/README.md b/docs/focus/README.md new file mode 100644 index 000000000..e5cc0f538 --- /dev/null +++ b/docs/focus/README.md @@ -0,0 +1,28 @@ +# Near-term plans + +Want to know what the Atom team is working on and what has our focus over the next few months? You've come to the right place. 🎯 + + +This roadmap is a [living document](https://en.wikipedia.org/wiki/Living_document): it represents our current plans, but we expect these plans to change from time to time. Follow [this link](https://github.com/atom/atom/blob/4fbad81a7cd2f2e3925d7e920086bc1ebf2fe210/docs/focus/README.md) to see the previous major version of this roadmap. + +You can find our bi-weekly iteration plans by searching for issues with the [`iteration-plan`](https://github.com/atom/atom/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aiteration-plan) label. + +--- + +### Core package development is streamlined +Everything in Atom is a package. While this adds to its hackability, it is not always the best path forward. Consolidating packages as well as thinking about other ways to decrease friction for contributors will help pay down some of our tech debt in this area. More information regarding planning was provided in [this RFC](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate-core-packages.md) + +- [ ] Merge at least 22 packages in to atom/atom + + +### Improve Communication and Process + +- [ ] Refine process for triaging issues and PRs across Atom org repositories +- [ ] Publish a document that outlines merge requirements for community PRs +- [ ] Reactive tickets are incorporated in to 80% of all sprints +- [ ] Automate some aspects of Atom issue and PR triage with Probot, especially around ensuring PRs follow our contribution guidelines + +### Establish and Measure + +- [ ] Implement Atom metrics dashboard that can be used to drive future decisions +- [ ] Determine what may be helpful to measure in the future building upon work [already in progress](http://blog.atom.io/2018/06/20/atom-metrics.html) diff --git a/docs/rfcs/000-template.md b/docs/rfcs/000-template.md new file mode 100644 index 000000000..52e852159 --- /dev/null +++ b/docs/rfcs/000-template.md @@ -0,0 +1,37 @@ +# Feature title + +## Status + +Proposed + +## Summary + +One paragraph explanation of the feature. + +## Motivation + +Why are we doing this? What use cases does it support? What is the expected outcome? + +## Explanation + +Explain the proposal as if it was already implemented and you were describing it to an Atom user. That generally means: + +- Introducing new named concepts. +- Explaining the feature largely in terms of examples. +- Explaining any changes to existing workflows. + +## Drawbacks + +Why should we *not* do this? + +## Rationale and alternatives + +- Why is this approach the best in the space of possible approaches? +- What other approaches have been considered and what is the rationale for not choosing them? +- What is the impact of not doing this? + +## Unresolved questions + +- What unresolved questions do you expect to resolve through the RFC process before this gets merged? +- What unresolved questions do you expect to resolve through the implementation of this feature before it is released in a new version of Atom? +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? diff --git a/docs/rfcs/001-updatable-bundled-packages.md b/docs/rfcs/001-updatable-bundled-packages.md new file mode 100644 index 000000000..dbf23b678 --- /dev/null +++ b/docs/rfcs/001-updatable-bundled-packages.md @@ -0,0 +1,105 @@ +# Updatable Bundled Packages + +## Status + +Proposed + +## Summary + +This feature will enable an opt-in subset of bundled Atom packages to be updated with `apm` outside of the Atom release cycle. This will enable users to receive new functionality and bug fixes for some bundled packages as regularly as needed without waiting for them to be included in a new Atom release. This is especially important for packages like [GitHub](https://github.com/atom/github/) and [Teletype](https://github.com/atom/teletype/) which provide essential Atom functionality and could be improved independently of Atom. + +## Motivation + +Atom currently uses a monthly release cycle with staged Stable and Beta releases so that major issues get caught early in Beta before reaching the Stable release. Because Atom releases updates monthly, this means that a new feature merged into `master` right after a new Atom release could take one month to reach the next Beta and then another month to reach Stable. + +Since a large part of Atom's built-in functionality is provided by bundled packages, it makes sense to allow some of those packages to be updated independently of Atom's monthly release cycle so that users can receive new features and fixes whenever they become available. + +Bundled packages are treated differently than community packages that you can install using `apm`: + +- You are not prompted to update them when new versions are released on `apm` +- `apm` will warn you at the command line when you try to install or update a bundled package +- If a user intentionally installs a bundled package from `apm` the [dalek package](https://github.com/atom/dalek/) will show a warning in the "deprecations" view asking the user to remove the offending package + +Despite all this, if the user *does* manually install an update to a bundled package using `apm`, it will be loaded into the editor and updated dutifully as new releases occur. The only new functionality needed is to enable `apm` to check bundled packages for updates when those packages haven't yet been installed in the user's `~/.atom/packages` folder. + +The primary use case for this improvement is enabling the GitHub package to ship improvements more frequently than Atom's release cycle since many of its improvements can be done without changes to Atom itself. If this approach is proven to work well for the GitHub package, we might also consider using it to ship Teletype as a bundled Atom package. + +## Explanation + +Any bundled Atom package can opt in to new updates released via `apm` by adding `"coreUpdatable": true` to its `package.json` file. This causes `apm` to consider it as part of the list of packages it checks for updates. If a community (non-bundled) package sets this field to `true` or `false` it will be ignored as it's only relevant to bundled packages. + +Atom shows update notifications for Updatable bundled packages whenever they are available so long as those updates support the engine version of the current Atom build. Bundled package updates can also be found and installed in the Settings view's *Updates* tab. + +The `dalek` package is aware of the new "Updatable" metadata and excludes updated bundled packages from its deprecation warnings. + +### User Experience Examples + +1. The user downloads and installs Atom 1.28.0 which includes GitHub package version 0.15.0. Two weeks later, GitHub package 0.16.0 is released with a few new features. The user is prompted to update to the new version and gets the new features even though Atom 1.29.0 hasn't been released yet. + +2. The user downloads and installs Atom 1.28.0, including GitHub package 0.15.0, which was released two weeks prior. Since that release the GitHub package has been updated to version 0.15.1 on `apm`. When the user starts Atom for the first time they are prompted to update the GitHub package. + +3. In the future, a user has an old install of Atom 1.28.0 and waits a long time between installing Atom updates. The GitHub package releases version 0.25.0 but the user is not prompted to install it because the GitHub package has set `engines` in `package.json` to restrict to Atom 1.32.0 and above. + +### Rules for Updatable Bundled Packages + +Any package that opts into this behavior must adhere to these rules: + +1. **Each release must ensure that its `engines` field in `package.json` reflects the necessary Atom version for the Atom, Electron, and Node.js APIs used in the package**. This field defines the range of Atom versions in which the package is expected to work. The field should always be set to the lowest possible Atom version that the package supports. + +2. **Any new update to a bundled package *must* support current Stable *and* Beta releases**. This enables the user to upgrade the package and continue to use it in side-by-side Stable and Beta installs on their machine. If a package wants to use API features of a newer version of Atom while still supporting older Atom versions, it must do so in a way that is aware of the user's version and adjust itself accordingly. + +3. **Atom's `package.json` *must* stay up to date with the latest supported version of the package** in the `master` and Beta release branches. This ensures that the user always gets the latest version of the package in a new release and also benefits from its inclusion in Atom's snapshot. + +For rule #3, it will be important to have automation to ensure that current Beta release and `master` are kept up to date with the latest compatible version of any updatable bundled package as it will be difficult for maintainers to do that manually. This could be accomplished by a nightly CI run which is focused explicitly on bumping package dependencies in this manner. + +## Drawbacks + +### Possible API incompatibility + +The primary drawback of this approach is that Updatable bundled packages might exhibit problems on older Atom versions due to missing or changed APIs in Atom, Electron, or Node.js. The solution for these packages is to keep their `engines` field updated appropriately, but there's still a chance that some updates will slip through without the necessary engine version changes. If this does occur and users are affected by it, the solution is to publish a new update which rolls back the package to the functionality of its previous release and then publish another new update with the new functionality restored and the proper `engines` version in place. + +### Increased Atom startup time + +Another major drawback is that the snapshotted code for the bundled package will no longer be used since a newer version has been installed. This updated version of the package cannot be easily added back into Atom's snapshot so it could cause a noticable drag on Atom's startup time. Some quick measurements with Timecop show a 10x increase in GitHub package load time for bundled (snapshot) vs updated (non-snapshot) package code: + +| GitHub Package Code | Load Time | +|----------------------------------|-----------| +| **Bundled** | 52 ms | +| **Updated (first load)** | 5026 ms | +| **Updated (subsequent loads)** | 591 ms | + +There was no measurable effect on shell or window startup time, only package load time. It seems that the transpilation phase of the first load of the package incurs a 100x increase in load time. Pre-transpilation of the package code (either when shipped or when installed using `apm`) will be useful in mitigating this cost. Further investigation into snapshotting package code will be needed to understand if the load time increase can be mitigated. + +There is a possibility that the GitHub package could load parts of its codebase on demand to mitigate the increased startup time when not loaded as part of Atom's snapshot. This approach is discussed in more detail at [atom/github#1522](https://github.com/atom/github/issues/1522). + +### Incompatibility across Atom release channels + +One other possible drawback is that an updated version of a bundled package might not be compatible across two different Atom channels. For example, if the user installs a new update to a bundled package that only supports the current Atom Beta release or higher, the user will no longer have access to that package if they open Atom Stable. However, this drawback is no different than what the user would face today installing a community package under the same circumstances, so this could be considered a general problem in the Atom package ecosystem. + +Finally, one risk of this approach is that the Atom team forgets to update a bundled package to its latest appropriate version on `apm` just before a new release. If this happens, the user will install a new Atom update and then be prompted to update a package that should have been snapshotted and shipped in-box. To avoid this problem we could add some build automation that checks for the latest version of a bundled package to see if the current Atom build would be supported by it. + +## Rationale and alternatives + +This is the best approach for updating bundled packages because it allows those packages to take control of their own release cycle so long as they manage their Atom engine version correctly. It also does so in a way that allows us to decide which packages can be updated independently, reducing the likelihood of problems for users. + +The primary alternative to this approach is to speed up the Atom release cycle so that bundled Atom package updates will reach users more frequently. This approach will be investigated independently of this RFC as it may still be valuable even with Updatable bundled packages. + +## Unresolved questions + +> - What unresolved questions do you expect to resolve through the RFC process before this gets merged? + +Is it enough to just depend on the `engines` field of `package.json` to protect users from installing a package update that doesn't work with their version of Atom? + +> - What unresolved questions do you expect to resolve through the implementation of this feature before it is released in a new version of Atom? + +Is there any optimization we can use to reduce the performance hit of loading updated bundled packages? + +> - What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? + +One issue that's out of scope for this RFC is how we ship new features and fixes to the core components of Atom (not its bundled packages) more frequently. There are two options we can investigate to accomplish this: + +- **Ship Atom updates more frequently, possibly every two weeks** + +- **Introduce a channel for nightly builds which surface the latest changes every day** + +Both of these possibilities will be covered in future RFCs as they could be implemented independently of the feature described in this RFC. diff --git a/docs/rfcs/002-atom-nightly-releases.md b/docs/rfcs/002-atom-nightly-releases.md new file mode 100644 index 000000000..5bb72c8ff --- /dev/null +++ b/docs/rfcs/002-atom-nightly-releases.md @@ -0,0 +1,57 @@ +# Atom Nightly Releases + +## Status + +Implemented in PR [#17538](https://github.com/atom/atom/pull/17538) + +## Summary + +This RFC proposes that Atom add a third official release channel which delivers new builds of Atom nightly from the `master` branch. Nightly releases will allow new improvements to reach users long before a new Stable or Beta release is shipped. This effort will also give us the opportunity to experiment with new release automation strategies that could eventually be used to speed up the Stable and Beta release cadence. + +## Motivation + +Atom currently uses a monthly release cycle with staged Stable and Beta releases so that major issues get caught early in Beta before reaching the Stable release. Because Atom releases updates monthly, this means that a new feature merged into `master` right after a new Atom release could take one month to reach the next Beta and then another month to reach Stable. + +This release process works well for delivering stable improvements to users on a regular basis but it results in friction for users who want to try out the latest Atom improvements and provide feedback. If we deliver a nightly release channel, it will be possible to deliver new features and bug fixes on a regular basis and get valuable feedback to guide our work. + +Today, a bleeding-edge user must manually pull Atom's `master` branch and compile their own build. There is a source of `dev` builds from `master` across our CI services but those aren't made available to users as an official distribution. + +## Explanation + +A user who wants to use the latest improvements to Atom each day can go to atom.io, download the Atom Nightly release, and install it on their machine. This release can be installed alongside Atom Stable and Atom Beta. + +Each night when there are new commits to Atom's `master` branch, a scheduled CI build creates a new Atom Nightly release with packages for Windows, macOS, and Linux. These packages are automatically uploaded to a new GitHub release on the `atom/atom-nightly-releases` repository using a monotonically-increasing nightly version based off of the version in `master` (e.g. `v1.29.0-nightly1`). + +Every 4 hours, an Atom Nightly release installed on Windows or macOS checks for a new update by consulting Electron's [update.electronjs.org](update-electron) service. If a new update is available, it is downloaded in the background and the user is notified to restart Atom once it's complete. This update flow is the same as what users experience in Atom Stable or Beta releases but updates occur more frequently. + +Linux users must manually download nightly releases for now as there isn't an easy way to automatically install new updates across the various Linux distributions. We may consider providing updatable [AppImage](http://appimage.org/) packages in the future; this will be proposed in a separate RFC. + +## Drawbacks + +There isn't a major downside to this effort since it would run in parallel to the existing Atom release process without affecting it. + +## Rationale and alternatives + +This is a useful approach because it allows us to achieve a much more rapid feedback loop with highly engaged users to ensure that Atom is improving regularly. It's the best approach because it allows us to get rapid feedback without sacrificing the stability of the Stable and Beta releases. + +Another option is to speed up Atom's release cadence to ship Stable and Beta every two weeks (or more regularly). This approach could shorten our feedback loop but at the expense of greater instability since new improvements would not have as much time to be polished before release. + +The impact of not taking this approach is that we continue to have to wait 1-2 months to get feedback from users about new features or bugs in Stable and Beta releases. + +## Unresolved questions + +- **What should we call this release channel?** + + Some ideas: + + - Atom Nightly + - Atom Reactor + - Atom Dev - Currently the name of dev builds but it might make sense to leave that for "normal" builds from `master` + + According to a [Twitter poll](https://twitter.com/daviwil/status/1006545552987701248) with about 1,600 responses, 50% of the voters chose "Atom Nightly". The final name will be determined before launch. + +- **Will Electron's new autoUpdate service work for all Atom releases?** + + One outcome of this effort is to use the new [update.electronjs.org](update-electron) service for Atom's update checks so that we can deprecate on our own custom update service. Building the Nightly channel on this service will allow us to evaluate it to see if it meets the needs of the Stable and Beta channels. + +[update-electron]: https://github.com/electron/update.electronjs.org diff --git a/docs/rfcs/003-consolidate-core-packages.md b/docs/rfcs/003-consolidate-core-packages.md new file mode 100644 index 000000000..960ac95ce --- /dev/null +++ b/docs/rfcs/003-consolidate-core-packages.md @@ -0,0 +1,345 @@ +# Consolidate Core Atom Packages + +## Status + +Accepted + +## Summary + +Atom's official distribution is comprised of 92 core packages which provide its built-in functionality. These packages currently live in their own independent repositories in the Atom organization, all with their own separate issues, PRs, releases, and CI configurations. This RFC proposes that by consolidating most, if not all, of these core packages back into the `atom/atom` repo, we will see the following benefits: + +- Less confusion for new contributors +- Simpler core package contribution experience +- Greatly reduced burden for maintainers + +## Motivation + +Let's cover each of the bullet points mentioned above: + +### Less confusion for contributors + +Imagine that a new contributor wants to add a small new feature to the `tree-view` package. The first place they are likely to look is the `atom/atom` repository. Scanning through the folders will lead to a dead end as nothing that looks like `tree-view` code can be found. They might take one of the following steps next: + +- By reading README.md, maybe they will decide to click the link to the Atom Flight Manual and _maybe_ find the [Contributing to Official Atom Packages](https://flight-manual.atom.io/hacking-atom/sections/contributing-to-official-atom-packages/) page there +- They could read the CONTRIBUTING.md file which [has a section](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#atom-and-packages) that explains where to find the repos for core packages and how to contribute, but we don't really have a clear pointer to that in our README.md +- If they don't happen to find that page, they might use Google to search for "atom tree view" and find the atom/tree-view repo and _maybe_ read the CONTRIBUTING.md file which sends them to Atom's overall contribution documentation +- They might go to the Atom Forum or Slack community to ask how to contribute to a particular part of Atom and *hopefully* get a helpful response that points them in the right direction + +Having all of the core Atom packages represented in a top-level `packages` folder, even if they all don't actually live in the repo, will go a long way to making the core package code more discoverable. + +### Simpler core package contribution experience + +Separating core Atom features out into individual repositories and delivering them to Atom builds via `apm` is a great idea in theory because it validates the Atom package ecosystem and gives developers many examples of how to develop an Atom package. It also gives Atom developers real-world experience working with Atom's APIs so that we ensure community package authors have the same hackability that Atom developers enjoy. + +On the other hand, having these packages live in separate repositories and released "independently" introduces a great deal of overhead when adding new features. Here is a comparison of the current package development workflow contrasted to what we could achieve with consolidated packages: + +#### Current Package Development Workflow + +For example, to add a single feature to the `tree-view` package, one must: + +1. Fork and clone the `tree-view` repository to their computer (making sure to pull the commit relevant to the version of Atom they are working with) +1. Run `apm install` and `apm link` inside of the repo folder +1. Make their desired changes to the code +1. Open a PR to the `tree-view` repo and wait for CI to pass and a maintainer to review it +1. Work with maintainers to get the PR approved and merged + +After this is finished, an Atom maintainer must take the following steps: + +1. Clone the `tree-view` repo +2. Run `apm publish` to publish a new release of the package +3. Edit `package.json` in the Atom repo to reflect the new version of `tree-view` +4. Commit and push the changes to the relevant branch where the change belongs (`master` or `1.nn-releases`) + +#### Simplified Package Development + +If we were to move `tree-view` (or any other core Atom package) back into `atom/atom`, the development workflow would look more like this: + +1. Fork and clone `atom/atom` and switch to a release branch if necessary +1. Build Atom and launch it in dev mode +1. Make desired changes to the code in `packages/tree-view` +1. Open a PR on `atom/atom` and wait for CI to pass and a maintainer to review it +1. Work with maintainers to get the PR approved and merged + +At this point, the change is merged into Atom and ready for inclusion in the next release. + +### Greatly reduced burden for maintainers + +Since packages all have their own repositories, this means that we have to watch 91 different repos for issues and pull requests. This also means that we have to redirect issues filed on `atom/atom` to the appropriate repository when a user doesn't know where it belongs. Even more importantly, there's not an easy way to prioritize and track issues across the Atom organization without using GitHub Projects. + +Also, as mentioned above, there's the added duty of doing the package "version dance" when we merge any new PRs to a package repository: publish the package update, update `package.json` in Atom. It's very easy to forget to do this and not have community contributions included in the next Atom release! + +The more core packages live in `atom/atom`, the less work Atom maintainers have to do overall. + +## Explanation + +Many of Atom's core packages now live in the core `atom/atom` repository. To the Atom user, this change will be imperceptible as these packages still show up in the list of Core Packages in the Settings View. Users can still optionally disable these packages. + +For maintainers and contributors, there will be less juggling of repositories and no more publishing of updates to these packages with `apm`: + +Contributors now clone and build `atom/atom` to work on improvements to core packages. They will no longer have to use `apm link` in dev mode to test changes they make to packages in the repo's `packages` folder. Core packages that aren't consolidated still have folders under `packages` with README.md files that point to the home repository for that package. + +When a contributor sends a PR to `atom/atom` that only affects files in a folder under `packages`, only the specs for the relevant package folders will be executed using Atom's CI scripts. This means that a full Atom build will not be required when no Atom Core code is changed in a PR. Package specs are also now run against all 3 OSes on Atom `master` and release builds. + +Atom maintainers no longer have to publish new versions to consolidated core packages and then edit `package.json` to bump the package version in a particular Atom release branch (Stable, Beta, or `master`). When a PR against a consolidated core package in `atom/atom` is merged, no version number change is required and the changes will immediately be a part of the next release from that branch. + +## Drawbacks + +One possible drawback of this approach is that there might be some initial confusion where core Atom packages live, especially if some are consolidated into `atom/atom` and others still live in their own repositories. We will manage this confusion by doing the following: + +- Include a `README.md` file in the `packages` folder which lists core packages that are not consolidated in the Atom repo. This will enable users to find the home repositories of those packages. + +- Archive the repositories for consolidated core packages, but only after migrating existing issues, merging or closing existing PRs, and updating the README.md to point to the new home of the package code. + +Also, contributors will now have to fork, clone, and build `atom/atom` to contribute to core packages where they would previously just need to clone the package repository. This might put added burden on them such as installing necessary build dependencies on their machine that they wouldn't otherwise need. It is very likely we could simplify this process for them, though. + +One final drawback is that it will now be harder to have single-package maintainers. We currently have 7 core packages where there is a maintainer who isn't a part of the core Atom maintainers team. These maintainers generally are able to merge community PRs and make commits to those packages with their own judgement. If we get rid of individual package repositories, do we now make those maintainers full Atom maintainers? + +## Rationale and alternatives + +The Motivation section explains most of the rationale, so this section will focus on the process of consolidating packages back into `atom/atom`. The set of packages we've chosen to consolidate were evaluated based on a few factors: + +- Number of open issues and PRs (exclude any with > 10 open PRs) +- Time since last update (longer duration since last update is prioritized) +- Number of package-only maintainers on the repo (exclude any with package maintainers for now) + +Using this criteria, all 91 packages have been evaluated and categorized to determine whether they are good candidates for consolidation: + +#### Initial Consolidation Candidates + +| Package | Open Issues | Open PRs | Outside Maintainers | Last Updated | +|---------|-------------|----------|---------------------| -------------| +| **[about]** | 2 | 0 | 0 | 7/11/18 | +| **[archive-view]** | 10 | 0 | 0 | 6/3/18 | +| **[atom-dark-syntax]** | 5 | 0 | 0 | 12/6/17 | +| **[atom-dark-ui]** | 1 | 2 | 0 | 2/13/18 | +| **[atom-light-syntax]** | 1 | 0 | 0 | 10/17/16 | +| **[atom-light-ui]** | 1 | 0 | 0 | 2/13/18 | +| **[autoflow]** | 17 | 4 | 0 | 4/17/18 | +| **[autosave]** | 13 | 0 | 0 | 9/16/17 | +| **[background-tips]** | 3 | 2 | 0 | 2/17/18 | +| **[base16-tomorrow-dark-theme]** | 5 | 0 | 0 | 1/10/17 | +| **[base16-tomorrow-light-theme]** | 1 | 0 | 0 | 1/10/17 | +| **[bookmarks]** | 19 | 4 | 0 | 12/10/17 | +| **[bracket-matcher]** | 74 | 8 | 0 | 3/20/18 | +| **[command-palette]** | 18 | 6 | 0 | 2/27/18 | +| **[dalek]** | 2 | 0 | 0 | 2/28/18 | +| **[deprecation-cop]** | 5 | 0 | 0 | 9/7/17 | +| **[dev-live-reload]** | 4 | 0 | 0 | 11/14/17 | +| **[encoding-selector]** | 11 | 2 | 0 | 4/19/18 | +| **[exception-reporting]** | 5 | 0 | 0 | 2/6/18 | +| **[git-diff]** | 38 | 1 | 0 | 1/18/18 | +| **[go-to-line]** | 5 | 2 | 0 | 1/25/18 | +| **[grammar-selector]** | 3 | 1 | 0 | 4/12/18 | +| **[image-view]** | 4 | 4 | 0 | 7/9/18 | +| **[incompatible-packages]** | 1 | 0 | 0 | 4/25/17 | +| **[keybinding-resolver]** | 11 | 3 | 0 | 7/6/18 | +| **[language-clojure]** | 13 | 3 | 0 | 1/26/18 | +| **[language-coffee-script]** | 9 | 2 | 0 | 11/1/17 | +| **[language-csharp]** | 1 | 1 | 0 | 4/27/18 | +| **[language-css]** | 6 | 7 | 0 | 6/11/18 | +| **[language-gfm]** | 52 | 9 | 0 | 6/15/18 | +| **[language-git]** | 4 | 2 | 0 | 4/18/17 | +| **[language-html]** | 11 | 4 | 0 | 7/5/18 | +| **[language-hyperlink]** | 2 | 3 | 0 | 10/25/17 | +| **[language-json]** | 1 | 0 | 0 | 5/11/18 | +| **[language-less]** | 5 | 1 | 0 | 6/11/18 | +| **[language-make]** | 7 | 3 | 0 | 11/26/16 | +| **[language-mustache]** | 0 | 0 | 0 | 2/5/18 | +| **[language-objective-c]** | 2 | 0 | 0 | 12/1/15 | +| **[language-php]** | 25 | 7 | 0 | 6/11/18 | +| **[language-property-list]** | 1 | 0 | 0 | 3/11/17 | +| **[language-python]** | 33 | 4 | 0 | 6/18/18 | +| **[language-ruby]** | 38 | 10 | 0 | 10/25/17 | +| **[language-ruby-on-rails]** | 9 | 6 | 0 | 12/7/17 | +| **[language-sass]** | 12 | 5 | 0 | 5/2/18 | +| **[language-shellscript]** | 12 | 3 | 0 | 6/18/18 | +| **[language-source]** | 0 | 0 | 0 | 1/6/15 | +| **[language-sql]** | 6 | 4 | 0 | 1/26/18 | +| **[language-text]** | 1 | 0 | 0 | 3/9/18 | +| **[language-todo]** | 10 | 6 | 0 | 1/26/18 | +| **[language-toml]** | 1 | 0 | 0 | 1/6/18 | +| **[language-typescript]** | 6 | 0 | 0 | 6/18/18 | +| **[language-xml]** | 2 | 1 | 0 | 6/12/17 | +| **[language-yaml]** | 8 | 2 | 0 | 3/9/18 | +| **[line-ending-selector]** | 10 | 0 | 0 | 5/18/18 | +| **[link]** | 0 | 1 | 0 | 11/14/17 | +| **[metrics]** | 1 | 2 | 0 | 7/5/18 | +| **[notifications]** | 29 | 8 | 0 | 3/22/18 | +| **[one-dark-syntax]** | 4 | 0 | 0 | 5/27/18 | +| **[one-dark-ui]** | 13 | 1 | 0 | 5/1/18 | +| **[one-light-syntax]** | 2 | 1 | 0 | 5/27/18 | +| **[one-light-ui]** | 2 | 0 | 0 | 5/1/18 | +| **[open-on-github]** | 8 | 3 | 0 | 11/21/17 | +| **[package-generator]** | 10 | 2 | 0 | 11/16/17 | +| **[status-bar]** | 25 | 3 | 0 | 11/6/17 | +| **[styleguide]** | 12 | 2 | 0 | 4/12/18 | +| **[tabs]** | 66 | 7 | 0 | 5/13/18 | +| **[timecop]** | 5 | 0 | 0 | 11/4/17 | +| **[update-package-dependencies]** | 0 | 0 | 0 | 12/10/17 | +| **[welcome]** | 0 | 0 | 0 | 11/21/17 | +| **[whitespace]** | 31 | 6 | 0 | 5/30/18 | +| **[wrap-guide]** | 3 | 4 | 0 | 11/27/17 | + +#### Packages to be Consolidated Later + +The following packages will not be consolidated until the stated reasons can be resolved or we decide on a consolidation strategy for them: + +| Package | Open Issues | Open PRs | Outside Maintainers | Last Updated | Reason | +|---------|-------------|----------|---------------------|--------------|-------| +| **[find-and-replace]** | 219 | 17 | 0 | 6/4/18 | Too many open PRs | +| **[fuzzy-finder]** | 89 | 22 | 0 | 5/17/18 | Too many open PRs | +| **[github]** | | | | | Independent project | +| **[language-c]** | 53 | 15 | 0 | 7/10/18 | Too many open PRs | +| **[language-go]** | 12 | 2 | **1** | 6/18/18 | Package maintainer, possibly inactive? | +| **[language-java]** | 8 | 2 | **1** | 6/11/18 | Package maintainer | +| **[language-javascript]** | 66 | 12 | 0 | 7/6/18 | Too many open PRs | +| **[language-perl]** | 17 | 1 | **1** | 10/30/17 | Package maintainer, possibly inactive? | +| **[markdown-preview]** | 139 | 12 | 0 | 1/8/18 | Too many open PRs | +| **[settings-view]** | 137 | 18 | 0 | 5/17/18 | Too many open PRs | +| **[snippets]** | 57 | 4 | **1** | 4/17/18 | Package maintainer | +| **[solarized-dark-syntax]** | 8 | 3 | **1** | 5/27/18 | Package maintainer | +| **[solarized-light-syntax]** | 2 | 3 | **1** | 5/27/18 | Package maintainer | +| **[spell-check]** | 68 | 14 | **1** | 5/25/18 | Too many open PRs, package maintainer | +| **[symbols-view]** | 86 | 13 | 0 | 12/10/17 | Too many open PRs | +| **[tree-view]** | 210 | 36 | 0 | 3/21/18 | Too many open PRs | + +#### Packages to Never Consolidate + +These packages will not be consolidated because they would inhibit contributions from our friends in the Nuclide team at Facebook: + +- **[autocomplete-atom-api]** +- **[autocomplete-css]** +- **[autocomplete-html]** +- **[autocomplete-plus]** +- **[autocomplete-snippets]** + +### Consolidation Process + +To consolidate a single core package repository back into `atom/atom`, the following steps will be taken: + +1. All open pull requests on the package's repository must either be closed or merged before consolidation can proceed +1. The package repository's code in `master` will be copied over to Atom's `packages` folder in a subfolder bearing that package's name. +1. Atom's `package.json` file will be updated to change the package's `packageDependencies` entry to reference its local path with the following syntax: `"tree-view": "file:./packages/tree-view"` +1. A test build will be created locally to manually verify that the package loads and works correctly at first glance +1. The package specs for the newly-consolidated package will be run against the local Atom build +1. A PR will be sent to `atom/atom` to verify that CI passes with the introduction of the consolidated package +1. Once CI is clean and the PR is approved, the PR will be merged +1. The package's original repository will have all of its existing issues moved over to `atom/atom` using a bulk issue mover tool, assigning a label to those issues relative to the package name, like `packages/tree-view` +1. The package's original repository will have its README.md updated to point contributors to the code's new home in `atom/atom` +1. The package's original repository will now be archived on GitHub + +### Alternative Approaches + +One alternative approach would be to break this core Atom functionality out of packages and put it directly in the Atom codebase without treating them as packages. This would simplify the development process even further but with the following drawbacks: + +- The Atom team would have less regular exposure to Atom package development +- Users would no longer be able to disable core packages to replace their behavior with other packages (different tree views, etc) + +## Unresolved questions + +- Is there a good reason to not move the `language-*` packages into `atom/atom`? + + One concern here is that there exist projects which depend directly on these repositories for the TextMate syntax grammars they contain. Moving the code into `atom/atom` would require that we notify the consumers of the grammars so that they can redirect their requests to the `atom/atom` repo. + +- Should we use `git subtree` to migrate the entire commit history of these packages over or just depend on the history from a package's original repository? + + For now, we won't use `git subtree` due to the possibility that bringing over thousands of commits could cause unknown problems in the Atom repo. We may try this for newly consolidated packages in the future if we decide that not having the package commit history is a sufficient impediment to problem investigations. + +- What are the criteria we might use to eventually decide to move larger packages like `tree-view`, `settings-view`, and `find-and-replace` back into `atom/atom`? + +- Will we be losing any useful data about these packages if we don't have standalone repositories anymore? + +- Should we use this as an opportunity to remove any unnecessary packages from the core Atom distribution? + +[about]: https://github.com/atom/about +[archive-view]: https://github.com/atom/archive-view +[atom-dark-syntax]: https://github.com/atom/atom-dark-syntax +[atom-dark-ui]: https://github.com/atom/atom-dark-ui +[atom-light-syntax]: https://github.com/atom/atom-light-syntax +[atom-light-ui]: https://github.com/atom/atom-light-ui +[autocomplete-atom-api]: https://github.com/atom/autocomplete-atom-api +[autocomplete-css]: https://github.com/atom/autocomplete-css +[autocomplete-html]: https://github.com/atom/autocomplete-html +[autocomplete-plus]: https://github.com/atom/autocomplete-plus +[autocomplete-snippets]: https://github.com/atom/autocomplete-snippets +[autoflow]: https://github.com/atom/autoflow +[autosave]: https://github.com/atom/autosave +[background-tips]: https://github.com/atom/background-tips +[base16-tomorrow-dark-theme]: https://github.com/atom/base16-tomorrow-dark-theme +[base16-tomorrow-light-theme]: https://github.com/atom/base16-tomorrow-light-theme +[bookmarks]: https://github.com/atom/bookmarks +[bracket-matcher]: https://github.com/atom/bracket-matcher +[command-palette]: https://github.com/atom/command-palette +[dalek]: https://github.com/atom/dalek +[deprecation-cop]: https://github.com/atom/deprecation-cop +[dev-live-reload]: https://github.com/atom/dev-live-reload +[encoding-selector]: https://github.com/atom/encoding-selector +[exception-reporting]: https://github.com/atom/exception-reporting +[find-and-replace]: https://github.com/atom/find-and-replace +[fuzzy-finder]: https://github.com/atom/fuzzy-finder +[git-diff]: https://github.com/atom/git-diff +[github]: https://github.com/atom/github +[go-to-line]: https://github.com/atom/go-to-line +[grammar-selector]: https://github.com/atom/grammar-selector +[image-view]: https://github.com/atom/image-view +[incompatible-packages]: https://github.com/atom/incompatible-packages +[keybinding-resolver]: https://github.com/atom/keybinding-resolver +[language-c]: https://github.com/atom/language-c +[language-clojure]: https://github.com/atom/language-clojure +[language-coffee-script]: https://github.com/atom/language-coffee-script +[language-csharp]: https://github.com/atom/language-csharp +[language-css]: https://github.com/atom/language-css +[language-gfm]: https://github.com/atom/language-gfm +[language-git]: https://github.com/atom/language-git +[language-go]: https://github.com/atom/language-go +[language-html]: https://github.com/atom/language-html +[language-hyperlink]: https://github.com/atom/language-hyperlink +[language-java]: https://github.com/atom/language-java +[language-javascript]: https://github.com/atom/language-javascript +[language-json]: https://github.com/atom/language-json +[language-less]: https://github.com/atom/language-less +[language-make]: https://github.com/atom/language-make +[language-mustache]: https://github.com/atom/language-mustache +[language-objective-c]: https://github.com/atom/language-objective-c +[language-perl]: https://github.com/atom/language-perl +[language-php]: https://github.com/atom/language-php +[language-property-list]: https://github.com/atom/language-property-list +[language-python]: https://github.com/atom/language-python +[language-ruby]: https://github.com/atom/language-ruby +[language-ruby-on-rails]: https://github.com/atom/language-ruby-on-rails +[language-sass]: https://github.com/atom/language-sass +[language-shellscript]: https://github.com/atom/language-shellscript +[language-source]: https://github.com/atom/language-source +[language-sql]: https://github.com/atom/language-sql +[language-text]: https://github.com/atom/language-text +[language-todo]: https://github.com/atom/language-todo +[language-toml]: https://github.com/atom/language-toml +[language-typescript]: https://github.com/atom/language-typescript +[language-xml]: https://github.com/atom/language-xml +[language-yaml]: https://github.com/atom/language-yaml +[line-ending-selector]: https://github.com/atom/line-ending-selector +[link]: https://github.com/atom/link +[markdown-preview]: https://github.com/atom/markdown-preview +[metrics]: https://github.com/atom/metrics +[notifications]: https://github.com/atom/notifications +[one-dark-syntax]: https://github.com/atom/one-dark-syntax +[one-dark-ui]: https://github.com/atom/one-dark-ui +[one-light-syntax]: https://github.com/atom/one-light-syntax +[one-light-ui]: https://github.com/atom/one-light-ui +[open-on-github]: https://github.com/atom/open-on-github +[package-generator]: https://github.com/atom/package-generator +[settings-view]: https://github.com/atom/settings-view +[snippets]: https://github.com/atom/snippets +[solarized-dark-syntax]: https://github.com/atom/solarized-dark-syntax +[solarized-light-syntax]: https://github.com/atom/solarized-light-syntax +[spell-check]: https://github.com/atom/spell-check +[status-bar]: https://github.com/atom/status-bar +[styleguide]: https://github.com/atom/styleguide +[symbols-view]: https://github.com/atom/symbols-view +[tabs]: https://github.com/atom/tabs +[timecop]: https://github.com/atom/timecop +[tree-view]: https://github.com/atom/tree-view +[update-package-dependencies]: https://github.com/atom/update-package-dependencies +[welcome]: https://github.com/atom/welcome +[whitespace]: https://github.com/atom/whitespace +[wrap-guide]: https://github.com/atom/wrap-guide diff --git a/exports/atom.js b/exports/atom.js index d7ca55909..359f0174e 100644 --- a/exports/atom.js +++ b/exports/atom.js @@ -1,13 +1,12 @@ -/** @babel */ - -import TextBuffer, {Point, Range} from 'text-buffer' -import {File, Directory} from 'pathwatcher' -import {Emitter, Disposable, CompositeDisposable} from 'event-kit' -import BufferedNodeProcess from '../src/buffered-node-process' -import BufferedProcess from '../src/buffered-process' -import GitRepository from '../src/git-repository' -import Notification from '../src/notification' -import {watchPath} from '../src/path-watcher' +const TextBuffer = require('text-buffer') +const {Point, Range} = TextBuffer +const {File, Directory} = require('pathwatcher') +const {Emitter, Disposable, CompositeDisposable} = require('event-kit') +const BufferedNodeProcess = require('../src/buffered-node-process') +const BufferedProcess = require('../src/buffered-process') +const GitRepository = require('../src/git-repository') +const Notification = require('../src/notification') +const {watchPath} = require('../src/path-watcher') const atomExport = { BufferedNodeProcess, @@ -42,4 +41,4 @@ if (process.type === 'renderer') { atomExport.TextEditor = require('../src/text-editor') } -export default atomExport +module.exports = atomExport diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index 6d576f102..ec73bc8c2 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -79,8 +79,8 @@ 'ctrl-shift-tab ^ctrl': 'pane:move-active-item-to-top-of-stack' 'cmd-=': 'window:increase-font-size' 'cmd-+': 'window:increase-font-size' - 'cmd--': 'window:decrease-font-size' 'cmd-_': 'window:decrease-font-size' + 'cmd--': 'window:decrease-font-size' 'cmd-0': 'window:reset-font-size' 'cmd-k up': 'pane:split-up-and-copy-active-item' # Atom Specific diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..f4b1aeb49 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5997 @@ +{ + "name": "atom", + "version": "1.31.0-dev", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@atom/nsfw": { + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/@atom/nsfw/-/nsfw-1.0.18.tgz", + "integrity": "sha512-YceKV9a3X62mh4Q78Nyi8aTRaoVGdpeJBHogL8gxU17iBhEpYvxGgMfTe02j1hH2taFT4barkZ5RdZkGKIsJ/w==", + "requires": { + "fs-extra": "^0.26.5", + "lodash.isinteger": "^4.0.4", + "lodash.isundefined": "^3.0.1", + "nan": "^2.0.0", + "promisify-node": "^0.3.0" + } + }, + "@atom/source-map-support": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@atom/source-map-support/-/source-map-support-0.3.4.tgz", + "integrity": "sha1-Vcy+DmSyx0LFszPzV/mpMWEUXP0=", + "requires": { + "source-map": "0.1.32" + } + }, + "@atom/temp": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@atom/temp/-/temp-0.8.4.tgz", + "integrity": "sha1-RVFIywz2ygNI5xpc+ZiGq8rERek=", + "requires": { + "os-tmpdir": "^1.0.0", + "rimraf": "~2.6.2" + } + }, + "@atom/watcher": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@atom/watcher/-/watcher-1.0.8.tgz", + "integrity": "sha512-53un+vGSaY7Fsbvmg8gerYOA3ISipMWR3qiYR9hZWqSfvFsksXJfGrmFsfbBj3uqGRQ3gPTi6wpxcFSWjbWVFQ==", + "requires": { + "event-kit": "^2.5.0", + "fs-extra": "^6.0.0", + "nan": "^2.10.0", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "CSSselect": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/CSSselect/-/CSSselect-0.4.1.tgz", + "integrity": "sha1-+Kt+H4QYzmPNput713ioXX7EkrI=", + "requires": { + "CSSwhat": "0.4", + "domutils": "1.4" + } + }, + "CSSwhat": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/CSSwhat/-/CSSwhat-0.4.7.tgz", + "integrity": "sha1-hn2g/zn3eGEyQsRM/qg/CqTr35s=" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "about": { + "version": "file:packages/about", + "requires": { + "etch": "0.9.0", + "semver": "^5.5.0" + }, + "dependencies": { + "etch": { + "version": "0.9.0", + "bundled": true + }, + "semver": { + "version": "5.5.1", + "bundled": true + } + } + }, + "acorn": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==" + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "alter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", + "requires": { + "stable": "~0.1.3" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "apparatus": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/apparatus/-/apparatus-0.0.10.tgz", + "integrity": "sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==", + "requires": { + "sylvester": ">= 0.0.8" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archive-view": { + "version": "https://www.atom.io/api/packages/archive-view/versions/0.65.1/tarball", + "integrity": "sha512-/rTgY/88lVONL0JNwygl+42eCdK+h2xrGWTb2kNBk2gnL/OQZfOXKaxxgOv2Wde7Kz0gOiAyZKYwxv+PIUOoaA==", + "requires": { + "etch": "0.9.0", + "fs-plus": "^3.0.0", + "humanize-plus": "~1.8.2", + "ls-archive": "1.3.1", + "temp": "~0.8.1" + }, + "dependencies": { + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + } + } + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, + "ast-traverse": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz", + "integrity": "sha1-ac8rg4bxnc2hux4F1o/jWdiJfeY=" + }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" + }, + "async": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz", + "integrity": "sha1-rT83PZJJrjJIgVZVgryQ4VKrvWg=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atom-babel6-transpiler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/atom-babel6-transpiler/-/atom-babel6-transpiler-1.2.0.tgz", + "integrity": "sha512-lZucrjVyRtPAPPJxvICCEBsAC1qn48wUHaIlieriWCXTXLqtLC2PvkQU7vNvU2w1eZ7tw9m0lojZ8PbpVyWTvg==", + "requires": { + "babel-core": "6.x" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "atom-dark-syntax": { + "version": "https://www.atom.io/api/packages/atom-dark-syntax/versions/0.29.0/tarball", + "integrity": "sha512-UNnRiQHU4nZNRRFzZcdPmzJqIsimQuIFzMXtpCcxLzgLNzOqDKYHFGmlrhXsDLHwgw9gmeKKkF+EIRCodEV1PQ==" + }, + "atom-dark-ui": { + "version": "https://www.atom.io/api/packages/atom-dark-ui/versions/0.53.2/tarball", + "integrity": "sha512-gIyeJvHEG+d8vIApvmehnDvBkVJf53TonBq8J7MbjZMW9eB0zTlDTPE+L1lXfntKr9v/OqwFSQ7IxrfZonFErw==" + }, + "atom-grammar-test": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/atom-grammar-test/-/atom-grammar-test-0.6.4.tgz", + "integrity": "sha1-2KU1A9H+k5mX9Ji3SirDEARKfU4=", + "requires": { + "chevrotain": "^0.18.0", + "escape-string-regexp": "^1.0.5" + } + }, + "atom-keymap": { + "version": "8.2.10", + "resolved": "https://registry.npmjs.org/atom-keymap/-/atom-keymap-8.2.10.tgz", + "integrity": "sha512-OGdBlLyxQxHNXD4/H0OyaN8/Sfq40MvXf9YcdzME/XycM8ZVCj3ZYEVN5OU1R9Kmz/vfBWjLM6E+/l2sYSWqhQ==", + "requires": { + "clear-cut": "^2", + "emissary": "^1.1.0", + "event-kit": "^1.0.0", + "fs-plus": "^3.0.0", + "grim": "^1.2.1", + "keyboard-layout": "2.0.13", + "pathwatcher": "^8.0.0", + "property-accessors": "^1", + "season": "^6.0.2" + }, + "dependencies": { + "event-kit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz", + "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=", + "requires": { + "grim": "^1.2.1" + } + } + } + }, + "atom-light-syntax": { + "version": "https://www.atom.io/api/packages/atom-light-syntax/versions/0.29.0/tarball", + "integrity": "sha512-xbpub1gyn9MwyRSUcN0zH94Mg2u3nzstRnk/yskDxxnBhr1MG/PY31TQLFAn82QmL9BRBTpLprjVRB07co6tGQ==" + }, + "atom-light-ui": { + "version": "https://www.atom.io/api/packages/atom-light-ui/versions/0.46.2/tarball", + "integrity": "sha512-6pRlZODnvQTr7u9NSI2w6LtfdmTV5XRhFg0pKADGmfmsK+B+zr90z4HSyCCSxIuXZNZp9OiC+dke0pSDmgOqSg==" + }, + "atom-pathspec": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/atom-pathspec/-/atom-pathspec-0.0.0.tgz", + "integrity": "sha512-7UMEHdTtBV5sJONT0uMeQ6M8JFdfMQy/14rxuP6OuoFfSiDjxyZHuorIbv8gqhRB3FQMMLPzqONoFJE2cpHiCg==" + }, + "atom-select-list": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/atom-select-list/-/atom-select-list-0.7.2.tgz", + "integrity": "sha512-a707OB1DhLGjzqtFrtMQKH7BBxFuCh8UBoUWxgFOrLrSwVh3g+/TlVPVDOz12+U0mDu3mIrnYLqQyhywQOTxhw==", + "requires": { + "etch": "^0.12.6", + "fuzzaldrin": "^2.1.0" + } + }, + "atom-slick": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atom-slick/-/atom-slick-2.0.0.tgz", + "integrity": "sha1-/w2+Fb4sTtomi50w124lF+C308o=" + }, + "atom-ui": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/atom-ui/-/atom-ui-0.4.1.tgz", + "integrity": "sha1-cNl3ZsukcW15jpSWKq0HkghB6lw=" + }, + "autocomplete-atom-api": { + "version": "https://www.atom.io/api/packages/autocomplete-atom-api/versions/0.10.7/tarball", + "integrity": "sha512-027xza+IwcoAut6ryUQYJGXkIOJkFVAA2mRzmOX5DdADSrifXDn3BZtPjfRpMMvqstC8H+xuxNs0dOdUYhssqw==" + }, + "autocomplete-css": { + "version": "https://www.atom.io/api/packages/autocomplete-css/versions/0.17.5/tarball", + "integrity": "sha512-iFsTHwAzESHV3p9HD23WnlZA69G8f5x3rvY6BmorrOMqPodx/6xBK1cq81SDGtlHgJ9hmwpc1DAtinpFy3qEOQ==" + }, + "autocomplete-html": { + "version": "https://www.atom.io/api/packages/autocomplete-html/versions/0.8.4/tarball", + "integrity": "sha512-K9bNynlYkqNfU0qLO33hMt0AQPa/ARTqkhapeyp04sq5xYc1OS3THwaLBDM6EsYKVsPPDYLMIkrLzeTcaqiFTA==" + }, + "autocomplete-plus": { + "version": "https://www.atom.io/api/packages/autocomplete-plus/versions/2.40.7/tarball", + "integrity": "sha512-Xp22SiKTv/hXwoDPwvfzg5GAaePvDwTY3zQMhPWq41v7PbqdHwLivbKYlNZKFFziDVPYt1jch1svc2Wf7Cqokw==", + "requires": { + "atom-slick": "^2.0.0", + "fuzzaldrin": "^2.1.0", + "fuzzaldrin-plus": "^0.6.0", + "grim": "^2.0.1", + "marked": "^0.3.17", + "minimatch": "^3.0.3", + "selector-kit": "^0.1", + "stable": "^0.1.5", + "underscore-plus": "^1.6.6" + }, + "dependencies": { + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "autocomplete-snippets": { + "version": "https://www.atom.io/api/packages/autocomplete-snippets/versions/1.12.0/tarball", + "integrity": "sha512-13AruoBEEXQu4J0a58Uz1bskgJuf1ZdYEFoEmUL6m/ojYThDYVwZnlYP0fMmwQNFp1kN3NIYloyhy8YBUfDgsQ==" + }, + "autoflow": { + "version": "https://www.atom.io/api/packages/autoflow/versions/0.29.4/tarball", + "integrity": "sha512-KG6nOam4TBOmpg3C5vid0WRVw/f+z+NIwKflFUgozCs67h7bkwm7JQ9FOfs0kRjcZ8atreJjw9zu67/cqlINVg==", + "requires": { + "underscore-plus": "^1.6.6" + } + }, + "autosave": { + "version": "https://www.atom.io/api/packages/autosave/versions/0.24.6/tarball", + "integrity": "sha512-RsKEDXkjLTMXuTi5AN/Y78kMBJUypYZvLHtyc3G6pK1wTJY0hmOmndBWQK9gvP3sECL/KfutMOQtP1oibHKv6Q==", + "requires": { + "fs-plus": "^3.0.0" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", + "optional": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + } + } + }, + "babel-core": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-5.8.38.tgz", + "integrity": "sha1-H8ruedfmG3ULALjlT238nQr4ZVg=", + "requires": { + "babel-plugin-constant-folding": "^1.0.1", + "babel-plugin-dead-code-elimination": "^1.0.2", + "babel-plugin-eval": "^1.0.1", + "babel-plugin-inline-environment-variables": "^1.0.1", + "babel-plugin-jscript": "^1.0.4", + "babel-plugin-member-expression-literals": "^1.0.1", + "babel-plugin-property-literals": "^1.0.1", + "babel-plugin-proto-to-assign": "^1.0.3", + "babel-plugin-react-constant-elements": "^1.0.3", + "babel-plugin-react-display-name": "^1.0.3", + "babel-plugin-remove-console": "^1.0.1", + "babel-plugin-remove-debugger": "^1.0.1", + "babel-plugin-runtime": "^1.0.7", + "babel-plugin-undeclared-variables-check": "^1.0.2", + "babel-plugin-undefined-to-void": "^1.1.6", + "babylon": "^5.8.38", + "bluebird": "^2.9.33", + "chalk": "^1.0.0", + "convert-source-map": "^1.1.0", + "core-js": "^1.0.0", + "debug": "^2.1.1", + "detect-indent": "^3.0.0", + "esutils": "^2.0.0", + "fs-readdir-recursive": "^0.1.0", + "globals": "^6.4.0", + "home-or-tmp": "^1.0.0", + "is-integer": "^1.0.4", + "js-tokens": "1.0.1", + "json5": "^0.4.0", + "lodash": "^3.10.0", + "minimatch": "^2.0.3", + "output-file-sync": "^1.1.0", + "path-exists": "^1.0.0", + "path-is-absolute": "^1.0.0", + "private": "^0.1.6", + "regenerator": "0.8.40", + "regexpu": "^1.3.0", + "repeating": "^1.1.2", + "resolve": "^1.1.6", + "shebang-regex": "^1.0.0", + "slash": "^1.0.0", + "source-map": "^0.5.0", + "source-map-support": "^0.2.10", + "to-fast-properties": "^1.0.0", + "trim-right": "^1.0.0", + "try-resolve": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "requires": { + "repeating": "^2.0.0" + } + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "esutils": "^2.0.2" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-chai-assert-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-chai-assert-async/-/babel-plugin-chai-assert-async-0.1.0.tgz", + "integrity": "sha512-7z/7GQpUtVRjSFnM6qQ46gJyuprYW5bQHW47Ofia9ge2w6LEYkjo2m80SpTQqMdWYxi/I/Q8gN6nXNR0k36C0w==" + }, + "babel-plugin-constant-folding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz", + "integrity": "sha1-g2HTZMmORJw2kr26Ue/whEKQqo4=" + }, + "babel-plugin-dead-code-elimination": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz", + "integrity": "sha1-X3xFEnTc18zNv7s+C4XdKBIfD2U=" + }, + "babel-plugin-eval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz", + "integrity": "sha1-ovrtJc5r5preS/7CY/cBaRlZUNo=" + }, + "babel-plugin-inline-environment-variables": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz", + "integrity": "sha1-H1jOkSB61qgmqL9kX6/mj/X+P/4=" + }, + "babel-plugin-jscript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz", + "integrity": "sha1-jzQsOCduh6R9X6CovT1etsytj8w=" + }, + "babel-plugin-member-expression-literals": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz", + "integrity": "sha1-zF7bD6qNyScXDnTW0cAkQAIWJNM=" + }, + "babel-plugin-property-literals": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz", + "integrity": "sha1-AlIwGQAZKYCxwRjv6kjOk6q4MzY=" + }, + "babel-plugin-proto-to-assign": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz", + "integrity": "sha1-xJ56/QL1d7xNoF6i3wAiUM980SM=", + "requires": { + "lodash": "^3.9.3" + } + }, + "babel-plugin-react-constant-elements": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz", + "integrity": "sha1-lGc26DeEKcvDSdz/YvUcFDs041o=" + }, + "babel-plugin-react-display-name": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz", + "integrity": "sha1-dU/jiSboQkpOexWrbqYTne4FFPw=" + }, + "babel-plugin-relay": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-relay/-/babel-plugin-relay-1.6.0.tgz", + "integrity": "sha512-C7ylRxOCw04pXMxuGZ16mBBol36VQTMdbnWolB9YdQWIygf0nEQuNUS8OW/IQxTaHj7Q87uJ94POXCNFDnuT0w==", + "requires": { + "babel-runtime": "^6.23.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-remove-console": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz", + "integrity": "sha1-2PJFVsOgUAXUKqqv0neH9T/wE6c=" + }, + "babel-plugin-remove-debugger": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz", + "integrity": "sha1-/S6jzWGkKK0fO5yJiC/0KT6MFMc=" + }, + "babel-plugin-runtime": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz", + "integrity": "sha1-v3x9lm3Vbs1cF/ocslPJrLflSq8=" + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=" + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=" + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=" + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "requires": { + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-react-display-name": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", + "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", + "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "requires": { + "babel-helper-builder-react-jsx": "^6.24.1", + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-self": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", + "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-react-jsx-source": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", + "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "requires": { + "babel-plugin-syntax-jsx": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-undeclared-variables-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz", + "integrity": "sha1-XPGqU52BP/ZOmWQSkK9iCWX2Xe4=", + "requires": { + "leven": "^1.0.2" + } + }, + "babel-plugin-undefined-to-void": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz", + "integrity": "sha1-f1eO+LeN+uYAM4XYQXph7aBuL4E=" + }, + "babel-preset-flow": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", + "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", + "requires": { + "babel-plugin-transform-flow-strip-types": "^6.22.0" + } + }, + "babel-preset-react": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", + "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", + "requires": { + "babel-plugin-syntax-jsx": "^6.3.13", + "babel-plugin-transform-react-display-name": "^6.23.0", + "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-plugin-transform-react-jsx-self": "^6.22.0", + "babel-plugin-transform-react-jsx-source": "^6.22.0", + "babel-preset-flow": "^6.23.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "requires": { + "source-map": "^0.5.6" + } + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + } + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + } + } + }, + "babylon": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-5.8.38.tgz", + "integrity": "sha1-7JsSCxG/bM1Bc6GL8hfmC3mFn/0=" + }, + "background-tips": { + "version": "https://www.atom.io/api/packages/background-tips/versions/0.28.0/tarball", + "integrity": "sha512-mEEkeL6bY6ZSPl7WCHjhJ4KjVUU9UElHb4CB4MhnW4b4mRTHaWR7rnnCVq312wRZ9cwjdvd/5OTXXbD2AQyfYw==", + "requires": { + "underscore-plus": "1.x" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base16-tomorrow-dark-theme": { + "version": "https://www.atom.io/api/packages/base16-tomorrow-dark-theme/versions/1.5.0/tarball", + "integrity": "sha512-rh/D3qYiV1v9JWBaZAMHOBq6y2pq+Iw0FzqUhKvqRLFSoRz4admNx/G/d4O/PxttaZ/cvhgV2/hKTObQMVaOQg==" + }, + "base16-tomorrow-light-theme": { + "version": "https://www.atom.io/api/packages/base16-tomorrow-light-theme/versions/1.5.0/tarball", + "integrity": "sha512-eIVJ1CL38Rha8N4rs8evOyofUiKWBuLcd5OvQg3bmSIDsIU4SBbS3kYwfPjQsGt2rfZpK4MzjLWcUyRxOPQ8YQ==" + }, + "batch-processor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", + "integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-search": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.4.tgz", + "integrity": "sha512-dPxU/vZLnH0tEVjVPgi015oSwqu6oLfCeHywuFRhBE0yM0mYocvleTl8qsdM1YFhRzTRhM1+VzS8XLDVrHPopg==" + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" + }, + "bookmarks": { + "version": "https://www.atom.io/api/packages/bookmarks/versions/0.45.1/tarball", + "integrity": "sha512-BcKY7ujoJoQ6x0C0CPeV4e9cw0cqwjlKvCVh3T6XeDwZQ1Na8KMrjPHWp8dO7B1cnGFN1LCWWQGWqGH624Ymkw==", + "requires": { + "atom-select-list": "^0.7.0" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.x.x" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "bracket-matcher": { + "version": "https://www.atom.io/api/packages/bracket-matcher/versions/0.89.2/tarball", + "integrity": "sha512-WW3ZdNhb2sioFXk+iGbsQb+mBubjY9/ME7hjkqfl4oOEHr3SqdzFFMdUau6GVr2eeWyROm7B1p0t4aUUwNY+Sw==", + "requires": { + "first-mate": "^7.0.1", + "underscore-plus": "1.x" + } + }, + "breakable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz", + "integrity": "sha1-eEp5eRWjjq0nutRWtVcstLuqeME=" + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cached-run-in-this-context": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cached-run-in-this-context/-/cached-run-in-this-context-0.4.1.tgz", + "integrity": "sha1-CMFYHi2cP2aba92poQCtYdqlRfM=", + "requires": { + "nan": "^2.1.0" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "requires": { + "assertion-error": "^1.0.1", + "deep-eql": "^0.1.3", + "type-detect": "^1.0.0" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, + "chart.js": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.2.tgz", + "integrity": "sha512-90wl3V9xRZ8tnMvMlpcW+0Yg13BelsGS9P9t0ClaDxv/hdypHDr/YAGf+728m11P5ljwyB0ZHfPKCapZFqSqYA==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", + "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", + "requires": { + "chartjs-color-string": "^0.5.0", + "color-convert": "^0.5.3" + } + }, + "chartjs-color-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", + "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", + "requires": { + "color-name": "^1.0.0" + } + }, + "checksum": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/checksum/-/checksum-0.1.1.tgz", + "integrity": "sha1-3GUn1MkL6FYNvR7Uzs8yl9Uo6ek=", + "requires": { + "optimist": "~0.3.5" + }, + "dependencies": { + "optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "requires": { + "wordwrap": "~0.0.2" + } + } + } + }, + "cheerio": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.15.0.tgz", + "integrity": "sha1-h3XsOrFvTGYZW5zGeX4MgrUeazQ=", + "requires": { + "CSSselect": "~0.4.0", + "entities": "~1.0.0", + "htmlparser2": "~3.7.0", + "lodash": "~2.4.1" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + } + } + }, + "chevrotain": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-0.18.0.tgz", + "integrity": "sha1-sodxTjFZC64sXR4vYRZz7+xHnYA=" + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + }, + "circular-json": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", + "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==" + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "clear-cut": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clear-cut/-/clear-cut-2.0.2.tgz", + "integrity": "sha1-CC2zLsqkSjWKewhoUv4dVIC77tE=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==" + }, + "coffeestack": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/coffeestack/-/coffeestack-1.1.2.tgz", + "integrity": "sha1-NSePO+uc5vXQraH7bgh4UrZXzpg=", + "requires": { + "coffee-script": "~1.8.0", + "fs-plus": "^2.5.0", + "source-map": "~0.1.43" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "coffee-script": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.8.0.tgz", + "integrity": "sha1-nJ8dK0pSoADe0Vtll5FwNkgmPB0=", + "requires": { + "mkdirp": "~0.3.5" + } + }, + "fs-plus": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-2.10.1.tgz", + "integrity": "sha1-MgR4HXhAYR5jZOe2+wWMljJ8WqU=", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "color": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/color/-/color-0.7.3.tgz", + "integrity": "sha1-qzrkvGy4z62110nEDzSuoIgQT4k=", + "requires": { + "color-convert": "0.5.x", + "color-string": "0.2.x" + } + }, + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.2.4.tgz", + "integrity": "sha1-Ih/2QjT3Gqo+E7yMfoyV883Y+Bo=", + "requires": { + "color-name": "1.0.x" + }, + "dependencies": { + "color-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.0.1.tgz", + "integrity": "sha1-azSyspt3FgE5crC51b7c+7Zxjfg=" + } + } + }, + "colors": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "command-palette": { + "version": "https://www.atom.io/api/packages/command-palette/versions/0.43.5/tarball", + "integrity": "sha512-aEs5dLDyXmdoXP2EjUJoZ3lJCpvbzCg45+GehXquccHzxip1JQCZA67NTSf/ePAWncin+kvqJMm0uoZ37rgrtg==", + "requires": { + "atom-select-list": "^0.7.1", + "fuzzaldrin": "^2.1.0", + "fuzzaldrin-plus": "^0.6.0", + "underscore-plus": "^1.0.0" + } + }, + "commander": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", + "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" + }, + "commoner": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", + "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", + "requires": { + "commander": "^2.5.0", + "detective": "^4.3.1", + "glob": "^5.0.15", + "graceful-fs": "^4.1.2", + "iconv-lite": "^0.4.5", + "mkdirp": "^0.5.0", + "private": "^0.1.6", + "q": "^1.1.2", + "recast": "^0.11.17" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "requires": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "compare-sets": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/compare-sets/-/compare-sets-1.0.1.tgz", + "integrity": "sha1-me1EydezCN54Uv8RFJcr1Poj5yc=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "optional": true, + "requires": { + "boom": "2.x.x" + } + }, + "cson-parser": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.3.5.tgz", + "integrity": "sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ=", + "requires": { + "coffee-script": "^1.10.0" + } + }, + "ctags": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ctags/-/ctags-3.0.0.tgz", + "integrity": "sha1-sLckF9B0ZRcqiZ0C9y8/wnP4KjY=", + "requires": { + "event-stream": "~3.1.0", + "nan": "^2" + } + }, + "d": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", + "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", + "requires": { + "es5-ext": "~0.10.2" + } + }, + "dalek": { + "version": "https://www.atom.io/api/packages/dalek/versions/0.2.2/tarball", + "integrity": "sha512-7p7KFZFakk54JTKiP/gsXaEMAcsO30Y5dT3lrHZG3jeAMg201YSq5ayAd735i79S/RbpH32X5DaE6XC3Qf9BSw==", + "requires": { + "grim": "^2.0.1" + }, + "dependencies": { + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=" + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "defs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.1.tgz", + "integrity": "sha1-siYJ8sehG6ej2xFoBcE5scr/qdI=", + "requires": { + "alter": "~0.2.0", + "ast-traverse": "~0.1.1", + "breakable": "~1.0.0", + "esprima-fb": "~15001.1001.0-dev-harmony-fb", + "simple-fmt": "~0.1.0", + "simple-is": "~0.2.0", + "stringmap": "~0.2.2", + "stringset": "~0.2.1", + "tryor": "~0.1.2", + "yargs": "~3.27.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "yargs": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.27.0.tgz", + "integrity": "sha1-ISBUaTFuk5Ex1Z8toMbX+YIh6kA=", + "requires": { + "camelcase": "^1.2.1", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "os-locale": "^1.4.0", + "window-size": "^0.1.2", + "y18n": "^3.2.0" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "delegato": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegato/-/delegato-1.0.0.tgz", + "integrity": "sha1-xzJK2/Mfo9ltH9YL82jF/MomlRA=", + "requires": { + "mixto": "1.x" + } + }, + "deprecation-cop": { + "version": "https://www.atom.io/api/packages/deprecation-cop/versions/0.56.9/tarball", + "integrity": "sha512-dTKNhWcDgK6Y5cR8dwZ507QW15lob+Lp//P71wXoTVidXboDqH13Y1yQ7Av5qASscv7fqp5GcvLhQWF55W5yng==", + "requires": { + "etch": "0.9.0", + "fs-plus": "^3.0.0", + "grim": "^2.0.1", + "marked": "^0.3.6", + "underscore-plus": "^1.0.0" + }, + "dependencies": { + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + }, + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "detect-indent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz", + "integrity": "sha1-ncXl3bzu+DJXZLlFGwK8bVQIT3U=", + "requires": { + "get-stdin": "^4.0.1", + "minimist": "^1.1.0", + "repeating": "^1.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "requires": { + "acorn": "^5.2.1", + "defined": "^1.0.0" + } + }, + "dev-live-reload": { + "version": "https://www.atom.io/api/packages/dev-live-reload/versions/0.48.1/tarball", + "integrity": "sha512-YSOLkdz7d/pETiG3raCzRKFmv64aErVC2d0cwDi7SLGtJyIGoR9+0OHMZjl8kcXCrX+u1s7awD8HhTDfZ56+iw==", + "requires": { + "fs-plus": "^3.0.0" + } + }, + "devtron": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/devtron/-/devtron-1.3.0.tgz", + "integrity": "sha1-aaHFk5tmlrMSB1TMYMmAc9JNNic=", + "requires": { + "highlight.js": "^9.3.0", + "humanize-plus": "^1.8.1" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" + } + } + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" + }, + "domhandler": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz", + "integrity": "sha1-Wd+dzSJ+gIs2Wuc+H2aErD2Ub8I=", + "requires": { + "domelementtype": "1" + } + }, + "dompurify": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.7.tgz", + "integrity": "sha512-1xK0JEda/jvIm3SgqHXKvRCh3AbEKCyBbUAGpNCMVIljBD145cPvBR66JSj3O4SdscFUx5NXsDkJpz6vDT8KLg==" + }, + "domutils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", + "integrity": "sha1-CGVRN5bGswYDGFDhdVFrr4C3Km8=", + "requires": { + "domelementtype": "1" + } + }, + "dugite": { + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/dugite/-/dugite-1.72.0.tgz", + "integrity": "sha512-HO7T9f+5/uMwFPauqKMhSg7kzI+MtF2fBDMYNSdWPAayGDBolK/7NI6R8JDoP3akPeUgRkoHGRBLkBZHwtTu0g==", + "requires": { + "checksum": "^0.1.1", + "mkdirp": "^0.5.1", + "progress": "^2.0.0", + "request": "^2.86.0", + "rimraf": "^2.5.4", + "tar": "^4.0.2" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "element-resize-detector": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.1.14.tgz", + "integrity": "sha1-rwZKCmGKggrVcKlcXuxbd74BKME=", + "requires": { + "batch-processor": "^1.0.0" + } + }, + "emissary": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/emissary/-/emissary-1.3.3.tgz", + "integrity": "sha1-phjZLWgrIy0xER3DYlpd9mF5lgY=", + "requires": { + "es6-weak-map": "^0.1.2", + "mixto": "1.x", + "property-accessors": "^1.1", + "underscore-plus": "1.x" + } + }, + "emoji-images": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/emoji-images/-/emoji-images-0.0.2.tgz", + "integrity": "sha1-SJDwkf6rLldUWNINLp74hnBg5BU=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "encoding-selector": { + "version": "https://www.atom.io/api/packages/encoding-selector/versions/0.23.9/tarball", + "integrity": "sha512-gR6sTS2/yyrGolNG9pTG8H7XviOzfzoI6NO//qAm2wyEVipbOWZIi2P+CW6Mh21+MTpXO8cvEMniXXtxghC4BA==", + "requires": { + "atom-select-list": "^0.7.0", + "iconv-lite": "^0.4.4", + "jschardet": "^1.1.0" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es5-ext": { + "version": "0.10.45", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + }, + "dependencies": { + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-iterator": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz", + "integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.5", + "es6-symbol": "~2.0.1" + } + }, + "es6-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz", + "integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.5" + } + }, + "es6-weak-map": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz", + "integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.6", + "es6-iterator": "~0.1.3", + "es6-symbol": "~2.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", + "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "etch": { + "version": "0.12.8", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.12.8.tgz", + "integrity": "sha1-wkvJvTphSPYiBM6GQ9Lombnsud4=" + }, + "event-kit": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.5.0.tgz", + "integrity": "sha512-tUDxeNC9JzN2Tw/f8mLtksY34v1hHmaR7lV7X4p04XSjaeUhFMfzjF6Nwov9e0EKGEx63BaKcgXKxjpQaPo0wg==" + }, + "event-stream": { + "version": "3.1.7", + "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.1.7.tgz", + "integrity": "sha1-tMVAAS0P4UmEIPPYlGAI22OTw3o=", + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.2", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + }, + "dependencies": { + "split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", + "requires": { + "through": "2" + } + } + } + }, + "exception-reporting": { + "version": "https://www.atom.io/api/packages/exception-reporting/versions/0.43.1/tarball", + "integrity": "sha512-IYDPs9MNXcbKJv+G/WH6FqkbikiaP9VBskWatMjCj6ca7aey0bbK/qEQwaMogzEGwh9C+n7f+4fAarpgWNKpsw==", + "requires": { + "fs-plus": "^3.0.0", + "node-uuid": "~1.4.7", + "stack-trace": "0.0.9", + "underscore-plus": "1.x" + } + }, + "expand-template": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", + "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==" + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "optional": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "fileset": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.1.8.tgz", + "integrity": "sha1-UGuRqTluqn4y+0KoQHfHoMc2t0E=", + "requires": { + "glob": "3.x", + "minimatch": "0.x" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + }, + "dependencies": { + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "minimatch": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.4.0.tgz", + "integrity": "sha1-vSx9Bg0sjI/Xzefx8u0tWycP2xs=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "find-and-replace": { + "version": "https://www.atom.io/api/packages/find-and-replace/versions/0.215.12/tarball", + "integrity": "sha512-LASeKxjBD4duvI09jREDZkY9osbJtN97rZn6ko2F3Dr83TxR9NYqTRbea6bX3ZXsSf2e/dkbVx1jjM59Z6yMMw==", + "requires": { + "binary-search": "^1.3.3", + "element-resize-detector": "^1.1.10", + "etch": "0.9.3", + "fs-plus": "^3.0.0", + "temp": "^0.8.3", + "underscore-plus": "1.x" + }, + "dependencies": { + "etch": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.3.tgz", + "integrity": "sha1-2uxSmVv2E1A9a5K0H1Si6qEuMis=" + } + } + }, + "find-parent-dir": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", + "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=" + }, + "first-mate": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/first-mate/-/first-mate-7.1.1.tgz", + "integrity": "sha512-Y3qum/WZt0V3uVaQnuoUqBiNXT8WA156ofqp1WTMjUsbUgrXso10vdRl8HzsFbQSjN06u3Q1r2Duo1RM+RqTAA==", + "requires": { + "emissary": "^1", + "event-kit": "^2.2.0", + "fs-plus": "^3.0.0", + "grim": "^2.0.1", + "oniguruma": "6.2.1", + "season": "^6.0.2", + "underscore-plus": "^1" + }, + "dependencies": { + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=" + }, + "focus-trap": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-2.4.5.tgz", + "integrity": "sha512-jkz7Dh6Pb4ox+z24GhVABDE7lFT19z7KVrpYGH5qqI6KK3Y2IcXhBx844W6ZXYahD+jOEUcGz49dLakXg2sjOQ==", + "requires": { + "tabbable": "^1.0.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "formatio": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", + "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", + "requires": { + "samsam": "~1.1" + } + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=" + }, + "fs-admin": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/fs-admin/-/fs-admin-0.1.6.tgz", + "integrity": "sha512-JHRSPVRBrYggAGM6kpvNvFdxuFmoDxamnBVQT/JApZtVji7bHKbhLOka1Y2pNSQ/OVChbmZFKcWdpwuZEpA65w==", + "requires": { + "mocha": "^3.5.0", + "nan": "^2.6.2" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "mocha": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "he": "1.1.1", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-plus": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-3.0.2.tgz", + "integrity": "sha1-a19Sp3EolMTd6f2PgfqMYN8EHz0=", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "fs-readdir-recursive": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz", + "integrity": "sha1-MVtPuMHKW4xH3v7zGdBz2tNWgFk=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.24.tgz", + "integrity": "sha1-Jn/p0DT0a8mfgkeJ04uYetAb6IQ=", + "requires": { + "graceful-fs": "~2.0.0", + "inherits": "~2.0.0", + "mkdirp": "0.3", + "rimraf": "2" + }, + "dependencies": { + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=" + }, + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + } + } + }, + "fuzzaldrin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", + "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=" + }, + "fuzzaldrin-plus": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.0.tgz", + "integrity": "sha1-gy9kifvodnaUWVmckUpnDsIpR+4=" + }, + "fuzzy-finder": { + "version": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.8.2/tarball", + "integrity": "sha512-wg3uX5fPtVItOByflO+vsQKHAqn3aSgutYM+xO3lEKQov9DuMdtMzMgbf/Tlam0YwjV+Qz/JV10LluJuliP03A==", + "requires": { + "async": "0.2.6", + "atom-select-list": "^0.7.0", + "fs-plus": "^3.0.0", + "fuzzaldrin": "^2.0", + "fuzzaldrin-plus": "^0.6.0", + "humanize-plus": "~1.8.2", + "minimatch": "~3.0.3", + "temp": "~0.8.1", + "underscore-plus": "^1.0.0", + "wrench": "^1.5" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.3.4.tgz", + "integrity": "sha1-X5S92gr+U7xxCWm81vKCVI1gwnk=", + "requires": { + "fileset": "~0.1.5", + "minimatch": "~0.2.9" + }, + "dependencies": { + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "get-parameter-names": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/get-parameter-names/-/get-parameter-names-0.2.0.tgz", + "integrity": "sha1-ohY60JLjUNlL7ilYl0/OzhvFPJk=" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-diff": { + "version": "https://www.atom.io/api/packages/git-diff/versions/1.3.9/tarball", + "integrity": "sha512-BVo2faEzrjXdoesTl7CjVfjQawmCCTd6rzaWwernVDfsMz+xSAH5Yig5ukLYWzav13Tqt2su7pOtPQgp0af4yg==", + "requires": { + "atom-select-list": "^0.7.0", + "fs-plus": "^3.0.0", + "temp": "~0.8.1" + } + }, + "git-utils": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/git-utils/-/git-utils-5.2.1.tgz", + "integrity": "sha512-PrXaX4qb6ti9yU4p15RWeWklHdyEXCEIcdjbm3X5mAWL1VCFpl1hPdxk7T2qcFRNhF7TVXq3giotnJVne+1htA==", + "requires": { + "fs-plus": "^3.0.0", + "nan": "^2.0.0" + } + }, + "github": { + "version": "https://www.atom.io/api/packages/github/versions/0.19.0/tarball", + "integrity": "sha512-JnAQGVCBXAmVW7qIrgw6a41G4MRAPIa81Mx3oFtnlZYC77sFA2vd6eUnWhujHBZpK1q2HmlihVsAhpvDKdkk0A==", + "requires": { + "atom-babel6-transpiler": "1.2.0", + "babel-generator": "6.26.1", + "babel-plugin-chai-assert-async": "0.1.0", + "babel-plugin-relay": "1.6.0", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", + "babel-plugin-transform-object-rest-spread": "6.26.0", + "babel-preset-react": "6.24.1", + "bytes": "^3.0.0", + "classnames": "2.2.6", + "compare-sets": "1.0.1", + "dugite": "^1.66.0", + "event-kit": "2.5.0", + "fs-extra": "4.0.3", + "graphql": "0.13.2", + "keytar": "4.2.1", + "lodash.memoize": "4.1.2", + "moment": "2.22.2", + "node-emoji": "^1.8.1", + "prop-types": "15.6.2", + "react": "16.4.0", + "react-dom": "16.4.0", + "react-relay": "1.6.0", + "react-select": "1.2.1", + "relay-runtime": "1.6.0", + "temp": "0.8.3", + "tinycolor2": "1.4.1", + "tree-kill": "1.2.0", + "what-the-diff": "0.4.0", + "what-the-status": "1.0.3", + "yubikiri": "1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-6.4.1.tgz", + "integrity": "sha1-hJgDKzttHMge68X3lpDY/in6v08=" + }, + "go-to-line": { + "version": "https://www.atom.io/api/packages/go-to-line/versions/0.33.0/tarball", + "integrity": "sha512-YD5zEkGQRTl6jrgAIOQ0Zr0rB/f/yPifxhz4od2kN+JfGVeb76xD9FG5OkR9dr0vEtPfJeDbG2WWTJ1JGMShqQ==" + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "grammar-selector": { + "version": "https://www.atom.io/api/packages/grammar-selector/versions/0.50.1/tarball", + "integrity": "sha512-tXoxzu+RtJffRc6no/KsIyI9YX1qfpuQjmMI3RJ42qPHtGk54RsEpAuFku8oOW4sIrSy5c7suM7iKmh4aUnsZg==", + "requires": { + "atom-select-list": "^0.7.0" + } + }, + "graphql": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.2.tgz", + "integrity": "sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==", + "requires": { + "iterall": "^1.2.1" + } + }, + "grim": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/grim/-/grim-1.5.0.tgz", + "integrity": "sha1-sysI71Z88YUvgXWe2caLDXE5ajI=", + "requires": { + "emissary": "^1.2.0" + } + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "optional": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "highlight.js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", + "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=" + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "home-or-tmp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-1.0.0.tgz", + "integrity": "sha1-S58eQIAMPlDGwn94FnavzOcfOYU=", + "requires": { + "os-tmpdir": "^1.0.1", + "user-home": "^1.1.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "htmlparser2": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.7.3.tgz", + "integrity": "sha1-amTHdjfAjG8w7CqBV6UzM758sF4=", + "requires": { + "domelementtype": "1", + "domhandler": "2.2", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + }, + "dependencies": { + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "humanize-plus": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/humanize-plus/-/humanize-plus-1.8.2.tgz", + "integrity": "sha1-pls0RZrWNnrbs3B6gqPJ+RYWcDA=" + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true + }, + "image-view": { + "version": "https://www.atom.io/api/packages/image-view/versions/0.63.1/tarball", + "integrity": "sha512-KMtreZG1QLdCiCmkoHPKnP54oe2mEXDyoliM0wYTsVvaTOIfXW6Gi1rbQgGClqi+iHHWOBFKuJdmVGj/phbK9Q==", + "requires": { + "bytes": "^2.4.0", + "etch": "0.9.0", + "fs-plus": "^3.0.0", + "underscore-plus": "^1.0.0" + }, + "dependencies": { + "bytes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.5.0.tgz", + "integrity": "sha1-TJQj6i0lLCcMQbK97+/5u2tiwGo=" + }, + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + } + } + }, + "incompatible-packages": { + "version": "https://www.atom.io/api/packages/incompatible-packages/versions/0.27.3/tarball", + "integrity": "sha512-OlkFBSpvHH7dUfYQTlcgTXEa+sjr9Es8d2lNPGPS2O5Rp5MiRKcnovQoMtaF3fkcuV2O7onim45ldFjbl4qdog==", + "requires": { + "etch": "^0.12.2" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-integer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.7.tgz", + "integrity": "sha1-a96Bqs3feLZZtmKdYpytxRqIbVw=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-2.0.4.tgz", + "integrity": "sha1-0jWS5qbwk++4TC5hUgVr4pTkFKE=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "iterall": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } + } + }, + "jasmine-focused": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/jasmine-focused/-/jasmine-focused-1.0.7.tgz", + "integrity": "sha1-uDx1fIAOaOHW78GjoaE/85/23NI=", + "requires": { + "jasmine-node": "git+https://github.com/kevinsawicki/jasmine-node.git#81af4f953a2b7dfb5bde8331c05362a4b464c5ef", + "underscore-plus": "1.x", + "walkdir": "0.0.7" + } + }, + "jasmine-json": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/jasmine-json/-/jasmine-json-0.0.3.tgz", + "integrity": "sha1-Xi6P1QqlhXAOjzWa9pawupZPg4c=" + }, + "jasmine-node": { + "version": "git+https://github.com/kevinsawicki/jasmine-node.git#81af4f953a2b7dfb5bde8331c05362a4b464c5ef", + "from": "jasmine-node@git+https://github.com/kevinsawicki/jasmine-node.git#81af4f953a2b7dfb5bde8331c05362a4b464c5ef", + "requires": { + "coffee-script": ">=1.0.1", + "coffeestack": ">=1 <2", + "gaze": "~0.3.2", + "jasmine-reporters": ">=0.2.0", + "mkdirp": "~0.3.5", + "requirejs": ">=0.27.1", + "underscore": ">= 1.3.1", + "walkdir": ">= 0.0.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + } + } + }, + "jasmine-reporters": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-1.1.0.tgz", + "integrity": "sha1-8zUIhYkMntqtEqCHxi8swZ3PZsA=", + "requires": { + "mkdirp": "~0.3.5" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", + "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" + } + } + }, + "jasmine-tagged": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/jasmine-tagged/-/jasmine-tagged-1.1.4.tgz", + "integrity": "sha1-vLlH2cWYWEolZRr8pXoT7YvvdNc=", + "requires": { + "jasmine-focused": "^1.0.7" + } + }, + "js-base64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.6.tgz", + "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==" + }, + "js-tokens": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.1.tgz", + "integrity": "sha1-zENaXIuUrRWst5gxQPyAGCyJrq4=" + }, + "js-yaml": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", + "integrity": "sha1-bl/mfYsgXOTSL60Ft3geja3MSzA=", + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "jschardet": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.6.0.tgz", + "integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ==" + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "optional": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "json5": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz", + "integrity": "sha1-BUNS5MTIDIbAkjh31EneF2pzLI0=" + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "optional": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "key-path-helpers": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/key-path-helpers/-/key-path-helpers-0.4.0.tgz", + "integrity": "sha1-6H9qFZFErfJpLkbypGQc4mnAkRk=" + }, + "keybinding-resolver": { + "version": "https://www.atom.io/api/packages/keybinding-resolver/versions/0.38.2/tarball", + "integrity": "sha512-8D34ekA01kvIJyFqvwABK9J9FcQCoWaC2hLh99Tkf1h8DhPE6hkPuu6qb1tQucyVa4PYXuOA3jH9myGwJ2gWxw==", + "requires": { + "etch": "0.9.0", + "fs-plus": "^3.0.0", + "temp": "^0.8.1" + }, + "dependencies": { + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + } + } + }, + "keyboard-layout": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.13.tgz", + "integrity": "sha512-WxVc3bBITttHozSyEYPsyr5rN2KQuXtEaXMlQfQjEze1JrkLw30yH/bcNn1IGx48b+tdOdybpnq++JFLU2FaZg==", + "requires": { + "event-kit": "^2.0.0", + "nan": "^2.0.0" + } + }, + "keytar": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.2.1.tgz", + "integrity": "sha1-igamV3/fY3PgqmsRInfmPex3/RI=", + "requires": { + "nan": "2.8.0", + "prebuild-install": "^2.4.1" + }, + "dependencies": { + "nan": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", + "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "language-c": { + "version": "https://www.atom.io/api/packages/language-c/versions/0.60.4/tarball", + "integrity": "sha512-6sgQUlfjGKkLcaMgz4hfRjty+JSYBUjDow7ayBOc+k87cB2UWn0skbvq4bwRZDf0bMUtmrJkDieagQGrsTW8DQ==", + "requires": { + "tree-sitter-c": "^0.13.4", + "tree-sitter-cpp": "^0.13.4" + } + }, + "language-clojure": { + "version": "https://www.atom.io/api/packages/language-clojure/versions/0.22.7/tarball", + "integrity": "sha512-HJfBRKbzOYGlEVQNnnRtx0BgiZ9pHlUioHUtG9pFV65EgO4jm4Q5cPHLDvBj+zEy0cm28eYgEYFjfacyBaZElg==" + }, + "language-coffee-script": { + "version": "https://www.atom.io/api/packages/language-coffee-script/versions/0.49.3/tarball", + "integrity": "sha512-2xXcSiiRySd0qE4lhsOTqMwcKlBVKc/0ru9HHGJSeTdp/On9iNrkcM1+asDYa/QmvppgAILC2/0xUS6+B/cnqQ==" + }, + "language-csharp": { + "version": "https://www.atom.io/api/packages/language-csharp/versions/1.0.4/tarball", + "integrity": "sha512-uq/REKnuS6WBQ/XLG8QARh8JEmp+Ni6c/Von8ZVcf+fJXAECm540eQj8+Afr5V0hpAVSFAevxtEAK4BaVM9zWw==" + }, + "language-css": { + "version": "https://www.atom.io/api/packages/language-css/versions/0.42.11/tarball", + "integrity": "sha512-cvEdst9Q6wa9OLnMZksVBecb/gCPNXnGfKgyqQjHPKKl1WGFhpcPV+pVuOc4OdeF22Q6lH6fDBYv2rNjeioVsA==" + }, + "language-gfm": { + "version": "https://www.atom.io/api/packages/language-gfm/versions/0.90.5/tarball", + "integrity": "sha512-LqDgDy9hlu2yxL6Cobq7LWHZp7RkxWVngZ+Ga0b8aU9tGZvzsEoTm0pa8Z71c+f1UhwStNgPKFH4mHd8G4rb2g==" + }, + "language-git": { + "version": "https://www.atom.io/api/packages/language-git/versions/0.19.1/tarball", + "integrity": "sha512-xvsGO/d3/XsKJmwdAz9VGHo6t7A13VuJeuEoZaoLmvzwkVpFdpJcK8PNwVMPHav+lpNeu73qiXmqS+YIlvLwLQ==" + }, + "language-go": { + "version": "https://www.atom.io/api/packages/language-go/versions/0.46.2/tarball", + "integrity": "sha512-78llnZAhiNaMwshN9dvVaZ4kn5Sgne6CaZnI/pJJEGtWoaQJApNVHWWljpUVyGMBnI5tEIGIZD9ktO6zZXPteg==", + "requires": { + "tree-sitter-go": "^0.13.1" + } + }, + "language-html": { + "version": "https://www.atom.io/api/packages/language-html/versions/0.51.2/tarball", + "integrity": "sha512-LXjFXYQ7WsMeOGpS6sArBkWB+DQqfsch+APrn4W4rDmELdCvtEKwKhp5ZAmi/GMfTz92C0LGIrhgYzCGvsSDgw==", + "requires": { + "atom-grammar-test": "^0.6.3", + "tree-sitter-embedded-template": "^0.13.0", + "tree-sitter-html": "^0.13.1" + } + }, + "language-hyperlink": { + "version": "https://www.atom.io/api/packages/language-hyperlink/versions/0.16.3/tarball", + "integrity": "sha512-IDkh820N85GVgcP0EiU2QceAcmRHyYQCzJkaG7eSwWmOxvf5e+bO9g2U28sED14hQjH+No4MRfU5+grEmAnvuw==" + }, + "language-java": { + "version": "https://www.atom.io/api/packages/language-java/versions/0.30.0/tarball", + "integrity": "sha512-x0AmmqK0Q3YWUH7XhIvIiqY+0E54mj3y4pXP5QjdDpkdDE3D9eZno/J+DiNmpiP5/l8+MV0Qas/DsZoK/HgulQ==" + }, + "language-javascript": { + "version": "https://www.atom.io/api/packages/language-javascript/versions/0.129.7/tarball", + "integrity": "sha512-HCyeD+T0Q5/WFUUqcSuT61fihdnQhO5UBcLKlw7eJ892VsAOqZceaCrZB2UlLpwtRbUJvJWZEPne4uL8Lipikw==", + "requires": { + "tree-sitter-javascript": "^0.13.5", + "tree-sitter-regex": "^0.13.0" + } + }, + "language-json": { + "version": "https://www.atom.io/api/packages/language-json/versions/0.19.2/tarball", + "integrity": "sha512-iJtZm5+vulzXeXgW4ig+8fGk1okZGCZPqlQwOGwlrQosLPx/h+fAhlZEAr4qt20hn8W9EFw/bUprf48XdJpVoQ==" + }, + "language-less": { + "version": "https://www.atom.io/api/packages/language-less/versions/0.34.2/tarball", + "integrity": "sha512-JEmNAPFFGXI1anCIB0ow96yUQAyQiIv9BfWBEZWxZVe3RrbfejTlg0ly2+ORbJhszFeKuIIQhUxJNzfY3KhXBA==" + }, + "language-make": { + "version": "https://www.atom.io/api/packages/language-make/versions/0.22.3/tarball", + "integrity": "sha512-G0KOZdbmApr253liQPN/4QjAg3Of06P2iJs9qGXStc8zz7dl++ZVcNmIrRCnS3EqmU/3Ui+678f2tK+urBpqtA==" + }, + "language-mustache": { + "version": "https://www.atom.io/api/packages/language-mustache/versions/0.14.5/tarball", + "integrity": "sha512-1aC1OAoYye+krEJ8t5RzXiLYTEA/RJ/Igv1efDsuxvZHnIkdrSDzS/UsssS3snqPkIGyLI+htRvU/v11famx6A==" + }, + "language-objective-c": { + "version": "https://www.atom.io/api/packages/language-objective-c/versions/0.15.1/tarball", + "integrity": "sha512-ZKlTy/xiyb+J7DnHztzM/ss8/rtwbPskSpd+Ox1gKc0k+NpiU7rmzfW6ki9/t/kFHGo1qX7QiImvdCavJ2LsgQ==" + }, + "language-perl": { + "version": "https://www.atom.io/api/packages/language-perl/versions/0.38.1/tarball", + "integrity": "sha512-XXHULyFvbxAiRoj+MxIXoeO//in3bQctHZbaD72p3vFxm3klxe2ebx7b3cFmFYqf/g0eajmLrR3tR5m1Rmz1XQ==" + }, + "language-php": { + "version": "https://www.atom.io/api/packages/language-php/versions/0.44.0/tarball", + "integrity": "sha512-sbspgPSLoe2SefY/tUMvu97MDJCLjretIicR2Rducf0DKWW38NpdHztP12DhKbaITmnix1LGDP/5pGsj20mISw==" + }, + "language-property-list": { + "version": "https://www.atom.io/api/packages/language-property-list/versions/0.9.1/tarball", + "integrity": "sha512-HD6HI41u57i0/Tu9catiriURhJsef0RDrzJDkGDtdFkE9F9KPxC9Fayq2JBLJrhIyADRVXFxwxsfwQ2Jmh6hxg==" + }, + "language-python": { + "version": "https://www.atom.io/api/packages/language-python/versions/0.51.4/tarball", + "integrity": "sha512-jSH0CCk+S3nvvKcDh2lqPP+OHsQYSedCy1JSOYirXVfXErwFMzbSIIZY8y+ChU+AvKDPUR2UOpe4uHPVQD2KDQ==", + "requires": { + "atom-grammar-test": "^0.6.4", + "tree-sitter-python": "^0.13.4" + } + }, + "language-ruby": { + "version": "https://www.atom.io/api/packages/language-ruby/versions/0.72.4/tarball", + "integrity": "sha512-/no4ikKXB7c4tx7ts2qM2rm9tucTHJ8tUb2jFNp9eM1hGNMC99F6XyU8eyfex/CPjP3iWjrRP3x2asU7FP6YeQ==", + "requires": { + "tree-sitter-ruby": "^0.13.5" + } + }, + "language-ruby-on-rails": { + "version": "https://www.atom.io/api/packages/language-ruby-on-rails/versions/0.25.3/tarball", + "integrity": "sha512-uI4ItSsq1J0/5gBblVgLv69C8TzWMcAoL19H8iFuosWWDRUsh9va1PrPMLeSNnNbnOYkw2fE53fqLlJjrgxiGw==" + }, + "language-sass": { + "version": "https://www.atom.io/api/packages/language-sass/versions/0.62.0/tarball", + "integrity": "sha512-qaH8BDNBOkpbR4thmcRimEphnrzzhpDxeQM+WCM3Unp3a8r3aV2xcY9LlvbZxpclz8TOUyvuc5qgj1YI//ge9w==" + }, + "language-shellscript": { + "version": "https://www.atom.io/api/packages/language-shellscript/versions/0.27.4/tarball", + "integrity": "sha512-c/4QgfPLd8+yX0fHMD5F8DIohTFmNfexE0bZ/IH2n4uS6+BIrIAbud+jmFxjSv/Bii7xsLMss0bca1jjXuwO9w==", + "requires": { + "tree-sitter-bash": "^0.13.2" + } + }, + "language-source": { + "version": "https://www.atom.io/api/packages/language-source/versions/0.9.0/tarball", + "integrity": "sha512-Uu/C5EQKdKgwUOiCWM95CkCUePhT93KpiqsrVqEgTV1TssLY/LRwT9fd1XJSZ5EDKSS71Tfzvbww/V117uoDWw==" + }, + "language-sql": { + "version": "https://www.atom.io/api/packages/language-sql/versions/0.25.10/tarball", + "integrity": "sha512-JXlwc9wV0qnhLn2fe3xRSNghxy/MtmCgy5+6xXN3Dqr9f6Q9Jh4vy3Kwrhz4xSgpPcHMocQwS72JcFuTI9CRdw==" + }, + "language-text": { + "version": "https://www.atom.io/api/packages/language-text/versions/0.7.4/tarball", + "integrity": "sha512-XPmROjdb8CvAznbyiDYNeJi0hKZegBA84bAyTSt/FbZR0enexxk+5NDlyjqYsmR7A1P+LtcMJJZdQYPgXr7mdw==" + }, + "language-todo": { + "version": "https://www.atom.io/api/packages/language-todo/versions/0.29.4/tarball", + "integrity": "sha512-mdSeM6hR7D9ZohrfMTA9wDH46MQbcbfTMxU5WpzYwvQXAvYEZyuhc2dzWZ827VsSOrUcOcAYVcOvTkTrx9nytg==" + }, + "language-toml": { + "version": "https://www.atom.io/api/packages/language-toml/versions/0.18.2/tarball", + "integrity": "sha512-r6eUkKAcfMa2Xv41zHILIZacf7TauLQH2D/lWl3CYekN1DcUMPPuyhUHutV/BpWX3wy5ZDXhhtIHFK4zsAyWtA==" + }, + "language-typescript": { + "version": "https://www.atom.io/api/packages/language-typescript/versions/0.4.6/tarball", + "integrity": "sha512-YEda7Z3azcmHVPhSmlUz6jtE/QviBxxyJdLiBU6sf+SEqE5Rb22XW/+pBi8xBPxBVtZ9O7GOtlVMWO9C3MJziw==", + "requires": { + "tree-sitter-typescript": "^0.13.3" + } + }, + "language-xml": { + "version": "https://www.atom.io/api/packages/language-xml/versions/0.35.2/tarball", + "integrity": "sha512-N7Ptj3GRUVYZpE9AuLRglSoKv1eAzcEUCbkzOFfCBEeBGo5dGJJL7LRr2y6HXJYh/V7VdrGyhBbhIeu1WB//Eg==" + }, + "language-yaml": { + "version": "https://www.atom.io/api/packages/language-yaml/versions/0.32.0/tarball", + "integrity": "sha512-kx6Qj//j3PuFaf8yhlfPGdirRJ3NVvLJw+9Oi2Gg998K6vG/XecgvwyP5jVs4xExX8eYMOTlvN7n6dgkPf6LHQ==" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", + "integrity": "sha512-KPdIJKWcEAb02TuJtaLrhue0krtRLoRoo7x6BNJIBelO00t/CCdJQUnHW5V34OnHMWzIktSalJxRO+FvytQlCQ==", + "requires": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.2.11", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "2.81.0", + "source-map": "^0.5.3" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "optional": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "optional": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "optional": true, + "requires": { + "ajv": "^4.9.1", + "har-schema": "^1.0.5" + } + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "optional": true, + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "optional": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "optional": true + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "optional": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "optional": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "optional": true + } + } + }, + "less-cache": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/less-cache/-/less-cache-1.1.0.tgz", + "integrity": "sha1-fi9rOV+lx6l0N0kFyFjy0+nRUyA=", + "requires": { + "fs-plus": "^3.0.0", + "less": "^2.7.1", + "underscore-plus": "1.x", + "walkdir": "0.0.11" + }, + "dependencies": { + "walkdir": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", + "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=" + } + } + }, + "leven": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz", + "integrity": "sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=" + }, + "line-ending-selector": { + "version": "https://www.atom.io/api/packages/line-ending-selector/versions/0.7.7/tarball", + "integrity": "sha512-cep3HJDiR3NI3WCO+j2L7ZGnCr34hPUJmmDZtU21S/ntGO9xaBx0EfmAgtF4W9sX2rKDWZHhVDRF/zEGOXOnmA==", + "requires": { + "atom-select-list": "^0.7.0", + "underscore-plus": "^1.6.6" + } + }, + "line-top-index": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/line-top-index/-/line-top-index-0.3.1.tgz", + "integrity": "sha1-hF9tiLaTmUjzia5t4B0miHJVlu4=", + "requires": { + "random-seed": "^0.2.0" + } + }, + "link": { + "version": "https://www.atom.io/api/packages/link/versions/0.31.4/tarball", + "integrity": "sha512-5GCYLIyLf2/3hiCP6crSDxtTmDEZzWkJfDpfs2MV1nBvgLZ6CUInWbPzS/z3a0eL9+k/uvBHtIQDGj9wckih8w==", + "requires": { + "first-mate": "^7.0.1", + "underscore-plus": "1.x" + } + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "requires": { + "lodash._baseassign": "^3.0.0", + "lodash._basecreate": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" + }, + "log4js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.5.tgz", + "integrity": "sha512-IX5c3G/7fuTtdr0JjOT2OIR12aTESVhsH6cEsijloYwKgcPRlO6DgOU72v0UFhWcoV1HN6+M3dwT89qVPLXm0w==", + "requires": { + "circular-json": "^0.5.5", + "date-format": "^1.2.0", + "debug": "^3.1.0", + "rfdc": "^1.1.2", + "streamroller": "0.7.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "lokijs": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/lokijs/-/lokijs-1.5.5.tgz", + "integrity": "sha1-HCH4KvdXkDf63nueSBNIXCNwi7Y=" + }, + "lolex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", + "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "loophole": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loophole/-/loophole-1.1.0.tgz", + "integrity": "sha1-N5Sf6kU7YlasxyXDIM4MWn9wor0=" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + } + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "ls-archive": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ls-archive/-/ls-archive-1.3.1.tgz", + "integrity": "sha512-qIzKetZtGFQtR+CQj7VI0f8BpctLPmbaJU9NfKUvTB6oxn51cgV53PBfP2KvfI4EbRxlqyQ8XgC8XqyqN4pMgQ==", + "requires": { + "async": "~0.2.9", + "colors": "~0.6.2", + "optimist": "~0.5.2", + "rimraf": "~2.2.6", + "tar": "^2.2.1", + "yauzl": "^2.9.1" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=" + }, + "markdown-preview": { + "version": "https://www.atom.io/api/packages/markdown-preview/versions/0.159.23/tarball", + "integrity": "sha512-Sa/XmtVqUbe918TsYpdUUadSpxu3+IskKkPmStcQhNDf5Y20Sd5cNbWuKYMTvQePTRV/9/lsZdXGOkQx1pHcmw==", + "requires": { + "dompurify": "^1.0.2", + "fs-plus": "^3.0.0", + "roaster": "^1.2.1", + "underscore-plus": "^1.0.0" + } + }, + "marked": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" + }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "requires": { + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" + } + }, + "metrics": { + "version": "https://www.atom.io/api/packages/metrics/versions/1.6.2/tarball", + "integrity": "sha512-UXJtTlpUPUIEJF7tYd5XSKeYzwM3e8kkbbIc6240QFqU418teooVImMTQHDPuE/nTMRZCSXKh3QyRHrtlJaTRQ==", + "requires": { + "fs-plus": "^3.0.0", + "grim": "^2.0.1", + "telemetry-github": "0.0.13" + }, + "dependencies": { + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "optional": true + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "minipass": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.4.tgz", + "integrity": "sha512-mlouk1OHlaUE8Odt1drMtG1bAJA4ZA6B/ehysgV0LUIrDHdKgo1KorZq3pK0b/7Z7LJIQ12MNM6aC+Tn6lUZ5w==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", + "requires": { + "minipass": "^2.2.1" + } + }, + "mixto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mixto/-/mixto-1.0.0.tgz", + "integrity": "sha1-wyDvYbUvKJj1IuF9i7xtUG2EJbY=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.1.tgz", + "integrity": "sha1-qdRqzkDvKfMlgnX/zLiXb2OU6j0=", + "requires": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + }, + "dependencies": { + "commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=" + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=" + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=" + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=" + } + } + }, + "mocha-junit-reporter": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz", + "integrity": "sha1-LlFJ7UD8XS48px5C21qx/snG2Fw=", + "requires": { + "debug": "^2.2.0", + "md5": "^2.1.0", + "mkdirp": "~0.5.1", + "strip-ansi": "^4.0.0", + "xml": "^1.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "mocha-multi-reporters": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz", + "integrity": "sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI=", + "requires": { + "debug": "^3.1.0", + "lodash": "^4.16.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + } + } + }, + "mock-spawn": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/mock-spawn/-/mock-spawn-0.2.6.tgz", + "integrity": "sha1-s5wVocBnUEMQFEFR8sHeNE0Dk38=", + "requires": { + "through": "2.3.x" + } + }, + "moment": { + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multi-integer-range": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multi-integer-range/-/multi-integer-range-2.1.0.tgz", + "integrity": "sha1-c2dVGbohRtuiLNNZYOnF6AT/4vw=" + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "natural": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/natural/-/natural-0.4.0.tgz", + "integrity": "sha1-PraS2Vanb/BfSjeaJ31FUzOQZ2Q=", + "requires": { + "apparatus": ">= 0.0.9", + "log4js": "*", + "sylvester": ">= 0.0.12", + "underscore": ">=1.3.1" + } + }, + "needle": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.2.tgz", + "integrity": "sha512-mW7W8dKuVYefCpNzE3Z7xUmPI9wSrSL/1qH31YGMxmSOAnjatS3S9Zv3cmiHrhx3Jkp1SrWWBdOFXjfF48Uq3A==", + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-abi": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.3.tgz", + "integrity": "sha512-b656V5C0628gOOA2kwcpNA/bxdlqYF9FvxJ+qqVX0ctdXNVZpS8J6xEUYir3WAKc7U0BH/NRlSpNbGsy+azjeg==", + "requires": { + "semver": "^5.4.1" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + } + } + }, + "node-emoji": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.8.1.tgz", + "integrity": "sha512-+ktMAh1Jwas+TnGodfCfjUbJKoANqPaJFN0z0iqh41eqD8dvguNzcitVSBSVK1pidz0AqGbLKcoVuVLRVZ/aVg==", + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-pre-gyp": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "dependencies": { + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" + } + } + }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + }, + "nodegit-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nodegit-promise/-/nodegit-promise-4.0.0.tgz", + "integrity": "sha1-VyKxhPLfcycWEGSnkdLoQskWezQ=", + "requires": { + "asap": "~2.0.3" + } + }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "notifications": { + "version": "https://www.atom.io/api/packages/notifications/versions/0.70.5/tarball", + "integrity": "sha512-Eye5knLSgDXOr4qQNv/mnSp+rfmckK+J3Gok6j+tQuaiIYwCPPmJ2rRjy5t6gaJo81yZRuheBf0ur3TpKTXuUw==", + "requires": { + "dompurify": "^1.0.3", + "fs-plus": "^3.0.0", + "marked": "^0.3.6", + "moment": "^2.19.3", + "semver": "^4.3.2", + "stacktrace-parser": "^0.1.3", + "temp": "^0.8.1" + } + }, + "npm-bundled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", + "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==" + }, + "npm-packlist": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.11.tgz", + "integrity": "sha512-CxKlZ24urLkJk+9kCm48RTQ7L4hsmgSVzEk0TLGPzzyuFxD7VNgy5Sl24tOLMzQv773a/NeJ1ce1DKeacqffEA==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nslog": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/nslog/-/nslog-3.0.0.tgz", + "integrity": "sha1-nvfjpGveHnVyRFQcyhP/K2dvexk=", + "requires": { + "nan": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "one-dark-syntax": { + "version": "https://www.atom.io/api/packages/one-dark-syntax/versions/1.8.4/tarball", + "integrity": "sha512-zBdZ/IQVmU/pw6nOPIOGnDXwFRMQ9uwuaLoW5xCznMoMR89nIFFVc+WhZC7K/E+RICCrSdrzH18Afr3RJS0sjA==" + }, + "one-dark-ui": { + "version": "https://www.atom.io/api/packages/one-dark-ui/versions/1.12.4/tarball", + "integrity": "sha512-zywfHRiF1PEVgUUSPJPv34S9ZN51w6p/hQat19KNdVywnCTlzdxpjgVPcsDLlEcAXLeNWmri1NSqNeR1okhcNw==" + }, + "one-light-syntax": { + "version": "https://www.atom.io/api/packages/one-light-syntax/versions/1.8.4/tarball", + "integrity": "sha512-fXUzR34G+uepHq9vzmZZyKK6bGehOt2shX91iNqdecPvoHxR3lwWmAHoUMux7O0rXvClz5z0efVmnxgiDml6hQ==" + }, + "one-light-ui": { + "version": "file:packages/one-light-ui" + }, + "oniguruma": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/oniguruma/-/oniguruma-6.2.1.tgz", + "integrity": "sha1-pQ7mlkKEStHSUmhaqxhxcbBuzgQ=", + "requires": { + "nan": "^2.0.9" + } + }, + "open-on-github": { + "version": "https://www.atom.io/api/packages/open-on-github/versions/1.3.1/tarball", + "integrity": "sha512-g99P4spSqC2HhNXeBNCBHEFqKQITUbX1AHRPiVHSdGFQpTXbv8sfmIw4N1IT8RcVuZA8YRH8T2YIGhII3JqTWQ==" + }, + "optimist": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.5.2.tgz", + "integrity": "sha1-hcjBRUszFeSniUfoV7HfAzRQv7w=", + "requires": { + "wordwrap": "~0.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "output-file-sync": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", + "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", + "requires": { + "graceful-fs": "^4.1.4", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.0" + } + }, + "package-generator": { + "version": "https://www.atom.io/api/packages/package-generator/versions/1.3.0/tarball", + "integrity": "sha512-twt7ewPEcSBJASqEytVeVSP14BGT3SiUhMhL4VmAIVpv+YttFo2UTaxNTHdubjYrNMV0we+J1la2CxoX/bx6Bg==", + "requires": { + "fs-plus": "^3.0.0", + "temp": "^0.8.1", + "underscore-plus": "^1.0.0" + } + }, + "path-exists": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz", + "integrity": "sha1-1aiZjrce83p0w06w2eum6HjuoIE=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + }, + "pathwatcher": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/pathwatcher/-/pathwatcher-8.0.1.tgz", + "integrity": "sha512-NN+P7PRWdT8Zd1FwGOX/eLFD8tNuFBdDW/ysL5ufi9BFqAgOfQrV0pMMY/oRDFOZEUR4Y8RudXpLuPjE9b2pmQ==", + "requires": { + "async": "~0.2.10", + "emissary": "^1.3.2", + "event-kit": "^2.1.0", + "fs-plus": "^3.0.0", + "grim": "^2.0.1", + "iconv-lite": "~0.4.4", + "nan": "2.x", + "underscore-plus": "~1.x" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "requires": { + "through": "~2.3" + } + }, + "pegjs": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.8.0.tgz", + "integrity": "sha1-l28GfaE+XFsVAcAXklZoolOBFWE=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "postcss": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.4.tgz", + "integrity": "sha1-jrS+4+XE4JFYWxFt8y2NskpTXyE=", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.1.2" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "postcss-selector-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.1.tgz", + "integrity": "sha1-/b9pYQOxKwpkBg5WEFB/QQSR98g=", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "prebuild-install": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz", + "integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.1.6", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promisify-node": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/promisify-node/-/promisify-node-0.3.0.tgz", + "integrity": "sha1-tLVaz5D6p9K4uQyjlomQhsAwYM8=", + "requires": { + "nodegit-promise": "~4.0.0" + } + }, + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "property-accessors": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/property-accessors/-/property-accessors-1.1.3.tgz", + "integrity": "sha1-Hd6EAkYxhlkJ7zBwM2VoDF+SixU=", + "requires": { + "es6-weak-map": "^0.1.2", + "mixto": "1.x" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "optional": true + }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "random-seed": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/random-seed/-/random-seed-0.2.0.tgz", + "integrity": "sha1-TRiJtG3ITvUjFs63dysM4KVE844=" + }, + "random-words": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/random-words/-/random-words-0.0.1.tgz", + "integrity": "sha1-QOMAkgM62Ptg1mrRW+NiDTwlxB8=" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "react": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.4.0.tgz", + "integrity": "sha512-K0UrkLXSAekf5nJu89obKUM7o2vc6MMN9LYoKnCa+c+8MJRAT120xzPLENcWSRc7GYKIg0LlgJRDorrufdglQQ==", + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" + } + }, + "react-dom": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.0.tgz", + "integrity": "sha512-bbLd+HYpBEnYoNyxDe9XpSG2t9wypMohwQPvKw8Hov3nF7SJiJIgK56b46zHpBUpHb06a1iEuw7G3rbrsnNL6w==", + "requires": { + "fbjs": "^0.8.16", + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.0" + } + }, + "react-input-autosize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.1.tgz", + "integrity": "sha512-3+K4CD13iE4lQQ2WlF8PuV5htfmTRLH6MDnfndHM6LuBRszuXnuyIfE7nhSKt8AzRBZ50bu0sAhkNMeS5pxQQA==", + "requires": { + "prop-types": "^15.5.8" + } + }, + "react-relay": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/react-relay/-/react-relay-1.6.0.tgz", + "integrity": "sha512-8clmRHXNo96pcdkA8ZeiqF7xGjE+mjSbdX/INj5upRm2M8AprSrFk2Oz5nH084O+0hvXQhZtFyraXJWQO9ld3A==", + "requires": { + "babel-runtime": "^6.23.0", + "fbjs": "^0.8.14", + "prop-types": "^15.5.8", + "relay-runtime": "1.6.0" + } + }, + "react-select": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-1.2.1.tgz", + "integrity": "sha512-vaCgT2bEl+uTyE/uKOEgzE5Dc/wLtzhnBvoHCeuLoJWc4WuadN6WQDhoL42DW+TziniZK2Gaqe/wUXydI3NSaQ==", + "requires": { + "classnames": "^2.2.4", + "prop-types": "^15.5.8", + "react-input-autosize": "^2.1.2" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "recast": { + "version": "0.10.33", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.33.tgz", + "integrity": "sha1-lCgI96oBbx+nFCxGHX5XBKqo1pc=", + "requires": { + "ast-types": "0.8.12", + "esprima-fb": "~15001.1001.0-dev-harmony-fb", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "dependencies": { + "ast-types": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.12.tgz", + "integrity": "sha1-oNkOQ1G7iHcWyD/WN+v4GK9K38w=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + }, + "regenerator": { + "version": "0.8.40", + "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.40.tgz", + "integrity": "sha1-oORXxY69uuV1yfjNdRJ+k3VkNdg=", + "requires": { + "commoner": "~0.10.3", + "defs": "~1.1.0", + "esprima-fb": "~15001.1001.0-dev-harmony-fb", + "private": "~0.1.5", + "recast": "0.10.33", + "through": "~2.3.8" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regexpu": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexpu/-/regexpu-1.3.0.tgz", + "integrity": "sha1-5TTcmRqeWEYFDJjebX3UpVyeoW0=", + "requires": { + "esprima": "^2.6.0", + "recast": "^0.10.10", + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + } + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "requires": { + "jsesc": "~0.5.0" + } + }, + "relay-runtime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-1.6.0.tgz", + "integrity": "sha512-UJiEHp8CX2uFxXdM0nVLTCQ6yAT0GLmyMceXLISuW/l2a9jrS9a4MdZgdr/9UkkYno7Sj1hU/EUIQ0GaVkou8g==", + "requires": { + "babel-runtime": "^6.23.0", + "fbjs": "^0.8.14" + } + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "integrity": "sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "mime-db": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", + "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" + }, + "mime-types": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", + "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "requires": { + "mime-db": "~1.35.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "requirejs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.5.tgz", + "integrity": "sha512-svnO+aNcR/an9Dpi44C7KSAy5fFGLtmPbaaCeQaklUz8BQhS64tWWIIlvEA5jrWICzlO/X9KSzSeXFnZdBu8nw==" + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "requires": { + "path-parse": "^1.0.5" + } + }, + "rfdc": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", + "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "^7.0.5" + } + }, + "roaster": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/roaster/-/roaster-1.2.1.tgz", + "integrity": "sha1-EXa/oyoZAWUvsRBo8cDqUn9jIGg=", + "requires": { + "cheerio": "0.15.0", + "emoji-images": "0.0.2", + "js-yaml": "3.6.1", + "marked": "~0.3.3", + "task-lists": "0.2.0", + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "samsam": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", + "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "scandal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scandal/-/scandal-3.1.0.tgz", + "integrity": "sha1-m0AkuXxxm74lAIzAm6rHn7tdNQE=", + "requires": { + "argparse": "^1.0.2", + "git-utils": "^5.0.0", + "isbinaryfile": "^2.0.4", + "minimatch": "^2.0.9", + "split": "^1.0.0", + "temp": "^0.8.3" + }, + "dependencies": { + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "requires": { + "brace-expansion": "^1.0.0" + } + } + } + }, + "scoped-property-store": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/scoped-property-store/-/scoped-property-store-0.17.0.tgz", + "integrity": "sha1-raAsANYC/SBQlh4nF92dArozGDE=", + "requires": { + "atom-slick": "^2", + "event-kit": "^1.0.0", + "grim": "^1.2.1", + "key-path-helpers": "^0.1.0", + "underscore-plus": "^1.6.3" + }, + "dependencies": { + "event-kit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz", + "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=", + "requires": { + "grim": "^1.2.1" + } + }, + "key-path-helpers": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/key-path-helpers/-/key-path-helpers-0.1.0.tgz", + "integrity": "sha1-zYFJULeZzHRaNGqlIfkilK9du6Q=" + } + } + }, + "scrollbar-style": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/scrollbar-style/-/scrollbar-style-3.2.0.tgz", + "integrity": "sha1-BmK2GJM2QWDLtbDEZxmAmwKHGKE=", + "requires": { + "event-kit": "^1.1.0", + "nan": "^2.0.0" + }, + "dependencies": { + "event-kit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz", + "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=", + "requires": { + "grim": "^1.2.1" + } + } + } + }, + "season": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/season/-/season-6.0.2.tgz", + "integrity": "sha1-naWPsd3SSCTXYhstxjpxI7UCF7Y=", + "requires": { + "cson-parser": "^1.3.0", + "fs-plus": "^3.0.0", + "yargs": "^3.23.0" + } + }, + "selector-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/selector-kit/-/selector-kit-0.1.0.tgz", + "integrity": "sha1-MEM4/OzOo17Cj/rdt5KrdxVjPm8=", + "requires": { + "atom-slick": "^2" + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + }, + "serializable": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/serializable/-/serializable-1.0.3.tgz", + "integrity": "sha1-ClqLa3d3yyRUTfEab4iabSs+EYk=", + "requires": { + "get-parameter-names": "~0.2.0", + "mixto": "1.x", + "underscore-plus": "1.x" + } + }, + "service-hub": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/service-hub/-/service-hub-0.7.4.tgz", + "integrity": "sha1-ttodHn6SkcpW1PPLPVwfzjKFoWI=", + "requires": { + "event-kit": "^1.0.2", + "semver": "^5.3.0" + }, + "dependencies": { + "event-kit": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-1.5.0.tgz", + "integrity": "sha1-Ek72qtgyjcsmtxxHWQtbjmPrxIc=", + "requires": { + "grim": "^1.2.1" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + } + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "settings-view": { + "version": "https://www.atom.io/api/packages/settings-view/versions/0.255.0/tarball", + "integrity": "sha512-HRio34fw4AzlaJaemErbtcfVDDr+hCH0z2gW8WkmpSioitIxLFUKxp0jlPVRx9SPpdPD4j7xQTlFmAAkeRGjSA==", + "requires": { + "async": "~0.2.9", + "dompurify": "^1.0.2", + "etch": "0.9.0", + "fs-plus": "^3.0.0", + "fuzzaldrin": "^2.1", + "glob": "4.3.1", + "hosted-git-info": "^2.1.4", + "marked": "^0.3.6", + "request": "^2.83.0", + "roaster": "^1.1.2", + "season": "^6.0.2", + "semver": "^5.3.0", + "underscore-plus": "^1.0.6" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + }, + "glob": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.1.tgz", + "integrity": "sha1-nQkJb4m00wlJ54ToPzEq88oE7BQ=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" + } + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-fmt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", + "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-is": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=" + }, + "sinon": { + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.4.tgz", + "integrity": "sha1-Tk/02Esgre4TE482rLEyyhzXLIM=", + "requires": { + "formatio": "1.1.1", + "lolex": "1.3.2", + "samsam": "1.1.2", + "util": ">=0.10.3 <1" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + }, + "snippets": { + "version": "https://www.atom.io/api/packages/snippets/versions/1.3.5/tarball", + "integrity": "sha512-2HNSTs6QbMlbcEFr5H8EilU1B3IZdQesv52UDQpVSVP21x6wPJCv9vB1isP7NoLM0+1bgslQv/l0vsGt3cLKwg==", + "requires": { + "async": "~0.2.6", + "atom-select-list": "^0.7.0", + "fs-plus": "^3.0.0", + "loophole": "^1", + "pegjs": "~0.8.0", + "scoped-property-store": "^0.17.0", + "season": "^6.0.2", + "temp": "~0.8.0", + "underscore-plus": "^1.0.0" + } + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "optional": true, + "requires": { + "hoek": "2.x.x" + } + }, + "solarized-dark-syntax": { + "version": "https://www.atom.io/api/packages/solarized-dark-syntax/versions/1.1.5/tarball", + "integrity": "sha512-Z9KEFoSSInvNtBppOfkNUE1SG7U3f3cHOgo2X8eFvJKmut5f3rnnmKgucsJKt3OLhUFQKNsEIdR/5XZqKrMqSA==" + }, + "solarized-light-syntax": { + "version": "https://www.atom.io/api/packages/solarized-light-syntax/versions/1.1.5/tarball", + "integrity": "sha512-fXhJxpSDe2pBH9eCmERXikXHGHdo/vUmewllPkQr78wc3cjKp9Ljz79BQBP88fsmon4JsIXD0dAoWAKJ4Hb5yQ==" + }, + "source-map": { + "version": "0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "integrity": "sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY=", + "requires": { + "amdefine": ">=0.0.4" + } + }, + "source-map-support": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz", + "integrity": "sha1-6lo5AKHByyUJagrozFwrSxDe09w=", + "requires": { + "source-map": "0.1.32" + } + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" + }, + "spell-check": { + "version": "https://www.atom.io/api/packages/spell-check/versions/0.74.0/tarball", + "integrity": "sha512-vEqWUXfDty+xx7Imr9IF6J7pZAOZ0weMzVSAb3rqk3KAqm/M8uE/dWXIR9YwUgSd1FUjcxS4D+qMfibXYziojQ==", + "requires": { + "atom-pathspec": "^0.0.0", + "atom-select-list": "^0.7.0", + "multi-integer-range": "^2.0.0", + "natural": "^0.4.0", + "spellchecker": "^3.4.4", + "spelling-manager": "^1.1.0", + "underscore-plus": "^1" + } + }, + "spellchecker": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/spellchecker/-/spellchecker-3.4.4.tgz", + "integrity": "sha512-l0s86YZs5+PzATeFbqD0sTSMEF7bgzqUYgxrU8+nBSw3V19tzRYKMi+hDGG6v8MskWeG2dRK0Q79sqs1eGIKwQ==", + "requires": { + "any-promise": "^1.3.0", + "nan": "^2.0.0" + } + }, + "spelling-manager": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/spelling-manager/-/spelling-manager-1.1.0.tgz", + "integrity": "sha512-PpTP6XUZflCWO9YZO3wBSGAmqrUP6BFwSdmVFS6WBT9rFYg3ysmrIfyD1KnaVcnW6wuIKf+FDwefvU8PsD8Smg==", + "requires": { + "natural": "0.5.0", + "xregexp": "^3.2.0" + }, + "dependencies": { + "natural": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/natural/-/natural-0.5.0.tgz", + "integrity": "sha1-Vam7aOzPXs5VNUhgBKV94mSuMYA=", + "requires": { + "apparatus": ">= 0.0.9", + "sylvester": ">= 0.0.12", + "underscore": ">=1.3.1" + } + } + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=" + }, + "stacktrace-parser": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.4.tgz", + "integrity": "sha1-ATl5IuX2Ls8whFUiyVxP4dJefU4=" + }, + "status-bar": { + "version": "https://www.atom.io/api/packages/status-bar/versions/1.8.15/tarball", + "integrity": "sha512-zQa+fdr6pAnix4Lw3tKiU6Uq8Hx1dLsb+w2SaxIDbJaZatO25rN9FTZqNrw0ZchJpCEiSkuLolqUutPB4iNydQ==", + "requires": { + "fs-plus": "^3.0.1", + "grim": "^2.0.1", + "underscore-plus": "^1.0.0" + }, + "dependencies": { + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "requires": { + "duplexer": "~0.1.1" + } + }, + "streamroller": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "requires": { + "date-format": "^1.2.0", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "readable-stream": "^2.3.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringmap": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", + "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=" + }, + "stringset": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", + "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=" + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "styleguide": { + "version": "https://www.atom.io/api/packages/styleguide/versions/0.49.12/tarball", + "integrity": "sha512-fmLcnTfHIb6nU5k/ccJHwK7J+iSbw7MutpTm4W4oNXlFOW5wbyphcJD7XsXgLKv4XZT2jWEDYZWox3ZIhxK7zg==", + "requires": { + "atom-select-list": "^0.7.0", + "dedent": "^0.7.0", + "etch": "0.9.0" + }, + "dependencies": { + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + } + } + }, + "superstring": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/superstring/-/superstring-2.3.4.tgz", + "integrity": "sha512-DcNkTCdB9F3FMZRdURSALsHi+7DWqFCI0cH+Eg8mwBg+kxQs6GeB3LrGUvCI5bEB6Dtlu2ox8UYN0onPN4JeZQ==", + "requires": { + "nan": "^2.10.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "sylvester": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.21.tgz", + "integrity": "sha1-KYexzivS84sNzio0OIiEv6RADqc=" + }, + "symbols-view": { + "version": "https://www.atom.io/api/packages/symbols-view/versions/0.118.2/tarball", + "integrity": "sha512-F83LvcjRLYqcxXD9z++jX28Vdj7fkcYRa2vhCo9A2m9PSZkt8kAfas5kJ95F2LFXvkFCjfWRqVVVrQ8gMumKsA==", + "requires": { + "async": "^0.2.6", + "atom-select-list": "^0.7.0", + "ctags": "^3.0.0", + "fs-plus": "^3.0.0", + "fuzzaldrin": "^2.1.0", + "humanize-plus": "^1.8.2", + "temp": "^0.8.3", + "underscore-plus": "^1.6.6" + } + }, + "tabbable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-1.1.3.tgz", + "integrity": "sha512-nOWwx35/JuDI4ONuF0ZTo6lYvI0fY0tZCH1ErzY2EXfu4az50ZyiUX8X073FLiZtmWUVlkRnuXsehjJgCw9tYg==" + }, + "tabs": { + "version": "https://www.atom.io/api/packages/tabs/versions/0.109.2/tarball", + "integrity": "sha512-IdKT8s9Wm4++Sm5wLzrI+nLCB57DMUtTMYnLGlA9Y/C/IRlfMp3PQC2aM/dHGuIzf9JsXsI2wfPsJcU4YXKFoQ==", + "requires": { + "fs-plus": "^3.0.0", + "temp": "~0.8.1", + "underscore-plus": "1.x" + } + }, + "tar": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz", + "integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==", + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", + "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.1.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.0", + "xtend": "^4.0.0" + } + }, + "task-lists": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/task-lists/-/task-lists-0.2.0.tgz", + "integrity": "sha1-dICLohPz4S9aexrn1oI5e/jpD74=", + "requires": { + "cheerio": "~0.15.0" + } + }, + "telemetry-github": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/telemetry-github/-/telemetry-github-0.0.13.tgz", + "integrity": "sha512-35+r699XSRdvuNf27WadJggsY4x9sTLEuoBdc8WRS0BdkHMKOodN+wzlbwk780iWVvG5ccZPPPKGw9VwkXCTGw==", + "requires": { + "lokijs": "^1.5.4", + "uuid": "^3.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "temp": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "requires": { + "os-tmpdir": "^1.0.0", + "rimraf": "~2.2.6" + }, + "dependencies": { + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + } + } + }, + "text-buffer": { + "version": "13.14.6", + "resolved": "https://registry.npmjs.org/text-buffer/-/text-buffer-13.14.6.tgz", + "integrity": "sha512-CC2uyrnn+bZXIOVtXMR8jNPyrfciPGMjfyKBY9BkenbtQc06vHy9QvBhkDRf0kY/J1uCD3sJ/cbV6OV7yYM/Sw==", + "requires": { + "delegato": "^1.0.0", + "diff": "^2.2.1", + "emissary": "^1.0.0", + "event-kit": "^2.4.0", + "fs-admin": "^0.1.4", + "fs-plus": "^3.0.0", + "grim": "^2.0.2", + "mkdirp": "^0.5.1", + "pathwatcher": "8.0.1", + "serializable": "^1.0.3", + "superstring": "2.3.4", + "underscore-plus": "^1.0.0" + }, + "dependencies": { + "diff": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=" + }, + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "timecop": { + "version": "https://www.atom.io/api/packages/timecop/versions/0.36.2/tarball", + "integrity": "sha512-m8FkLOjmzV5e0LiycEh+IwOiHXbD6odk6DSbBxWL3hSPF89eHkaFT8Ea/NT6g/ufYO4ZSzAbvlXpuFuskAb/1w==", + "requires": { + "dedent": "^0.7.0", + "etch": "^0.12.6", + "underscore-plus": "^1.0.0" + } + }, + "tinycolor2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", + "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + }, + "to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=" + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "optional": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tree-kill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", + "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==" + }, + "tree-sitter": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.13.8.tgz", + "integrity": "sha512-LfKtMRxRjYfVSnrkwAMfwO8MH493G05fnw8QHaiB6p2iSjP9RHOLnXP1VxR4FlhQqyYRIbdUx/VCBoyntzB5Pg==", + "requires": { + "nan": "^2.10.0", + "prebuild-install": "^5.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "prebuild-install": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.0.0.tgz", + "integrity": "sha512-AvcPLFqNz/hDd6o7qLj8i9xB479P9jSjA/p6m4927CRfY3tsmPfyFmD7RKXtdp6I2d1BAIVBgJoj5mxRJDZL4w==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.2.7", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + } + } + } + }, + "tree-sitter-bash": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/tree-sitter-bash/-/tree-sitter-bash-0.13.2.tgz", + "integrity": "sha512-6viPvaHwTLRVYLeUXyCSvu+ybLAOVDbkqhvNcp5Ak+GQMqzkflpsuuVIIriyl2lSumrkbUShHnrWiQw4yoZl4g==", + "requires": { + "nan": "^2.10.0", + "prebuild-install": "^5.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "prebuild-install": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.0.0.tgz", + "integrity": "sha512-AvcPLFqNz/hDd6o7qLj8i9xB479P9jSjA/p6m4927CRfY3tsmPfyFmD7RKXtdp6I2d1BAIVBgJoj5mxRJDZL4w==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.2.7", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + } + } + } + }, + "tree-sitter-c": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/tree-sitter-c/-/tree-sitter-c-0.13.4.tgz", + "integrity": "sha512-wtu4PczfuG05GD4M0+2n2F1FytFN4Jra6UVwPqDjrzfoRUvfYcvtIgIlKmO5s/Oyd8sY5jEn1dKdC/lX1DEi4g==", + "requires": { + "nan": "^2.10.0" + } + }, + "tree-sitter-cpp": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/tree-sitter-cpp/-/tree-sitter-cpp-0.13.4.tgz", + "integrity": "sha512-WkPeb7r83lexK5nbpo7tfjgkSPhIDIo+Gl1ZOHBbRwG/r+O1xnsy2xgp7Gby5O/yAH0Mz5JgEGvoy+V6qBS8LQ==", + "requires": { + "nan": "^2.10.0" + } + }, + "tree-sitter-embedded-template": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/tree-sitter-embedded-template/-/tree-sitter-embedded-template-0.13.0.tgz", + "integrity": "sha512-IJVjMcL2Bg+qF+HibtEXTF4CE6A66ppGSqU8E+2ddn2pCqDtZGREhI+KfqerF9NpKSo1OtbvhXiEXPdXQANLGg==", + "requires": { + "nan": "^2.0.0" + } + }, + "tree-sitter-go": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/tree-sitter-go/-/tree-sitter-go-0.13.1.tgz", + "integrity": "sha512-lWcwS6cEvS42Ayo3m4uqQnpEmIjlex/Tb2BSNZ9ppxhiIVJrntR6/sirlYmhYVj7Zhu7eOWSVFfNX9y7PKw/2w==", + "requires": { + "nan": "^2.10.0" + } + }, + "tree-sitter-html": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/tree-sitter-html/-/tree-sitter-html-0.13.1.tgz", + "integrity": "sha512-bmt5OoMdeg3OH1XzSXzbnZ0a9g2szFKRq/xdKwiDVKfgBG1qp6ecOEzfVhEJjZMuGRKXWEMX1OvyXuZWzlD3FQ==", + "requires": { + "nan": "^2.8.0" + } + }, + "tree-sitter-javascript": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/tree-sitter-javascript/-/tree-sitter-javascript-0.13.5.tgz", + "integrity": "sha512-80sUl2+kj0evGTueODpDmJksTNQbGtkG4EOxIygsjZ5Q6kFW/uBRHXHSlBtX5vkEIjljApZ8qse4C7cmG1CIFA==", + "requires": { + "nan": "^2.4.0" + } + }, + "tree-sitter-python": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/tree-sitter-python/-/tree-sitter-python-0.13.4.tgz", + "integrity": "sha512-JQ75/68VsPMSWteoitNxgdTlAw5spbTFGK2XgiR41tiTtkk8cONdtfB7Pe2yWSgoXGDNtcu0/Tkq4HrKrWrKfQ==", + "requires": { + "nan": "^2.4.0" + } + }, + "tree-sitter-regex": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/tree-sitter-regex/-/tree-sitter-regex-0.13.0.tgz", + "integrity": "sha512-b8EnSNTAYGSlfP4sHEA0NfVf+mn2eyCqWIyTaBHKkEUlxkKloo7551tY7LzxicetG4R6WMzPG/ZsI+jrIyFplQ==", + "requires": { + "nan": "^2.0.0" + } + }, + "tree-sitter-ruby": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/tree-sitter-ruby/-/tree-sitter-ruby-0.13.5.tgz", + "integrity": "sha512-Trg4EOKkL1yx0y74f/b5YVvqUYO1cBNUpGsfltDqizPdO3yfLTykBZZgd31N1l9HEiaZs1iNp6vsvdpnLQ1d3w==", + "requires": { + "nan": "^2.10.0", + "prebuild-install": "^5.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "prebuild-install": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.0.0.tgz", + "integrity": "sha512-AvcPLFqNz/hDd6o7qLj8i9xB479P9jSjA/p6m4927CRfY3tsmPfyFmD7RKXtdp6I2d1BAIVBgJoj5mxRJDZL4w==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.2.7", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + } + } + } + }, + "tree-sitter-typescript": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.13.3.tgz", + "integrity": "sha512-PF/yDQNtjOU0pDHdy4QqYB8yPgfEeZz7PsG+IwYYC6xt3XQBRKJDIGyPPSHSvSZFE2ln4GBnJ7/EDneiNXqrlA==", + "requires": { + "nan": "^2.10.0" + } + }, + "tree-view": { + "version": "https://www.atom.io/api/packages/tree-view/versions/0.224.2/tarball", + "integrity": "sha512-zQgrwWo2aBInkSCsldNP9sZJA4OnzznQgKJzQNhClMZJMWEr1Pl1tWnq3x67ie6dINkJ/JWqzbjxz2v2T5U2ig==", + "requires": { + "@atom/temp": "~0.8.4", + "fs-plus": "^3.0.0", + "minimatch": "~0.3.0", + "pathwatcher": "^8.0.0", + "underscore-plus": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "try-resolve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz", + "integrity": "sha1-z95vq9ctY+V5fPqrhzq76OcA6RI=" + }, + "tryor": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", + "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=" + }, + "typescript": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-1.4.1.tgz", + "integrity": "sha1-602phtG38BRS6vtXVZ4MyPUWzUg=" + }, + "typescript-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typescript-simple/-/typescript-simple-1.0.0.tgz", + "integrity": "sha1-/eFtnJxJTvc9OtOdgHEZsI8sOZg=", + "requires": { + "typescript": "~1.4.1" + } + }, + "ua-parser-js": { + "version": "0.7.18", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", + "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "underscore-plus": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.6.8.tgz", + "integrity": "sha512-88PrCeMKeAAC1L4xjSiiZ3Fg6kZOYrLpLGVPPeqKq/662DfQe/KTSKdSR/Q/tucKNnfW2MNAUGSCkDf8HmXC5Q==", + "requires": { + "underscore": "~1.8.3" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "update-package-dependencies": { + "version": "https://www.atom.io/api/packages/update-package-dependencies/versions/0.13.1/tarball", + "integrity": "sha512-A0mvGI/fSHKsGPOTz/HVZ94UHUJOOJuwI4lAaauPeyZlEDHC2+VGoRDHcvLYxTyamumCGRhSVPDK3Gp8KItF9A==" + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" + }, + "util": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.0.tgz", + "integrity": "sha512-5n12uMzKCjvB2HPFHnbQSjaqAa98L5iIXmHrZCLavuZVe0qe/SJGbDGWlpaHk5lnBkWRDO+dRu1/PgmUYKPPTw==", + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walkdir": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.7.tgz", + "integrity": "sha1-BNoCcKh6d4VAFzzb8KLbSZqNnik=" + }, + "welcome": { + "version": "https://www.atom.io/api/packages/welcome/versions/0.36.7/tarball", + "integrity": "sha512-z1EOTRYfN23fBL75Shrbe/j2VDelw2c8oKRXC2MqLLBiWUCFDkxsEo1R7OfiCaNZi7q/0ue0fqLCpENHker4FA==", + "requires": { + "etch": "0.9.0" + }, + "dependencies": { + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + } + } + }, + "what-the-diff": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/what-the-diff/-/what-the-diff-0.4.0.tgz", + "integrity": "sha512-Aw5OoYs5pY4RcZhD9UrS/brg/YRFm/SRRwJEI3f12PTWYadXzkvmf2eGDggSwcZuH2OH8J5HmtUK6LH+jRc2aA==" + }, + "what-the-status": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/what-the-status/-/what-the-status-1.0.3.tgz", + "integrity": "sha512-6zNdYtQtHTpLVPomSrr+Eyt5Ci4H40ytwScwp7Moi2iqxztV6+juQV9Orj2szAo0ZrV9tphk6WtL+BY3ukCS/Q==", + "requires": { + "split": "^1.0.0" + } + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, + "whitespace": { + "version": "https://www.atom.io/api/packages/whitespace/versions/0.37.6/tarball", + "integrity": "sha512-I5xkJnYf/Pqs1LgXihzVBsiRddVTAGFluDruwZR6fg6c3r0shVwSOxoYX0Ba+xxRVEcAixahnq0lOp68tnl7oA==" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + }, + "winreg": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.4.tgz", + "integrity": "sha1-ugZWKbepJRMOFXeRCM9UCZDpjRs=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrap-guide": { + "version": "https://www.atom.io/api/packages/wrap-guide/versions/0.40.3/tarball", + "integrity": "sha512-TQdO+E8t8sS3c4UUym2Orrf4s6/AYGrfu32lUHLMgnSYcBov/t8J3jYJfPImNkCq7kgYgfycQZM5UryW54J4KA==", + "requires": { + "grim": "^2.0.1" + }, + "dependencies": { + "grim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz", + "integrity": "sha512-Qj7hTJRfd87E/gUgfvM0YIH/g2UA2SV6niv6BYXk1o6w4mhgv+QyYM1EjOJQljvzgEj4SqSsRWldXIeKHz3e3Q==", + "requires": { + "event-kit": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "wrench": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.9.tgz", + "integrity": "sha1-QRaRxjqbJTGxcAJnJ5veyiOyFCo=" + }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=" + }, + "xregexp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.2.0.tgz", + "integrity": "sha1-yzYBmHv+JpW1hAAMGPHEqMMih44=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=" + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "requires": { + "camelcase": "^2.0.1", + "cliui": "^3.0.3", + "decamelize": "^1.1.1", + "os-locale": "^1.4.0", + "string-width": "^1.0.1", + "window-size": "^0.1.4", + "y18n": "^3.2.0" + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yubikiri": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yubikiri/-/yubikiri-1.0.0.tgz", + "integrity": "sha1-TQ+EGugA10f11pM/fVQvVQJiw64=" + } + } +} diff --git a/package.json b/package.json index b59746676..9ff62ad33 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "1.25.0-dev", + "version": "1.32.0-dev", "description": "A hackable text editor for the 21st Century.", "main": "./src/main-process/main.js", "repository": { @@ -12,50 +12,128 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "1.7.10", + "electronVersion": "2.0.7", "dependencies": { "@atom/nsfw": "^1.0.18", "@atom/source-map-support": "^0.3.4", + "@atom/watcher": "1.0.8", + "about": "file:packages/about", + "archive-view": "https://www.atom.io/api/packages/archive-view/versions/0.65.1/tarball", "async": "0.2.6", - "atom-keymap": "8.2.8", - "atom-select-list": "^0.7.0", + "atom-dark-syntax": "https://www.atom.io/api/packages/atom-dark-syntax/versions/0.29.0/tarball", + "atom-dark-ui": "https://www.atom.io/api/packages/atom-dark-ui/versions/0.53.2/tarball", + "atom-keymap": "8.2.10", + "atom-light-syntax": "https://www.atom.io/api/packages/atom-light-syntax/versions/0.29.0/tarball", + "atom-light-ui": "https://www.atom.io/api/packages/atom-light-ui/versions/0.46.2/tarball", + "atom-select-list": "^0.7.2", "atom-ui": "0.4.1", + "autocomplete-atom-api": "https://www.atom.io/api/packages/autocomplete-atom-api/versions/0.10.7/tarball", + "autocomplete-css": "https://www.atom.io/api/packages/autocomplete-css/versions/0.17.5/tarball", + "autocomplete-html": "https://www.atom.io/api/packages/autocomplete-html/versions/0.8.4/tarball", + "autocomplete-plus": "https://www.atom.io/api/packages/autocomplete-plus/versions/2.40.7/tarball", + "autocomplete-snippets": "https://www.atom.io/api/packages/autocomplete-snippets/versions/1.12.0/tarball", + "autoflow": "https://www.atom.io/api/packages/autoflow/versions/0.29.4/tarball", + "autosave": "https://www.atom.io/api/packages/autosave/versions/0.24.6/tarball", "babel-core": "5.8.38", + "background-tips": "https://www.atom.io/api/packages/background-tips/versions/0.28.0/tarball", + "base16-tomorrow-dark-theme": "https://www.atom.io/api/packages/base16-tomorrow-dark-theme/versions/1.5.0/tarball", + "base16-tomorrow-light-theme": "https://www.atom.io/api/packages/base16-tomorrow-light-theme/versions/1.5.0/tarball", + "bookmarks": "https://www.atom.io/api/packages/bookmarks/versions/0.45.1/tarball", + "bracket-matcher": "https://www.atom.io/api/packages/bracket-matcher/versions/0.89.2/tarball", "cached-run-in-this-context": "0.4.1", "chai": "3.5.0", "chart.js": "^2.3.0", "clear-cut": "^2.0.2", "coffee-script": "1.12.7", "color": "^0.7.3", + "command-palette": "https://www.atom.io/api/packages/command-palette/versions/0.43.5/tarball", + "dalek": "https://www.atom.io/api/packages/dalek/versions/0.2.2/tarball", "dedent": "^0.7.0", + "deprecation-cop": "https://www.atom.io/api/packages/deprecation-cop/versions/0.56.9/tarball", + "dev-live-reload": "https://www.atom.io/api/packages/dev-live-reload/versions/0.48.1/tarball", "devtron": "1.3.0", + "encoding-selector": "https://www.atom.io/api/packages/encoding-selector/versions/0.23.9/tarball", "etch": "^0.12.6", - "event-kit": "^2.4.0", + "event-kit": "^2.5.0", + "exception-reporting": "https://www.atom.io/api/packages/exception-reporting/versions/0.43.1/tarball", + "find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.215.12/tarball", "find-parent-dir": "^0.3.0", - "first-mate": "7.1.0", - "focus-trap": "^2.3.0", + "first-mate": "7.1.1", + "focus-trap": "2.4.5", "fs-admin": "^0.1.6", "fs-plus": "^3.0.1", "fstream": "0.1.24", "fuzzaldrin": "^2.1", - "git-utils": "5.2.0", + "fuzzy-finder": "https://www.atom.io/api/packages/fuzzy-finder/versions/1.8.2/tarball", + "git-diff": "https://www.atom.io/api/packages/git-diff/versions/1.3.9/tarball", + "git-utils": "5.2.1", + "github": "https://www.atom.io/api/packages/github/versions/0.19.0/tarball", "glob": "^7.1.1", + "go-to-line": "https://www.atom.io/api/packages/go-to-line/versions/0.33.0/tarball", + "grammar-selector": "https://www.atom.io/api/packages/grammar-selector/versions/0.50.1/tarball", "grim": "1.5.0", + "image-view": "https://www.atom.io/api/packages/image-view/versions/0.63.1/tarball", + "incompatible-packages": "https://www.atom.io/api/packages/incompatible-packages/versions/0.27.3/tarball", "jasmine-json": "~0.0", "jasmine-reporters": "1.1.0", "jasmine-tagged": "^1.1.4", "key-path-helpers": "^0.4.0", + "keybinding-resolver": "https://www.atom.io/api/packages/keybinding-resolver/versions/0.38.2/tarball", + "language-c": "https://www.atom.io/api/packages/language-c/versions/0.60.4/tarball", + "language-clojure": "https://www.atom.io/api/packages/language-clojure/versions/0.22.7/tarball", + "language-coffee-script": "https://www.atom.io/api/packages/language-coffee-script/versions/0.49.3/tarball", + "language-csharp": "https://www.atom.io/api/packages/language-csharp/versions/1.0.4/tarball", + "language-css": "https://www.atom.io/api/packages/language-css/versions/0.42.11/tarball", + "language-gfm": "https://www.atom.io/api/packages/language-gfm/versions/0.90.5/tarball", + "language-git": "https://www.atom.io/api/packages/language-git/versions/0.19.1/tarball", + "language-go": "https://www.atom.io/api/packages/language-go/versions/0.46.2/tarball", + "language-html": "https://www.atom.io/api/packages/language-html/versions/0.51.2/tarball", + "language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.16.3/tarball", + "language-java": "https://www.atom.io/api/packages/language-java/versions/0.30.0/tarball", + "language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.129.7/tarball", + "language-json": "https://www.atom.io/api/packages/language-json/versions/0.19.2/tarball", + "language-less": "https://www.atom.io/api/packages/language-less/versions/0.34.2/tarball", + "language-make": "https://www.atom.io/api/packages/language-make/versions/0.22.3/tarball", + "language-mustache": "https://www.atom.io/api/packages/language-mustache/versions/0.14.5/tarball", + "language-objective-c": "https://www.atom.io/api/packages/language-objective-c/versions/0.15.1/tarball", + "language-perl": "https://www.atom.io/api/packages/language-perl/versions/0.38.1/tarball", + "language-php": "https://www.atom.io/api/packages/language-php/versions/0.44.0/tarball", + "language-property-list": "https://www.atom.io/api/packages/language-property-list/versions/0.9.1/tarball", + "language-python": "https://www.atom.io/api/packages/language-python/versions/0.51.4/tarball", + "language-ruby": "https://www.atom.io/api/packages/language-ruby/versions/0.72.4/tarball", + "language-ruby-on-rails": "https://www.atom.io/api/packages/language-ruby-on-rails/versions/0.25.3/tarball", + "language-sass": "https://www.atom.io/api/packages/language-sass/versions/0.62.0/tarball", + "language-shellscript": "https://www.atom.io/api/packages/language-shellscript/versions/0.27.4/tarball", + "language-source": "https://www.atom.io/api/packages/language-source/versions/0.9.0/tarball", + "language-sql": "https://www.atom.io/api/packages/language-sql/versions/0.25.10/tarball", + "language-text": "https://www.atom.io/api/packages/language-text/versions/0.7.4/tarball", + "language-todo": "https://www.atom.io/api/packages/language-todo/versions/0.29.4/tarball", + "language-toml": "https://www.atom.io/api/packages/language-toml/versions/0.18.2/tarball", + "language-typescript": "https://www.atom.io/api/packages/language-typescript/versions/0.4.6/tarball", + "language-xml": "https://www.atom.io/api/packages/language-xml/versions/0.35.2/tarball", + "language-yaml": "https://www.atom.io/api/packages/language-yaml/versions/0.32.0/tarball", "less-cache": "1.1.0", + "line-ending-selector": "https://www.atom.io/api/packages/line-ending-selector/versions/0.7.7/tarball", "line-top-index": "0.3.1", - "marked": "^0.3.6", + "link": "https://www.atom.io/api/packages/link/versions/0.31.4/tarball", + "markdown-preview": "https://www.atom.io/api/packages/markdown-preview/versions/0.159.23/tarball", + "marked": "^0.3.12", + "metrics": "https://www.atom.io/api/packages/metrics/versions/1.6.2/tarball", "minimatch": "^3.0.3", "mocha": "2.5.1", "mocha-junit-reporter": "^1.13.0", "mocha-multi-reporters": "^1.1.4", "mock-spawn": "^0.2.6", "normalize-package-data": "^2.0.0", + "notifications": "https://www.atom.io/api/packages/notifications/versions/0.70.5/tarball", "nslog": "^3", + "one-dark-syntax": "https://www.atom.io/api/packages/one-dark-syntax/versions/1.8.4/tarball", + "one-dark-ui": "https://www.atom.io/api/packages/one-dark-ui/versions/1.12.4/tarball", + "one-light-syntax": "https://www.atom.io/api/packages/one-light-syntax/versions/1.8.4/tarball", + "one-light-ui": "file:packages/one-light-ui", "oniguruma": "6.2.1", + "open-on-github": "https://www.atom.io/api/packages/open-on-github/versions/1.3.1/tarball", + "package-generator": "https://www.atom.io/api/packages/package-generator/versions/1.3.0/tarball", "pathwatcher": "8.0.1", "postcss": "5.2.4", "postcss-selector-parser": "2.2.1", @@ -68,108 +146,123 @@ "season": "^6.0.2", "semver": "^4.3.3", "service-hub": "^0.7.4", + "settings-view": "https://www.atom.io/api/packages/settings-view/versions/0.255.0/tarball", "sinon": "1.17.4", + "snippets": "https://www.atom.io/api/packages/snippets/versions/1.3.5/tarball", + "solarized-dark-syntax": "https://www.atom.io/api/packages/solarized-dark-syntax/versions/1.1.5/tarball", + "solarized-light-syntax": "https://www.atom.io/api/packages/solarized-light-syntax/versions/1.1.5/tarball", + "spell-check": "https://www.atom.io/api/packages/spell-check/versions/0.74.0/tarball", + "status-bar": "https://www.atom.io/api/packages/status-bar/versions/1.8.15/tarball", + "styleguide": "https://www.atom.io/api/packages/styleguide/versions/0.49.12/tarball", + "symbols-view": "https://www.atom.io/api/packages/symbols-view/versions/0.118.2/tarball", + "tabs": "https://www.atom.io/api/packages/tabs/versions/0.109.2/tarball", "temp": "^0.8.3", - "text-buffer": "13.11.0", - "tree-sitter": "^0.8.4", + "text-buffer": "13.14.6", + "timecop": "https://www.atom.io/api/packages/timecop/versions/0.36.2/tarball", + "tree-sitter": "0.13.8", + "tree-view": "https://www.atom.io/api/packages/tree-view/versions/0.224.2/tarball", "typescript-simple": "1.0.0", - "underscore-plus": "^1.6.6", + "underscore-plus": "^1.6.8", + "update-package-dependencies": "https://www.atom.io/api/packages/update-package-dependencies/versions/0.13.1/tarball", + "welcome": "https://www.atom.io/api/packages/welcome/versions/0.36.7/tarball", + "whitespace": "https://www.atom.io/api/packages/whitespace/versions/0.37.6/tarball", "winreg": "^1.2.1", + "wrap-guide": "https://www.atom.io/api/packages/wrap-guide/versions/0.40.3/tarball", "yargs": "^3.23.0" }, "packageDependencies": { "atom-dark-syntax": "0.29.0", - "atom-dark-ui": "0.53.1", + "atom-dark-ui": "0.53.2", "atom-light-syntax": "0.29.0", - "atom-light-ui": "0.46.1", + "atom-light-ui": "0.46.2", "base16-tomorrow-dark-theme": "1.5.0", "base16-tomorrow-light-theme": "1.5.0", - "one-dark-ui": "1.10.10", - "one-light-ui": "1.10.10", - "one-dark-syntax": "1.8.2", - "one-light-syntax": "1.8.2", - "solarized-dark-syntax": "1.1.4", - "solarized-light-syntax": "1.1.4", - "about": "1.7.8", - "archive-view": "0.64.2", - "autocomplete-atom-api": "0.10.6", + "one-dark-ui": "1.12.4", + "one-light-ui": "file:./packages/one-light-ui", + "one-dark-syntax": "1.8.4", + "one-light-syntax": "1.8.4", + "solarized-dark-syntax": "1.1.5", + "solarized-light-syntax": "1.1.5", + "about": "file:./packages/about", + "archive-view": "0.65.1", + "autocomplete-atom-api": "0.10.7", "autocomplete-css": "0.17.5", "autocomplete-html": "0.8.4", - "autocomplete-plus": "2.40.0", - "autocomplete-snippets": "1.11.2", - "autoflow": "0.29.3", + "autocomplete-plus": "2.40.7", + "autocomplete-snippets": "1.12.0", + "autoflow": "0.29.4", "autosave": "0.24.6", - "background-tips": "0.27.1", + "background-tips": "0.28.0", "bookmarks": "0.45.1", - "bracket-matcher": "0.88.3", - "command-palette": "0.43.0", - "dalek": "0.2.1", + "bracket-matcher": "0.89.2", + "command-palette": "0.43.5", + "dalek": "0.2.2", "deprecation-cop": "0.56.9", "dev-live-reload": "0.48.1", - "encoding-selector": "0.23.8", - "exception-reporting": "0.42.0", - "find-and-replace": "0.215.0", - "fuzzy-finder": "1.7.5", - "github": "0.9.1", - "git-diff": "1.3.7", - "go-to-line": "0.32.1", - "grammar-selector": "0.49.9", - "image-view": "0.62.4", + "encoding-selector": "0.23.9", + "exception-reporting": "0.43.1", + "find-and-replace": "0.215.12", + "fuzzy-finder": "1.8.2", + "github": "0.19.0", + "git-diff": "1.3.9", + "go-to-line": "0.33.0", + "grammar-selector": "0.50.1", + "image-view": "0.63.1", "incompatible-packages": "0.27.3", - "keybinding-resolver": "0.38.1", - "line-ending-selector": "0.7.5", + "keybinding-resolver": "0.38.2", + "line-ending-selector": "0.7.7", "link": "0.31.4", - "markdown-preview": "0.159.20", - "metrics": "1.2.6", - "notifications": "0.70.2", + "markdown-preview": "0.159.23", + "metrics": "1.6.2", + "notifications": "0.70.5", "open-on-github": "1.3.1", "package-generator": "1.3.0", - "settings-view": "0.253.4", - "snippets": "1.3.0", - "spell-check": "0.72.5", + "settings-view": "0.255.0", + "snippets": "1.3.5", + "spell-check": "0.74.0", "status-bar": "1.8.15", - "styleguide": "0.49.10", + "styleguide": "0.49.12", "symbols-view": "0.118.2", - "tabs": "0.109.1", + "tabs": "0.109.2", "timecop": "0.36.2", - "tree-view": "0.221.3", + "tree-view": "0.224.2", "update-package-dependencies": "0.13.1", - "welcome": "0.36.6", - "whitespace": "0.37.5", + "welcome": "0.36.7", + "whitespace": "0.37.6", "wrap-guide": "0.40.3", - "language-c": "0.59.0", - "language-clojure": "0.22.5", + "language-c": "0.60.4", + "language-clojure": "0.22.7", "language-coffee-script": "0.49.3", - "language-csharp": "0.14.4", - "language-css": "0.42.8", - "language-gfm": "0.90.3", + "language-csharp": "1.0.4", + "language-css": "0.42.11", + "language-gfm": "0.90.5", "language-git": "0.19.1", - "language-go": "0.45.0", - "language-html": "0.48.5", + "language-go": "0.46.2", + "language-html": "0.51.2", "language-hyperlink": "0.16.3", - "language-java": "0.27.6", - "language-javascript": "0.128.0", - "language-json": "0.19.1", - "language-less": "0.34.1", + "language-java": "0.30.0", + "language-javascript": "0.129.7", + "language-json": "0.19.2", + "language-less": "0.34.2", "language-make": "0.22.3", - "language-mustache": "0.14.4", + "language-mustache": "0.14.5", "language-objective-c": "0.15.1", "language-perl": "0.38.1", - "language-php": "0.43.0", + "language-php": "0.44.0", "language-property-list": "0.9.1", - "language-python": "0.47.0", - "language-ruby": "0.71.4", + "language-python": "0.51.4", + "language-ruby": "0.72.4", "language-ruby-on-rails": "0.25.3", - "language-sass": "0.61.4", - "language-shellscript": "0.26.0", + "language-sass": "0.62.0", + "language-shellscript": "0.27.4", "language-source": "0.9.0", - "language-sql": "0.25.9", - "language-text": "0.7.3", - "language-todo": "0.29.3", - "language-toml": "0.18.1", - "language-typescript": "0.3.0", + "language-sql": "0.25.10", + "language-text": "0.7.4", + "language-todo": "0.29.4", + "language-toml": "0.18.2", + "language-typescript": "0.4.6", "language-xml": "0.35.2", - "language-yaml": "0.31.1" + "language-yaml": "0.32.0" }, "private": true, "scripts": { diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 000000000..12ff907c5 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,194 @@ +# Atom Core Packages + +This folder contains core packages that are bundled with Atom releases. Not all Atom core packages are kept here; please +see the table below for the location of every core Atom package. + +> **NOTE:** There is an ongoing effort to migrate more Atom packages from their individual repositories to this folder. +See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate-core-packages.md) for more details. + +| Package | Where to find it | Migration issue | +|---------|------------------|-----------------| +| **about** | [`./packages/about`](./about) | [#17832](https://github.com/atom/atom/issues/17832) | +| **atom-dark-syntax** | [`atom/atom-dark-syntax`][atom-dark-syntax] | [#17849](https://github.com/atom/atom/issues/17849) | +| **atom-dark-ui** | [`atom/atom-dark-ui`][atom-dark-ui] | [#17850](https://github.com/atom/atom/issues/17850) | +| **atom-light-syntax** | [`atom/atom-light-syntax`][atom-light-syntax] | [#17851](https://github.com/atom/atom/issues/17851) | +| **atom-light-ui** | [`atom/atom-light-ui`][atom-light-ui] | [#17852](https://github.com/atom/atom/issues/17852) | +| **autocomplete-atom-api** | [`atom/autocomplete-atom-api`][autocomplete-atom-api] | | +| **autocomplete-css** | [`atom/autocomplete-css`][autocomplete-css] | | +| **autocomplete-html** | [`atom/autocomplete-html`][autocomplete-html] | | +| **autocomplete-plus** | [`atom/autocomplete-plus`][autocomplete-plus] | | +| **autocomplete-snippets** | [`atom/autocomplete-snippets`][autocomplete-snippets] | | +| **autoflow** | [`atom/autoflow`][autoflow] | [#17833](https://github.com/atom/atom/issues/17833) | +| **autosave** | [`atom/autosave`][autosave] | [#17834](https://github.com/atom/atom/issues/17834) | +| **background-tips** | [`atom/background-tips`][background-tips] | [#17835](https://github.com/atom/atom/issues/17835) | +| **base16-tomorrow-dark-theme** | [`atom/base16-tomorrow-dark-theme`][base16-tomorrow-dark-theme] | [#17836](https://github.com/atom/atom/issues/17836) | +| **base16-tomorrow-light-theme** | [`atom/base16-tomorrow-light-theme`][base16-tomorrow-light-theme] | [#17837](https://github.com/atom/atom/issues/17837) | +| **bookmarks** | [`atom/bookmarks`][bookmarks] | | +| **bracket-matcher** | [`atom/bracket-matcher`][bracket-matcher] | | +| **command-palette** | [`atom/command-palette`][command-palette] | | +| **dalek** | [`atom/dalek`][dalek] | [#17838](https://github.com/atom/atom/issues/17838) | +| **deprecation-cop** | [`atom/deprecation-cop`][deprecation-cop] | [#17839](https://github.com/atom/atom/issues/17839) | +| **dev-live-reload** | [`atom/dev-live-reload`][dev-live-reload] | [#17840](https://github.com/atom/atom/issues/17840) | +| **encoding-selector** | [`atom/encoding-selector`][encoding-selector] | [#17841](https://github.com/atom/atom/issues/17841) | +| **exception-reporting** | [`atom/exception-reporting`][exception-reporting] | [#17842](https://github.com/atom/atom/issues/17842) | +| **find-and-replace** | [`atom/find-and-replace`][find-and-replace] | | +| **fuzzy-finder** | [`atom/fuzzy-finder`][fuzzy-finder] | | +| **github** | [`atom/github`][github] | | +| **git-diff** | [`atom/git-diff`][git-diff] | [#17843](https://github.com/atom/atom/issues/17843) | +| **go-to-line** | [`atom/go-to-line`][go-to-line] | [#17844](https://github.com/atom/atom/issues/17844) | +| **grammar-selector** | [`atom/grammar-selector`][grammar-selector] | [#17845](https://github.com/atom/atom/issues/17845) | +| **image-view** | [`atom/image-view`][image-view] | | +| **incompatible-packages** | [`atom/incompatible-packages`][incompatible-packages] | [#17846](https://github.com/atom/atom/issues/17846) | +| **keybinding-resolver** | [`atom/keybinding-resolver`][keybinding-resolver] | | +| **language-c** | [`atom/language-c`][language-c] | | +| **language-clojure** | [`atom/language-clojure`][language-clojure] | | +| **language-coffee-script** | [`atom/language-coffee-script`][language-coffee-script] | | +| **language-csharp** | [`atom/language-csharp`][language-csharp] | | +| **language-css** | [`atom/language-css`][language-css] | | +| **language-gfm** | [`atom/language-gfm`][language-gfm] | | +| **language-git** | [`atom/language-git`][language-git] | | +| **language-go** | [`atom/language-go`][language-go] | | +| **language-html** | [`atom/language-html`][language-html] | | +| **language-hyperlink** | [`atom/language-hyperlink`][language-hyperlink] | | +| **language-java** | [`atom/language-java`][language-java] | | +| **language-javascript** | [`atom/language-javascript`][language-javascript] | | +| **language-json** | [`atom/language-json`][language-json] | | +| **language-less** | [`atom/language-less`][language-less] | | +| **language-make** | [`atom/language-make`][language-make] | | +| **language-mustache** | [`atom/language-mustache`][language-mustache] | | +| **language-objective-c** | [`atom/language-objective-c`][language-objective-c] | | +| **language-perl** | [`atom/language-perl`][language-perl] | | +| **language-php** | [`atom/language-php`][language-php] | | +| **language-property-list** | [`atom/language-property-list`][language-property-list] | | +| **language-python** | [`atom/language-python`][language-python] | | +| **language-ruby** | [`atom/language-ruby`][language-ruby] | | +| **language-ruby-on-rails** | [`atom/language-ruby-on-rails`][language-ruby-on-rails] | | +| **language-sass** | [`atom/language-sass`][language-sass] | | +| **language-shellscript** | [`atom/language-shellscript`][language-shellscript] | | +| **language-source** | [`atom/language-source`][language-source] | | +| **language-sql** | [`atom/language-sql`][language-sql] | | +| **language-text** | [`atom/language-text`][language-text] | | +| **language-todo** | [`atom/language-todo`][language-todo] | | +| **language-toml** | [`atom/language-toml`][language-toml] | | +| **language-typescript** | [`atom/language-typescript`][language-typescript] | | +| **language-xml** | [`atom/language-xml`][language-xml] | | +| **language-yaml** | [`atom/language-yaml`][language-yaml] | | +| **line-ending-selector** | [`atom/line-ending-selector`][line-ending-selector] | [#17847](https://github.com/atom/atom/issues/17847) | +| **link** | [`atom/link`][link] | [#17848](https://github.com/atom/atom/issues/17848) | +| **markdown-preview** | [`atom/markdown-preview`][markdown-preview] | | +| **metrics** | [`atom/metrics`][metrics] | | +| **notifications** | [`atom/notifications`][notifications] | | +| **one-dark-syntax** | [`atom/one-dark-syntax`][one-dark-syntax] | [#17853](https://github.com/atom/atom/issues/17853) | +| **one-dark-ui** | [`atom/one-dark-ui`][one-dark-ui] | [#17854](https://github.com/atom/atom/issues/17854) | +| **one-light-syntax** | [`atom/one-light-syntax`][one-light-syntax] | [#17855](https://github.com/atom/atom/issues/17855) | +| **one-light-ui** | [`./packages/one-light-ui`](./one-light-ui) | [#17856](https://github.com/atom/atom/issues/17856) | +| **open-on-github** | [`atom/open-on-github`][open-on-github] | | +| **package-generator** | [`atom/package-generator`][package-generator] | | +| **settings-view** | [`atom/settings-view`][settings-view] | | +| **snippets** | [`atom/snippets`][snippets] | | +| **solarized-dark-syntax** | [`atom/solarized-dark-syntax`][solarized-dark-syntax] | | +| **solarized-light-syntax** | [`atom/solarized-light-syntax`][solarized-light-syntax] | | +| **spell-check** | [`atom/spell-check`][spell-check] | | +| **status-bar** | [`atom/status-bar`][status-bar] | | +| **styleguide** | [`atom/styleguide`][styleguide] | | +| **symbols-view** | [`atom/symbols-view`][symbols-view] | | +| **tabs** | [`atom/tabs`][tabs] | | +| **timecop** | [`atom/timecop`][timecop] | | +| **tree-view** | [`atom/tree-view`][tree-view] | | +| **update-package-dependencies** | [`atom/update-package-dependencies`][update-package-dependencies] | | +| **welcome** | [`atom/welcome`][welcome] | | +| **whitespace** | [`atom/whitespace`][whitespace] | | +| **wrap-guide** | [`atom/wrap-guide`][wrap-guide] | | + +[about]: https://github.com/atom/about +[archive-view]: https://github.com/atom/archive-view +[atom-dark-syntax]: https://github.com/atom/atom-dark-syntax +[atom-dark-ui]: https://github.com/atom/atom-dark-ui +[atom-light-syntax]: https://github.com/atom/atom-light-syntax +[atom-light-ui]: https://github.com/atom/atom-light-ui +[autocomplete-atom-api]: https://github.com/atom/autocomplete-atom-api +[autocomplete-css]: https://github.com/atom/autocomplete-css +[autocomplete-html]: https://github.com/atom/autocomplete-html +[autocomplete-plus]: https://github.com/atom/autocomplete-plus +[autocomplete-snippets]: https://github.com/atom/autocomplete-snippets +[autoflow]: https://github.com/atom/autoflow +[autosave]: https://github.com/atom/autosave +[background-tips]: https://github.com/atom/background-tips +[base16-tomorrow-dark-theme]: https://github.com/atom/base16-tomorrow-dark-theme +[base16-tomorrow-light-theme]: https://github.com/atom/base16-tomorrow-light-theme +[bookmarks]: https://github.com/atom/bookmarks +[bracket-matcher]: https://github.com/atom/bracket-matcher +[command-palette]: https://github.com/atom/command-palette +[dalek]: https://github.com/atom/dalek +[deprecation-cop]: https://github.com/atom/deprecation-cop +[dev-live-reload]: https://github.com/atom/dev-live-reload +[encoding-selector]: https://github.com/atom/encoding-selector +[exception-reporting]: https://github.com/atom/exception-reporting +[find-and-replace]: https://github.com/atom/find-and-replace +[fuzzy-finder]: https://github.com/atom/fuzzy-finder +[git-diff]: https://github.com/atom/git-diff +[github]: https://github.com/atom/github +[go-to-line]: https://github.com/atom/go-to-line +[grammar-selector]: https://github.com/atom/grammar-selector +[image-view]: https://github.com/atom/image-view +[incompatible-packages]: https://github.com/atom/incompatible-packages +[keybinding-resolver]: https://github.com/atom/keybinding-resolver +[language-c]: https://github.com/atom/language-c +[language-clojure]: https://github.com/atom/language-clojure +[language-coffee-script]: https://github.com/atom/language-coffee-script +[language-csharp]: https://github.com/atom/language-csharp +[language-css]: https://github.com/atom/language-css +[language-gfm]: https://github.com/atom/language-gfm +[language-git]: https://github.com/atom/language-git +[language-go]: https://github.com/atom/language-go +[language-html]: https://github.com/atom/language-html +[language-hyperlink]: https://github.com/atom/language-hyperlink +[language-java]: https://github.com/atom/language-java +[language-javascript]: https://github.com/atom/language-javascript +[language-json]: https://github.com/atom/language-json +[language-less]: https://github.com/atom/language-less +[language-make]: https://github.com/atom/language-make +[language-mustache]: https://github.com/atom/language-mustache +[language-objective-c]: https://github.com/atom/language-objective-c +[language-perl]: https://github.com/atom/language-perl +[language-php]: https://github.com/atom/language-php +[language-property-list]: https://github.com/atom/language-property-list +[language-python]: https://github.com/atom/language-python +[language-ruby]: https://github.com/atom/language-ruby +[language-ruby-on-rails]: https://github.com/atom/language-ruby-on-rails +[language-sass]: https://github.com/atom/language-sass +[language-shellscript]: https://github.com/atom/language-shellscript +[language-source]: https://github.com/atom/language-source +[language-sql]: https://github.com/atom/language-sql +[language-text]: https://github.com/atom/language-text +[language-todo]: https://github.com/atom/language-todo +[language-toml]: https://github.com/atom/language-toml +[language-typescript]: https://github.com/atom/language-typescript +[language-xml]: https://github.com/atom/language-xml +[language-yaml]: https://github.com/atom/language-yaml +[line-ending-selector]: https://github.com/atom/line-ending-selector +[link]: https://github.com/atom/link +[markdown-preview]: https://github.com/atom/markdown-preview +[metrics]: https://github.com/atom/metrics +[notifications]: https://github.com/atom/notifications +[one-dark-syntax]: https://github.com/atom/one-dark-syntax +[one-dark-ui]: https://github.com/atom/one-dark-ui +[one-light-syntax]: https://github.com/atom/one-light-syntax +[one-light-ui]: https://github.com/atom/one-light-ui +[open-on-github]: https://github.com/atom/open-on-github +[package-generator]: https://github.com/atom/package-generator +[settings-view]: https://github.com/atom/settings-view +[snippets]: https://github.com/atom/snippets +[solarized-dark-syntax]: https://github.com/atom/solarized-dark-syntax +[solarized-light-syntax]: https://github.com/atom/solarized-light-syntax +[spell-check]: https://github.com/atom/spell-check +[status-bar]: https://github.com/atom/status-bar +[styleguide]: https://github.com/atom/styleguide +[symbols-view]: https://github.com/atom/symbols-view +[tabs]: https://github.com/atom/tabs +[timecop]: https://github.com/atom/timecop +[tree-view]: https://github.com/atom/tree-view +[update-package-dependencies]: https://github.com/atom/update-package-dependencies +[welcome]: https://github.com/atom/welcome +[whitespace]: https://github.com/atom/whitespace +[wrap-guide]: https://github.com/atom/wrap-guide diff --git a/packages/about/.gitignore b/packages/about/.gitignore new file mode 100644 index 000000000..ade14b919 --- /dev/null +++ b/packages/about/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +npm-debug.log +node_modules diff --git a/packages/about/LICENSE.md b/packages/about/LICENSE.md new file mode 100644 index 000000000..cf3de7776 --- /dev/null +++ b/packages/about/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2015 Machisté N. Quintana + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/about/README.md b/packages/about/README.md new file mode 100644 index 000000000..83aeb8755 --- /dev/null +++ b/packages/about/README.md @@ -0,0 +1,21 @@ +# About package + +View useful information about your Atom installation. + +![About Atom](https://cloud.githubusercontent.com/assets/16760489/19395499/69bbb780-922d-11e6-9779-2b8327027ea5.png) + +This is a package for [Atom](https://atom.io), a hackable text editor for the 21st Century. + +## Usage + +This package provides a cross-platform "About Atom" view that displays information about your Atom installation, which currently includes the current version, the license, and the Terms of Use. + +## Contributing +Always feel free to help out! Whether it's filing bugs and feature requests +or working on some of the open issues, Atom's [contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md) +will help get you started while the [guide for contributing to packages](https://github.com/atom/atom/blob/master/docs/contributing-to-packages.md) +has some extra information. + +## License + +[MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](https://github.com/atom/about/blob/master/LICENSE.md) for more details. diff --git a/packages/about/lib/about.js b/packages/about/lib/about.js new file mode 100644 index 000000000..1221bda6d --- /dev/null +++ b/packages/about/lib/about.js @@ -0,0 +1,93 @@ +const {CompositeDisposable, Emitter} = require('atom') +const AboutView = require('./components/about-view') + +// Deferred requires +let shell + +module.exports = class About { + constructor (initialState) { + this.subscriptions = new CompositeDisposable() + this.emitter = new Emitter() + + this.state = initialState + this.views = { + aboutView: null + } + + this.subscriptions.add(atom.workspace.addOpener((uriToOpen) => { + if (uriToOpen === this.state.uri) { + return this.deserialize() + } + })) + + this.subscriptions.add(atom.commands.add('atom-workspace', 'about:view-release-notes', () => { + shell = shell || require('electron').shell + shell.openExternal(this.state.updateManager.getReleaseNotesURLForCurrentVersion()) + })) + } + + destroy () { + if (this.views.aboutView) this.views.aboutView.destroy() + this.views.aboutView = null + + if (this.state.updateManager) this.state.updateManager.dispose() + this.setState({updateManager: null}) + + this.subscriptions.dispose() + } + + setState (newState) { + if (newState && typeof newState === 'object') { + let {state} = this + this.state = Object.assign({}, state, newState) + + this.didChange() + } + } + + didChange () { + this.emitter.emit('did-change') + } + + onDidChange (callback) { + this.emitter.on('did-change', callback) + } + + deserialize (state) { + if (!this.views.aboutView) { + this.setState(state) + + this.views.aboutView = new AboutView({ + uri: this.state.uri, + updateManager: this.state.updateManager, + currentAtomVersion: this.state.currentAtomVersion, + currentElectronVersion: this.state.currentElectronVersion, + currentChromeVersion: this.state.currentChromeVersion, + currentNodeVersion: this.state.currentNodeVersion, + availableVersion: this.state.updateManager.getAvailableVersion() + }) + this.handleStateChanges() + } + + return this.views.aboutView + } + + handleStateChanges () { + this.onDidChange(() => { + if (this.views.aboutView) { + this.views.aboutView.update({ + updateManager: this.state.updateManager, + currentAtomVersion: this.state.currentAtomVersion, + currentElectronVersion: this.state.currentElectronVersion, + currentChromeVersion: this.state.currentChromeVersion, + currentNodeVersion: this.state.currentNodeVersion, + availableVersion: this.state.updateManager.getAvailableVersion() + }) + } + }) + + this.state.updateManager.onDidChange(() => { + this.didChange() + }) + } +} diff --git a/packages/about/lib/components/about-status-bar.js b/packages/about/lib/components/about-status-bar.js new file mode 100644 index 000000000..d55800f5d --- /dev/null +++ b/packages/about/lib/components/about-status-bar.js @@ -0,0 +1,30 @@ +const {CompositeDisposable} = require('atom') +const etch = require('etch') +const EtchComponent = require('../etch-component') + +const $ = etch.dom + +module.exports = +class AboutStatusBar extends EtchComponent { + constructor () { + super() + this.subscriptions = new CompositeDisposable() + + this.subscriptions.add(atom.tooltips.add(this.element, {title: 'An update will be installed the next time Atom is relaunched.

Click the squirrel icon for more information.'})) + } + + handleClick () { + atom.workspace.open('atom://about') + } + + render () { + return $.div({className: 'about-release-notes inline-block', onclick: this.handleClick.bind(this)}, + $.span({type: 'button', className: 'icon icon-squirrel'}) + ) + } + + destroy () { + super.destroy() + this.subscriptions.dispose() + } +} diff --git a/packages/about/lib/components/about-view.js b/packages/about/lib/components/about-view.js new file mode 100644 index 000000000..3e370b171 --- /dev/null +++ b/packages/about/lib/components/about-view.js @@ -0,0 +1,159 @@ +const {Disposable} = require('atom') +const etch = require('etch') +const shell = require('shell') +const AtomLogo = require('./atom-logo') +const EtchComponent = require('../etch-component') +const UpdateView = require('./update-view') + +const $ = etch.dom + +module.exports = +class AboutView extends EtchComponent { + handleAtomVersionClick (e) { + e.preventDefault() + atom.clipboard.write(this.props.currentAtomVersion) + } + + handleElectronVersionClick (e) { + e.preventDefault() + atom.clipboard.write(this.props.currentElectronVersion) + } + + handleChromeVersionClick (e) { + e.preventDefault() + atom.clipboard.write(this.props.currentChromeVersion) + } + + handleNodeVersionClick (e) { + e.preventDefault() + atom.clipboard.write(this.props.currentNodeVersion) + } + + handleReleaseNotesClick (e) { + e.preventDefault() + shell.openExternal(this.props.updateManager.getReleaseNotesURLForAvailableVersion()) + } + + handleLicenseClick (e) { + e.preventDefault() + atom.commands.dispatch(atom.views.getView(atom.workspace), 'application:open-license') + } + + handleTermsOfUseClick (e) { + e.preventDefault() + shell.openExternal('https://atom.io/terms') + } + + handleHowToUpdateClick (e) { + e.preventDefault() + shell.openExternal('https://flight-manual.atom.io/getting-started/sections/installing-atom/') + } + + handleShowMoreClick (e) { + e.preventDefault() + var showMoreDiv = document.querySelector('.show-more') + var showMoreText = document.querySelector('.about-more-expand') + switch (showMoreText.textContent) { + case 'Show more': + showMoreDiv.classList.toggle('hidden') + showMoreText.textContent = 'Hide' + break + case 'Hide': + showMoreDiv.classList.toggle('hidden') + showMoreText.textContent = 'Show more' + break + } + } + + render () { + return $.div({className: 'pane-item native-key-bindings about'}, + $.div({className: 'about-container'}, + $.header({className: 'about-header'}, + $.a({className: 'about-atom-io', href: 'https://atom.io'}, + $(AtomLogo) + ), + $.div({className: 'about-header-info'}, + $.span({className: 'about-version-container inline-block atom', onclick: this.handleAtomVersionClick.bind(this)}, + $.span({className: 'about-version'}, `${this.props.currentAtomVersion} ${process.arch}`), + $.span({className: 'icon icon-clippy about-copy-version'}) + ), + $.a({className: 'about-header-release-notes', onclick: this.handleReleaseNotesClick.bind(this)}, 'Release Notes') + ), + $.span({className: 'about-version-container inline-block show-more-expand', onclick: this.handleShowMoreClick.bind(this)}, + $.span({className: 'about-more-expand'}, 'Show more') + ), + $.div({className: 'show-more hidden about-more-info'}, + $.div({className: 'about-more-info'}, + $.span({className: 'about-version-container inline-block electron', onclick: this.handleElectronVersionClick.bind(this)}, + $.span({className: 'about-more-version'}, `Electron: ${this.props.currentElectronVersion} `), + $.span({className: 'icon icon-clippy about-copy-version'}) + ) + ), + $.div({className: 'about-more-info'}, + $.span({className: 'about-version-container inline-block chrome', onclick: this.handleChromeVersionClick.bind(this)}, + $.span({className: 'about-more-version'}, `Chrome: ${this.props.currentChromeVersion} `), + $.span({className: 'icon icon-clippy about-copy-version'}) + ) + ), + $.div({className: 'about-more-info'}, + $.span({className: 'about-version-container inline-block node', onclick: this.handleNodeVersionClick.bind(this)}, + $.span({className: 'about-more-version'}, `Node: ${this.props.currentNodeVersion} `), + $.span({className: 'icon icon-clippy about-copy-version'}) + ) + ) + ) + ) + ), + + $(UpdateView, { + updateManager: this.props.updateManager, + availableVersion: this.props.availableVersion, + viewUpdateReleaseNotes: this.handleReleaseNotesClick.bind(this), + viewUpdateInstructions: this.handleHowToUpdateClick.bind(this) + }), + + $.div({className: 'about-actions group-item'}, + $.div({className: 'btn-group'}, + $.button({className: 'btn view-license', onclick: this.handleLicenseClick.bind(this)}, 'License'), + $.button({className: 'btn terms-of-use', onclick: this.handleTermsOfUseClick.bind(this)}, 'Terms of Use') + ) + ), + + $.div({className: 'about-love group-start'}, + $.span({className: 'icon icon-code'}), + $.span({className: 'inline'}, ' with '), + $.span({className: 'icon icon-heart'}), + $.span({className: 'inline'}, ' by '), + $.a({className: 'icon icon-logo-github', href: 'https://github.com'}) + ), + + $.div({className: 'about-credits group-item'}, + $.span({className: 'inline'}, 'And the awesome '), + $.a({href: 'https://github.com/atom/atom/contributors'}, 'Atom Community') + ) + ) + } + + serialize () { + return { + deserializer: this.constructor.name, + uri: this.props.uri + } + } + + onDidChangeTitle () { + return new Disposable() + } + + onDidChangeModified () { + return new Disposable() + } + + getTitle () { + return 'About' + } + + getIconName () { + return 'info' + } +} diff --git a/packages/about/lib/components/atom-logo.js b/packages/about/lib/components/atom-logo.js new file mode 100644 index 000000000..f8b620ce1 --- /dev/null +++ b/packages/about/lib/components/atom-logo.js @@ -0,0 +1,28 @@ +const etch = require('etch') +const EtchComponent = require('../etch-component') + +const $ = etch.dom + +module.exports = +class AtomLogo extends EtchComponent { + render () { + return $.svg({className: 'about-logo', width: '330px', height: '68px', viewBox: '0 0 330 68'}, + $.g({stroke: 'none', 'stroke-width': '1', fill: 'none', 'fill-rule': 'evenodd'}, + $.g({transform: 'translate(2.000000, 1.000000)'}, + $.g({transform: 'translate(96.000000, 8.000000)', fill: 'currentColor'}, + $.path({d: 'M185.498,3.399 C185.498,2.417 186.34,1.573 187.324,1.573 L187.674,1.573 C188.447,1.573 189.01,1.995 189.5,2.628 L208.676,30.862 L227.852,2.628 C228.272,1.995 228.905,1.573 229.676,1.573 L230.028,1.573 C231.01,1.573 231.854,2.417 231.854,3.399 L231.854,49.403 C231.854,50.387 231.01,51.231 230.028,51.231 C229.044,51.231 228.202,50.387 228.202,49.403 L228.202,8.246 L210.151,34.515 C209.729,35.148 209.237,35.428 208.606,35.428 C207.973,35.428 207.481,35.148 207.061,34.515 L189.01,8.246 L189.01,49.475 C189.01,50.457 188.237,51.231 187.254,51.231 C186.27,51.231 185.498,50.458 185.498,49.475 L185.498,3.399 L185.498,3.399 Z'}), + $.path({d: 'M113.086,26.507 L113.086,26.367 C113.086,12.952 122.99,0.941 137.881,0.941 C152.77,0.941 162.533,12.811 162.533,26.225 L162.533,26.367 C162.533,39.782 152.629,51.792 137.74,51.792 C122.85,51.792 113.086,39.923 113.086,26.507 M158.74,26.507 L158.74,26.367 C158.74,14.216 149.89,4.242 137.74,4.242 C125.588,4.242 116.879,14.075 116.879,26.225 L116.879,26.367 C116.879,38.518 125.729,48.491 137.881,48.491 C150.031,48.491 158.74,38.658 158.74,26.507'}), + $.path({d: 'M76.705,5.155 L60.972,5.155 C60.06,5.155 59.287,4.384 59.287,3.469 C59.287,2.556 60.059,1.783 60.972,1.783 L96.092,1.783 C97.004,1.783 97.778,2.555 97.778,3.469 C97.778,4.383 97.005,5.155 96.092,5.155 L80.358,5.155 L80.358,49.405 C80.358,50.387 79.516,51.231 78.532,51.231 C77.55,51.231 76.706,50.387 76.706,49.405 L76.706,5.155 L76.705,5.155 Z'}), + $.path({d: 'M0.291,48.562 L21.291,3.05 C21.783,1.995 22.485,1.292 23.75,1.292 L23.891,1.292 C25.155,1.292 25.858,1.995 26.348,3.05 L47.279,48.421 C47.49,48.843 47.56,49.194 47.56,49.546 C47.56,50.458 46.788,51.231 45.803,51.231 C44.961,51.231 44.329,50.599 43.978,49.826 L38.219,37.183 L9.21,37.183 L3.45,49.897 C3.099,50.739 2.538,51.231 1.694,51.231 C0.781,51.231 0.008,50.529 0.008,49.685 C0.009,49.404 0.08,48.983 0.291,48.562 L0.291,48.562 Z M36.673,33.882 L23.749,5.437 L10.755,33.882 L36.673,33.882 L36.673,33.882 Z'}) + ), + $.g({}, + $.path({d: 'M40.363,32.075 C40.874,34.44 39.371,36.77 37.006,37.282 C34.641,37.793 32.311,36.29 31.799,33.925 C31.289,31.56 32.791,29.23 35.156,28.718 C37.521,28.207 39.851,29.71 40.363,32.075', fill: 'currentColor'}), + $.path({d: 'M48.578,28.615 C56.851,45.587 58.558,61.581 52.288,64.778 C45.822,68.076 33.326,56.521 24.375,38.969 C15.424,21.418 13.409,4.518 19.874,1.221 C22.689,-0.216 26.648,1.166 30.959,4.629', stroke: 'currentColor', 'stroke-width': '3.08', 'stroke-linecap': 'round'}), + $.path({d: 'M7.64,39.45 C2.806,36.94 -0.009,33.915 0.154,30.79 C0.531,23.542 16.787,18.497 36.462,19.52 C56.137,20.544 71.781,27.249 71.404,34.497 C71.241,37.622 68.127,40.338 63.06,42.333', stroke: 'currentColor', 'stroke-width': '3.08', 'stroke-linecap': 'round'}), + $.path({d: 'M28.828,59.354 C23.545,63.168 18.843,64.561 15.902,62.653 C9.814,58.702 13.572,42.102 24.296,25.575 C35.02,9.048 48.649,-1.149 54.736,2.803 C57.566,4.639 58.269,9.208 57.133,15.232', stroke: 'currentColor', 'stroke-width': '3.08', 'stroke-linecap': 'round'}) + ) + ) + ) + ) + } +} diff --git a/packages/about/lib/components/update-view.js b/packages/about/lib/components/update-view.js new file mode 100644 index 000000000..a4e97e74d --- /dev/null +++ b/packages/about/lib/components/update-view.js @@ -0,0 +1,121 @@ +const etch = require('etch') +const EtchComponent = require('../etch-component') +const UpdateManager = require('../update-manager') + +const $ = etch.dom + +module.exports = +class UpdateView extends EtchComponent { + constructor (props) { + super(props) + + if (this.props.updateManager.getAutoUpdatesEnabled() && this.props.updateManager.getState() === UpdateManager.State.Idle) { + this.props.updateManager.checkForUpdate() + } + } + + handleAutoUpdateCheckbox (e) { + atom.config.set('core.automaticallyUpdate', e.target.checked) + } + + shouldUpdateActionButtonBeDisabled () { + let {state} = this.props.updateManager + return state === UpdateManager.State.CheckingForUpdate || state === UpdateManager.State.DownloadingUpdate + } + + executeUpdateAction () { + if (this.props.updateManager.state === UpdateManager.State.UpdateAvailableToInstall) { + this.props.updateManager.restartAndInstallUpdate() + } else { + this.props.updateManager.checkForUpdate() + } + } + + renderUpdateStatus () { + let updateStatus = '' + + switch (this.props.updateManager.state) { + case UpdateManager.State.Idle: + updateStatus = $.div({className: 'about-updates-item is-shown about-default-update-message'}, + this.props.updateManager.getAutoUpdatesEnabled() ? 'Atom will check for updates automatically' : 'Automatic updates are disabled please check manually' + ) + break + case UpdateManager.State.CheckingForUpdate: + updateStatus = $.div({className: 'about-updates-item app-checking-for-updates'}, + $.span({className: 'about-updates-label icon icon-search'}, 'Checking for updates...') + ) + break + case UpdateManager.State.DownloadingUpdate: + updateStatus = $.div({className: 'about-updates-item app-downloading-update'}, + $.span({className: 'loading loading-spinner-tiny inline-block'}), + $.span({className: 'about-updates-label'}, 'Downloading update') + ) + break + case UpdateManager.State.UpdateAvailableToInstall: + updateStatus = $.div({className: 'about-updates-item app-update-available-to-install'}, + $.span({className: 'about-updates-label icon icon-squirrel'}, 'New update'), + $.span({className: 'about-updates-version'}, this.props.availableVersion), + $.a({className: 'about-updates-release-notes', onclick: this.props.viewUpdateReleaseNotes}, 'Release Notes') + ) + break + case UpdateManager.State.UpToDate: + updateStatus = $.div({className: 'about-updates-item app-up-to-date'}, + $.span({className: 'icon icon-check'}), + $.span({className: 'about-updates-label is-strong'}, 'Atom is up to date!') + ) + break + case UpdateManager.State.Unsupported: + updateStatus = $.div({className: 'about-updates-item app-unsupported'}, + $.span({className: 'about-updates-label is-strong'}, 'Your system does not support automatic updates'), + $.a({className: 'about-updates-instructions', onclick: this.props.viewUpdateInstructions}, 'How to update') + ) + break + case UpdateManager.State.Error: + updateStatus = $.div({className: 'about-updates-item app-update-error'}, + $.span({className: 'icon icon-x'}), + $.span({className: 'about-updates-label app-error-message is-strong'}, this.props.updateManager.getErrorMessage()) + ) + break + } + + return updateStatus + } + + render () { + return $.div({className: 'about-updates group-start'}, + $.div({className: 'about-updates-box'}, + $.div({className: 'about-updates-status'}, this.renderUpdateStatus()), + $.button( + { + className: 'btn about-update-action-button', + disabled: this.shouldUpdateActionButtonBeDisabled(), + onclick: this.executeUpdateAction.bind(this), + style: { + display: this.props.updateManager.state === UpdateManager.State.Unsupported ? 'none' : 'block' + } + }, + this.props.updateManager.state === 'update-available' ? 'Restart and install' : 'Check now' + ) + ), + $.div( + { + className: 'about-auto-updates', + style: { + display: this.props.updateManager.state === UpdateManager.State.Unsupported ? 'none' : 'block' + } + }, + $.label({}, + $.input( + { + className: 'input-checkbox', + type: 'checkbox', + checked: this.props.updateManager.getAutoUpdatesEnabled(), + onchange: this.handleAutoUpdateCheckbox.bind(this) + } + ), + $.span({}, 'Automatically download updates') + ) + ) + ) + } +} diff --git a/packages/about/lib/etch-component.js b/packages/about/lib/etch-component.js new file mode 100644 index 000000000..f75edce94 --- /dev/null +++ b/packages/about/lib/etch-component.js @@ -0,0 +1,57 @@ +const etch = require('etch') + +/* + Public: Abstract class for handling the initialization + boilerplate of an Etch component. +*/ +module.exports = +class EtchComponent { + constructor (props) { + this.props = props + + etch.initialize(this) + EtchComponent.setScheduler(atom.views) + } + + /* + Public: Gets the scheduler Etch uses for coordinating DOM updates. + + Returns a {Scheduler} + */ + static getScheduler () { + return etch.getScheduler() + } + + /* + Public: Sets the scheduler Etch uses for coordinating DOM updates. + + * `scheduler` {Scheduler} + */ + static setScheduler (scheduler) { + etch.setScheduler(scheduler) + } + + /* + Public: Updates the component's properties and re-renders it. Only the + properties you specify in this object will update – any other properties + the component stores will be unaffected. + + * `props` an {Object} representing the properties you want to update + */ + update (props) { + let oldProps = this.props + this.props = Object.assign({}, oldProps, props) + return etch.update(this) + } + + /* + Public: Destroys the component, removing it from the DOM. + */ + destroy () { + etch.destroy(this) + } + + render () { + throw new Error('Etch components must implement a `render` method') + } +} diff --git a/packages/about/lib/main.js b/packages/about/lib/main.js new file mode 100644 index 000000000..f0b855649 --- /dev/null +++ b/packages/about/lib/main.js @@ -0,0 +1,96 @@ +const {CompositeDisposable} = require('atom') +const semver = require('semver') +const UpdateManager = require('./update-manager') +const About = require('./about') +const StatusBarView = require('./components/about-status-bar') +let updateManager + +// The local storage key for the available update version. +const AvailableUpdateVersion = 'about:version-available' +const AboutURI = 'atom://about' + +module.exports = { + activate () { + this.subscriptions = new CompositeDisposable() + + this.createModel() + + let availableVersion = window.localStorage.getItem(AvailableUpdateVersion) + if (atom.getReleaseChannel() === 'dev' || (availableVersion && semver.lte(availableVersion, atom.getVersion()))) { + this.clearUpdateState() + } + + this.subscriptions.add(updateManager.onDidChange(() => { + if (updateManager.getState() === UpdateManager.State.UpdateAvailableToInstall) { + window.localStorage.setItem(AvailableUpdateVersion, updateManager.getAvailableVersion()) + this.showStatusBarIfNeeded() + } + })) + + this.subscriptions.add(atom.commands.add('atom-workspace', 'about:clear-update-state', () => { + this.clearUpdateState() + })) + }, + + deactivate () { + this.model.destroy() + if (this.statusBarTile) this.statusBarTile.destroy() + + if (updateManager) { + updateManager.dispose() + updateManager = undefined + } + }, + + clearUpdateState () { + window.localStorage.removeItem(AvailableUpdateVersion) + }, + + consumeStatusBar (statusBar) { + this.statusBar = statusBar + this.showStatusBarIfNeeded() + }, + + deserializeAboutView (state) { + if (!this.model) { + this.createModel() + } + + return this.model.deserialize(state) + }, + + createModel () { + updateManager = updateManager || new UpdateManager() + + this.model = new About({ + uri: AboutURI, + currentAtomVersion: atom.getVersion(), + currentElectronVersion: process.versions.electron, + currentChromeVersion: process.versions.chrome, + currentNodeVersion: process.version, + updateManager: updateManager + }) + }, + + isUpdateAvailable () { + let availableVersion = window.localStorage.getItem(AvailableUpdateVersion) + return availableVersion && semver.gt(availableVersion, atom.getVersion()) + }, + + showStatusBarIfNeeded () { + if (this.isUpdateAvailable() && this.statusBar) { + let statusBarView = new StatusBarView() + + if (this.statusBarTile) { + this.statusBarTile.destroy() + } + + this.statusBarTile = this.statusBar.addRightTile({ + item: statusBarView, + priority: -100 + }) + + return this.statusBarTile + } + } +} diff --git a/packages/about/lib/update-manager.js b/packages/about/lib/update-manager.js new file mode 100644 index 000000000..0db55f08d --- /dev/null +++ b/packages/about/lib/update-manager.js @@ -0,0 +1,146 @@ +const {Emitter, CompositeDisposable} = require('atom') + +const Unsupported = 'unsupported' +const Idle = 'idle' +const CheckingForUpdate = 'checking' +const DownloadingUpdate = 'downloading' +const UpdateAvailableToInstall = 'update-available' +const UpToDate = 'no-update-available' +const ErrorState = 'error' + +let UpdateManager = class UpdateManager { + constructor () { + this.emitter = new Emitter() + this.currentVersion = atom.getVersion() + this.availableVersion = atom.getVersion() + this.resetState() + this.listenForAtomEvents() + } + + listenForAtomEvents () { + this.subscriptions = new CompositeDisposable() + + this.subscriptions.add( + atom.autoUpdater.onDidBeginCheckingForUpdate(() => { + this.setState(CheckingForUpdate) + }), + atom.autoUpdater.onDidBeginDownloadingUpdate(() => { + this.setState(DownloadingUpdate) + }), + atom.autoUpdater.onDidCompleteDownloadingUpdate(({releaseVersion}) => { + this.setAvailableVersion(releaseVersion) + }), + atom.autoUpdater.onUpdateNotAvailable(() => { + this.setState(UpToDate) + }), + atom.autoUpdater.onUpdateError(() => { + this.setState(ErrorState) + }), + atom.config.observe('core.automaticallyUpdate', (value) => { + this.autoUpdatesEnabled = value + this.emitDidChange() + }) + ) + + // TODO: When https://github.com/atom/electron/issues/4587 is closed we can add this support. + // atom.autoUpdater.onUpdateAvailable => + // @find('.about-updates-item').removeClass('is-shown') + // @updateAvailable.addClass('is-shown') + } + + dispose () { + this.subscriptions.dispose() + } + + onDidChange (callback) { + return this.emitter.on('did-change', callback) + } + + emitDidChange () { + this.emitter.emit('did-change') + } + + getAutoUpdatesEnabled () { + return this.autoUpdatesEnabled && this.state !== UpdateManager.State.Unsupported + } + + setAutoUpdatesEnabled (enabled) { + return atom.config.set('core.automaticallyUpdate', enabled) + } + + getErrorMessage () { + return atom.autoUpdater.getErrorMessage() + } + + getState () { + return this.state + } + + setState (state) { + this.state = state + this.emitDidChange() + } + + resetState () { + this.state = atom.autoUpdater.platformSupportsUpdates() ? atom.autoUpdater.getState() : Unsupported + this.emitDidChange() + } + + getAvailableVersion () { + return this.availableVersion + } + + setAvailableVersion (version) { + this.availableVersion = version + + if (this.availableVersion !== this.currentVersion) { + this.state = UpdateAvailableToInstall + } else { + this.state = UpToDate + } + + this.emitDidChange() + } + + checkForUpdate () { + atom.autoUpdater.checkForUpdate() + } + + restartAndInstallUpdate () { + atom.autoUpdater.restartAndInstallUpdate() + } + + getReleaseNotesURLForCurrentVersion () { + return this.getReleaseNotesURLForVersion(this.currentVersion) + } + + getReleaseNotesURLForAvailableVersion () { + return this.getReleaseNotesURLForVersion(this.availableVersion) + } + + getReleaseNotesURLForVersion (appVersion) { + // Dev versions will not have a releases page + if (appVersion.indexOf('dev') > -1) { + return 'https://atom.io/releases' + } + + if (!appVersion.startsWith('v')) { + appVersion = `v${appVersion}` + } + + const releaseRepo = appVersion.indexOf('nightly') > -1 ? 'atom-nightly-releases' : 'atom' + return `https://github.com/atom/${releaseRepo}/releases/tag/${appVersion}` + } +} + +UpdateManager.State = { + Unsupported: Unsupported, + Idle: Idle, + CheckingForUpdate: CheckingForUpdate, + DownloadingUpdate: DownloadingUpdate, + UpdateAvailableToInstall: UpdateAvailableToInstall, + UpToDate: UpToDate, + Error: ErrorState +} + +module.exports = UpdateManager diff --git a/packages/about/package-lock.json b/packages/about/package-lock.json new file mode 100644 index 000000000..ab0d5c51b --- /dev/null +++ b/packages/about/package-lock.json @@ -0,0 +1,1806 @@ +{ + "name": "about", + "version": "1.9.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "dev": true, + "requires": { + "color-name": "1.1.1" + } + }, + "color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "deglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", + "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^3.0.9", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.18.2.tgz", + "integrity": "sha512-qy4i3wODqKMYfz9LUI8N2qYDkHkoieTbiHpMrYUI/WbjhXJQr7lI4VngixTgaG+yHX+NBCv7nW4hA0ShbvaNKw==", + "dev": true, + "requires": { + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.2", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + } + }, + "eslint-config-standard": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz", + "integrity": "sha512-oDdENzpViEe5fwuRCWla7AXQd++/oyIp8zP+iP9jiUPG6NBj3SHgdgtl/kTn00AjeN+1HNvavTKmYbMo+xMOlw==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-5.0.0.tgz", + "integrity": "sha512-rLToPAEqLMPBfWnYTu6xRhm2OWziS2n40QFqJ8jAM8NSVzeVKTa3nclhsU4DpPJQRY60F34Oo1wi/71PN/eITg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "eslint-module-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz", + "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz", + "integrity": "sha1-JgAu+/ylmJtyiKwEdQi9JPIXsWk=", + "dev": true, + "requires": { + "builtin-modules": "^1.1.1", + "contains-path": "^0.1.0", + "debug": "^2.6.8", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.1", + "eslint-module-utils": "^2.1.1", + "has": "^1.0.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.3", + "read-pkg-up": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz", + "integrity": "sha512-Q/Cc2sW1OAISDS+Ji6lZS2KV4b7ueA/WydVWd1BECTQwVvfQy5JAi3glhINoKzoMnfnuRgNP+ZWKrGAbp3QDxw==", + "dev": true, + "requires": { + "ignore": "^3.3.6", + "minimatch": "^3.0.4", + "resolve": "^1.3.3", + "semver": "^5.4.1" + } + }, + "eslint-plugin-promise": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz", + "integrity": "sha512-2WO+ZFh7vxUKRfR0cOIMrWgYKdR6S1AlOezw6pC52B6oYpd5WFghN+QHxvrRdZMtbo8h3dfUZ2o1rWb0UPbKtg==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz", + "integrity": "sha512-KC7Snr4YsWZD5flu6A5c0AcIZidzW3Exbqp7OT67OaD2AppJtlBr/GuPrW/vaQM/yfZotEvKAdrxrO+v8vwYJA==", + "dev": true, + "requires": { + "doctrine": "^2.0.2", + "has": "^1.0.1", + "jsx-ast-utils": "^2.0.1", + "prop-types": "^15.6.0" + } + }, + "eslint-plugin-standard": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz", + "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=", + "dev": true + }, + "eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etch": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/etch/-/etch-0.9.0.tgz", + "integrity": "sha1-CSJpiPLO4GkL3yCMyyXkFNXfrV8=" + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", + "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "jsx-ast-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", + "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", + "dev": true, + "requires": { + "array-includes": "^3.0.3" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "prop-types": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", + "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "dev": true, + "requires": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "*" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "standard": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/standard/-/standard-11.0.1.tgz", + "integrity": "sha512-nu0jAcHiSc8H+gJCXeiziMVZNDYi8MuqrYJKxTgjP4xKXZMKm311boqQIzDrYI/ktosltxt2CbDjYQs9ANC8IA==", + "dev": true, + "requires": { + "eslint": "~4.18.0", + "eslint-config-standard": "11.0.0", + "eslint-config-standard-jsx": "5.0.0", + "eslint-plugin-import": "~2.9.0", + "eslint-plugin-node": "~6.0.0", + "eslint-plugin-promise": "~3.7.0", + "eslint-plugin-react": "~7.7.0", + "eslint-plugin-standard": "~3.0.1", + "standard-engine": "~8.0.0" + } + }, + "standard-engine": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-8.0.1.tgz", + "integrity": "sha512-LA531C3+nljom/XRvdW/hGPXwmilRkaRkENhO3FAGF1Vtq/WtCXzgmnc5S6vUHHsgv534MRy02C1ikMwZXC+tw==", + "dev": true, + "requires": { + "deglob": "^2.1.0", + "get-stdin": "^6.0.0", + "minimist": "^1.1.0", + "pkg-conf": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} diff --git a/packages/about/package.json b/packages/about/package.json new file mode 100644 index 000000000..a96955185 --- /dev/null +++ b/packages/about/package.json @@ -0,0 +1,44 @@ +{ + "name": "about", + "author": "Machisté N. Quintana ", + "main": "./lib/main", + "version": "1.9.1", + "description": "View useful information about your Atom installation.", + "keywords": [], + "repository": "https://github.com/atom/about", + "license": "MIT", + "scripts": { + "lint": "standard" + }, + "engines": { + "atom": ">=1.7 <2.0.0" + }, + "dependencies": { + "etch": "0.9.0", + "semver": "^5.5.0" + }, + "devDependencies": { + "standard": "^11.0.0" + }, + "consumedServices": { + "status-bar": { + "versions": { + "^1.0.0": "consumeStatusBar" + } + } + }, + "deserializers": { + "AboutView": "deserializeAboutView" + }, + "standard": { + "env": [ + "browser", + "node", + "atomtest", + "jasmine" + ], + "globals": [ + "atom" + ] + } +} diff --git a/packages/about/spec/about-spec.js b/packages/about/spec/about-spec.js new file mode 100644 index 000000000..60c4136d8 --- /dev/null +++ b/packages/about/spec/about-spec.js @@ -0,0 +1,101 @@ +const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./helpers/async-spec-helpers') // eslint-disable-line no-unused-vars + +describe('About', () => { + let workspaceElement + + beforeEach(async () => { + let storage = {} + + spyOn(window.localStorage, 'setItem').andCallFake((key, value) => { + storage[key] = value + }) + spyOn(window.localStorage, 'getItem').andCallFake((key) => { + return storage[key] + }) + + workspaceElement = atom.views.getView(atom.workspace) + await atom.packages.activatePackage('about') + }) + + it('deserializes correctly', () => { + let deserializedAboutView = atom.deserializers.deserialize({ + deserializer: 'AboutView', + uri: 'atom://about' + }) + + expect(deserializedAboutView).toBeTruthy() + }) + + describe('when the about:about-atom command is triggered', () => { + it('shows the About Atom view', async () => { + // Attaching the workspaceElement to the DOM is required to allow the + // `toBeVisible()` matchers to work. Anything testing visibility or focus + // requires that the workspaceElement is on the DOM. Tests that attach the + // workspaceElement to the DOM are generally slower than those off DOM. + jasmine.attachToDOM(workspaceElement) + + expect(workspaceElement.querySelector('.about')).not.toExist() + await atom.workspace.open('atom://about') + + let aboutElement = workspaceElement.querySelector('.about') + expect(aboutElement).toBeVisible() + }) + }) + + describe('when the Atom version number is clicked', () => { + it('copies the version number to the clipboard', async () => { + await atom.workspace.open('atom://about') + + let aboutElement = workspaceElement.querySelector('.about') + let versionContainer = aboutElement.querySelector('.atom') + versionContainer.click() + expect(atom.clipboard.read()).toBe(atom.getVersion()) + }) + }) + + describe('when the show more link is clicked', () => { + it('expands to show additional version numbers', async () => { + await atom.workspace.open('atom://about') + jasmine.attachToDOM(workspaceElement) + + let aboutElement = workspaceElement.querySelector('.about') + let showMoreElement = aboutElement.querySelector('.show-more-expand') + let moreInfoElement = workspaceElement.querySelector('.show-more') + showMoreElement.click() + expect(moreInfoElement).toBeVisible() + }) + }) + + describe('when the Electron version number is clicked', () => { + it('copies the version number to the clipboard', async () => { + await atom.workspace.open('atom://about') + + let aboutElement = workspaceElement.querySelector('.about') + let versionContainer = aboutElement.querySelector('.electron') + versionContainer.click() + expect(atom.clipboard.read()).toBe(process.versions.electron) + }) + }) + + describe('when the Chrome version number is clicked', () => { + it('copies the version number to the clipboard', async () => { + await atom.workspace.open('atom://about') + + let aboutElement = workspaceElement.querySelector('.about') + let versionContainer = aboutElement.querySelector('.chrome') + versionContainer.click() + expect(atom.clipboard.read()).toBe(process.versions.chrome) + }) + }) + + describe('when the Node version number is clicked', () => { + it('copies the version number to the clipboard', async () => { + await atom.workspace.open('atom://about') + + let aboutElement = workspaceElement.querySelector('.about') + let versionContainer = aboutElement.querySelector('.node') + versionContainer.click() + expect(atom.clipboard.read()).toBe(process.version) + }) + }) +}) diff --git a/packages/about/spec/about-status-bar-spec.js b/packages/about/spec/about-status-bar-spec.js new file mode 100644 index 000000000..a611af67a --- /dev/null +++ b/packages/about/spec/about-status-bar-spec.js @@ -0,0 +1,179 @@ +const {it, fit, ffit, fffit, beforeEach, afterEach, conditionPromise} = require('./helpers/async-spec-helpers') // eslint-disable-line no-unused-vars +const MockUpdater = require('./mocks/updater') + +describe('the status bar', () => { + let atomVersion + let workspaceElement + + beforeEach(async () => { + let storage = {} + + spyOn(window.localStorage, 'setItem').andCallFake((key, value) => { + storage[key] = value + }) + spyOn(window.localStorage, 'getItem').andCallFake((key) => { + return storage[key] + }) + spyOn(atom, 'getVersion').andCallFake(() => { + return atomVersion + }) + + workspaceElement = atom.views.getView(atom.workspace) + + await atom.packages.activatePackage('status-bar') + await atom.workspace.open('sample.js') + }) + + afterEach(async () => { + await atom.packages.deactivatePackage('about') + await atom.packages.deactivatePackage('status-bar') + }) + + describe('on a stable version', function () { + beforeEach(async () => { + atomVersion = '1.2.3' + + await atom.packages.activatePackage('about') + }) + + describe('with no update', () => { + it('does not show the view', () => { + expect(workspaceElement).not.toContain('.about-release-notes') + }) + }) + + describe('with an update', () => { + it('shows the view when the update finishes downloading', () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + expect(workspaceElement).toContain('.about-release-notes') + }) + + describe('clicking on the status', () => { + it('opens the about page', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + workspaceElement.querySelector('.about-release-notes').click() + await conditionPromise(() => workspaceElement.querySelector('.about')) + expect(workspaceElement.querySelector('.about')).toExist() + }) + }) + + it('continues to show the squirrel until Atom is updated to the new version', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + expect(workspaceElement).toContain('.about-release-notes') + + await atom.packages.deactivatePackage('about') + expect(workspaceElement).not.toContain('.about-release-notes') + + await atom.packages.activatePackage('about') + await Promise.resolve() // Service consumption hooks are deferred until the next tick + expect(workspaceElement).toContain('.about-release-notes') + + await atom.packages.deactivatePackage('about') + expect(workspaceElement).not.toContain('.about-release-notes') + + atomVersion = '42.0.0' + await atom.packages.activatePackage('about') + + await Promise.resolve() // Service consumption hooks are deferred until the next tick + expect(workspaceElement).not.toContain('.about-release-notes') + }) + + it('does not show the view if Atom is updated to a newer version than notified', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + + await atom.packages.deactivatePackage('about') + + atomVersion = '43.0.0' + await atom.packages.activatePackage('about') + + await Promise.resolve() // Service consumption hooks are deferred until the next tick + expect(workspaceElement).not.toContain('.about-release-notes') + }) + }) + }) + + describe('on a beta version', function () { + beforeEach(async () => { + atomVersion = '1.2.3-beta4' + + await atom.packages.activatePackage('about') + }) + + describe('with no update', () => { + it('does not show the view', () => { + expect(workspaceElement).not.toContain('.about-release-notes') + }) + }) + + describe('with an update', () => { + it('shows the view when the update finishes downloading', () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + expect(workspaceElement).toContain('.about-release-notes') + }) + + describe('clicking on the status', () => { + it('opens the about page', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + workspaceElement.querySelector('.about-release-notes').click() + await conditionPromise(() => workspaceElement.querySelector('.about')) + expect(workspaceElement.querySelector('.about')).toExist() + }) + }) + + it('continues to show the squirrel until Atom is updated to the new version', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + expect(workspaceElement).toContain('.about-release-notes') + + await atom.packages.deactivatePackage('about') + expect(workspaceElement).not.toContain('.about-release-notes') + + await atom.packages.activatePackage('about') + await Promise.resolve() // Service consumption hooks are deferred until the next tick + expect(workspaceElement).toContain('.about-release-notes') + + await atom.packages.deactivatePackage('about') + expect(workspaceElement).not.toContain('.about-release-notes') + + atomVersion = '42.0.0' + await atom.packages.activatePackage('about') + + await Promise.resolve() // Service consumption hooks are deferred until the next tick + expect(workspaceElement).not.toContain('.about-release-notes') + }) + + it('does not show the view if Atom is updated to a newer version than notified', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + + await atom.packages.deactivatePackage('about') + + atomVersion = '43.0.0' + await atom.packages.activatePackage('about') + + await Promise.resolve() // Service consumption hooks are deferred until the next tick + expect(workspaceElement).not.toContain('.about-release-notes') + }) + }) + }) + + describe('on a development version', function () { + beforeEach(async () => { + atomVersion = '1.2.3-dev-0123abcd' + + await atom.packages.activatePackage('about') + }) + + describe('with no update', () => { + it('does not show the view', () => { + expect(workspaceElement).not.toContain('.about-release-notes') + }) + }) + + describe('with a previously downloaded update', () => { + it('does not show the view', () => { + window.localStorage.setItem('about:version-available', '42.0.0') + + expect(workspaceElement).not.toContain('.about-release-notes') + }) + }) + }) +}) diff --git a/packages/about/spec/helpers/async-spec-helpers.js b/packages/about/spec/helpers/async-spec-helpers.js new file mode 100644 index 000000000..377024691 --- /dev/null +++ b/packages/about/spec/helpers/async-spec-helpers.js @@ -0,0 +1,65 @@ +/** @babel */ + +const {now} = Date +const {setTimeout} = global + +export function beforeEach (fn) { + global.beforeEach(function () { + const result = fn() + if (result instanceof Promise) { + waitsForPromise(() => result) + } + }) +} + +export function afterEach (fn) { + global.afterEach(function () { + const result = fn() + if (result instanceof Promise) { + waitsForPromise(() => result) + } + }) +} + +['it', 'fit', 'ffit', 'fffit'].forEach(function (name) { + module.exports[name] = function (description, fn) { + global[name](description, function () { + const result = fn() + if (result instanceof Promise) { + waitsForPromise(() => result) + } + }) + } +}) + +export async function conditionPromise (condition) { + const startTime = now() + + while (true) { + await timeoutPromise(100) + + if (await condition()) { + return + } + + if (now() - startTime > 5000) { + throw new Error('Timed out waiting on condition') + } + } +} + +export function timeoutPromise (timeout) { + return new Promise(function (resolve) { + setTimeout(resolve, timeout) + }) +} + +function waitsForPromise (fn) { + const promise = fn() + global.waitsFor('spec promise to resolve', function (done) { + promise.then(done, function (error) { + jasmine.getEnv().currentSpec.fail(error) + done() + }) + }) +} diff --git a/packages/about/spec/mocks/updater.js b/packages/about/spec/mocks/updater.js new file mode 100644 index 000000000..c96ea4ec1 --- /dev/null +++ b/packages/about/spec/mocks/updater.js @@ -0,0 +1,21 @@ +module.exports = { + updateError () { + atom.autoUpdater.emitter.emit('update-error') + }, + + checkForUpdate () { + atom.autoUpdater.emitter.emit('did-begin-checking-for-update') + }, + + updateNotAvailable () { + atom.autoUpdater.emitter.emit('update-not-available') + }, + + downloadUpdate () { + atom.autoUpdater.emitter.emit('did-begin-downloading-update') + }, + + finishDownloadingUpdate (releaseVersion) { + atom.autoUpdater.emitter.emit('did-complete-downloading-update', {releaseVersion}) + } +} diff --git a/packages/about/spec/update-manager-spec.js b/packages/about/spec/update-manager-spec.js new file mode 100644 index 000000000..294e33b8b --- /dev/null +++ b/packages/about/spec/update-manager-spec.js @@ -0,0 +1,22 @@ +const UpdateManager = require('../lib/update-manager') + +describe('UpdateManager', () => { + let updateManager + + beforeEach(() => { + updateManager = new UpdateManager() + }) + + describe('::getReleaseNotesURLForVersion', () => { + it('returns atom.io releases when dev version', () => { + expect(updateManager.getReleaseNotesURLForVersion('1.7.0-dev-e44b57d')).toContain('atom.io/releases') + }) + + it('returns the page for the release when not a dev version', () => { + expect(updateManager.getReleaseNotesURLForVersion('1.7.0')).toContain('atom/atom/releases/tag/v1.7.0') + expect(updateManager.getReleaseNotesURLForVersion('v1.7.0')).toContain('atom/atom/releases/tag/v1.7.0') + expect(updateManager.getReleaseNotesURLForVersion('1.7.0-beta10')).toContain('atom/atom/releases/tag/v1.7.0-beta10') + expect(updateManager.getReleaseNotesURLForVersion('1.7.0-nightly10')).toContain('atom/atom-nightly-releases/releases/tag/v1.7.0-nightly10') + }) + }) +}) diff --git a/packages/about/spec/update-view-spec.js b/packages/about/spec/update-view-spec.js new file mode 100644 index 000000000..83ddf5ac1 --- /dev/null +++ b/packages/about/spec/update-view-spec.js @@ -0,0 +1,280 @@ +const {shell} = require('electron') +const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./helpers/async-spec-helpers') // eslint-disable-line no-unused-vars +const main = require('../lib/main') +const AboutView = require('../lib/components/about-view') +const UpdateView = require('../lib/components/update-view') +const MockUpdater = require('./mocks/updater') + +describe('UpdateView', () => { + let aboutElement + let updateManager + let workspaceElement + let scheduler + + beforeEach(async () => { + let storage = {} + + spyOn(window.localStorage, 'setItem').andCallFake((key, value) => { + storage[key] = value + }) + spyOn(window.localStorage, 'getItem').andCallFake((key) => { + return storage[key] + }) + + workspaceElement = atom.views.getView(atom.workspace) + await atom.packages.activatePackage('about') + spyOn(atom.autoUpdater, 'getState').andReturn('idle') + spyOn(atom.autoUpdater, 'checkForUpdate') + spyOn(atom.autoUpdater, 'platformSupportsUpdates').andReturn(true) + }) + + describe('when the About page is open', () => { + beforeEach(async () => { + jasmine.attachToDOM(workspaceElement) + await atom.workspace.open('atom://about') + aboutElement = workspaceElement.querySelector('.about') + updateManager = main.model.state.updateManager + scheduler = AboutView.getScheduler() + }) + + describe('when the updates are not supported by the platform', () => { + beforeEach(async () => { + atom.autoUpdater.platformSupportsUpdates.andReturn(false) + updateManager.resetState() + await scheduler.getNextUpdatePromise() + }) + + it('hides the auto update UI and shows the update instructions link', async () => { + expect(aboutElement.querySelector('.about-update-action-button')).not.toBeVisible() + expect(aboutElement.querySelector('.about-auto-updates')).not.toBeVisible() + }) + + it('opens the update instructions page when the instructions link is clicked', async () => { + spyOn(shell, 'openExternal') + let link = aboutElement.querySelector('.app-unsupported .about-updates-instructions') + link.click() + + let args = shell.openExternal.mostRecentCall.args + expect(shell.openExternal).toHaveBeenCalled() + expect(args[0]).toContain('installing-atom') + }) + }) + + describe('when updates are supported by the platform', () => { + beforeEach(async () => { + atom.autoUpdater.platformSupportsUpdates.andReturn(true) + updateManager.resetState() + await scheduler.getNextUpdatePromise() + }) + + it('shows the auto update UI', () => { + expect(aboutElement.querySelector('.about-updates')).toBeVisible() + }) + + it('shows the correct panels when the app checks for updates and there is no update available', async () => { + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + + MockUpdater.checkForUpdate() + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible() + expect(aboutElement.querySelector('.app-checking-for-updates')).toBeVisible() + + MockUpdater.updateNotAvailable() + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-up-to-date')).toBeVisible() + expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() + }) + + it('shows the correct panels when the app checks for updates and encounters an error', async () => { + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + + MockUpdater.checkForUpdate() + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible() + expect(aboutElement.querySelector('.app-checking-for-updates')).toBeVisible() + + spyOn(atom.autoUpdater, 'getErrorMessage').andReturn('an error message') + MockUpdater.updateError() + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-update-error')).toBeVisible() + expect(aboutElement.querySelector('.app-error-message').textContent).toBe('an error message') + expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + }) + + it('shows the correct panels and button states when the app checks for updates and an update is downloaded', async () => { + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + + MockUpdater.checkForUpdate() + await scheduler.getNextUpdatePromise() + + expect(aboutElement.querySelector('.app-up-to-date')).not.toBeVisible() + expect(aboutElement.querySelector('.app-checking-for-updates')).toBeVisible() + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(true) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + + MockUpdater.downloadUpdate() + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() + expect(aboutElement.querySelector('.app-downloading-update')).toBeVisible() + // TODO: at some point it would be nice to be able to cancel an update download, and then this would be a cancel button + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(true) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + + MockUpdater.finishDownloadingUpdate('42.0.0') + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-downloading-update')).not.toBeVisible() + expect(aboutElement.querySelector('.app-update-available-to-install')).toBeVisible() + + expect(aboutElement.querySelector('.app-update-available-to-install .about-updates-version').textContent).toBe('42.0.0') + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Restart and install') + }) + + it('opens the release notes for the downloaded release when the release notes link are clicked', async () => { + MockUpdater.finishDownloadingUpdate('1.2.3') + await scheduler.getNextUpdatePromise() + + spyOn(shell, 'openExternal') + let link = aboutElement.querySelector('.app-update-available-to-install .about-updates-release-notes') + link.click() + + let args = shell.openExternal.mostRecentCall.args + expect(shell.openExternal).toHaveBeenCalled() + expect(args[0]).toContain('/v1.2.3') + }) + + it('executes checkForUpdate() when the check for update button is clicked', () => { + let button = aboutElement.querySelector('.about-update-action-button') + button.click() + expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled() + }) + + it('executes restartAndInstallUpdate() when the restart and install button is clicked', async () => { + spyOn(atom.autoUpdater, 'restartAndInstallUpdate') + MockUpdater.finishDownloadingUpdate('42.0.0') + await scheduler.getNextUpdatePromise() + + let button = aboutElement.querySelector('.about-update-action-button') + button.click() + expect(atom.autoUpdater.restartAndInstallUpdate).toHaveBeenCalled() + }) + + it("starts in the same state as atom's AutoUpdateManager", async () => { + atom.autoUpdater.getState.andReturn('downloading') + updateManager.resetState() + + await scheduler.getNextUpdatePromise() + expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible() + expect(aboutElement.querySelector('.app-downloading-update')).toBeVisible() + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(true) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Check now') + }) + + describe('when core.automaticallyUpdate is toggled', () => { + beforeEach(async () => { + expect(atom.config.get('core.automaticallyUpdate')).toBe(true) + atom.autoUpdater.checkForUpdate.reset() + }) + + it('shows the auto update UI', async () => { + expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(true) + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Atom will check for updates automatically') + + atom.config.set('core.automaticallyUpdate', false) + await scheduler.getNextUpdatePromise() + + expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(false) + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Automatic updates are disabled please check manually') + }) + + it('updates config and the UI when the checkbox is used to toggle', async () => { + expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(true) + + aboutElement.querySelector('.about-auto-updates input').click() + await scheduler.getNextUpdatePromise() + + expect(atom.config.get('core.automaticallyUpdate')).toBe(false) + expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(false) + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Automatic updates are disabled please check manually') + + aboutElement.querySelector('.about-auto-updates input').click() + await scheduler.getNextUpdatePromise() + + expect(atom.config.get('core.automaticallyUpdate')).toBe(true) + expect(aboutElement.querySelector('.about-auto-updates input').checked).toBe(true) + expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible() + expect(aboutElement.querySelector('.about-default-update-message').textContent).toBe('Atom will check for updates automatically') + }) + + describe('checking for updates', function () { + afterEach(() => { + this.updateView = null + }) + + it('checks for update when the about page is shown', () => { + expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled() + + this.updateView = new UpdateView({ + updateManager: updateManager, + availableVersion: '9999.0.0', + viewUpdateReleaseNotes: () => {} + }) + + expect(atom.autoUpdater.checkForUpdate).toHaveBeenCalled() + }) + + it('does not check for update when the about page is shown and the update manager is not in the idle state', () => { + atom.autoUpdater.getState.andReturn('downloading') + updateManager.resetState() + expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled() + + this.updateView = new UpdateView({ + updateManager: updateManager, + availableVersion: '9999.0.0', + viewUpdateReleaseNotes: () => {} + }) + + expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled() + }) + + it('does not check for update when the about page is shown and auto updates are turned off', () => { + atom.config.set('core.automaticallyUpdate', false) + expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled() + + this.updateView = new UpdateView({ + updateManager: updateManager, + availableVersion: '9999.0.0', + viewUpdateReleaseNotes: () => {} + }) + + expect(atom.autoUpdater.checkForUpdate).not.toHaveBeenCalled() + }) + }) + }) + }) + }) + + describe('when the About page is not open and an update is downloaded', () => { + it('should display the new version when it is opened', async () => { + MockUpdater.finishDownloadingUpdate('42.0.0') + + jasmine.attachToDOM(workspaceElement) + await atom.workspace.open('atom://about') + aboutElement = workspaceElement.querySelector('.about') + updateManager = main.model.state.updateManager + scheduler = AboutView.getScheduler() + + expect(aboutElement.querySelector('.app-update-available-to-install')).toBeVisible() + expect(aboutElement.querySelector('.app-update-available-to-install .about-updates-version').textContent).toBe('42.0.0') + expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe(false) + expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe('Restart and install') + }) + }) +}) diff --git a/packages/about/styles/about.less b/packages/about/styles/about.less new file mode 100644 index 000000000..1877f47cf --- /dev/null +++ b/packages/about/styles/about.less @@ -0,0 +1,175 @@ +@import "ui-variables"; +@import "variables"; + +.about { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + -webkit-user-select: none; + cursor: default; + overflow: auto; + text-align: center; + font-size: 1.25em; + line-height: 1.4; + padding: 4em; + color: @text-color; + background-color: @base-background-color; + + button { + cursor: default; + } + + a:focus { + // Don't use Bootstrap default here + color: inherit; + } + + img, a { + -webkit-user-drag: none; + } + + .input-checkbox { + margin-top: -.2em; + } + + // used to group different elements + .group-start { + margin-top: 4em; + } + .group-item { + margin-top: 1.5em; + } +} + +.about-container { + width: 100%; + max-width: 500px; +} + +// Header -------------------------------- + +.about-atom-io:hover { + .about-logo { + color: @atom-green; + } +} + +.about-logo { + display: block; + width: 100%; + max-width: 280px; + margin: 0 auto 1em auto; + color: @text-color-highlight; + transition: color 0.2s; +} + +.about-version-container { + &:hover { + color: lighten(@text-color, 15%); + } + &:active { + color: lighten(@text-color, 30%); + } +} + +.about-version { + margin-right: .5em; + font-size: 1.25em; + vertical-align: middle; +} + +.about-more-version { + color: @text-color-subtle; + font-size: .9em; +} + +.about-header-release-notes { + vertical-align: middle; + margin-left: 1em; +} + + +// Updates -------------------------------- + +.about-updates { + width: 100%; + max-width: 500px; +} + +.about-updates-box { + display: flex; + align-items: center; + padding: @component-padding; + border: 1px solid @base-border-color; + border-radius: @component-border-radius * 2; + background-color: @background-color-highlight; +} + +.about-updates-status { + flex: 1; + margin-left: .5em; + text-align: left; +} + +.about-updates-item, +.about-default-update-message .about-updates-label { + display: block; +} + +.about-updates-label { + color: @text-color-subtle; + &.is-strong { + color: @text-color; + } +} + +.about-updates-version { + margin: 0 .4em; +} + +.about-updates-release-notes, +.about-updates-instructions { + margin: 0 1em 0 1.5em; +} + +.about-auto-updates { + margin-top: 1em; + input { + margin-right: .5em; + } +} + + +// Love -------------------------------- + +.about-love { + .icon::before { + // Make these octicons look good inlined with text + position: relative; + width: auto; + height: auto; + margin-right: 0; + font-size: 1.5em; + vertical-align: text-top; + } + + .icon-logo-github::before { + font-size: 3.6em; + height: .36em; + } +} + +.about-credits { + color: @text-color-subtle; +} + + +// the blue squirrel -------------------------------- + +.about-release-notes { + color: @background-color-info; + &:hover { + color: lighten(@background-color-info, 15%); + } +} diff --git a/packages/about/styles/variables.less b/packages/about/styles/variables.less new file mode 100644 index 000000000..fcb4ba3c8 --- /dev/null +++ b/packages/about/styles/variables.less @@ -0,0 +1 @@ +@atom-green: #40a977; diff --git a/packages/one-light-ui/.coffeelintignore b/packages/one-light-ui/.coffeelintignore new file mode 100644 index 000000000..1db51fed7 --- /dev/null +++ b/packages/one-light-ui/.coffeelintignore @@ -0,0 +1 @@ +spec/fixtures diff --git a/packages/one-light-ui/.gitignore b/packages/one-light-ui/.gitignore new file mode 100644 index 000000000..3c3629e64 --- /dev/null +++ b/packages/one-light-ui/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/one-light-ui/LICENSE.md b/packages/one-light-ui/LICENSE.md new file mode 100644 index 000000000..4d231b456 --- /dev/null +++ b/packages/one-light-ui/LICENSE.md @@ -0,0 +1,20 @@ +Copyright (c) 2014 GitHub Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/one-light-ui/README.md b/packages/one-light-ui/README.md new file mode 100644 index 000000000..0c8bb0229 --- /dev/null +++ b/packages/one-light-ui/README.md @@ -0,0 +1,42 @@ +## One Light UI theme + +A light UI theme that adapts to most syntax themes. + +![One light UI](https://cloud.githubusercontent.com/assets/378023/26246819/0826f04e-3cd6-11e7-98eb-cd94bc48b090.png) + +> The font used in the screenshot is [Fira Mono](https://github.com/mozilla/Fira). + + +### Install + +This theme comes bundled with Atom and can be activated by going to the __Settings > Themes__ section and selecting "One Light" from the __UI Themes__ drop-down menu. + + +### Settings + +In the theme settings you can: + +- Change the __Font Size__ to scale the whole UI up or down. +- Choose between 3 __Tab Sizing__ modes. +- Hide the __dock buttons__. + +To make changes, go to `Settings > Themes > One Light UI > Settings` or the cog icon next to the theme picker. + + +### Customize + +It's also possible to resize only certain areas by adding the following to your `styles.less` (Use DevTools to find the right selectors): + +```css +.theme-one-light-ui { + .tab-bar { font-size: 18px; } + .tree-view { font-size: 14px; } + .status-bar { font-size: 12px; } +} +``` + + +### FAQ + +__Why do the colors change when I switch Syntax themes.__ +This UI theme uses the same background color as the chosen syntax theme. If that syntax theme has a dark background color, it only uses its hue, but otherwise stays light. This lets you use light-dark combos. diff --git a/packages/one-light-ui/coffeelint.json b/packages/one-light-ui/coffeelint.json new file mode 100644 index 000000000..a5dd715e3 --- /dev/null +++ b/packages/one-light-ui/coffeelint.json @@ -0,0 +1,37 @@ +{ + "max_line_length": { + "level": "ignore" + }, + "no_empty_param_list": { + "level": "error" + }, + "arrow_spacing": { + "level": "error" + }, + "no_interpolation_in_single_quotes": { + "level": "error" + }, + "no_debugger": { + "level": "error" + }, + "prefer_english_operator": { + "level": "error" + }, + "colon_assignment_spacing": { + "spacing": { + "left": 0, + "right": 1 + }, + "level": "error" + }, + "braces_spacing": { + "spaces": 0, + "level": "error" + }, + "spacing_after_comma": { + "level": "error" + }, + "no_stand_alone_at": { + "level": "error" + } +} diff --git a/packages/one-light-ui/index.less b/packages/one-light-ui/index.less new file mode 100644 index 000000000..09b35c279 --- /dev/null +++ b/packages/one-light-ui/index.less @@ -0,0 +1,35 @@ + +// Atom UI Theme: One + +@import "styles/ui-variables.less"; +@import "styles/ui-mixins.less"; +@import "octicon-mixins.less"; // https://github.com/atom/atom/blob/master/static/variables/octicon-mixins.less + +@import "styles/atom.less"; +@import "styles/badges.less"; +@import "styles/buttons.less"; +@import "styles/docks.less"; +@import "styles/editor.less"; +@import "styles/git.less"; +@import "styles/inputs.less"; +@import "styles/lists.less"; +@import "styles/messages.less"; +@import "styles/nav.less"; +@import "styles/notifications.less"; +@import "styles/modal.less"; +@import "styles/panels.less"; +@import "styles/panes.less"; +@import "styles/progress.less"; +@import "styles/tabs.less"; +@import "styles/text.less"; +@import "styles/title-bar.less"; +@import "styles/tooltips.less"; +@import "styles/tree-view.less"; +@import "styles/status-bar.less"; +@import "styles/key-binding.less"; +@import "styles/sites.less"; + +@import "styles/settings.less"; +@import "styles/packages.less"; +@import "styles/core.less"; +@import "styles/config.less"; diff --git a/packages/one-light-ui/lib/main.coffee b/packages/one-light-ui/lib/main.coffee new file mode 100644 index 000000000..815691a56 --- /dev/null +++ b/packages/one-light-ui/lib/main.coffee @@ -0,0 +1,86 @@ +root = document.documentElement +themeName = 'one-light-ui' + + +module.exports = + activate: (state) -> + atom.config.observe "#{themeName}.fontSize", (value) -> + setFontSize(value) + + atom.config.observe "#{themeName}.tabSizing", (value) -> + setTabSizing(value) + + atom.config.observe "#{themeName}.tabCloseButton", (value) -> + setTabCloseButton(value) + + atom.config.observe "#{themeName}.hideDockButtons", (value) -> + setHideDockButtons(value) + + atom.config.observe "#{themeName}.stickyHeaders", (value) -> + setStickyHeaders(value) + + # DEPRECATED: This can be removed at some point (added in Atom 1.17/1.18ish) + # It removes `layoutMode` + if atom.config.get("#{themeName}.layoutMode") + atom.config.unset("#{themeName}.layoutMode") + + deactivate: -> + unsetFontSize() + unsetTabSizing() + unsetTabCloseButton() + unsetHideDockButtons() + unsetStickyHeaders() + + +# Font Size ----------------------- + +setFontSize = (currentFontSize) -> + root.style.fontSize = "#{currentFontSize}px" + +unsetFontSize = -> + root.style.fontSize = '' + + +# Tab Sizing ----------------------- + +setTabSizing = (tabSizing) -> + root.setAttribute("theme-#{themeName}-tabsizing", tabSizing.toLowerCase()) + +unsetTabSizing = -> + root.removeAttribute("theme-#{themeName}-tabsizing") + + +# Tab Close Button ----------------------- + +setTabCloseButton = (tabCloseButton) -> + if tabCloseButton is 'Left' + root.setAttribute("theme-#{themeName}-tab-close-button", 'left') + else + unsetTabCloseButton() + +unsetTabCloseButton = -> + root.removeAttribute("theme-#{themeName}-tab-close-button") + + +# Dock Buttons ----------------------- + +setHideDockButtons = (hideDockButtons) -> + if hideDockButtons + root.setAttribute("theme-#{themeName}-dock-buttons", 'hidden') + else + unsetHideDockButtons() + +unsetHideDockButtons = -> + root.removeAttribute("theme-#{themeName}-dock-buttons") + + +# Sticky Headers ----------------------- + +setStickyHeaders = (stickyHeaders) -> + if stickyHeaders + root.setAttribute("theme-#{themeName}-sticky-headers", 'sticky') + else + unsetStickyHeaders() + +unsetStickyHeaders = -> + root.removeAttribute("theme-#{themeName}-sticky-headers") diff --git a/packages/one-light-ui/package.json b/packages/one-light-ui/package.json new file mode 100644 index 000000000..9d4c47be7 --- /dev/null +++ b/packages/one-light-ui/package.json @@ -0,0 +1,78 @@ +{ + "name": "one-light-ui", + "theme": "ui", + "version": "1.12.5", + "description": "Atom One light UI theme", + "keywords": [ + "light", + "adaptive", + "ui" + ], + "license": "MIT", + "repository": "https://github.com/atom/one-light-ui", + "main": "lib/main", + "engines": { + "atom": ">0.40.0" + }, + "devDependencies": { + "coffeelint": "^1.9.7" + }, + "configSchema": { + "fontSize": { + "title": "Font Size", + "description": "Change the font size for the UI.", + "type": "integer", + "default": 12, + "enum": [ + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "order": 1 + }, + "tabSizing": { + "title": "Tab Sizing", + "description": "In Even mode all tabs will be the same size. Great for quickly closing many tabs. In Maximum mode the tabs will expand to take up the full width. In Minimum mode the tabs will only take as little space as needed and also show longer file names.", + "type": "string", + "default": "Even", + "enum": [ + "Even", + "Maximum", + "Minimum" + ], + "order": 2 + }, + "tabCloseButton": { + "title": "Tab Close Button", + "description": "Choose the position of the close button shown in tabs.", + "type": "string", + "default": "Right", + "enum": [ + "Left", + "Right" + ], + "order": 3 + }, + "hideDockButtons": { + "title": "Hide dock toggle buttons", + "description": "Note: When hiding the toggle buttons, opening a dock needs to be done by using the keyboard or other alternatives.", + "type": "boolean", + "default": "false", + "order": 4 + }, + "stickyHeaders": { + "title": "Make tree-view project headers sticky", + "type": "boolean", + "default": "false", + "order": 5 + } + } +} diff --git a/packages/one-light-ui/spec/theme-spec.coffee b/packages/one-light-ui/spec/theme-spec.coffee new file mode 100644 index 000000000..909939f69 --- /dev/null +++ b/packages/one-light-ui/spec/theme-spec.coffee @@ -0,0 +1,36 @@ +themeName = 'one-light-ui' + +describe "#{themeName} theme", -> + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage(themeName) + + it "allows the font size to be set via config", -> + expect(document.documentElement.style.fontSize).toBe '12px' + + atom.config.set("#{themeName}.fontSize", '10') + expect(document.documentElement.style.fontSize).toBe '10px' + + it "allows the tab sizing to be set via config", -> + atom.config.set("#{themeName}.tabSizing", 'Maximum') + expect(document.documentElement.getAttribute("theme-#{themeName}-tabsizing")).toBe 'maximum' + + it "allows the tab sizing to be set via config", -> + atom.config.set("#{themeName}.tabSizing", 'Minimum') + expect(document.documentElement.getAttribute("theme-#{themeName}-tabsizing")).toBe 'minimum' + + it "allows the tab close button to be shown on the left via config", -> + atom.config.set("#{themeName}.tabCloseButton", 'Left') + expect(document.documentElement.getAttribute("theme-#{themeName}-tab-close-button")).toBe 'left' + + it "allows the dock toggle buttons to be hidden via config", -> + atom.config.set("#{themeName}.hideDockButtons", true) + expect(document.documentElement.getAttribute("theme-#{themeName}-dock-buttons")).toBe 'hidden' + + it "allows the tree-view headers to be sticky via config", -> + atom.config.set("#{themeName}.stickyHeaders", true) + expect(document.documentElement.getAttribute("theme-#{themeName}-sticky-headers")).toBe 'sticky' + + it "allows the tree-view headers to not be sticky via config", -> + atom.config.set("#{themeName}.stickyHeaders", false) + expect(document.documentElement.getAttribute("theme-#{themeName}-sticky-headers")).toBe null diff --git a/packages/one-light-ui/styles/atom.less b/packages/one-light-ui/styles/atom.less new file mode 100644 index 000000000..911a7ac5a --- /dev/null +++ b/packages/one-light-ui/styles/atom.less @@ -0,0 +1,67 @@ +* { + box-sizing: border-box; +} + +html { + font-size: @font-size; +} + +atom-workspace { + background-color: @app-background-color; +} + + +// Scrollbars ------------------------------------ + +.scrollbars-visible-always { + ::-webkit-scrollbar { + width: 10px; + height: 10px; + } + + ::-webkit-scrollbar-track { + background: @scrollbar-background-color; + } + + ::-webkit-scrollbar-thumb { + border-radius: 5px; + border: 3px solid @scrollbar-background-color; + background: @scrollbar-color; + background-clip: content-box; + } + + ::-webkit-scrollbar-corner { + background: @scrollbar-background-color; + } + + ::-webkit-scrollbar-thumb:vertical:active { + border-radius: 0; + border-left-width: 0; + border-right-width: 0; + } + + ::-webkit-scrollbar-thumb:horizontal:active { + border-radius: 0; + border-top-width: 0; + border-bottom-width: 0; + } + + atom-text-editor { + ::-webkit-scrollbar-track { + background: @scrollbar-background-color-editor; + } + ::-webkit-scrollbar-corner { + background: @scrollbar-background-color-editor; + } + ::-webkit-scrollbar-thumb { + border-color: @scrollbar-background-color-editor; + background: @scrollbar-color-editor; + } + } +} + +// TODO: Move to a better place, not sure where it gets used +.caret { + border-top: 5px solid #fff; + margin-top: -1px; +} diff --git a/packages/one-light-ui/styles/badges.less b/packages/one-light-ui/styles/badges.less new file mode 100644 index 000000000..a1020805d --- /dev/null +++ b/packages/one-light-ui/styles/badges.less @@ -0,0 +1,14 @@ +.badge { + padding: @ui-padding/4 @ui-padding/2.5; + min-width: @ui-padding*1.25; + .text(highlight); + border-radius: @ui-size*2; + background-color: @badge-background-color; + + // Icon ---------------------- + &.icon { + font-size: @ui-size; + padding: @ui-padding-icon @ui-padding-icon*1.5; + } + +} diff --git a/packages/one-light-ui/styles/buttons.less b/packages/one-light-ui/styles/buttons.less new file mode 100644 index 000000000..8a1b3989a --- /dev/null +++ b/packages/one-light-ui/styles/buttons.less @@ -0,0 +1,186 @@ + +@btn-border: 1px solid @button-border-color; +@btn-padding: 0 @ui-size/1.25; + +// Mixins ----------------------- + +.btn-default (@color, @hover-color, @selected-color, @text-color) { + color: @text-color; + text-shadow: none; + border: @btn-border; + background-color: @color; + background-image: linear-gradient(lighten(@color, 2%), @color); + + &:hover { + color: @text-color-highlight; + background-image: linear-gradient(lighten(@hover-color, 2%), @hover-color); + } + &:active { + background: darken(@color, 4%); + box-shadow: none; + } + &.selected { + background: @selected-color; + } + &.selected:focus, + &.selected:hover { + background: lighten(@selected-color, 2%); + } + &:focus { + .focus(); // unfortunately :focus styles stay even after releasing mouse. + } +} + +.btn-variant (@color) { + @_text-color: contrast(@color, white, hsl(0,0%,20%), 33% ); + .btn-default( + @color, + lighten(@color, 3%), + saturate(darken(@color, 12%), 20%), + @text-color-highlight + ); + color: @_text-color; + + & when (@ui-lightness > 50%) { + border-color: transparent; // hide border on light backgrounds + } + + &:hover, + &:focus { + color: @_text-color; + } + &:focus { + border-color: transparent; + background-clip: padding-box; + box-shadow: inset 0 0 0 1px fade(@base-border-color, 50%), 0 0 0 1px @color; + } + + &.icon:before { + color: @_text-color; + } +} + + +// Buttons ----------------------- + +.btn { + height: initial; + padding: @btn-padding; + font-size: @ui-size; + line-height: @ui-line-height; +} + +.btn, +.btn.btn-default { + .btn-default(@button-background-color, @button-background-color-hover, @button-background-color-selected, @text-color); +} + +.btn.btn-primary { + .btn-variant(@accent-bg-color); +} +.btn.btn-info { + .btn-variant(@background-color-info); +} +.btn.btn-success { + .btn-variant(@background-color-success); +} +.btn.btn-warning { + .btn-variant(@background-color-warning); +} +.btn.btn-error { + .btn-variant(@background-color-error); +} + + +// Button Sizes ----------------------- + +.btn.btn-xs, +.btn-group-xs > .btn { + font-size: @ui-size*.8; + line-height: @ui-line-height; + padding: @btn-padding; +} +.btn.btn-sm, +.btn-group-sm > .btn { + font-size: @ui-size*.9; + line-height: @ui-line-height; + padding: @btn-padding; +} +.btn.btn-lg, +.btn-group-lg > .btn { + font-size: @ui-size * 1.5; + line-height: @ui-line-height; + padding: @btn-padding; +} + + +// Button Group ----------------------- + +.btn-group > .btn { + z-index: 0; + &:hover { + z-index: 0; + } + &.btn:focus { + z-index: 1; + .focus(); + } + + &:first-child { + border-left: @btn-border; + } + &:last-child, + &.selected:last-child { + border-right: @btn-border; + } + + // hide border on light backgrounds + & when (@ui-lightness > 50%) { + &.btn-primary:first-child, + &.btn-info:first-child, + &.btn-success:first-child, + &.btn-warning:first-child, + &.btn-error:first-child { + border-left-color: transparent; + } + + &.btn-primary:last-child, + &.btn-info:last-child, + &.btn-success:last-child, + &.btn-warning:last-child, + &.btn-error:last-child { + border-right-color: transparent; + } + } + + &.selected, + &.selected:first-child, + &.selected:last-child { + color: @button-text-color-selected; + border-color: @button-border-color-selected; + } + + & when (@ui-lightness > 50%) { + &.selected + .btn { + border-left-color: @button-border-color-selected; + } + &.selected + .selected { + border-left-color: mix(@button-border-color, @button-border-color-selected); + } + } + + &.selected:focus { + border-color: @button-background-color-selected; + box-shadow: inset 0 0 0 1px fade(@base-border-color, 50%), 0 0 0 1px @button-background-color-selected; + } +} + + +// Button Icons ----------------------- + +.btn.icon:before { + width: auto; + height: auto; + font-size: 1.333333em; + vertical-align: -.1em; +} diff --git a/packages/one-light-ui/styles/config.less b/packages/one-light-ui/styles/config.less new file mode 100644 index 000000000..a0970cc8c --- /dev/null +++ b/packages/one-light-ui/styles/config.less @@ -0,0 +1,156 @@ + +// Theme config +// This gets changed from the theme settings + +@theme-tabsizing: ~'theme-@{ui-theme-name}-tabsizing'; +@theme-dockButtons: ~'theme-@{ui-theme-name}-dock-buttons'; +@theme-stickyHeaders: ~'theme-@{ui-theme-name}-sticky-headers'; +@theme-closeButton: ~'theme-@{ui-theme-name}-tab-close-button'; + + +// Tabs ---------------------------------------------- + +@tab-min-width: 7em; // ~ icon + 6 characters + +// Even (default) + +.tab-bar { + .tab, + .tab.active { + flex: 1 1 0; + max-width: 22em; + min-width: @tab-min-width; + } + atom-dock & { + .tab, + .tab.active { + max-width: none; + } + } + + // TODO: Turn this into a config + // Truncates the beginning instead + // .title.title.title { + // direction: rtl; // change direction + // } +} + + +// Maximum (full width) + +[@{theme-tabsizing}="maximum"] .tab-bar { + .tab, + .tab.active { + max-width: none; + } +} + + +// Minimum (show long paths) + +[@{theme-tabsizing}="minimum"] .tab-bar { + .tab, + .tab.active { + flex: 0 0 auto; + min-width: 2.75em; + max-width: @tab-min-width * 3.3; + } + atom-dock { + .tab, + .tab.active { + max-width: @tab-min-width * 2; + } + } +} + + +// Tabs: close button position ------------------------------ + +[@{theme-closeButton}="left"] { + + .tab-bar .tab { + .close-icon { + right: auto; + left: @icon-padding-right; + } + } + +} + + +// Hide docks toggle buttons ------------------------------ + +[@{theme-dockButtons}="hidden"] { + + // Hide docks when not open + .atom-dock-inner:not(.atom-dock-open) { + display: none; + } + + // Hide toggle buttons + .atom-dock-toggle-button { + display: none; + } + +} + + +// Sticky Projects ------------------------------ + +[@{theme-stickyHeaders}="sticky"] { + + .tree-view { + .project-root-header { + position: sticky; + top: 0; + z-index: 3; + padding-left: 5px; + padding-right: 10px; + border-bottom: 1px solid @base-border-color; + background-color: @tree-view-background-color; + } + .project-root.project-root { + margin-left: -5px; + margin-right: -10px; + + // Disable selection + &::before { + display: none; + } + + // Add selection back + &.selected .project-root-header { + background-color: @background-color-selected; + } + } + &:focus .selected .project-root-header.project-root-header { + background: @button-background-color-selected; + } + + // Fix sticky header from covering auto-revealed files + .entry.file.selected { + padding-top: @ui-tab-height; + margin-top: -@ui-tab-height; + } + + // Fix sticky header from covering auto-revealed directories when using up/down keys + // for directories, scroll test moves to .header, see https://github.com/atom/tree-view/blob/d2857ad4d7eeb7dad5cf94b33257a8740211480e/lib/tree-view.coffee#L839 + .entry.directory.selected:not(.project-root) { + & > .header { + padding-top: @ui-tab-height; + margin-top: -@ui-tab-height; + } + &::before { + margin-top: @ui-tab-height; + } + } + + // Fix above directory is not being clickable + .entry.directory:not(.project-root) > .header { + z-index: 2; + } + .entry.directory.selected:not(.project-root) > .header { + z-index: 1; + } + } +} diff --git a/packages/one-light-ui/styles/core.less b/packages/one-light-ui/styles/core.less new file mode 100644 index 000000000..0f30626c0 --- /dev/null +++ b/packages/one-light-ui/styles/core.less @@ -0,0 +1,25 @@ +// Misc + +.preview-pane .results-view .path-match-number { + // show number also on selected item + color: inherit; + opacity: .6; +} + +.tool-panel.incompatible-packages { + // incompatible-packages isn't really a tool-panel and more a whole pane + .text(normal); + background-color: @level-2-color; +} + +// Styleguide ---------------------------------------------- + +.styleguide { + // Modal + atom-panel.modal:after { + position: absolute; // prevent overlay backdrop from leaking outside + left: -@ui-padding; + right: -@ui-padding; + bottom: -@ui-padding; + } +} diff --git a/packages/one-light-ui/styles/docks.less b/packages/one-light-ui/styles/docks.less new file mode 100644 index 000000000..c91681e21 --- /dev/null +++ b/packages/one-light-ui/styles/docks.less @@ -0,0 +1,43 @@ + +// Docks ------------------------------ + +// Make handles not take up any space when dock is open +.atom-dock-resize-handle { + position: absolute; + z-index: 11; // same as toggle buttons + + &.left { + top: 0; + right: 0; + bottom: 0; + } + &.right { + top: 0; + left: 0; + bottom: 0; + } + &.bottom { + top: 0; + left: 0; + right: 0; + } +} + +// Add borders +.atom-dock-inner.atom-dock-open.left { + border-right: 1px solid @base-border-color; +} +.atom-dock-inner.atom-dock-open.right { + border-left: 1px solid @base-border-color; +} + +// Make toggle buttons cover ^ border +.atom-dock-toggle-button.left { + margin-left: -2px; +} +.atom-dock-toggle-button.right { + margin-right: -2px; +} +.atom-dock-inner:not(.atom-dock-open) .atom-dock-toggle-button.bottom { + margin-bottom: -1px; +} diff --git a/packages/one-light-ui/styles/dropdowns.less b/packages/one-light-ui/styles/dropdowns.less new file mode 100644 index 000000000..d793523bb --- /dev/null +++ b/packages/one-light-ui/styles/dropdowns.less @@ -0,0 +1,15 @@ +.dropdown-menu { + background-color: @overlay-background-color; + border-radius: @component-border-radius; + border: 1px solid @base-border-color; + padding: 0; + + > li > a { + .text(normal); + } + + > li > a:hover { + .text(highlight); + background-color: @background-color-highlight; + } +} diff --git a/packages/one-light-ui/styles/editor.less b/packages/one-light-ui/styles/editor.less new file mode 100644 index 000000000..1a73d4dcc --- /dev/null +++ b/packages/one-light-ui/styles/editor.less @@ -0,0 +1,44 @@ + +// Editor in a panel + +// TODO: Find a better selector, maybe a new class like atom-text-editor[medium] +atom-panel-container atom-text-editor.is-focused { + .focus(); +} + + +// Mini +// Usually just single line inputs + +atom-text-editor[mini] { + overflow: auto; + font-size: @ui-input-size; + line-height: @ui-line-height; + max-height: @ui-line-height * 5; // rows + padding-left: @ui-padding/3; + border-radius: @component-border-radius; + color: @text-color-highlight; + border: 1px solid @input-border-color; + background-color: @input-background-color; + + .placeholder-text { + color: @text-color-subtle; + } + + .selection .region { + background-color: @input-selection-color; + } + + .cursor { + border-color: @accent-color; + border-width: 2px; + } + + &.is-focused { + .focus(); + background-color: @input-background-color-focus; + .selection .region { + background-color: @input-selection-color-focus; + } + } +} diff --git a/packages/one-light-ui/styles/git.less b/packages/one-light-ui/styles/git.less new file mode 100644 index 000000000..2b6f2f8a4 --- /dev/null +++ b/packages/one-light-ui/styles/git.less @@ -0,0 +1,6 @@ +.status { .text(normal); } +.status-added { .text(success); } // green +.status-ignored { .text(subtle); } // faded +.status-modified { .text(warning); } // orange +.status-removed { .text(error); } // red +.status-renamed { .text(info); } // blue diff --git a/packages/one-light-ui/styles/inputs.less b/packages/one-light-ui/styles/inputs.less new file mode 100644 index 000000000..fe7e5e294 --- /dev/null +++ b/packages/one-light-ui/styles/inputs.less @@ -0,0 +1,87 @@ + +// +// Checkbox +// ------------------------- + +.input-checkbox { + &:active { + background-color: @accent-color; + } + &:before, + &:after { + background-color: @accent-text-color; + } + &:checked { + background-color: @accent-color; + } + + &:indeterminate { + background-color: @accent-color; + } +} + + +// +// Radio +// ------------------------- + +.input-radio { + &:before { + background-color: @accent-text-color; + } + &:active { + background-color: @accent-color; + } + &:checked { + background-color: @accent-color; + } +} + + +// +// Range (Slider) +// ------------------------- + +.input-range { + &::-webkit-slider-thumb { + background-color: @accent-color; + } +} + + +// +// Toggle +// ------------------------- + +.input-toggle { + &:checked { + background-color: @accent-color; + } + &:before { + background-color: @accent-text-color; + } +} + + + +// States ------------------------- + +.input-text, +.input-search, +.input-number, +.input-textarea, +.input-select, +.input-color { + &:focus { + .focus(); + } +} + +.input-text, +.input-search, +.input-number, +.input-textarea { + &:invalid { + .invalid(); + } +} diff --git a/packages/one-light-ui/styles/key-binding.less b/packages/one-light-ui/styles/key-binding.less new file mode 100644 index 000000000..c4dff4133 --- /dev/null +++ b/packages/one-light-ui/styles/key-binding.less @@ -0,0 +1,12 @@ +.key-binding { + display: inline-block; + margin-left: @ui-padding-icon; + padding: 0 @ui-padding/4; + line-height: 2; + font-family: inherit; + font-size: max(1em, @ui-size*.85); + letter-spacing: @ui-size/10; + border-radius: @component-border-radius; + color: @accent-bg-text-color; + background-color: @accent-bg-color; +} diff --git a/packages/one-light-ui/styles/lists.less b/packages/one-light-ui/styles/lists.less new file mode 100644 index 000000000..ddae9f44a --- /dev/null +++ b/packages/one-light-ui/styles/lists.less @@ -0,0 +1,150 @@ +.list-group, +.list-tree { + li:not(.list-nested-item), + li.list-nested-item > .list-item { + .text(normal); + } + + .generate-list-item-text-color(@class) { + li:not(.list-nested-item).text-@{class}, + li.list-nested-item.text-@{class} > .list-item { + .text(@class); + } + } + .generate-list-item-text-color(subtle); + .generate-list-item-text-color(info); + .generate-list-item-text-color(success); + .generate-list-item-text-color(warning); + .generate-list-item-text-color(error); + .generate-list-item-text-color(selected); + + .generate-list-item-status-color(@color, @status) { + li:not(.list-nested-item).status-@{status}, + li.list-nested-item.status-@{status} > .list-item { + color: @color; + } + + li:not(.list-nested-item).selected.status-@{status}, + li.list-nested-item.selected.status-@{status} > .list-item { + color: @color; + } + + } + + .generate-list-item-status-color(@text-color-added, added); + .generate-list-item-status-color(@text-color-ignored, ignored); + .generate-list-item-status-color(@text-color-modified, modified); + .generate-list-item-status-color(@text-color-removed, removed); + .generate-list-item-status-color(@text-color-renamed, renamed); + + li:not(.list-nested-item).selected, + li.list-nested-item.selected > .list-item { + .text(selected); + } + + .no-icon { + padding-left: calc(@ui-padding-icon ~"+" @component-icon-size); + } +} + +.list-tree.has-collapsable-children .list-nested-item > .list-item::before { + text-align: center; +} + +.select-list ol.list-group, +&.select-list ol.list-group { + li.two-lines { + .secondary-line { + color: @text-color-subtle; + } + &.selected .secondary-line { + color: fade(@text-color-highlight, 50%); + text-shadow: none; + } + } + + // Reset icon to allow nesting + .icon { + display: initial; + height: initial; + } + + // We want to highlight the background of the list items because we dont + // know their size. + li.selected { + background-color: @background-color-selected; + &:before{ display: none; } + } + + &.mark-active { + @active-icon-size: 14px; + + // pad in front of the text where the icon would be We'll pad the non- + // active items with a 'fake' icon so other classes can pad the item + // without worrying about the icon padding. + li:before { + content: ''; + background-color: transparent; + position: static; + display: inline-block; + left: auto; right: auto; + height: @active-icon-size; + width: @active-icon-size; + font-size: @active-icon-size; + } + > li:not(.active):before { + margin-right: @ui-padding-icon; + } + li.active { + .octicon(check, @active-icon-size); + &:before { + margin-right: @ui-padding-icon; + color: @text-color-success; + } + } + } +} + +.select-list.popover-list { + @popover-list-padding: @ui-padding/4; + background-color: @overlay-background-color; + box-shadow: 0 2px 8px 1px rgba(0, 0, 0, 0.3); + padding: @popover-list-padding; + border-radius: @component-border-radius; + + atom-text-editor[mini] { + margin-bottom: @popover-list-padding; + } + + ol.list-group { + margin-top: @popover-list-padding; + } + + .list-group li { + padding-left: @popover-list-padding; + } +} + +.ui-sortable { + li { + line-height: 2.5; + } + + // For sortable lists in the settings view + li.ui-sortable-placeholder { + visibility: visible !important; + background-color: darken(@pane-item-background-color, 10%); + } +} + +li.ui-draggable-dragging, +li.ui-sortable-helper { + line-height: @ui-line-height; + height: @ui-line-height; + border: 0; + border-radius: 0; + list-style: none; + padding: 0 @ui-padding; + background: @background-color-highlight; + box-shadow: 0 0 1px @base-border-color; +} diff --git a/packages/one-light-ui/styles/messages.less b/packages/one-light-ui/styles/messages.less new file mode 100644 index 000000000..dda181c60 --- /dev/null +++ b/packages/one-light-ui/styles/messages.less @@ -0,0 +1,16 @@ +background-tips ul.background-message { + font-weight: 500; + font-size: 2em; + color: @text-color-faded; + + .message { + padding: 0 @component-padding * 10; + + .keystroke { + white-space: nowrap; + vertical-align: middle; + line-height: 1; + padding: .1em .4em; + } + } +} diff --git a/packages/one-light-ui/styles/modal.less b/packages/one-light-ui/styles/modal.less new file mode 100644 index 000000000..7bc0a42d1 --- /dev/null +++ b/packages/one-light-ui/styles/modal.less @@ -0,0 +1,125 @@ + +@modal-padding: @ui-padding/2 @ui-padding/1.5; +@modal-width: @ui-size * 50; + +atom-panel-container.modal { + position: absolute; + top: 0; left: 0; right: 0; +} + +atom-panel.modal { + position: relative; + width: 100%; + max-width: @modal-width; + margin: 0 auto; + left: initial; + color: @text-color; + background-color: transparent; + padding: @ui-padding/2; + + &.from-top { + top: @component-padding * 5; + } + + atom-text-editor[mini] { + margin-bottom: @ui-padding/2; + } + + .select-list ol.list-group, + &.select-list ol.list-group { + border: 1px solid @overlay-border-color; + background-color: lighten(@overlay-background-color, 2%); + + &:empty { + border: none; + margin-top: 0; + } + + li { + padding: @modal-padding; + line-height: @ui-line-height; + border-bottom: 1px solid @overlay-border-color; + + &:last-of-type { + border-bottom: none; + } + + .icon::before { + margin-left: 1px; + } + + .icon.status { + float: right; + margin-left: @ui-padding-icon; + &:before { + margin-left: 0; + margin-right: 0; + } + } + + &.selected { + .status.icon { + color: @text-color-selected; + } + } + } + + } + + .select-list .key-binding { + margin-top: -1px; + margin-left: @ui-padding/2; + margin-right: calc( -@ui-padding/3 ~"+" 1px); + } + + .select-list .primary-line { + display: block; + } + + & > * { + position: relative; // fixes stacking order + } + + .command-palette { + padding: 1px; // prevents the box-shadow of the input from being cut off + background-color: @overlay-background-color; + } + + + // Container + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 0; + background-color: @overlay-background-color; + border-radius: @component-border-radius*2; + box-shadow: 0 6px 12px -2px hsla(0,0%,0%,.4); + } + + // Backdrop + // TODO: Add extra wrapper to translate individually or easier positioning + + &:after { + content: ""; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + background: @overlay-backdrop-color; + opacity: @overlay-backdrop-opacity; + backface-visibility: hidden; // fixes scrollbar on retina screens + -webkit-animation: overlay-fade .24s cubic-bezier(0.215, 0.61, 0.355, 1); + } + + @-webkit-keyframes overlay-fade { + 0% { opacity: 0; } + 100% { opacity: @overlay-backdrop-opacity; } + } + +} diff --git a/packages/one-light-ui/styles/nav.less b/packages/one-light-ui/styles/nav.less new file mode 100644 index 000000000..2d35bfdd9 --- /dev/null +++ b/packages/one-light-ui/styles/nav.less @@ -0,0 +1,25 @@ +.nav-tabs { + border-bottom: 1px solid @base-border-color; + li { + a, + &.active a { + border: none; + margin-right: 0px; + margin-bottom: 1px; + } + + a:hover, + &.active a, + &.active a:hover { + background-color: @background-color-highlight; + border: none; + color: @text-color-selected; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + } + + &.active a { + background-color: @tab-background-color-active; + } + } +} diff --git a/packages/one-light-ui/styles/notifications.less b/packages/one-light-ui/styles/notifications.less new file mode 100644 index 000000000..3f7ec520d --- /dev/null +++ b/packages/one-light-ui/styles/notifications.less @@ -0,0 +1,45 @@ + +atom-notifications { + font-size: @ui-size * 1.2; + + atom-notification { + width: 32em; + &.has-detail { + width: 32em; + } + + &:first-child.has-close .message { + padding-right: 9em; + } + &:only-child.has-close .message, + &.has-close .message { + padding-right: 2.5em; + } + .item { + padding: @ui-padding/2; + } + + .detail, + .description { + font-size: .85em; + } + + &.icon:before { + padding-top: .85em; + } + .close { + width: 2.5em; + height: 3em; + line-height: 3em; + font-size: inherit; + } + .close-all.btn { + top: .5em; + right: 2.5em; + } + .btn-copy-report { + line-height: 2em; + margin-left: .5em; + } + } +} diff --git a/packages/one-light-ui/styles/packages.less b/packages/one-light-ui/styles/packages.less new file mode 100644 index 000000000..73dd79c28 --- /dev/null +++ b/packages/one-light-ui/styles/packages.less @@ -0,0 +1,231 @@ +// Overrides packages + +// find-and-replace + project-find --------------------------- + +.find-and-replace, +.project-find { + padding: @ui-padding/4; + .input-block-item { + padding: @ui-padding/4; + } +} + +// find-and-replace +.find-and-replace { + .header, + .input-block { + min-width: @ui-size*22; + } + + .input-block-item { + flex: 1 1 @ui-size*22; + } + .input-block-item--flex { + flex: 100 1 @ui-size*22; + } + + .btn, + .btn-group-options .btn { + font-size: @ui-size*1.1; + padding: 0; + } + + .btn-group-options .btn, + .btn-group-options .btn.option-selection, + .btn-group-options .btn.option-whole-word { + padding: 0; + font-size: @ui-input-size; // keep same as text input + } + + .find-container atom-text-editor { + padding-right: @ui-size*5; // leave some room for the results count + } + .find-meta-container { + top: 0; + font-size: @ui-size; + line-height: @ui-size*2.5; + } +} + +// project-find +.project-find { + .header, + .input-block { + min-width: @ui-size*15; + } + + .input-block-item { + flex: 1 1 @ui-size*14; + } + .input-block-item--flex { + flex: 100 1 @ui-size*20; + } + + .btn { + font-size: @ui-size*1.1; + padding: 0; + } + .btn-group-options .btn { + padding: 0; + font-size: @ui-input-size; // keep same as text input + } +} + +// Colorize find-and-replace based on results +& when (@ui-hue >= 190) and (@ui-hue <= 340) { + .find-and-replace { + &.has-no-results .find-container atom-text-editor[mini].is-focused { + .invalid(); + .selection .region { + background-color: mix(@text-color-error, @input-background-color, 50%); + } + .cursor { + border-color: @text-color-error; + } + } + + &.has-results .find-container atom-text-editor[mini].is-focused { + .valid(); + .selection .region { + background-color: mix(@text-color-success, @input-background-color, 50%); + } + .cursor { + border-color: @text-color-success; + } + } + + &.has-results .find-container .result-counter { color: @text-color-success; } + &.has-no-results .find-container .result-counter { color: @text-color-error; } + } +} + + + + +// Timecop --------------------------- + +.timecop { + .timecop-panel { + padding: @component-padding/2; + background-color: @level-2-color; + } + + .tool-panel { + padding: @component-padding/2; + background-color: @level-2-color; + } + + .inset-panel { + border: 1px solid @base-border-color; + } + + .panel-heading { + .text(highlight); + border-color: @base-border-color; + background-color: @level-1-color; + } + + .list-item .inline-block { + line-height: 1.5; + } +} + + +// Command Palette + Fuzzy Finder --------------------------- + +.command-palette .list-group .character-match, +.fuzzy-finder .list-group .character-match { + color: @accent-only-text-color; +} + + +// Deprecation Cop --------------------------- + +.deprecation-cop { + .deprecation-overview { + background-color: @level-2-color; + border-bottom: 1px solid @base-border-color; + } +} + + +// Tool Bar --------------------------- + +.tool-bar { + // Make it look the same as other panels + background-color: @level-3-color; + border: none; + + // just a single border + more spacing + &.tool-bar-horizontal .tool-bar-spacer { + border-left: 0 none; + margin-left: .5em; + margin-right: .5em; + } + &.tool-bar-vertical .tool-bar-spacer { + border-bottom: 0 none; + margin-top: .5em; + margin-bottom: .5em; + } + + // only show button styles on hover + button.tool-bar-btn { + background-color: @level-3-color; + background-image: none; + border-color: @level-3-color; + } +} + + + +// GitHub package --------------------------------------------------- + +.github { + + // Fix focus styles + // Since it's not possible to add a padding to + // a pseudo element is used to add the border when focused. + &-CommitView-editor atom-text-editor.is-focused { + box-shadow: none; + &:before { + content: ""; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + border: 2px solid; + border-color: inherit; + border-radius: @component-border-radius; + } + } + + // Add focus styles since :focus doesn't work + &-CommitView-coAuthorEditor { + &.is-focused { + .focus(); + } + &.is-open { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + + .Select-option { + &.is-focused { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + color: @accent-text-color; + background-color: @accent-color; + } + } + .Select-menu-outer { + left: -2px; + right: -2px; + bottom: 100%; + border: 2px solid @accent-color; + background-color: @overlay-background-color; + } + } + +} diff --git a/packages/one-light-ui/styles/panels.less b/packages/one-light-ui/styles/panels.less new file mode 100644 index 000000000..0d8b10850 --- /dev/null +++ b/packages/one-light-ui/styles/panels.less @@ -0,0 +1,64 @@ + +// Panels + +atom-panel { + .text(normal); + position: relative; + border-bottom: 1px solid @base-border-color; + + &.top { + border-right: 1px solid @base-border-color; + } + &.left { + border-right: 1px solid @base-border-color; + } + &.right { + border-left: 1px solid @base-border-color; + } + &.bottom { + border-right: 1px solid @base-border-color; + } + &.footer:last-child { + border-bottom: none; + } + &.tool-panel:empty { + border: none; + } +} + +.panel { + &.bordered { + border: 1px solid @base-border-color; + border-radius: @component-border-radius; + } +} + +.inset-panel { + position: relative; + background-color: @inset-panel-background-color; + border-radius: @component-border-radius; + &.bordered { + border: 1px solid @base-border-color; + border-radius: @component-border-radius; + } + & .panel-heading { + border-color: @inset-panel-border-color; + } +} + +.panel-heading { + .text(normal); + border-bottom: 1px solid @panel-heading-border-color; + background-color: @panel-heading-background-color; + + .btn { + padding-left: 8px; + padding-right: 8px; + .btn-default( + lighten(@button-background-color, 10%), + lighten(@button-background-color-hover, 10%), + lighten(@button-background-color-selected, 10%), + lighten(@text-color, 10%) + ); + } +} diff --git a/packages/one-light-ui/styles/panes.less b/packages/one-light-ui/styles/panes.less new file mode 100644 index 000000000..1842937ee --- /dev/null +++ b/packages/one-light-ui/styles/panes.less @@ -0,0 +1,22 @@ + +atom-pane-container { + + atom-pane { + position: relative; + border-right: 1px solid @base-border-color; + border-bottom: 1px solid @base-border-color; + + .item-views { + // prevent atom-text-editor from leaking ouside might improve performance + overflow: hidden; + } + } + +} + +// Hide right-most border +atom-pane:only-child, +atom-pane-axis.pane-row > atom-pane:last-child, +atom-pane-axis.pane-column:last-child > atom-pane { + border-right: none; +} diff --git a/packages/one-light-ui/styles/progress.less b/packages/one-light-ui/styles/progress.less new file mode 100644 index 000000000..0c070df60 --- /dev/null +++ b/packages/one-light-ui/styles/progress.less @@ -0,0 +1,94 @@ + +// Spinner ---------------------- + +@spinner-duration: 1.2s; + +.loading-spinner(@size) { + position: relative; + display: block; + width: 1em; + height: 1em; + font-size: @size; + background: radial-gradient(@accent-color .1em, transparent .11em); + + &::before, + &::after { + content: ""; + position: absolute; + z-index: 10; // prevent sibling elements from getting their own layers + top: 0; + left: 0; + border-radius: 1em; + width: inherit; + height: inherit; + border-radius: 1em; + border: 2px solid; + -webkit-animation: spinner-animation @spinner-duration infinite; + -webkit-animation-fill-mode: backwards; + } + &::before { + border-color: @accent-color transparent transparent transparent; + } + &::after { + border-color: transparent lighten(@accent-color, 15%) transparent transparent; + -webkit-animation-delay: @spinner-duration/2; + } + + &.inline-block { + display: inline-block; + } +} + +@-webkit-keyframes spinner-animation { + 0% { transform: rotateZ( 0deg); -webkit-animation-timing-function: cubic-bezier(0, 0, .8, .2); } + 50% { transform: rotateZ(180deg); -webkit-animation-timing-function: cubic-bezier(.2, .8, 1, 1); } + 100% { transform: rotateZ(360deg); } +} + +// Spinner sizes +.loading-spinner-tiny { .loading-spinner(16px); &::before, &::after { border-width: 1px; } } +.loading-spinner-small { .loading-spinner(32px); } +.loading-spinner-medium { .loading-spinner(48px); } +.loading-spinner-large { .loading-spinner(64px); } + + + + +// Progress Bar ---------------------- + +@progress-height: 8px; +@progress-buffer-color: fade(@progress-background-color, 20%); + +progress { + -webkit-appearance: none; + height: @progress-height; + border-radius: @component-border-radius; + background-color: @input-background-color; + box-shadow: inset 0 0 0 1px @input-border-color; + + &::-webkit-progress-bar { + background-color: transparent; + } + + &::-webkit-progress-value { + border-radius: @component-border-radius; + background-color: @progress-background-color; + } + + // Is buffering (when no value is set) + &:indeterminate { + background-image: + linear-gradient(-45deg, transparent 33%, @progress-buffer-color 33%, + @progress-buffer-color 66%, transparent 66%); + background-size: 25px @progress-height, 100% 100%, 100% 100%; + + // Plays animation for 1min (12runs) at normal speed, + // then slows down frame-rate for 9mins (108runs) to limit CPU usage + -webkit-animation: progress-buffering 5s linear 12, + progress-buffering 5s 60s steps(10) 108; + } +} + +@-webkit-keyframes progress-buffering { + 100% { background-position: -100px 0px; } +} diff --git a/packages/one-light-ui/styles/settings.less b/packages/one-light-ui/styles/settings.less new file mode 100644 index 000000000..a3a2642ef --- /dev/null +++ b/packages/one-light-ui/styles/settings.less @@ -0,0 +1,140 @@ + +// Settings + +// Modular Scale (1.125): http://www.modularscale.com/?1&em&1.125&web&table +@ms-6: @ui-size * 2.027; +@ms-5: @ui-size * 1.802; +@ms-4: @ui-size * 1.602; +@ms-3: @ui-size * 1.424; +@ms-2: @ui-size * 1.266; +@ms-1: @ui-size * 1.125; +@ms-0: @ui-size * 1; +@ms_1: @ui-size * 0.889; +@ms_2: @ui-size * 0.790; + + + +.settings-view { + + // Menu ------------------------------ + + .config-menu { + position: relative; + min-width: @ui-size * 15; + max-width: @ui-size * 20; + border-width: 0 1px 0 0; + border-image: linear-gradient(@level-2-color 10px, @base-border-color 200px) 0 1 0 0 stretch; + background: @level-2-color; + + .btn { + white-space: initial; + font-size: @ms_1; + line-height: 1; + padding: @ui-padding/3 @ui-padding/2; + &::before { + vertical-align: middle; + } + } + + + } + .nav { + & > li > a { + padding: @ui-padding/2 @ui-padding; + line-height: @ui-line-height; + } + } + + + // Sections ------------------------------ + + & > .panels { + background-color: @level-2-color; + } + + .section-container { + max-width: @ui-size*60; + } + .sub-section { + margin: @ui-padding*3 0; + } + + .section, + .section:first-child, + .section:last-child { + padding: @ui-padding*3; + } + + .themes-panel .control-group { + margin-top: @ui-padding*2; + } + + + // Titles ------------------------------ + + .section .section-heading { + margin-bottom: @ui-padding/1.5; + } + + .sub-section-heading.icon:before, + .section-heading.icon:before { + margin-right: @ui-padding-icon; + } + + + + // Cards ------------------------------ + + .package-card { + padding: @ui-padding; + .meta-controls .status-indicator { + width: @ui-padding/4; + &:before { + content: "\00a0"; // fixes 0 height + } + } + } + + + // Components ------------------------------ + + .icon::before { + color: @text-color-subtle; + } + + .editor-container { + margin: @ui-padding 0; + } + + .form-control { + font-size: @ui-size*1.25; + height: @ui-line-height; + padding-top: 0; + padding-bottom: 0; + } + + .update-all-button { + font-size: .75em; + } + + .install-button { + .btn-variant(@accent-bg-color); + } + + input[type="checkbox"] { + background-color: @background-color-selected; + &:active, + &:checked { + background-color: @accent-color; + } + &:before, + &:after { + background-color: @accent-text-color; + } + } + + .search-container .btn { + font-size: @ui-input-size; + } + +} diff --git a/packages/one-light-ui/styles/sites.less b/packages/one-light-ui/styles/sites.less new file mode 100644 index 000000000..4a02affed --- /dev/null +++ b/packages/one-light-ui/styles/sites.less @@ -0,0 +1,13 @@ +// Site Colors + +.ui-site(@num, @color) { + .ui-site-@{num} { + background-color: @color; + } +} + +.ui-site(1, @ui-site-color-1); +.ui-site(2, @ui-site-color-2); +.ui-site(3, @ui-site-color-3); +.ui-site(4, @ui-site-color-4); +.ui-site(5, @ui-site-color-5); diff --git a/packages/one-light-ui/styles/status-bar.less b/packages/one-light-ui/styles/status-bar.less new file mode 100644 index 000000000..1a171f501 --- /dev/null +++ b/packages/one-light-ui/styles/status-bar.less @@ -0,0 +1,97 @@ + +@status-bar-height: @ui-tab-height; // same as tabs +@status-bar-padding: @ui-padding; + +.status-bar { + font-size: @ui-size; + height: @status-bar-height; + line-height: @status-bar-height; + background-color: @level-3-color; + + .flexbox-repaint-hack { + padding: 0; // override default + } + + // underlines should only be used for external links + a:hover, + a:focus { + text-decoration: none; + cursor: default; + } + + .inline-block { + margin: 0; // override default + padding: 0 @status-bar-padding/2; + vertical-align: top; + + &:hover { + text-decoration: none; + background-color: @level-3-color-hover; + } + &:active { + background-color: @level-3-color-active; + } + + // reset on child inline-block + .inline-block { + margin: 0; + padding: 0; + } + } + + .status-bar-right { + .inline-block { + margin-left: 0; // override default + } + } + .icon { + vertical-align: middle; + } + .icon::before { + font-size: 1.33333em; // should be 16px with a default of 12px + width: auto; // use natural width + line-height: 1; + height: 1em; // same as line-height + margin-right: .25em; + top: auto; + } +} + + +// Package overrides ------------------------------- + +.status-bar.status-bar { + + // Read-only -> Remove hover effect + .is-read-only, // <- use this class in packages + status-bar-launch-mode, + busy-signal { + &:hover, + &:active, + .inline-block:hover, + .inline-block:active { + background-color: transparent; + } + } + + // Remove underline + .package-updates-status-view, + .github-ChangedFilesCount { + &:hover, + &:focus { + text-decoration: none; + cursor: default; + } + } + + // Remove margin for icon without text + status-bar-launch-mode::before, // Launch mode + .about-release-notes::before, // New release squirrel + .PortalStatusBarIndicator .icon::before, // Teletype + .icon.is-icon-only::before { + margin-right: 0; + } + .github-PushPull-label.is-push:empty { // GitHub package when nothing to push + margin-right: -.25em; + } +} diff --git a/packages/one-light-ui/styles/tabs.less b/packages/one-light-ui/styles/tabs.less new file mode 100644 index 000000000..be3c0990e --- /dev/null +++ b/packages/one-light-ui/styles/tabs.less @@ -0,0 +1,250 @@ + +// Tabs + +@tab-border: 1px solid @tab-border-color; +@title-padding: .66em; +@icon-padding-top: .5em; // 2.5 (total) - 1.5 (text) / 2 +@icon-padding-right: .5em; + +.tab-bar { + position: relative; + height: @ui-tab-height; + box-shadow: inset 0 -1px 0 @tab-border-color; + background: @tab-bar-background-color; + overflow-x: auto; + overflow-y: hidden; + border-radius: 0; + + &::-webkit-scrollbar { + display: none; + } + + &:empty { + display: none; + } + + + // Tab ---------------------- + + .tab { + position: relative; + top: 0; + padding: 0; + margin: 0; + height: inherit; + font-size: inherit; + line-height: @ui-tab-height; + color: @tab-text-color; + background-color: @tab-background-color; + box-shadow: inherit; + border-left: @tab-border; + &.active { + color: @tab-text-color-active; + background-color: @tab-background-color-active; + box-shadow: none; + } + &:first-of-type { + border-left-color: transparent; + } + &:last-of-type { + // use box-shadow to not take up any space + box-shadow: inset 0 -1px 0 @tab-border-color, 1px 0 0 @base-border-color; + } + &.active:last-of-type { + box-shadow: 1px 0 0 @base-border-color; + } + + + // Title ---------------------- + + .title { + text-align: center; + margin: 0 @title-padding; + } + + // VCS coloring ---------------------- + &:not(.active) .status-added { color: @tab-inactive-status-added; } + &:not(.active) .status-modified { color: @tab-inactive-status-modified; } + + + // Icons ---------------------- + + .title.title:before { + margin-right: .3em; + width: auto; + height: auto; + line-height: 1; + font-size: 1.125em; + vertical-align: -.0625em; // Adjust center for the 0.1em font-size increase + } + + // Close icon ---------------------- + + .close-icon { + top: @icon-padding-top; + right: @icon-padding-right; + z-index: 2; + font-size: 1em; + width: 1.5em; + height: 1.5em; + line-height: 1.5; + text-align: center; + border-radius: @component-border-radius; + background-color: inherit; + overflow: hidden; + transform: scale(0); + transition: transform .08s; + &:hover { + color: @accent-text-color; + background-color: @accent-color; + } + &:active { + background-color: fade(@accent-color, 50%); + } + &::before { + z-index: 1; + font-size: 1.1em; + vertical-align: -.05em; // Adjust center for the 0.1em font-size increase + width: auto; + height: auto; + pointer-events: none; + } + } + &:hover .close-icon { + transform: scale(1); + transition-duration: .16s; + } + } + + // Modified icon ---------------------- + + .tab.modified { + &:hover .close-icon { + color: @accent-color; + &:hover { + color: @accent-bg-text-color; + } + } + &:not(:hover) .close-icon { + top: @icon-padding-top; + right: @icon-padding-right; + width: 1.5em; + height: 1.5em; + line-height: 1.5; + color: @accent-color; + border-radius: @component-border-radius; + border: none; + transform: scale(1); + &::before { + content: "\f052"; + display: inline-block; + } + } + } + + + // Tabs in the docks ---------------------- + + atom-dock & { + .tab.active { + background-color: @tool-panel-background-color; + } + } + + + // Dragging ---------------------- + + .tab.is-dragging { + opacity: .5; + + .close-icon, + &:before { + visibility: hidden; + } + } + + .placeholder { + position: relative; + pointer-events: none; + + // bar + &:before { + z-index: 1; + margin: 0; + width: 2px; + height: @ui-tab-height; + background-color: @accent-color; + } + + // arrow + &:after { + z-index: 0; + top: @ui-tab-height/2; + margin: -4px 0 0 -3px; + border-radius: 0; + border: 4px solid @accent-color; + transform: rotate(45deg); + background: transparent; + } + + &:last-child { + &:before { + margin-left: -2px; + } + &:after { + transform: none; + margin-left: -10px; + border-color: transparent @accent-color transparent transparent; + } + } + } + + + // Overrides ---------------------- + + // keep tabs same size when active + .tab, + .tab.active { + padding-right: 0; + .title { + padding: 0; + } + } +} + + +// Active pane marker -------------- + +atom-pane-axis > atom-pane.active, +atom-pane-container > atom-pane.pane { + .tab.active:before { + content: ""; + position: absolute; + pointer-events: none; + z-index: 2; + top: 0; + left: -1px; // cover left border + bottom: 0; + width: 2px; + background: @accent-color; + } +} + +// hide marker in docks +atom-dock .tab-bar .tab::before { + display: none; +} + + +// Custom tabs -------------- + +.tab-bar .tab.active { + &[data-type$="Editor"], + &[data-type$="AboutView"], + &[data-type$="TimecopView"], + &[data-type$="StyleguideView"], + &[data-type="MarkdownPreviewView"] { + color: @tab-text-color-editor; + background-color: @tab-background-color-editor; // Match syntax background color + } +} diff --git a/packages/one-light-ui/styles/text.less b/packages/one-light-ui/styles/text.less new file mode 100644 index 000000000..399ff06d1 --- /dev/null +++ b/packages/one-light-ui/styles/text.less @@ -0,0 +1,84 @@ +h1, +h2, +h3 { + line-height: 1em; + margin-bottom: 15px +} +h1 { font-size: 2em; } +h2 { font-size: 1.5em; } +h3 { font-size: 1.2em; } + +p { + line-height: 1.6; + margin-bottom: 15px; +} + +label { + font-weight: normal; +} + +pre { + box-shadow: none; + color: @text-color; + background: @inset-panel-background-color; + border-radius: @component-border-radius; + border: none; + margin: 0; +} + +code { + .text(highlight); + background: @background-color-highlight; + border-radius: @component-border-radius; +} + +.selected { .text(highlight); } + +.text-smaller { font-size: 0.9em; } + +.text-subtle { .text(subtle); } +.text-highlight { .text(highlight); } + +.text-error { .text(error); } +.text-info { + .text(info); + &:hover { color: @text-color-info; } +} +.text-warning { + .text(warning); + &:hover { color: @text-color-warning; } +} +.text-success { + .text(success); + &:hover { color: @text-color-success; } +} + +.highlight-mixin { + padding: 1px 4px; + border-radius: 2px; +} + +.highlight { + .highlight-mixin(); + font-weight: 700; + color: @text-color-highlight; + background-color: @background-color-highlight; +} + +.highlight-color(@name, @background-color) { + .highlight-@{name} { + .highlight-mixin(); + font-weight: 500; + color: white; + text-shadow: 0 1px 0px hsla(0,0%,0%,.2); + background-color: @background-color; + } +} +.highlight-color( info, @background-color-info); +.highlight-color(warning, @background-color-warning); +.highlight-color( error, @background-color-error); +.highlight-color(success, @background-color-success); + +.results-view .path-details.list-item { + color: darken(@text-color-highlight, 18%); +} diff --git a/packages/one-light-ui/styles/title-bar.less b/packages/one-light-ui/styles/title-bar.less new file mode 100644 index 000000000..7e1b945a4 --- /dev/null +++ b/packages/one-light-ui/styles/title-bar.less @@ -0,0 +1,4 @@ +.title-bar { + height: 22px; // remove 1px since there is no border + border-bottom: none; +} diff --git a/packages/one-light-ui/styles/tooltips.less b/packages/one-light-ui/styles/tooltips.less new file mode 100644 index 000000000..f034f0e9b --- /dev/null +++ b/packages/one-light-ui/styles/tooltips.less @@ -0,0 +1,53 @@ +.tooltip { + white-space: nowrap; + font-size: @ui-size*1.15; + + &.in { + opacity: 1; + transition: opacity .12s ease-out; + } + + .tooltip-inner { + line-height: 1; + padding: @ui-padding*.5 @ui-padding*.65; + border-radius: @component-border-radius; + background-color: @tooltip-background-color; + color: @tooltip-text-color; + white-space: nowrap; + max-width: none; + } + + .keystroke { + font-size: max(1em, @ui-size*.85); + padding: .1em .4em; + margin: 0 @ui-padding*-.35 0 @ui-padding*.25; + border-radius: max(2px, @component-border-radius / 2); + color: @tooltip-text-key-color; + background: @tooltip-background-key-color; + } + + &.top .tooltip-arrow { + border-top-color: @tooltip-background-color; + } + &.top-left .tooltip-arrow { + border-top-color: @tooltip-background-color; + } + &.top-right .tooltip-arrow { + border-top-color: @tooltip-background-color; + } + &.right .tooltip-arrow { + border-right-color: @tooltip-background-color; + } + &.left .tooltip-arrow { + border-left-color: @tooltip-background-color; + } + &.bottom .tooltip-arrow { + border-bottom-color: @tooltip-background-color; + } + &.bottom-left .tooltip-arrow { + border-bottom-color: @tooltip-background-color; + } + &.bottom-right .tooltip-arrow { + border-bottom-color: @tooltip-background-color; + } +} diff --git a/packages/one-light-ui/styles/tree-view.less b/packages/one-light-ui/styles/tree-view.less new file mode 100644 index 000000000..9063d7dd9 --- /dev/null +++ b/packages/one-light-ui/styles/tree-view.less @@ -0,0 +1,85 @@ +@tree-view-height: @ui-line-height; + +.tree-view { + font-size: @ui-size; + background: @tree-view-background-color; + + .project-root.project-root { + &:before { + height: @ui-tab-height; + background-clip: padding-box; + } + & > .header .name { + line-height: @ui-tab-height; + } + } + + // Selected state + .selected:before { + background: @background-color-selected; + } + + // Focus + selected state + &:focus { + .selected.list-item > .name, // files + .selected.list-nested-item > .list-item > .name, // folders + .selected.list-nested-item > .header:before { // arrow icon + color: contrast(@button-background-color-selected); + } + .selected:before { + background: @button-background-color-selected; + } + } +} + +.theme-one-dark-ui .tree-view .project-root.project-root::before { + border-top: 1px solid transparent; + background-clip: padding-box; +} + +.tree-view-resizer { + .tree-view-resize-handle { + width: 8px; + } +} + +// Variable height, based on ems +.list-group li:not(.list-nested-item), +.list-tree li:not(.list-nested-item), +.list-group li.list-nested-item > .list-item, +.list-tree li.list-nested-item > .list-item { + line-height: @tree-view-height; +} + +.list-group .selected::before, +.list-tree .selected::before { + height: @tree-view-height; +} + +// icon +.list-group .icon, +.list-tree .icon { + display: inline-block; + height: inherit; + &::before { + top: initial; + line-height: inherit; + height: inherit; + vertical-align: top; + } +} + +// Arrow icon +.list-group, +.list-tree { + .header.header.header.header::before { + top: initial; + line-height: inherit; + height: inherit; + vertical-align: top; + font-size: inherit; + } +} +.tree-view .project-root-header.project-root-header.project-root-header.project-root-header::before { + line-height: @ui-tab-height; +} diff --git a/packages/one-light-ui/styles/ui-mixins.less b/packages/one-light-ui/styles/ui-mixins.less new file mode 100644 index 000000000..e50a63aff --- /dev/null +++ b/packages/one-light-ui/styles/ui-mixins.less @@ -0,0 +1,49 @@ +// Pattern matching; ish is cray. +// http://lesscss.org/#-pattern-matching-and-guard-expressions + +.text(normal) { + font-weight: normal; + color: @text-color; +} +.text(subtle) { + font-weight: normal; + color: @text-color-subtle; +} +.text(highlight) { + font-weight: normal; + color: @text-color-highlight; +} +.text(selected) { + .text(highlight) +} + +.text(info) { + color: @text-color-info; +} +.text(success) { + color: @text-color-success; +} +.text(warning) { + color: @text-color-warning; +} +.text(error) { + color: @text-color-error; +} + +.focus() { + outline: none; + border-color: @accent-color; + box-shadow: 0 0 0 1px @accent-color; +} + +.valid() { + border-color: @text-color-success; + box-shadow: 0 0 0 1px @text-color-success; + background-color: mix(@text-color-success, @input-background-color, 10%); +} + +.invalid() { + border-color: @text-color-error; + box-shadow: 0 0 0 1px @text-color-error; + background-color: mix(@text-color-error, @input-background-color, 10%); +} diff --git a/packages/one-light-ui/styles/ui-variables-custom.less b/packages/one-light-ui/styles/ui-variables-custom.less new file mode 100644 index 000000000..c43f5aabd --- /dev/null +++ b/packages/one-light-ui/styles/ui-variables-custom.less @@ -0,0 +1,132 @@ + +// ONE light UI variables +// ---------------------------------------------- + +@import "syntax-variables"; + +.ui-syntax-color() { @syntax-background-color: hsl(220,1%,98%); } .ui-syntax-color(); // fallback color +@ui-syntax-color: @syntax-background-color; + +// Color guards ----------------- +@ui-s-h: hue(@ui-syntax-color); +.ui-hue() when (@ui-s-h = 0) { @ui-hue: 220; } // Use blue hue when no saturation +.ui-hue() when (@ui-s-h > 0) { @ui-hue: @ui-s-h; } +.ui-hue(); + +@ui-saturation: min( saturation(@ui-syntax-color), 24%); // max saturation +@ui-lightness: max( lightness(@ui-syntax-color), 92%); // min lightness + +// Main colors ----------------- +@ui-fg: hsl(@ui-hue, @ui-saturation, @ui-lightness - 72%); +@ui-bg: hsl(@ui-hue, @ui-saturation, @ui-lightness); // normalized @syntax-background-color +@ui-border: darken(@level-3-color, 6%); + + + + +// Custom variables +// These variables are only used in this theme +// ---------------------------------------------- + +@ui-theme-name: one-light-ui; + +// Text (Custom) ----------------- +@text-color-faded: fade(@text-color, 30%); + +@text-color-added: @text-color-success; // green +@text-color-ignored: @text-color-subtle; // faded +@text-color-modified: @text-color-warning; // orange +@text-color-removed: @text-color-error; // red +@text-color-renamed: @text-color-info; // blue + + +// Background (Custom) ----------------- +@level-1-color: lighten(@base-background-color, 4%); +@level-2-color: @base-background-color; +@level-3-color: darken(@base-background-color, 6%); + +@level-3-color-hover: darken(@level-3-color, 6%); +@level-3-color-active: darken(@level-3-color, 3%); + + +// Accent (Custom) ----------------- +@accent-luma: luma( hsl(@ui-hue, 50%, 50%) ); // get lightness of current hue + +// used for marker, inputs (smaller things) +@accent-color: mix( hsv( @ui-hue, 60%, 60%), hsl( @ui-hue, 100%, 68%), @accent-luma * 2 ); // mix hsv + hsl (favor hsl for dark, hsv for light colors) +@accent-text-color: contrast(@accent-color, hsl(@ui-hue,100%,16%), #fff, 40% ); + +// used for button, tooltip (larger things) +@accent-bg-color: mix( hsv( @ui-hue, 40%, 72%), hsl( @ui-hue, 100%, 66%), @accent-luma * 2 ); // mix hsv + hsl (favor hsl for dark, hsv for light colors) +@accent-bg-text-color: contrast(@accent-bg-color, hsl(@ui-hue,100%,10%), #fff, 40% ); + +// used for text only +@accent-only-text-color: mix( hsv( @ui-hue, 70%, 50%), hsl( @ui-hue, 100%, 60%), @accent-luma * 2 ); // mix hsv + hsl (favor hsl for dark, hsv for light colors) + + +// Components (Custom) ----------------- +@badge-background-color: @background-color-selected; + +@button-text-color-selected: @accent-bg-text-color; +@button-border-color-selected: @accent-color; + +@checkbox-background-color: fade(@accent-bg-color, 33%); + +@input-background-color-focus: hsl(@ui-hue, 100%, 96%); +@input-selection-color: mix( hsv( @ui-hue, 33%, 95%), hsl( @ui-hue, 100%, 98%), @accent-luma * 2 ); // mix hsv + hsl (favor hsl for dark, hsv for light colors) +@input-selection-color-focus: mix( hsv( @ui-hue, 44%, 90%), hsl( @ui-hue, 100%, 94%), @accent-luma * 2 ); // mix hsv + hsl (favor hsl for dark, hsv for light colors) + +@overlay-backdrop-color: hsl(@ui-hue, @ui-saturation*0.4, @ui-lightness*0.8); +@overlay-backdrop-opacity: .66; + +@progress-background-color: @accent-color; + +@scrollbar-color: darken(@level-3-color, 14%); +@scrollbar-background-color: @level-3-color; // replaced `transparent` with a solid color to test https://github.com/atom/one-light-ui/issues/4 +@scrollbar-color-editor: contrast(@ui-syntax-color, darken(@ui-syntax-color, 14%), lighten(@ui-syntax-color, 9%) ); +@scrollbar-background-color-editor: @ui-syntax-color; + +@tab-text-color: @text-color-subtle; +@tab-text-color-active: @text-color-highlight; +@tab-text-color-editor: contrast(@ui-syntax-color, lighten(@ui-syntax-color, 70%), @text-color-highlight ); +@tab-background-color-editor: @ui-syntax-color; +@tab-inactive-status-added: fade(@text-color-success, 77%); +@tab-inactive-status-modified: fade(@text-color-warning, 77%); + +@tooltip-background-color: @accent-bg-color; +@tooltip-text-color: @accent-bg-text-color; +@tooltip-text-key-color: @tooltip-background-color; +@tooltip-background-key-color: @tooltip-text-color; + + +// Sizes (Custom) ----------------- + +@ui-size: 1em; +@ui-input-size: @ui-size*1.15; +@ui-padding: @ui-size*1.5; +@ui-padding-pane: @ui-size*.5; +@ui-padding-icon: @ui-padding/3.3; +@ui-line-height: @ui-size*2; +@ui-tab-height: @ui-size*2.5; + + + + + +// Packages variables +// These variables are used to override packages +// ---------------------------------------------- + +@settings-list-background-color: darken(@level-2-color, 3%); +@theme-config-box-shadow: inset 0 1px 2px hsla(0, 0%, 0%, .2), 0 1px 0 hsla(0, 0%, 100%, .3); +@theme-config-box-shadow-selected: inset 0 1px 3px hsla(0, 0%, 0%, .2); +@theme-config-border-selected: hsla(0, 0%, 0%, .5); + + +// Debug +// Output variables to the top of the UI +// ------------------------------------- + +// html:before { +// content: "@{variable}"; +// } diff --git a/packages/one-light-ui/styles/ui-variables.less b/packages/one-light-ui/styles/ui-variables.less new file mode 100644 index 000000000..5749b6393 --- /dev/null +++ b/packages/one-light-ui/styles/ui-variables.less @@ -0,0 +1,97 @@ + +@import "ui-variables-custom.less"; // import colors and custom variables + +// ONE light UI variables +// ---------------------------------------------- + +// Official variables +// These variables must be defined in every theme +// Source: https://github.com/atom/atom/blob/master/static/variables/ui-variables.less +// ---------------------------------------------- + + +// Text ----------------- +@text-color: @ui-fg; +@text-color-subtle: lighten(@text-color, 30%); +@text-color-highlight: darken(@text-color, 12%); +@text-color-selected: darken(@text-color-highlight, 12%); + +@text-color-info: hsl(208, 100%, 54%); +@text-color-success: hsl(132, 60%, 44%); +@text-color-warning: hsl( 37, 90%, 44%); +@text-color-error: hsl( 0, 90%, 56%); + + +// Background ----------------- +@background-color-info: hsl(208, 100%, 56%); +@background-color-success: hsl(132, 52%, 48%); +@background-color-warning: hsl( 40, 60%, 48%); +@background-color-error: hsl( 5, 72%, 56%); + +@background-color-highlight: darken(@level-3-color, 2%); +@background-color-selected: darken(@level-3-color, 6%); + +@app-background-color: @level-3-color; + + +// Base ----------------- +@base-background-color: @ui-bg; +@base-border-color: @ui-border; + + +// Components ----------------- +@pane-item-background-color: @base-background-color; +@pane-item-border-color: @base-border-color; + +@input-background-color: @level-1-color; +@input-border-color: @base-border-color; + +@tool-panel-background-color: @level-3-color; +@tool-panel-border-color: @base-border-color; + +@inset-panel-background-color: lighten(@level-2-color, 4%); +@inset-panel-border-color: fadeout(@base-border-color, 15%); + +@panel-heading-background-color: @level-2-color; +@panel-heading-border-color: @base-border-color; + +@overlay-background-color: mix(@level-2-color, @level-3-color); +@overlay-border-color: @base-border-color; + +@button-background-color: @level-1-color; +@button-background-color-hover: darken(@button-background-color, 4%); +@button-background-color-selected: @accent-bg-color; +@button-border-color: @base-border-color; + +@tab-bar-background-color: @level-3-color; +@tab-bar-border-color: @base-border-color; +@tab-background-color: @level-3-color; +@tab-background-color-active: @level-2-color; +@tab-border-color: @base-border-color; + +@tree-view-background-color: @level-3-color; +@tree-view-border-color: @base-border-color; + +@ui-site-color-1: hsl(208, 100%, 56%); // blue +@ui-site-color-2: hsl(132, 48%, 48%); // green +@ui-site-color-3: hsl( 40, 60%, 52%); // orange +@ui-site-color-4: #D831B0; // pink +@ui-site-color-5: #EBDD5B; // yellow + + +// Sizes ----------------- +@font-size: 12px; +@input-font-size: 14px; +@disclosure-arrow-size: 12px; + +@component-padding: 10px; +@component-icon-padding: 5px; +@component-icon-size: 16px; // needs to stay 16px to look sharpest +@component-line-height: 25px; +@component-border-radius: 3px; + +@tab-height: 30px; + + +// Font ----------------- +@font-family: system-ui; diff --git a/resources/app-icons/nightly/atom.icns b/resources/app-icons/nightly/atom.icns new file mode 100644 index 000000000..e49166fe2 Binary files /dev/null and b/resources/app-icons/nightly/atom.icns differ diff --git a/resources/app-icons/nightly/atom.ico b/resources/app-icons/nightly/atom.ico new file mode 100644 index 000000000..158a02f0f Binary files /dev/null and b/resources/app-icons/nightly/atom.ico differ diff --git a/resources/app-icons/nightly/png/1024.png b/resources/app-icons/nightly/png/1024.png new file mode 100644 index 000000000..b81ab2a2e Binary files /dev/null and b/resources/app-icons/nightly/png/1024.png differ diff --git a/resources/app-icons/nightly/png/128.png b/resources/app-icons/nightly/png/128.png new file mode 100644 index 000000000..374043550 Binary files /dev/null and b/resources/app-icons/nightly/png/128.png differ diff --git a/resources/app-icons/nightly/png/16.png b/resources/app-icons/nightly/png/16.png new file mode 100644 index 000000000..582356487 Binary files /dev/null and b/resources/app-icons/nightly/png/16.png differ diff --git a/resources/app-icons/nightly/png/24.png b/resources/app-icons/nightly/png/24.png new file mode 100644 index 000000000..32f6999cf Binary files /dev/null and b/resources/app-icons/nightly/png/24.png differ diff --git a/resources/app-icons/nightly/png/256.png b/resources/app-icons/nightly/png/256.png new file mode 100644 index 000000000..89618fec4 Binary files /dev/null and b/resources/app-icons/nightly/png/256.png differ diff --git a/resources/app-icons/nightly/png/32.png b/resources/app-icons/nightly/png/32.png new file mode 100644 index 000000000..a83e9a4de Binary files /dev/null and b/resources/app-icons/nightly/png/32.png differ diff --git a/resources/app-icons/nightly/png/48.png b/resources/app-icons/nightly/png/48.png new file mode 100644 index 000000000..3637315e4 Binary files /dev/null and b/resources/app-icons/nightly/png/48.png differ diff --git a/resources/app-icons/nightly/png/512.png b/resources/app-icons/nightly/png/512.png new file mode 100644 index 000000000..7afe3fbe3 Binary files /dev/null and b/resources/app-icons/nightly/png/512.png differ diff --git a/resources/app-icons/nightly/png/64.png b/resources/app-icons/nightly/png/64.png new file mode 100644 index 000000000..2403b7b01 Binary files /dev/null and b/resources/app-icons/nightly/png/64.png differ diff --git a/resources/linux/atom.desktop.in b/resources/linux/atom.desktop.in index 6fa5d79c8..141397ab1 100644 --- a/resources/linux/atom.desktop.in +++ b/resources/linux/atom.desktop.in @@ -8,3 +8,4 @@ Type=Application StartupNotify=true Categories=GNOME;GTK;Utility;TextEditor;Development; MimeType=text/plain; +StartupWMClass=Atom diff --git a/resources/linux/debian/control.in b/resources/linux/debian/control.in index e8cb1c90e..c376be354 100644 --- a/resources/linux/debian/control.in +++ b/resources/linux/debian/control.in @@ -1,6 +1,6 @@ Package: <%= appFileName %> Version: <%= version %> -Depends: git, gconf2, gconf-service, libgtk2.0-0, libudev0 | libudev1, libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3, python, gvfs-bin, xdg-utils, libcap2 +Depends: git, gconf2, gconf-service, libgtk-3-0 (>= 3.9.10), libudev0 | libudev1, libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3 (>= 2:3.22), python, gvfs-bin, xdg-utils, libcap2, libx11-xcb1, libxss1, libasound2 (>= 1.0.16), libxkbfile1 Recommends: lsb-release Suggests: libsecret-1-0, gir1.2-gnomekeyring-1.0 Section: devel diff --git a/script/bootstrap b/script/bootstrap index 430d7959a..156ddf286 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -2,8 +2,9 @@ 'use strict' -const childProcess = require('child_process') +const path = require('path') const CONFIG = require('./config') +const childProcess = require('child_process') const cleanDependencies = require('./lib/clean-dependencies') const deleteMsbuildFromPath = require('./lib/delete-msbuild-from-path') const dependenciesFingerprint = require('./lib/dependencies-fingerprint') @@ -17,6 +18,14 @@ process.on('unhandledRejection', function (e) { process.exit(1) }) +// We can't use yargs until installScriptDependencies() is executed, so... +let ci = process.argv.indexOf('--ci') !== -1 + +if (!ci && process.env.CI === 'true' && process.argv.indexOf('--no-ci') === -1) { + console.log('Automatically enabling --ci because CI is set in the environment') + ci = true +} + verifyMachineRequirements() if (dependenciesFingerprint.isOutdated()) { @@ -25,13 +34,13 @@ if (dependenciesFingerprint.isOutdated()) { if (process.platform === 'win32') deleteMsbuildFromPath() -installScriptDependencies() -installApm() +installScriptDependencies(ci) +installApm(ci) childProcess.execFileSync( CONFIG.getApmBinPath(), ['--version'], {stdio: 'inherit'} ) -runApmInstall(CONFIG.repositoryRootPath) +runApmInstall(CONFIG.repositoryRootPath, ci) dependenciesFingerprint.write() diff --git a/script/build b/script/build index 55cebe96d..7b84a4eff 100755 --- a/script/build +++ b/script/build @@ -21,14 +21,15 @@ const argv = yargs .describe('create-debian-package', 'Create .deb package (Linux only)') .describe('create-rpm-package', 'Create .rpm package (Linux only)') .describe('compress-artifacts', 'Compress Atom binaries (and symbols on macOS)') + .describe('generate-api-docs', 'Only build the API documentation') .describe('install', 'Install Atom') .string('install') + .describe('ci', 'Install dependencies quickly (package-lock.json files must be up to date)') .wrap(yargs.terminalWidth()) .argv const checkChromedriverVersion = require('./lib/check-chromedriver-version') const cleanOutputDirectory = require('./lib/clean-output-directory') -const cleanPackageLock = require('./lib/clean-package-lock') const codeSignOnMac = require('./lib/code-sign-on-mac') const codeSignOnWindows = require('./lib/code-sign-on-windows') const compressArtifacts = require('./lib/compress-artifacts') @@ -59,7 +60,6 @@ const CONFIG = require('./config') let binariesPromise = Promise.resolve() if (!argv.existingBinaries) { - cleanPackageLock() checkChromedriverVersion() cleanOutputDirectory() copyAssets() @@ -72,65 +72,74 @@ if (!argv.existingBinaries) { prebuildLessCache() generateMetadata() generateAPIDocs() - binariesPromise = dumpSymbols() + if (!argv.generateApiDocs) { + binariesPromise = dumpSymbols() + } } -binariesPromise - .then(packageApplication) - .then(packagedAppPath => generateStartupSnapshot(packagedAppPath).then(() => packagedAppPath)) - .then(packagedAppPath => { - switch (process.platform) { - case 'darwin': { - if (argv.codeSign) { - codeSignOnMac(packagedAppPath) - } else { - console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray) - } - } - case 'win32': { - if (argv.codeSign) { - const executablesToSign = [ path.join(packagedAppPath, 'Atom.exe') ] - if (argv.createWindowsInstaller) { - executablesToSign.push(path.join(__dirname, 'node_modules', 'electron-winstaller', 'vendor', 'Update.exe')) +if (!argv.generateApiDocs) { + binariesPromise + .then(packageApplication) + .then(packagedAppPath => generateStartupSnapshot(packagedAppPath).then(() => packagedAppPath)) + .then(packagedAppPath => { + switch (process.platform) { + case 'darwin': { + if (argv.codeSign) { + codeSignOnMac(packagedAppPath) + } else { + console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray) } - codeSignOnWindows(executablesToSign) - } else { - console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray) + break } - if (argv.createWindowsInstaller) { - return createWindowsInstaller(packagedAppPath) - .then(() => argv.codeSign && codeSignOnWindows([ path.join(CONFIG.buildOutputPath, 'AtomSetup.exe') ])) - .then(() => packagedAppPath) - } else { - console.log('Skipping creating installer. Specify the --create-windows-installer option to create a Squirrel-based Windows installer.'.gray) + case 'win32': { + if (argv.codeSign) { + const executablesToSign = [ path.join(packagedAppPath, 'Atom.exe') ] + if (argv.createWindowsInstaller) { + executablesToSign.push(path.join(__dirname, 'node_modules', 'electron-winstaller', 'vendor', 'Update.exe')) + } + codeSignOnWindows(executablesToSign) + } else { + console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray) + } + if (argv.createWindowsInstaller) { + return createWindowsInstaller(packagedAppPath) + .then((installerPath) => { + argv.codeSign && codeSignOnWindows([installerPath]) + return packagedAppPath + }) + } else { + console.log('Skipping creating installer. Specify the --create-windows-installer option to create a Squirrel-based Windows installer.'.gray) + } + break + } + case 'linux': { + if (argv.createDebianPackage) { + createDebianPackage(packagedAppPath) + } else { + console.log('Skipping creating debian package. Specify the --create-debian-package option to create it.'.gray) + } + + if (argv.createRpmPackage) { + createRpmPackage(packagedAppPath) + } else { + console.log('Skipping creating rpm package. Specify the --create-rpm-package option to create it.'.gray) + } + break } } - case 'linux': { - if (argv.createDebianPackage) { - createDebianPackage(packagedAppPath) - } else { - console.log('Skipping creating debian package. Specify the --create-debian-package option to create it.'.gray) - } - if (argv.createRpmPackage) { - createRpmPackage(packagedAppPath) - } else { - console.log('Skipping creating rpm package. Specify the --create-rpm-package option to create it.'.gray) - } + return Promise.resolve(packagedAppPath) + }).then(packagedAppPath => { + if (argv.compressArtifacts) { + compressArtifacts(packagedAppPath) + } else { + console.log('Skipping artifacts compression. Specify the --compress-artifacts option to compress Atom binaries (and symbols on macOS)'.gray) } - } - return Promise.resolve(packagedAppPath) - }).then(packagedAppPath => { - if (argv.compressArtifacts) { - compressArtifacts(packagedAppPath) - } else { - console.log('Skipping artifacts compression. Specify the --compress-artifacts option to compress Atom binaries (and symbols on macOS)'.gray) - } - - if (argv.install != null) { - installApplication(packagedAppPath, argv.install) - } else { - console.log('Skipping installation. Specify the --install option to install Atom'.gray) - } - }) + if (argv.install != null) { + installApplication(packagedAppPath, argv.install) + } else { + console.log('Skipping installation. Specify the --install option to install Atom'.gray) + } + }) +} diff --git a/script/config.js b/script/config.js index 1dd670702..16b7cadb5 100644 --- a/script/config.js +++ b/script/config.js @@ -5,6 +5,7 @@ const fs = require('fs') const path = require('path') +const spawnSync = require('./lib/spawn-sync') const repositoryRootPath = path.resolve(__dirname, '..') const apmRootPath = path.join(repositoryRootPath, 'apm') @@ -19,12 +20,16 @@ const atomHomeDirPath = process.env.ATOM_HOME || path.join(homeDirPath, '.atom') const appMetadata = require(path.join(repositoryRootPath, 'package.json')) const apmMetadata = require(path.join(apmRootPath, 'package.json')) -const channel = getChannel() +const computedAppVersion = computeAppVersion(process.env.ATOM_RELEASE_VERSION || appMetadata.version) +const channel = getChannel(computedAppVersion) +const appName = getAppName(channel) module.exports = { appMetadata, apmMetadata, channel, + appName, + computedAppVersion, repositoryRootPath, apmRootPath, scriptRootPath, @@ -40,14 +45,30 @@ module.exports = { snapshotAuxiliaryData: {} } -function getChannel () { - if (appMetadata.version.match(/dev/)) { - return 'dev' - } else if (appMetadata.version.match(/beta/)) { - return 'beta' - } else { - return 'stable' +function getChannel (version) { + const match = version.match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/) + if (!match) { + throw new Error(`Found incorrectly formatted Atom version ${version}`) + } else if (match[2]) { + return match[2] } + + return 'stable' +} + +function getAppName (channel) { + return channel === 'stable' + ? 'Atom' + : `Atom ${process.env.ATOM_CHANNEL_DISPLAY_NAME || channel.charAt(0).toUpperCase() + channel.slice(1)}` +} + +function computeAppVersion (version) { + if (version.match(/-dev$/)) { + const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: repositoryRootPath}) + const commitHash = result.stdout.toString().trim() + version += '-' + commitHash + } + return version } function getApmBinPath () { @@ -55,8 +76,8 @@ function getApmBinPath () { return path.join(apmRootPath, 'node_modules', 'atom-package-manager', 'bin', apmBinName) } -function getNpmBinPath () { +function getNpmBinPath (external = false) { const npmBinName = process.platform === 'win32' ? 'npm.cmd' : 'npm' const localNpmBinPath = path.resolve(repositoryRootPath, 'script', 'node_modules', '.bin', npmBinName) - return fs.existsSync(localNpmBinPath) ? localNpmBinPath : npmBinName + return !external && fs.existsSync(localNpmBinPath) ? localNpmBinPath : npmBinName } diff --git a/script/create-installer.cmd b/script/create-installer.cmd deleted file mode 100644 index 0354f0bac..000000000 --- a/script/create-installer.cmd +++ /dev/null @@ -1,6 +0,0 @@ -@ECHO OFF -IF NOT EXIST C:\sqtemp MKDIR C:\sqtemp -SET SQUIRREL_TEMP=C:\sqtemp -del script\package-lock.json /q -del apm\package-lock.json /q -script\build.cmd --existing-binaries --code-sign --create-windows-installer diff --git a/script/lib/clean-caches.js b/script/lib/clean-caches.js index 1df3aa9c2..3861908bb 100644 --- a/script/lib/clean-caches.js +++ b/script/lib/clean-caches.js @@ -14,6 +14,7 @@ module.exports = function () { path.join(CONFIG.atomHomeDirPath, '.apm'), path.join(CONFIG.atomHomeDirPath, '.npm'), path.join(CONFIG.atomHomeDirPath, 'compile-cache'), + path.join(CONFIG.atomHomeDirPath, 'snapshot-cache'), path.join(CONFIG.atomHomeDirPath, 'atom-shell'), path.join(CONFIG.atomHomeDirPath, 'electron'), path.join(os.tmpdir(), 'atom-build'), diff --git a/script/lib/clean-package-lock.js b/script/lib/clean-package-lock.js deleted file mode 100644 index 01376c9c5..000000000 --- a/script/lib/clean-package-lock.js +++ /dev/null @@ -1,18 +0,0 @@ -// This module exports a function that deletes all `package-lock.json` files that do -// not exist under a `node_modules` directory. - -'use strict' - -const CONFIG = require('../config') -const fs = require('fs-extra') -const glob = require('glob') -const path = require('path') - -module.exports = function () { - console.log('Deleting problematic package-lock.json files') - let paths = glob.sync(path.join(CONFIG.repositoryRootPath, '**', 'package-lock.json'), {ignore: path.join('**', 'node_modules', '**')}) - - for (let path of paths) { - fs.unlinkSync(path) - } -} diff --git a/script/lib/code-sign-on-mac.js b/script/lib/code-sign-on-mac.js index a97438835..1d8243808 100644 --- a/script/lib/code-sign-on-mac.js +++ b/script/lib/code-sign-on-mac.js @@ -16,6 +16,36 @@ module.exports = function (packagedAppPath) { downloadFileFromGithub(process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath) } try { + console.log(`Ensuring keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN} exists`) + try { + spawnSync('security', [ + 'show-keychain-info', + process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN + ], {stdio: 'inherit'}) + } catch (err) { + console.log(`Creating keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`) + // The keychain doesn't exist, try to create it + spawnSync('security', [ + 'create-keychain', + '-p', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD, + process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN + ], {stdio: 'inherit'}) + + // List the keychain to "activate" it. Somehow this seems + // to be needed otherwise the signing operation fails + spawnSync('security', [ + 'list-keychains', + '-s', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN + ], {stdio: 'inherit'}) + + // Make sure it doesn't time out before we use it + spawnSync('security', [ + 'set-keychain-settings', + '-t', '3600', + '-u', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN + ], {stdio: 'inherit'}) + } + console.log(`Unlocking keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`) const unlockArgs = ['unlock-keychain'] // For signing on local workstations, password could be entered interactively diff --git a/script/lib/compress-artifacts.js b/script/lib/compress-artifacts.js index 7d9125925..74f31313f 100644 --- a/script/lib/compress-artifacts.js +++ b/script/lib/compress-artifacts.js @@ -3,6 +3,7 @@ const fs = require('fs-extra') const path = require('path') const spawnSync = require('./spawn-sync') +const { path7za } = require('7zip-bin') const CONFIG = require('../config') @@ -19,7 +20,7 @@ module.exports = function (packagedAppPath) { function getArchiveName () { switch (process.platform) { case 'darwin': return 'atom-mac.zip' - case 'win32': return 'atom-windows.zip' + case 'win32': return `atom-${process.arch === 'x64' ? 'x64-' : ''}windows.zip` default: return `atom-${getLinuxArchiveArch()}.tar.gz` } } @@ -44,7 +45,7 @@ function compress (inputDirPath, outputArchivePath) { compressCommand = 'zip' compressArguments = ['-r', '--symlinks'] } else if (process.platform === 'win32') { - compressCommand = '7z.exe' + compressCommand = path7za compressArguments = ['a', '-r'] } else { compressCommand = 'tar' diff --git a/script/lib/copy-assets.js b/script/lib/copy-assets.js index d53515f4a..27b5f086c 100644 --- a/script/lib/copy-assets.js +++ b/script/lib/copy-assets.js @@ -23,7 +23,7 @@ module.exports = function () { ] srcPaths = srcPaths.concat(glob.sync(path.join(CONFIG.repositoryRootPath, 'spec', '*.*'), {ignore: path.join('**', '*-spec.*')})) for (let srcPath of srcPaths) { - fs.copySync(srcPath, computeDestinationPath(srcPath), {filter: includePathInPackagedApp}) + fs.copySync(srcPath, computeDestinationPath(srcPath), {filter: includePathInPackagedApp, dereference: true}) } fs.copySync( diff --git a/script/lib/create-debian-package.js b/script/lib/create-debian-package.js index 120463f7b..1aa179b70 100644 --- a/script/lib/create-debian-package.js +++ b/script/lib/create-debian-package.js @@ -10,9 +10,8 @@ const CONFIG = require('../config') module.exports = function (packagedAppPath) { console.log(`Creating Debian package for "${packagedAppPath}"`) - const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom' - const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm' - const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom' + const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}` + const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}` const appDescription = CONFIG.appMetadata.description const appVersion = CONFIG.appMetadata.version let arch @@ -88,7 +87,7 @@ module.exports = function (packagedAppPath) { console.log(`Writing desktop entry file into "${debianPackageApplicationsDirPath}"`) const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in')) const desktopEntryContents = template(desktopEntryTemplate)({ - appName: appName, + appName: CONFIG.appName, appFileName: atomExecutableName, description: appDescription, installDir: '/usr', diff --git a/script/lib/create-rpm-package.js b/script/lib/create-rpm-package.js index f76385aab..cdef23300 100644 --- a/script/lib/create-rpm-package.js +++ b/script/lib/create-rpm-package.js @@ -10,9 +10,9 @@ const CONFIG = require('../config') module.exports = function (packagedAppPath) { console.log(`Creating rpm package for "${packagedAppPath}"`) - const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom' - const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm' - const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom' + const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}` + const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}` + const appName = CONFIG.appName const appDescription = CONFIG.appMetadata.description // RPM versions can't have dashes or tildes in them. // (Ref.: https://twiki.cern.ch/twiki/bin/view/Main/RPMAndDebVersioning) diff --git a/script/lib/create-windows-installer.js b/script/lib/create-windows-installer.js index ddc46d484..f5e387e7f 100644 --- a/script/lib/create-windows-installer.js +++ b/script/lib/create-windows-installer.js @@ -1,7 +1,7 @@ 'use strict' const electronInstaller = require('electron-winstaller') -const fs = require('fs-extra') +const fs = require('fs') const glob = require('glob') const path = require('path') @@ -16,17 +16,32 @@ module.exports = (packagedAppPath) => { loadingGif: path.join(CONFIG.repositoryRootPath, 'resources', 'win', 'loading.gif'), outputDirectory: CONFIG.buildOutputPath, noMsi: true, - remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.appMetadata.version}`, + noDelta: CONFIG.channel === 'nightly', // Delta packages are broken for nightly versions past nightly9 due to Squirrel/NuGet limitations + remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.computedAppVersion}`, + setupExe: `AtomSetup${process.arch === 'x64' ? '-x64' : ''}.exe`, setupIcon: path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico') } const cleanUp = () => { - for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/*.nupkg`)) { - if (!nupkgPath.includes(CONFIG.appMetadata.version)) { + const releasesPath = `${CONFIG.buildOutputPath}/RELEASES` + if (process.arch === 'x64' && fs.existsSync(releasesPath)) { + fs.renameSync(releasesPath, `${releasesPath}-x64`) + } + + for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/atom-*.nupkg`)) { + if (!nupkgPath.includes(CONFIG.computedAppVersion)) { console.log(`Deleting downloaded nupkg for previous version at ${nupkgPath} to prevent it from being stored as an artifact`) - fs.removeSync(nupkgPath) + fs.unlinkSync(nupkgPath) + } else { + if (process.arch === 'x64') { + // Use the original .nupkg filename to generate the `atom-x64` name by inserting `-x64` after `atom` + const newNupkgPath = nupkgPath.replace('atom-', 'atom-x64-') + fs.renameSync(nupkgPath, newNupkgPath) + } } } + + return `${CONFIG.buildOutputPath}/${options.setupExe}` } console.log(`Creating Windows Installer for ${packagedAppPath}`) diff --git a/script/lib/generate-metadata.js b/script/lib/generate-metadata.js index 6bcd18618..ae61b6633 100644 --- a/script/lib/generate-metadata.js +++ b/script/lib/generate-metadata.js @@ -6,7 +6,6 @@ const fs = require('fs-plus') const normalizePackageData = require('normalize-package-data') const path = require('path') const semver = require('semver') -const spawnSync = require('./spawn-sync') const CONFIG = require('../config') @@ -16,7 +15,7 @@ module.exports = function () { CONFIG.appMetadata._atomMenu = buildPlatformMenuMetadata() CONFIG.appMetadata._atomKeymaps = buildPlatformKeymapsMetadata() CONFIG.appMetadata._deprecatedPackages = deprecatedPackagesMetadata - CONFIG.appMetadata.version = computeAppVersion() + CONFIG.appMetadata.version = CONFIG.computedAppVersion checkDeprecatedPackagesMetadata() fs.writeFileSync(path.join(CONFIG.intermediateAppPath, 'package.json'), JSON.stringify(CONFIG.appMetadata)) } @@ -162,13 +161,3 @@ function checkDeprecatedPackagesMetadata () { } } } - -function computeAppVersion () { - let version = CONFIG.appMetadata.version - if (CONFIG.channel === 'dev') { - const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: CONFIG.repositoryRootPath}) - const commitHash = result.stdout.toString().trim() - version += '-' + commitHash - } - return version -} diff --git a/script/lib/generate-startup-snapshot.js b/script/lib/generate-startup-snapshot.js index 89812a762..d570ce551 100644 --- a/script/lib/generate-startup-snapshot.js +++ b/script/lib/generate-startup-snapshot.js @@ -15,54 +15,58 @@ module.exports = function (packagedAppPath) { mainPath: path.resolve(baseDirPath, '..', 'src', 'initialize-application-window.js'), cachePath: path.join(CONFIG.atomHomeDirPath, 'snapshot-cache'), auxiliaryData: CONFIG.snapshotAuxiliaryData, - shouldExcludeModule: (modulePath) => { + shouldExcludeModule: ({requiringModulePath, requiredModulePath}) => { if (processedFiles > 0) { process.stdout.write('\r') } process.stdout.write(`Generating snapshot script at "${snapshotScriptPath}" (${++processedFiles})`) - const relativePath = path.relative(baseDirPath, modulePath) + const requiringModuleRelativePath = path.relative(baseDirPath, requiringModulePath) + const requiredModuleRelativePath = path.relative(baseDirPath, requiredModulePath) return ( - modulePath.endsWith('.node') || - coreModules.has(modulePath) || - (relativePath.startsWith(path.join('..', 'src')) && relativePath.endsWith('-element.js')) || - relativePath.startsWith(path.join('..', 'node_modules', 'dugite')) || - relativePath.endsWith(path.join('node_modules', 'coffee-script', 'lib', 'coffee-script', 'register.js')) || - relativePath.endsWith(path.join('node_modules', 'fs-extra', 'lib', 'index.js')) || - relativePath.endsWith(path.join('node_modules', 'graceful-fs', 'graceful-fs.js')) || - relativePath.endsWith(path.join('node_modules', 'htmlparser2', 'lib', 'index.js')) || - relativePath.endsWith(path.join('node_modules', 'minimatch', 'minimatch.js')) || - relativePath === path.join('..', 'exports', 'atom.js') || - relativePath === path.join('..', 'src', 'electron-shims.js') || - relativePath === path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') || - relativePath === path.join('..', 'node_modules', 'babel-core', 'index.js') || - relativePath === path.join('..', 'node_modules', 'cached-run-in-this-context', 'lib', 'main.js') || - relativePath === path.join('..', 'node_modules', 'decompress-zip', 'lib', 'decompress-zip.js') || - relativePath === path.join('..', 'node_modules', 'debug', 'node.js') || - relativePath === path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') || - relativePath === path.join('..', 'node_modules', 'glob', 'glob.js') || - relativePath === path.join('..', 'node_modules', 'iconv-lite', 'lib', 'index.js') || - relativePath === path.join('..', 'node_modules', 'less', 'index.js') || - relativePath === path.join('..', 'node_modules', 'less', 'lib', 'less', 'fs.js') || - relativePath === path.join('..', 'node_modules', 'less', 'lib', 'less-node', 'index.js') || - relativePath === path.join('..', 'node_modules', 'lodash.isequal', 'index.js') || - relativePath === path.join('..', 'node_modules', 'node-fetch', 'lib', 'fetch-error.js') || - relativePath === path.join('..', 'node_modules', 'superstring', 'index.js') || - relativePath === path.join('..', 'node_modules', 'oniguruma', 'src', 'oniguruma.js') || - relativePath === path.join('..', 'node_modules', 'request', 'index.js') || - relativePath === path.join('..', 'node_modules', 'resolve', 'index.js') || - relativePath === path.join('..', 'node_modules', 'resolve', 'lib', 'core.js') || - relativePath === path.join('..', 'node_modules', 'settings-view', 'node_modules', 'glob', 'glob.js') || - relativePath === path.join('..', 'node_modules', 'spellchecker', 'lib', 'spellchecker.js') || - relativePath === path.join('..', 'node_modules', 'spelling-manager', 'node_modules', 'natural', 'lib', 'natural', 'index.js') || - relativePath === path.join('..', 'node_modules', 'tar', 'tar.js') || - relativePath === path.join('..', 'node_modules', 'temp', 'lib', 'temp.js') || - relativePath === path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') || - relativePath === path.join('..', 'node_modules', 'tree-sitter', 'index.js') || - relativePath === path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js') + requiredModulePath.endsWith('.node') || + coreModules.has(requiredModulePath) || + requiringModuleRelativePath.endsWith(path.join('node_modules/xregexp/xregexp-all.js')) || + (requiredModuleRelativePath.startsWith(path.join('..', 'src')) && requiredModuleRelativePath.endsWith('-element.js')) || + requiredModuleRelativePath.startsWith(path.join('..', 'node_modules', 'dugite')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'coffee-script', 'lib', 'coffee-script', 'register.js')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'fs-extra', 'lib', 'index.js')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'graceful-fs', 'graceful-fs.js')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'htmlparser2', 'lib', 'index.js')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'minimatch', 'minimatch.js')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'request', 'index.js')) || + requiredModuleRelativePath.endsWith(path.join('node_modules', 'request', 'request.js')) || + requiredModuleRelativePath === path.join('..', 'exports', 'atom.js') || + requiredModuleRelativePath === path.join('..', 'src', 'electron-shims.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'atom-keymap', 'lib', 'command-event.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'babel-core', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'cached-run-in-this-context', 'lib', 'main.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'debug', 'node.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'git-utils', 'src', 'git.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'glob', 'glob.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'iconv-lite', 'lib', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'less', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'less', 'lib', 'less', 'fs.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'less', 'lib', 'less-node', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'lodash.isequal', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'node-fetch', 'lib', 'fetch-error.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'superstring', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'oniguruma', 'src', 'oniguruma.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'resolve', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'resolve', 'lib', 'core.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'settings-view', 'node_modules', 'glob', 'glob.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'spellchecker', 'lib', 'spellchecker.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'spelling-manager', 'node_modules', 'natural', 'lib', 'natural', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'tar', 'tar.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'ls-archive', 'node_modules', 'tar', 'tar.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'temp', 'lib', 'temp.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'tmp', 'lib', 'tmp.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'tree-sitter', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'yauzl', 'index.js') || + requiredModuleRelativePath === path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js') ) } - }).then((snapshotScript) => { + }).then(({snapshotScript}) => { fs.writeFileSync(snapshotScriptPath, snapshotScript) process.stdout.write('\n') @@ -70,7 +74,7 @@ module.exports = function (packagedAppPath) { const verifySnapshotScriptPath = path.join(CONFIG.repositoryRootPath, 'script', 'verify-snapshot-script') let nodeBundledInElectronPath if (process.platform === 'darwin') { - const executableName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom' + const executableName = CONFIG.appName nodeBundledInElectronPath = path.join(packagedAppPath, 'Contents', 'MacOS', executableName) } else if (process.platform === 'win32') { nodeBundledInElectronPath = path.join(packagedAppPath, 'atom.exe') diff --git a/script/lib/include-path-in-packaged-app.js b/script/lib/include-path-in-packaged-app.js index 603f14da0..842cfab2f 100644 --- a/script/lib/include-path-in-packaged-app.js +++ b/script/lib/include-path-in-packaged-app.js @@ -14,7 +14,6 @@ const EXCLUDE_REGEXPS_SOURCES = [ escapeRegExp('.pairs'), escapeRegExp('.travis.yml'), escapeRegExp('appveyor.yml'), - escapeRegExp('circle.yml'), escapeRegExp('.idea'), escapeRegExp('.editorconfig'), escapeRegExp('.lint'), @@ -69,7 +68,6 @@ const EXCLUDE_REGEXPS_SOURCES = [ // Ignore node_module files we won't need at runtime 'node_modules' + escapeRegExp(path.sep) + '.*' + escapeRegExp(path.sep) + '_*te?sts?_*' + escapeRegExp(path.sep), 'node_modules' + escapeRegExp(path.sep) + '.*' + escapeRegExp(path.sep) + 'examples?' + escapeRegExp(path.sep), - 'node_modules' + escapeRegExp(path.sep) + '.*' + '\\.md$', 'node_modules' + escapeRegExp(path.sep) + '.*' + '\\.d\\.ts$', 'node_modules' + escapeRegExp(path.sep) + '.*' + '\\.js\\.map$', '.*' + escapeRegExp(path.sep) + 'test.*\\.html$' diff --git a/script/lib/install-apm.js b/script/lib/install-apm.js index ce68b79a6..81c9e849d 100644 --- a/script/lib/install-apm.js +++ b/script/lib/install-apm.js @@ -4,8 +4,9 @@ const childProcess = require('child_process') const CONFIG = require('../config') -module.exports = function () { +module.exports = function (ci) { console.log('Installing apm') + // npm ci leaves apm with a bunch of unmet dependencies childProcess.execFileSync( CONFIG.getNpmBinPath(), ['--global-style', '--loglevel=error', 'install'], diff --git a/script/lib/install-application.js b/script/lib/install-application.js index d21a6e53c..8a29372cd 100644 --- a/script/lib/install-application.js +++ b/script/lib/install-application.js @@ -4,30 +4,54 @@ const fs = require('fs-extra') const handleTilde = require('./handle-tilde') const path = require('path') const template = require('lodash.template') +const startCase = require('lodash.startcase') +const execSync = require('child_process').execSync const CONFIG = require('../config') +function install (installationDirPath, packagedAppFileName, packagedAppPath) { + if (fs.existsSync(installationDirPath)) { + console.log(`Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"`) + fs.removeSync(installationDirPath) + } + + console.log(`Installing "${packagedAppFileName}" at "${installationDirPath}"`) + fs.copySync(packagedAppPath, installationDirPath) +} + +/** + * Finds the path to the base directory of the icon default icon theme + * This follows the freedesktop Icon Theme Specification: + * https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#install_icons + * and the XDG Base Directory Specification: + * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables + */ +function findBaseIconThemeDirPath () { + const defaultBaseIconThemeDir = '/usr/share/icons/hicolor' + const dataDirsString = process.env.XDG_DATA_DIRS + if (dataDirsString) { + const dataDirs = dataDirsString.split(path.delimiter) + if (dataDirs.includes('/usr/share/') || dataDirs.includes('/usr/share')) { + return defaultBaseIconThemeDir + } else { + return path.join(dataDirs[0], 'icons', 'hicolor') + } + } else { + return defaultBaseIconThemeDir + } +} + module.exports = function (packagedAppPath, installDir) { const packagedAppFileName = path.basename(packagedAppPath) if (process.platform === 'darwin') { const installPrefix = installDir !== '' ? handleTilde(installDir) : path.join(path.sep, 'Applications') const installationDirPath = path.join(installPrefix, packagedAppFileName) - if (fs.existsSync(installationDirPath)) { - console.log(`Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"`) - fs.removeSync(installationDirPath) - } - console.log(`Installing "${packagedAppPath}" at "${installationDirPath}"`) - fs.copySync(packagedAppPath, installationDirPath) + install(installationDirPath, packagedAppFileName, packagedAppPath) } else if (process.platform === 'win32') { const installPrefix = installDir !== '' ? installDir : process.env.LOCALAPPDATA const installationDirPath = path.join(installPrefix, packagedAppFileName, 'app-dev') try { - if (fs.existsSync(installationDirPath)) { - console.log(`Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"`) - fs.removeSync(installationDirPath) - } - console.log(`Installing "${packagedAppPath}" at "${installationDirPath}"`) - fs.copySync(packagedAppPath, installationDirPath) + install(installationDirPath, packagedAppFileName, packagedAppPath) } catch (e) { console.log(`Administrator elevation required to install into "${installationDirPath}"`) const fsAdmin = require('fs-admin') @@ -38,59 +62,92 @@ module.exports = function (packagedAppPath, installDir) { }) } } else { - const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom' - const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm' - const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom' + const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : 'atom-' + CONFIG.channel + const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : 'apm-' + CONFIG.channel + const appName = CONFIG.channel === 'stable' ? 'Atom' : startCase('Atom ' + CONFIG.channel) const appDescription = CONFIG.appMetadata.description const prefixDirPath = installDir !== '' ? handleTilde(installDir) : path.join('/usr', 'local') const shareDirPath = path.join(prefixDirPath, 'share') const installationDirPath = path.join(shareDirPath, atomExecutableName) const applicationsDirPath = path.join(shareDirPath, 'applications') - const desktopEntryPath = path.join(applicationsDirPath, `${atomExecutableName}.desktop`) + const binDirPath = path.join(prefixDirPath, 'bin') - const atomBinDestinationPath = path.join(binDirPath, atomExecutableName) - const apmBinDestinationPath = path.join(binDirPath, apmExecutableName) fs.mkdirpSync(applicationsDirPath) fs.mkdirpSync(binDirPath) - if (fs.existsSync(installationDirPath)) { - console.log(`Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"`) - fs.removeSync(installationDirPath) - } - console.log(`Installing "${packagedAppFileName}" at "${installationDirPath}"`) - fs.copySync(packagedAppPath, installationDirPath) + install(installationDirPath, packagedAppFileName, packagedAppPath) - if (fs.existsSync(desktopEntryPath)) { - console.log(`Removing existing desktop entry file at "${desktopEntryPath}"`) - fs.removeSync(desktopEntryPath) - } - console.log(`Writing desktop entry file at "${desktopEntryPath}"`) - const iconPath = path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png', '1024.png') - const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in')) - const desktopEntryContents = template(desktopEntryTemplate)({ - appName, - appFileName: atomExecutableName, - description: appDescription, - installDir: prefixDirPath, - iconPath - }) - fs.writeFileSync(desktopEntryPath, desktopEntryContents) + { // Install icons + const baseIconThemeDirPath = findBaseIconThemeDirPath() + const fullIconName = atomExecutableName + '.png' - if (fs.existsSync(atomBinDestinationPath)) { - console.log(`Removing existing executable at "${atomBinDestinationPath}"`) - fs.removeSync(atomBinDestinationPath) - } - console.log(`Copying atom.sh to "${atomBinDestinationPath}"`) - fs.copySync(path.join(CONFIG.repositoryRootPath, 'atom.sh'), atomBinDestinationPath) + let existingIconsFound = false + fs.readdirSync(baseIconThemeDirPath).forEach(size => { + const iconPath = path.join(baseIconThemeDirPath, size, 'apps', fullIconName) + if (fs.existsSync(iconPath)) { + if (!existingIconsFound) { + console.log(`Removing existing icons from "${baseIconThemeDirPath}"`) + } + existingIconsFound = true + fs.removeSync(iconPath) + } + }) - try { - fs.lstatSync(apmBinDestinationPath) - console.log(`Removing existing executable at "${apmBinDestinationPath}"`) - fs.removeSync(apmBinDestinationPath) - } catch (e) { } - console.log(`Symlinking apm to "${apmBinDestinationPath}"`) - fs.symlinkSync(path.join('..', 'share', atomExecutableName, 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), apmBinDestinationPath) + console.log(`Installing icons at "${baseIconThemeDirPath}"`) + const appIconsPath = path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png') + fs.readdirSync(appIconsPath).forEach(imageName => { + if (/\.png$/.test(imageName)) { + const size = path.basename(imageName, '.png') + const iconPath = path.join(appIconsPath, imageName) + fs.copySync(iconPath, path.join(baseIconThemeDirPath, `${size}x${size}`, 'apps', fullIconName)) + } + }) + + console.log(`Updating icon cache for "${baseIconThemeDirPath}"`) + try { + execSync(`gtk-update-icon-cache ${baseIconThemeDirPath} --force`) + } catch (e) {} + } + + { // Install xdg desktop file + const desktopEntryPath = path.join(applicationsDirPath, `${atomExecutableName}.desktop`) + if (fs.existsSync(desktopEntryPath)) { + console.log(`Removing existing desktop entry file at "${desktopEntryPath}"`) + fs.removeSync(desktopEntryPath) + } + console.log(`Writing desktop entry file at "${desktopEntryPath}"`) + const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in')) + const desktopEntryContents = template(desktopEntryTemplate)({ + appName, + appFileName: atomExecutableName, + description: appDescription, + installDir: prefixDirPath, + iconPath: atomExecutableName + }) + fs.writeFileSync(desktopEntryPath, desktopEntryContents) + } + + { // Add atom executable to the PATH + const atomBinDestinationPath = path.join(binDirPath, atomExecutableName) + if (fs.existsSync(atomBinDestinationPath)) { + console.log(`Removing existing executable at "${atomBinDestinationPath}"`) + fs.removeSync(atomBinDestinationPath) + } + console.log(`Copying atom.sh to "${atomBinDestinationPath}"`) + fs.copySync(path.join(CONFIG.repositoryRootPath, 'atom.sh'), atomBinDestinationPath) + } + + { // Link apm executable to the PATH + const apmBinDestinationPath = path.join(binDirPath, apmExecutableName) + try { + fs.lstatSync(apmBinDestinationPath) + console.log(`Removing existing executable at "${apmBinDestinationPath}"`) + fs.removeSync(apmBinDestinationPath) + } catch (e) { } + console.log(`Symlinking apm to "${apmBinDestinationPath}"`) + fs.symlinkSync(path.join('..', 'share', atomExecutableName, 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), apmBinDestinationPath) + } console.log(`Changing permissions to 755 for "${installationDirPath}"`) fs.chmodSync(installationDirPath, '755') diff --git a/script/lib/install-script-dependencies.js b/script/lib/install-script-dependencies.js index 5a36e69a6..76f6e94c6 100644 --- a/script/lib/install-script-dependencies.js +++ b/script/lib/install-script-dependencies.js @@ -4,11 +4,11 @@ const childProcess = require('child_process') const CONFIG = require('../config') -module.exports = function () { +module.exports = function (ci) { console.log('Installing script dependencies') childProcess.execFileSync( - CONFIG.getNpmBinPath(), - ['--loglevel=error', 'install'], + CONFIG.getNpmBinPath(ci), + ['--loglevel=error', ci ? 'ci' : 'install'], {env: process.env, cwd: CONFIG.scriptRootPath} ) } diff --git a/script/lib/lint-less-paths.js b/script/lib/lint-less-paths.js index 84e6aa3ae..c46d847c3 100644 --- a/script/lib/lint-less-paths.js +++ b/script/lib/lint-less-paths.js @@ -1,64 +1,63 @@ 'use strict' -const csslint = require('csslint').CSSLint -const expandGlobPaths = require('./expand-glob-paths') -const LessCache = require('less-cache') +const stylelint = require('stylelint') const path = require('path') -const readFiles = require('./read-files') const CONFIG = require('../config') -const LESS_CACHE_VERSION = require('less-cache/package.json').version module.exports = function () { - const globPathsToLint = [ - path.join(CONFIG.repositoryRootPath, 'static/**/*.less') - ] - const lintOptions = { - 'adjoining-classes': false, - 'duplicate-background-images': false, - 'box-model': false, - 'box-sizing': false, - 'bulletproof-font-face': false, - 'compatible-vendor-prefixes': false, - 'display-property-grouping': false, - 'duplicate-properties': false, - 'fallback-colors': false, - 'font-sizes': false, - 'gradients': false, - 'ids': false, - 'important': false, - 'known-properties': false, - 'order-alphabetical': false, - 'outline-none': false, - 'overqualified-elements': false, - 'regex-selectors': false, - 'qualified-headings': false, - 'unique-headings': false, - 'universal-selector': false, - 'vendor-prefix': false - } - for (let rule of csslint.getRules()) { - if (!lintOptions.hasOwnProperty(rule.id)) lintOptions[rule.id] = true - } - const lessCache = new LessCache({ - cacheDir: path.join(CONFIG.intermediateAppPath, 'less-compile-cache'), - fallbackDir: path.join(CONFIG.atomHomeDirPath, 'compile-cache', 'prebuild-less', LESS_CACHE_VERSION), - syncCaches: true, - resourcePath: CONFIG.repositoryRootPath, - importPaths: [ - path.join(CONFIG.intermediateAppPath, 'static', 'variables'), - path.join(CONFIG.intermediateAppPath, 'static') - ] - }) - return expandGlobPaths(globPathsToLint).then(readFiles).then((files) => { - const errors = [] - for (let file of files) { - const css = lessCache.cssForFile(file.path, file.content) - const result = csslint.verify(css, lintOptions) - for (let message of result.messages) { - errors.push({path: file.path.replace(/\.less$/, '.css'), lineNumber: message.line, message: message.message, rule: message.rule.id}) + return stylelint + .lint({ + files: path.join(CONFIG.repositoryRootPath, 'static/**/*.less'), + configBasedir: __dirname, + configFile: path.resolve(__dirname, '..', '..', 'stylelint.config.js') + }) + .then(({results}) => { + const errors = [] + + for (const result of results) { + for (const deprecation of result.deprecations) { + console.log('stylelint encountered deprecation:', deprecation.text) + if (deprecation.reference != null) { + console.log('more information at', deprecation.reference) + } + } + + for (const invalidOptionWarning of result.invalidOptionWarnings) { + console.warn( + 'stylelint encountered invalid option:', + invalidOptionWarning.text + ) + } + + if (result.errored) { + for (const warning of result.warnings) { + if (warning.severity === 'error') { + errors.push({ + path: result.source, + lineNumber: warning.line, + message: warning.text, + rule: warning.rule + }) + } else { + console.warn( + 'stylelint encountered non-critical warning in file', + result.source, + 'at line', + warning.line, + 'for rule', + warning.rule + ':', + warning.text + ) + } + } + } } - } - return errors - }) + + return errors + }) + .catch(err => { + console.error('There was a problem linting LESS:') + throw err + }) } diff --git a/script/lib/package-application.js b/script/lib/package-application.js index 842883e51..1b3c19b28 100644 --- a/script/lib/package-application.js +++ b/script/lib/package-application.js @@ -114,7 +114,7 @@ function buildAsarUnpackGlobExpression () { function getAppName () { if (process.platform === 'darwin') { - return CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom' + return CONFIG.appName } else { return 'atom' } @@ -156,7 +156,7 @@ function renamePackagedAppDir (packageOutputDirPath) { if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath) fs.renameSync(path.join(packageOutputDirPath, appBundleName), packagedAppPath) } else if (process.platform === 'linux') { - const appName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom' + const appName = CONFIG.channel !== 'stable' ? `atom-${CONFIG.channel}` : 'atom' let architecture if (process.arch === 'ia32') { architecture = 'i386' @@ -169,8 +169,7 @@ function renamePackagedAppDir (packageOutputDirPath) { if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath) fs.renameSync(packageOutputDirPath, packagedAppPath) } else { - const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom' - packagedAppPath = path.join(CONFIG.buildOutputPath, appName) + packagedAppPath = path.join(CONFIG.buildOutputPath, CONFIG.appName) if (process.platform === 'win32' && process.arch !== 'ia32') { packagedAppPath += ` ${process.arch}` } diff --git a/script/lib/run-apm-install.js b/script/lib/run-apm-install.js index 3f56e8dd4..982ed7165 100644 --- a/script/lib/run-apm-install.js +++ b/script/lib/run-apm-install.js @@ -4,7 +4,7 @@ const childProcess = require('child_process') const CONFIG = require('../config') -module.exports = function (packagePath) { +module.exports = function (packagePath, ci, stdioOptions) { const installEnv = Object.assign({}, process.env) // Set resource path so that apm can load metadata related to Atom. installEnv.ATOM_RESOURCE_PATH = CONFIG.repositoryRootPath @@ -13,7 +13,7 @@ module.exports = function (packagePath) { installEnv.npm_config_target = CONFIG.appMetadata.electronVersion childProcess.execFileSync( CONFIG.getApmBinPath(), - ['--loglevel=error', 'install'], - {env: installEnv, cwd: packagePath, stdio: 'inherit'} + ['--loglevel=error', ci ? 'ci' : 'install'], + {env: installEnv, cwd: packagePath, stdio: stdioOptions || 'inherit'} ) } diff --git a/script/lib/transpile-babel-paths.js b/script/lib/transpile-babel-paths.js index b7906b9b6..befd2477b 100644 --- a/script/lib/transpile-babel-paths.js +++ b/script/lib/transpile-babel-paths.js @@ -16,9 +16,6 @@ module.exports = function () { function getPathsToTranspile () { let paths = [] - paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'benchmarks', '**', '*.js'), {nodir: true})) - paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'exports', '**', '*.js'), {nodir: true})) - paths = paths.concat(glob.sync(path.join(CONFIG.intermediateAppPath, 'src', '**', '*.js'), {nodir: true})) for (let packageName of Object.keys(CONFIG.appMetadata.packageDependencies)) { paths = paths.concat(glob.sync( path.join(CONFIG.intermediateAppPath, 'node_modules', packageName, '**', '*.js'), diff --git a/script/lib/upload-to-s3.js b/script/lib/upload-to-s3.js new file mode 100644 index 000000000..91c50b384 --- /dev/null +++ b/script/lib/upload-to-s3.js @@ -0,0 +1,57 @@ +'use strict' + +const fs = require('fs') +const path = require('path') +const aws = require('aws-sdk') + +module.exports = function (s3Key, s3Secret, s3Bucket, directory, assets) { + const s3 = new aws.S3({ + accessKeyId: s3Key, + secretAccessKey: s3Secret, + params: { Bucket: s3Bucket } + }) + + function listExistingAssetsForDirectory (directory) { + return s3.listObjectsV2({ Prefix: directory }).promise().then((res) => { + return res.Contents.map((obj) => { return { Key: obj.Key } }) + }) + } + + function deleteExistingAssets (existingAssets) { + if (existingAssets.length > 0) { + return s3.deleteObjects({ Delete: { Objects: existingAssets } }).promise() + } else { + return Promise.resolve(true) + } + } + + function uploadAssets (assets, directory) { + return assets.reduce( + function (promise, asset) { + return promise.then(() => uploadAsset(directory, asset)) + }, Promise.resolve()) + } + + function uploadAsset (directory, assetPath) { + return new Promise((resolve, reject) => { + console.info(`Uploading ${assetPath}`) + const params = { + Key: `${directory}${path.basename(assetPath)}`, + ACL: 'public-read', + Body: fs.createReadStream(assetPath) + } + + s3.upload(params, error => { + if (error) { + reject(error) + } else { + resolve() + } + }) + }) + } + + return listExistingAssetsForDirectory(directory) + .then(deleteExistingAssets) + .then(() => uploadAssets(assets, directory)) +} diff --git a/script/package-lock.json b/script/package-lock.json new file mode 100644 index 000000000..d6e16a900 --- /dev/null +++ b/script/package-lock.json @@ -0,0 +1,10747 @@ +{ + "name": "atom-build-scripts", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "7zip-bin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-4.0.2.tgz", + "integrity": "sha512-XtGk+IF57pr852UK1AhQJXqmm1WmSgS5uISL+LPs0z/iAxXouMvdlLJrHPeukP6gd7yR2rDTMSMkHNODgwIq7A==" + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz", + "integrity": "sha512-LAQ1d4OPfSJ/BMbI2DuizmYrrkD9JMaTdi2hQTlI53lQ4kRQPyZQRS4CYQ7O66bnBBnP/oYdRxbk++X0xuFU6A==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "abstract-leveldown": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz", + "integrity": "sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A==", + "requires": { + "xtend": "~4.0.0" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "acorn": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==" + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=" + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "alter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "integrity": "sha1-x1iICGF1cgNKrmJICvJrHU0cs80=", + "requires": { + "stable": "~0.1.3" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archiver": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-0.6.1.tgz", + "integrity": "sha1-1AKZJXH9F5rtZy2Xl/iI5Wjh3Vw=", + "requires": { + "file-utils": "~0.1.5", + "lazystream": "~0.1.0", + "lodash": "~2.4.1", + "readable-stream": "~1.0.24", + "zip-stream": "~0.2.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + } + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "array-iterate": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-1.1.2.tgz", + "integrity": "sha512-1hWSHTIlG/8wtYD+PPX5AOBtKWngpDFjrsrHgZpe+JdgNGz0udYu6ZIkAa/xuenIUEqFv7DvE2Yr60jxweJSrQ==" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asar": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/asar/-/asar-0.11.0.tgz", + "integrity": "sha1-uSbnksMV+MBIxDNx4yWwnJenZGQ=", + "requires": { + "chromium-pickle-js": "^0.1.0", + "commander": "^2.9.0", + "cuint": "^0.2.1", + "glob": "^6.0.4", + "minimatch": "^3.0.0", + "mkdirp": "^0.5.0", + "mksnapshot": "^0.3.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "ast-traverse": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz", + "integrity": "sha1-ac8rg4bxnc2hux4F1o/jWdiJfeY=" + }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" + }, + "ast-util": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/ast-util/-/ast-util-0.6.0.tgz", + "integrity": "sha1-DZE9BPDpgx5T+ZkdyZAJ4tp3SBA=", + "requires": { + "ast-types": "~0.6.7", + "private": "~0.1.6" + }, + "dependencies": { + "ast-types": { + "version": "0.6.16", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.6.16.tgz", + "integrity": "sha1-BCBbcu3dGVqP6qCB8R0ClKJN7ZM=" + } + } + }, + "async": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.0.1.tgz", + "integrity": "sha1-twnMAoCpw28J9FNr6CPIOKkEniU=", + "requires": { + "lodash": "^4.8.0" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", + "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=" + }, + "atomdoc": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/atomdoc/-/atomdoc-1.0.6.tgz", + "integrity": "sha512-DU9ABgZw7egM0mxAe2AZX1RqEDyXu/PeIsVni/R3hxeuXEyyf+GVfygcYwclx1d7bEUVVMP+zTB8Aw4itei4sA==", + "requires": { + "marked": "^0.3.6" + } + }, + "autoprefixer": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", + "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", + "requires": { + "browserslist": "^3.2.8", + "caniuse-lite": "^1.0.30000864", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.23", + "postcss-value-parser": "^3.2.3" + } + }, + "aws-sdk": { + "version": "2.275.1", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.275.1.tgz", + "integrity": "sha512-lcpgoiHLhdcolUT7aJdg/CmlYO5ecf+3A+4dIceO72mFovCWZde1Rvr07QNbQ8gT0paqr5j2rs2b6c23Y/K0RQ==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.1.0", + "xml2js": "0.4.19" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + }, + "babel-core": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-5.8.38.tgz", + "integrity": "sha1-H8ruedfmG3ULALjlT238nQr4ZVg=", + "requires": { + "babel-plugin-constant-folding": "^1.0.1", + "babel-plugin-dead-code-elimination": "^1.0.2", + "babel-plugin-eval": "^1.0.1", + "babel-plugin-inline-environment-variables": "^1.0.1", + "babel-plugin-jscript": "^1.0.4", + "babel-plugin-member-expression-literals": "^1.0.1", + "babel-plugin-property-literals": "^1.0.1", + "babel-plugin-proto-to-assign": "^1.0.3", + "babel-plugin-react-constant-elements": "^1.0.3", + "babel-plugin-react-display-name": "^1.0.3", + "babel-plugin-remove-console": "^1.0.1", + "babel-plugin-remove-debugger": "^1.0.1", + "babel-plugin-runtime": "^1.0.7", + "babel-plugin-undeclared-variables-check": "^1.0.2", + "babel-plugin-undefined-to-void": "^1.1.6", + "babylon": "^5.8.38", + "bluebird": "^2.9.33", + "chalk": "^1.0.0", + "convert-source-map": "^1.1.0", + "core-js": "^1.0.0", + "debug": "^2.1.1", + "detect-indent": "^3.0.0", + "esutils": "^2.0.0", + "fs-readdir-recursive": "^0.1.0", + "globals": "^6.4.0", + "home-or-tmp": "^1.0.0", + "is-integer": "^1.0.4", + "js-tokens": "1.0.1", + "json5": "^0.4.0", + "lodash": "^3.10.0", + "minimatch": "^2.0.3", + "output-file-sync": "^1.1.0", + "path-exists": "^1.0.0", + "path-is-absolute": "^1.0.0", + "private": "^0.1.6", + "regenerator": "0.8.40", + "regexpu": "^1.3.0", + "repeating": "^1.1.2", + "resolve": "^1.1.6", + "shebang-regex": "^1.0.0", + "slash": "^1.0.0", + "source-map": "^0.5.0", + "source-map-support": "^0.2.10", + "to-fast-properties": "^1.0.0", + "trim-right": "^1.0.0", + "try-resolve": "^1.0.0" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "babel-plugin-constant-folding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz", + "integrity": "sha1-g2HTZMmORJw2kr26Ue/whEKQqo4=" + }, + "babel-plugin-dead-code-elimination": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz", + "integrity": "sha1-X3xFEnTc18zNv7s+C4XdKBIfD2U=" + }, + "babel-plugin-eval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz", + "integrity": "sha1-ovrtJc5r5preS/7CY/cBaRlZUNo=" + }, + "babel-plugin-inline-environment-variables": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz", + "integrity": "sha1-H1jOkSB61qgmqL9kX6/mj/X+P/4=" + }, + "babel-plugin-jscript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz", + "integrity": "sha1-jzQsOCduh6R9X6CovT1etsytj8w=" + }, + "babel-plugin-member-expression-literals": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz", + "integrity": "sha1-zF7bD6qNyScXDnTW0cAkQAIWJNM=" + }, + "babel-plugin-property-literals": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz", + "integrity": "sha1-AlIwGQAZKYCxwRjv6kjOk6q4MzY=" + }, + "babel-plugin-proto-to-assign": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz", + "integrity": "sha1-xJ56/QL1d7xNoF6i3wAiUM980SM=", + "requires": { + "lodash": "^3.9.3" + }, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "babel-plugin-react-constant-elements": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz", + "integrity": "sha1-lGc26DeEKcvDSdz/YvUcFDs041o=" + }, + "babel-plugin-react-display-name": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz", + "integrity": "sha1-dU/jiSboQkpOexWrbqYTne4FFPw=" + }, + "babel-plugin-remove-console": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz", + "integrity": "sha1-2PJFVsOgUAXUKqqv0neH9T/wE6c=" + }, + "babel-plugin-remove-debugger": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz", + "integrity": "sha1-/S6jzWGkKK0fO5yJiC/0KT6MFMc=" + }, + "babel-plugin-runtime": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz", + "integrity": "sha1-v3x9lm3Vbs1cF/ocslPJrLflSq8=" + }, + "babel-plugin-undeclared-variables-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz", + "integrity": "sha1-XPGqU52BP/ZOmWQSkK9iCWX2Xe4=", + "requires": { + "leven": "^1.0.2" + } + }, + "babel-plugin-undefined-to-void": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz", + "integrity": "sha1-f1eO+LeN+uYAM4XYQXph7aBuL4E=" + }, + "babylon": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-5.8.38.tgz", + "integrity": "sha1-7JsSCxG/bM1Bc6GL8hfmC3mFn/0=" + }, + "bail": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", + "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" + }, + "boom": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", + "requires": { + "hoek": "0.9.x" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "breakable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz", + "integrity": "sha1-eEp5eRWjjq0nutRWtVcstLuqeME=" + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", + "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "builtins": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-0.0.4.tgz", + "integrity": "sha1-7G1MpLpaOhc3SfEBRr3Noo6m1l0=" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + } + } + }, + "caniuse-lite": { + "version": "1.0.30000865", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz", + "integrity": "sha512-vs79o1mOSKRGv/1pSkp4EXgl4ZviWeYReXw60XfacPU64uQWZwJT6vZNmxRF9O+6zu71sJwMxLK5JXxbzuVrLw==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "ccount": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", + "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chainit": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chainit/-/chainit-2.1.1.tgz", + "integrity": "sha1-5TRdnAcdRz5zJ0yIrqZskVE2KME=", + "requires": { + "queue": "~1.0.2" + } + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "character-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", + "integrity": "sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ==" + }, + "character-entities-html4": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.2.tgz", + "integrity": "sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw==" + }, + "character-entities-legacy": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz", + "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA==" + }, + "character-reference-invalid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", + "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ==" + }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=" + }, + "chromium-pickle-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.1.0.tgz", + "integrity": "sha1-HUixB9ghJqLz4hHC6iX4A7pVGyE=" + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "cli-width": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.1.1.tgz", + "integrity": "sha1-pNKT72frt7iNSk1CwMzwDE0eNm0=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "clone-regexp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", + "integrity": "sha512-Fcij9IwRW27XedRIJnSOEupS7RVcXtObJXbcUOX93UCLqqOdRpkvzKywOOSizmEK/Is3S/RHX9dLdfo6R1Q1mw==", + "requires": { + "is-regexp": "^1.0.0", + "is-supported-regexp-flag": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "coffee-script": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", + "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=" + }, + "coffeelint": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/coffeelint/-/coffeelint-1.15.7.tgz", + "integrity": "sha1-9mmCqUBV1zU3bFz18cu54oFDNOk=", + "requires": { + "coffee-script": "~1.10.0", + "glob": "^4.0.0", + "ignore": "^3.0.9", + "optimist": "^0.6.1", + "resolve": "^0.6.3", + "strip-json-comments": "^1.0.2" + }, + "dependencies": { + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" + } + }, + "resolve": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz", + "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=" + } + } + }, + "collapse-white-space": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", + "integrity": "sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw==" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", + "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "requires": { + "color-name": "1.1.1" + } + }, + "color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz", + "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==" + }, + "commoner": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.8.tgz", + "integrity": "sha1-NPw2cs0kOT6LtH5wyqApOBH08sU=", + "requires": { + "commander": "^2.5.0", + "detective": "^4.3.1", + "glob": "^5.0.15", + "graceful-fs": "^4.1.2", + "iconv-lite": "^0.4.5", + "mkdirp": "^0.5.0", + "private": "^0.1.6", + "q": "^1.1.2", + "recast": "^0.11.17" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "requires": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + } + } + } + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "convert-source-map": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", + "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.5.tgz", + "integrity": "sha512-94j37OtvxS5w7qr7Ta6dt67tWdnOxigBVN4VnSxNXFez9o18PGQ0D33SchKP17r9LAcWVTYV72G6vDayAUBFIg==", + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "cross-spawn-async": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", + "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", + "requires": { + "lru-cache": "^4.0.0", + "which": "^1.2.8" + } + }, + "cryptiles": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", + "optional": true, + "requires": { + "boom": "0.4.x" + } + }, + "cson-parser": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-1.0.9.tgz", + "integrity": "sha1-t5/BuCp3V0NoDw7/uL+tMRNNrHQ=", + "requires": { + "coffee-script": "1.9.0" + }, + "dependencies": { + "coffee-script": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.9.0.tgz", + "integrity": "sha1-dJLLvD8DYcxdiGWv9yN1Uv8z4fc=" + } + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=" + }, + "css-value": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", + "integrity": "sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo=" + }, + "ctype": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", + "optional": true + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "^0.10.9" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=" + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-zip": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.3.0.tgz", + "integrity": "sha1-rjvLfjTGWHmt/nfhnDD4ZgK0vbA=", + "requires": { + "binary": "^0.3.0", + "graceful-fs": "^4.1.3", + "mkpath": "^0.1.0", + "nopt": "^3.0.1", + "q": "^1.1.2", + "readable-stream": "^1.1.8", + "touch": "0.0.3" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "deepmerge": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-0.2.10.tgz", + "integrity": "sha1-iQa/nlJaT78bIDsq/LRkAkmCEhk=" + }, + "deferred-leveldown": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz", + "integrity": "sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww==", + "requires": { + "abstract-leveldown": "~5.0.0", + "inherits": "^2.0.3" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "defs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.1.tgz", + "integrity": "sha1-siYJ8sehG6ej2xFoBcE5scr/qdI=", + "requires": { + "alter": "~0.2.0", + "ast-traverse": "~0.1.1", + "breakable": "~1.0.0", + "esprima-fb": "~15001.1001.0-dev-harmony-fb", + "simple-fmt": "~0.1.0", + "simple-is": "~0.2.0", + "stringmap": "~0.2.2", + "stringset": "~0.2.1", + "tryor": "~0.1.2", + "yargs": "~3.27.0" + }, + "dependencies": { + "yargs": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.27.0.tgz", + "integrity": "sha1-ISBUaTFuk5Ex1Z8toMbX+YIh6kA=", + "requires": { + "camelcase": "^1.2.1", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "os-locale": "^1.4.0", + "window-size": "^0.1.2", + "y18n": "^3.2.0" + } + } + } + }, + "deglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.1.tgz", + "integrity": "sha512-2kjwuGGonL7gWE1XU4Fv79+vVzpoQCl0V+boMwWtOQJV2AGDabCwez++nB1Nli/8BabAfZQ/UuHPlp6AymKdWw==", + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^3.0.9", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-indent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz", + "integrity": "sha1-ncXl3bzu+DJXZLlFGwK8bVQIT3U=", + "requires": { + "get-stdin": "^4.0.1", + "minimist": "^1.1.0", + "repeating": "^1.1.0" + } + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "detective": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", + "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", + "requires": { + "acorn": "^5.2.1", + "defined": "^1.0.0" + } + }, + "dezalgo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", + "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" + } + } + }, + "domelementtype": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "donna": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/donna/-/donna-1.0.16.tgz", + "integrity": "sha1-w/+yM9P2Zk0qJvGDJ4mNq9MmZZ0=", + "requires": { + "async": ">= 0.1.22", + "builtins": "0.0.4", + "coffee-script": "1.10.x", + "optimist": "~0.6", + "source-map": "0.1.29", + "underscore": ">= 0.1.0", + "underscore.string": ">= 0.1.0", + "walkdir": ">= 0.0.2" + }, + "dependencies": { + "source-map": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.29.tgz", + "integrity": "sha1-OdVxoJiPt6VIpnbE3nLbeJFNFzw=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "requires": { + "is-obj": "^1.0.0" + } + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "~1.1.9" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "editor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz", + "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" + }, + "electron-chromedriver": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/electron-chromedriver/-/electron-chromedriver-2.0.0.tgz", + "integrity": "sha512-kERk/Wzhc9RzW9jUKXA5kJc4m8BlL6c9p5QH+CrIlst0saeqZL1Up7vzD4ZOnuBDpAVBBYJ4jhkAKIssf8ZlXg==", + "requires": { + "electron-download": "^4.1.0", + "extract-zip": "^1.6.5" + } + }, + "electron-download": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.0.tgz", + "integrity": "sha1-v5MsdG8vh//MCdHdRy8v9rkYeEU=", + "requires": { + "debug": "^2.2.0", + "env-paths": "^1.0.0", + "fs-extra": "^2.0.0", + "minimist": "^1.2.0", + "nugget": "^2.0.0", + "path-exists": "^3.0.0", + "rc": "^1.1.2", + "semver": "^5.3.0", + "sumchecker": "^2.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-2.1.2.tgz", + "integrity": "sha1-BGxwFjzvmq1GsOSn+kZ/si1x3jU=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "electron-link": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/electron-link/-/electron-link-0.2.2.tgz", + "integrity": "sha1-uWvx/MrowwyAuiaTBq+UVOYtP2U=", + "requires": { + "ast-util": "^0.6.0", + "encoding-down": "~5.0.0", + "indent-string": "^2.1.0", + "leveldown": "~4.0.0", + "levelup": "~3.0.0", + "recast": "^0.12.6", + "resolve": "^1.5.0", + "source-map": "^0.5.6" + }, + "dependencies": { + "ast-types": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.1.tgz", + "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==" + }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "recast": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.9.tgz", + "integrity": "sha512-y7ANxCWmMW8xLOaiopiRDlyjQ9ajKRENBH+2wjntIbk3A6ZR1+BLQttkmSHMY7Arl+AAZFwJ10grg2T6f1WI8A==", + "requires": { + "ast-types": "0.10.1", + "core-js": "^2.4.1", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + } + } + }, + "electron-mksnapshot": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/electron-mksnapshot/-/electron-mksnapshot-2.0.0.tgz", + "integrity": "sha512-OoZwZJNKgHP+DwhCGVTJEuDSeb478hOzAbHeg7dKGCHDbKKmUWmjGc+pEjxGutpqQ3Mn8hCdLzdx2c/lAJcTLA==", + "requires": { + "electron-download": "^4.1.0", + "extract-zip": "^1.6.5" + } + }, + "electron-osx-sign": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.3.2.tgz", + "integrity": "sha1-iPp9brrbXZx5NouWSRoNjEYwFG4=", + "requires": { + "debug": "^2.2.0", + "minimist": "^1.1.1", + "run-series": "^1.1.1" + } + }, + "electron-packager": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-7.3.0.tgz", + "integrity": "sha1-WDP3Xz/WwMSQiXrQ5kf6EtM7V30=", + "requires": { + "asar": "^0.11.0", + "electron-download": "^2.0.0", + "electron-osx-sign": "^0.3.0", + "extract-zip": "^1.0.3", + "fs-extra": "^0.28.0", + "get-package-info": "0.0.2", + "minimist": "^1.1.1", + "plist": "^1.1.0", + "rcedit": "^0.5.1", + "resolve": "^1.1.6", + "run-series": "^1.1.1" + }, + "dependencies": { + "electron-download": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-2.2.1.tgz", + "integrity": "sha1-PXivNkXJZDXjvz35uIKhTMLKKUw=", + "requires": { + "debug": "^2.2.0", + "home-path": "^1.0.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.0", + "mv": "^2.0.3", + "nugget": "^1.5.1", + "path-exists": "^1.0.0", + "rc": "^1.1.2" + } + }, + "fs-extra": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.28.0.tgz", + "integrity": "sha1-mhwHCOqMUWkperBv2MuRT1ZHsnI=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "nugget": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nugget/-/nugget-1.6.2.tgz", + "integrity": "sha1-iMpuA7pXBqmRc/XaCQJZPWvK4Qc=", + "requires": { + "debug": "^2.1.3", + "minimist": "^1.1.0", + "pretty-bytes": "^1.0.2", + "progress-stream": "^1.1.0", + "request": "^2.45.0", + "single-line-log": "^0.4.1", + "throttleit": "0.0.2" + } + }, + "single-line-log": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", + "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=" + } + } + }, + "electron-to-chromium": { + "version": "1.3.52", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.52.tgz", + "integrity": "sha1-0tnxJwuko7lnuDHEDvcftNmrXOA=" + }, + "electron-winstaller": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-2.6.4.tgz", + "integrity": "sha1-a0gHboc6bqNWJR8Ve2i55dwDtak=", + "requires": { + "asar": "^0.11.0", + "bluebird": "^3.3.4", + "debug": "^2.2.0", + "fs-extra": "^0.26.7", + "lodash.template": "^4.2.2", + "temp": "^0.8.3" + }, + "dependencies": { + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + } + } + }, + "encoding-down": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-5.0.4.tgz", + "integrity": "sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw==", + "requires": { + "abstract-leveldown": "^5.0.0", + "inherits": "^2.0.3", + "level-codec": "^9.0.0", + "level-errors": "^2.0.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", + "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=" + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.45", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", + "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.7.1.tgz", + "integrity": "sha1-f6qEWZ4P6kIvBLwy20kFQFGj8Ro=", + "requires": { + "chalk": "^1.1.3", + "concat-stream": "^1.4.6", + "debug": "^2.1.1", + "doctrine": "^1.2.2", + "escope": "^3.6.0", + "espree": "^3.3.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "glob": "^7.0.3", + "globals": "^9.2.0", + "ignore": "^3.1.5", + "imurmurhash": "^0.1.4", + "inquirer": "^0.12.0", + "is-my-json-valid": "^2.10.0", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.5.1", + "json-stable-stringify": "^1.0.0", + "levn": "^0.3.0", + "lodash": "^4.0.0", + "mkdirp": "^0.5.0", + "natural-compare": "^1.4.0", + "optionator": "^0.8.1", + "path-is-inside": "^1.0.1", + "pluralize": "^1.2.1", + "progress": "^1.1.8", + "require-uncached": "^1.0.2", + "shelljs": "^0.6.0", + "strip-bom": "^3.0.0", + "strip-json-comments": "~1.0.1", + "table": "^3.7.8", + "text-table": "~0.2.0", + "user-home": "^2.0.0" + }, + "dependencies": { + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "requires": { + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=" + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "mute-stream": "0.0.5" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "requires": { + "os-homedir": "^1.0.0" + } + } + } + }, + "eslint-config-standard": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-6.2.0.tgz", + "integrity": "sha1-HSOE7gdN5va2wPK76XaGMDJWWu4=" + }, + "eslint-config-standard-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-3.2.0.tgz", + "integrity": "sha1-wkDibtkZoRpCqk3oBZRys4Jo1iA=" + }, + "eslint-plugin-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.0.0.tgz", + "integrity": "sha1-FShjXQFg80hOQlzOIWnLdM7AGGo=" + }, + "eslint-plugin-react": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.4.1.tgz", + "integrity": "sha1-fRqt50fbFYkvce7h/qSt35e8+is=", + "requires": { + "doctrine": "^1.2.2", + "jsx-ast-utils": "^1.3.1" + } + }, + "eslint-plugin-standard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz", + "integrity": "sha1-NYlpn/nJF/LCX3apFmh/ZBw2n/M=" + }, + "espree": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", + "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=" + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "execa": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.4.0.tgz", + "integrity": "sha1-TrZGejaglfq7KXD/nV4/t7zm68M=", + "requires": { + "cross-spawn-async": "^2.1.1", + "is-stream": "^1.1.0", + "npm-run-path": "^1.0.0", + "object-assign": "^4.0.1", + "path-key": "^1.0.0", + "strip-eof": "^1.0.0" + } + }, + "execall": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-1.0.0.tgz", + "integrity": "sha1-c9CQTjlbPKsGWLCNCewlMH8pu3M=", + "requires": { + "clone-regexp": "^1.0.0" + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "requires": { + "fill-range": "^2.1.0" + } + }, + "expand-template": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz", + "integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg==" + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "extract-zip": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", + "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.1", + "yauzl": "2.4.1" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-future": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fast-future/-/fast-future-1.0.2.tgz", + "integrity": "sha1-hDWpqqAteSSNF9cE52JZMB2ZKAo=" + }, + "fast-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.2.tgz", + "integrity": "sha512-TR6zxCKftDQnUAPvkrCWdBgDq/gbqx8A3ApnBrR5rMvpp6+KMJI0Igw7fkWPgeVK0uhRXTXdvO3O+YP0CaUX2g==", + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.0.1", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.1", + "micromatch": "^3.1.10" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, + "file-utils": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/file-utils/-/file-utils-0.1.5.tgz", + "integrity": "sha1-3IFTyFU4fLTaywoXJVMfpESmtIw=", + "requires": { + "findup-sync": "~0.1.2", + "glob": "~3.2.6", + "iconv-lite": "~0.2.11", + "isbinaryfile": "~0.1.9", + "lodash": "~2.1.0", + "minimatch": "~0.2.12", + "rimraf": "~2.2.2" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + }, + "dependencies": { + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" + }, + "lodash": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.1.0.tgz", + "integrity": "sha1-Bjfqqjaooc/IZcOt+5Qhib+wmY0=" + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + } + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "findup-sync": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", + "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", + "requires": { + "glob": "~3.2.9", + "lodash": "~2.4.1" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "requires": { + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-admin": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/fs-admin/-/fs-admin-0.1.6.tgz", + "integrity": "sha512-JHRSPVRBrYggAGM6kpvNvFdxuFmoDxamnBVQT/JApZtVji7bHKbhLOka1Y2pNSQ/OVChbmZFKcWdpwuZEpA65w==", + "requires": { + "mocha": "^3.5.0", + "nan": "^2.6.2" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "fs-plus": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/fs-plus/-/fs-plus-2.10.1.tgz", + "integrity": "sha1-MgR4HXhAYR5jZOe2+wWMljJ8WqU=", + "requires": { + "async": "^1.5.2", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2", + "underscore-plus": "1.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "fs-readdir-recursive": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz", + "integrity": "sha1-MVtPuMHKW4xH3v7zGdBz2tNWgFk=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "requires": { + "is-property": "^1.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-package-info": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-0.0.2.tgz", + "integrity": "sha1-csOPvuLnZyhCSgDcFOJN0aKMI5E=", + "requires": { + "bluebird": "^3.1.1", + "lodash.get": "^4.0.0", + "resolve": "^1.1.6" + }, + "dependencies": { + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + } + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "ghauth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ghauth/-/ghauth-2.0.1.tgz", + "integrity": "sha1-ebfWiwvPjn0IUqI7FHU539MUrPY=", + "requires": { + "bl": "~0.9.4", + "hyperquest": "~1.2.0", + "mkdirp": "~0.5.0", + "read": "~1.0.5", + "xtend": "~4.0.0" + }, + "dependencies": { + "bl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "integrity": "sha1-wGt5evCF6gC8Unr8jvzxHeIjIFQ=", + "requires": { + "readable-stream": "~1.0.26" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, + "github-url-from-git": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz", + "integrity": "sha1-+YX+3MCpqledyI16/waNVcxiUaA=" + }, + "github-url-from-username-repo": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/github-url-from-username-repo/-/github-url-from-username-repo-1.0.2.tgz", + "integrity": "sha1-fdeTMNKr5pwQws73lxTJchV5Hfo=" + }, + "github-url-to-object": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/github-url-to-object/-/github-url-to-object-1.6.0.tgz", + "integrity": "sha1-iR73+7+rqP7XFRCs2xtOk0apcNw=", + "requires": { + "is-url": "^1.1.0" + } + }, + "glob": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.3.tgz", + "integrity": "sha1-CqI1kxpKlqwT1g/6wvuHe9btT1g=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "requires": { + "is-glob": "^2.0.0" + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=" + }, + "globals": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-6.4.1.tgz", + "integrity": "sha1-hJgDKzttHMge68X3lpDY/in6v08=" + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=" + }, + "gonzales-pe": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.2.3.tgz", + "integrity": "sha512-Kjhohco0esHQnOiqqdJeNz/5fyPkOMD/d6XVjwTAoPGUFh0mCollPUTUTa2OZy4dYNAqlPIQdTiNzJTWdd9Htw==", + "requires": { + "minimist": "1.1.x" + }, + "dependencies": { + "minimist": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=" + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hawk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "integrity": "sha1-uQuxaYByhUEdp//LjdJZhQLTtS0=", + "optional": true, + "requires": { + "boom": "0.4.x", + "cryptiles": "0.2.x", + "hoek": "0.9.x", + "sntp": "0.2.x" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "hoek": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=" + }, + "home-or-tmp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-1.0.0.tgz", + "integrity": "sha1-S58eQIAMPlDGwn94FnavzOcfOYU=", + "requires": { + "os-tmpdir": "^1.0.1", + "user-home": "^1.1.1" + } + }, + "home-path": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.6.tgz", + "integrity": "sha512-wo+yjrdAtoXt43Vy92a+0IPCYViiyLAHyp0QVS4xL/tfvVz5sXIW1ubLZk3nhVkD92fQpUMKX+fzMjr5F489vw==" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=" + }, + "htmlparser2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", + "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "http-basic": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", + "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", + "requires": { + "caseless": "~0.11.0", + "concat-stream": "^1.4.6", + "http-response-object": "^1.0.0" + }, + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + } + } + }, + "http-response-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", + "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "hyperquest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperquest/-/hyperquest-1.2.0.tgz", + "integrity": "sha1-OeH+9miI3Hzg3sbA3YFPb8iUStU=", + "requires": { + "duplexer2": "~0.0.2", + "through2": "~0.6.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" + }, + "import-lazy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-3.1.0.tgz", + "integrity": "sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + }, + "dependencies": { + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + } + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", + "integrity": "sha1-29dAz2yjtzEpamPOb22WGFHzNt8=", + "requires": { + "ansi-regex": "^1.1.1", + "chalk": "^1.0.0", + "cli-width": "^1.0.1", + "figures": "^1.3.5", + "lodash": "^3.3.1", + "readline2": "^0.1.1", + "rx": "^2.4.3", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", + "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + } + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-alphabetical": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz", + "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg==" + }, + "is-alphanumeric": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", + "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=" + }, + "is-alphanumerical": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz", + "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-decimal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", + "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-hexadecimal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", + "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==" + }, + "is-integer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.7.tgz", + "integrity": "sha1-a96Bqs3feLZZtmKdYpytxRqIbVw=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "is-my-ip-valid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", + "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==" + }, + "is-my-json-valid": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", + "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "requires": { + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-supported-regexp-flag": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz", + "integrity": "sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-whitespace-character": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz", + "integrity": "sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ==" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-word-character": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.2.tgz", + "integrity": "sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-0.1.9.tgz", + "integrity": "sha1-Fe7ONcSrcI2JJNqZ+4dPK1zAtsQ=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jju": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.3.0.tgz", + "integrity": "sha1-2t2e8BkkvHKLA/L3l5vb1i96Kqo=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "joanna": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/joanna/-/joanna-0.0.10.tgz", + "integrity": "sha512-V0b0S+7yFBesai5c+F1jGt3cWDLRVFkn8q4T6fcEzY5/7Wa+A9N4sl/cqdpr7vQ7IAThOT0baC5n3NNxY8gXjg==", + "requires": { + "babylon": "^6.8.4", + "tello": "^1.0.6", + "walkdir": ">= 0.0.2" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + } + } + }, + "js-base64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.6.tgz", + "integrity": "sha512-O9SR2NVICx6rCqh1qsU91QZ5IoNa+2T1ROJ0OQlfvATKGmnjsAvg3r0E5ufPZ4a95jdKTPXhFWiE/sOZ7a5Rtg==" + }, + "js-tokens": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.1.tgz", + "integrity": "sha1-zENaXIuUrRWst5gxQPyAGCyJrq4=" + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-helpfulerror": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", + "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=", + "requires": { + "jju": "^1.1.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "json5": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz", + "integrity": "sha1-BUNS5MTIDIbAkjh31EneF2pzLI0=" + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=" + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "klaw-sync": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-1.1.2.tgz", + "integrity": "sha1-tbxnokTiYbDqcdl+WG6gUh5zSpo=", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^2.3.11" + } + }, + "known-css-properties": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.6.1.tgz", + "integrity": "sha512-nQRpMcHm1cQ6gmztdvLcIvxocznSMqH/y6XtERrWrHaymOYdDGroRqetJvJycxGEr1aakXiigDgn7JnzuXlk6A==" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lazystream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-0.1.0.tgz", + "integrity": "sha1-GyXWPHcqTCDwpe0KnXf0hLbhaSA=", + "requires": { + "readable-stream": "~1.0.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "legal-eagle": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/legal-eagle/-/legal-eagle-0.14.0.tgz", + "integrity": "sha1-ITk4bWO9NdZY03hBYMgL1aHbSD4=", + "requires": { + "read-installed": "3.1.3", + "underscore": "~1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "level-codec": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.0.tgz", + "integrity": "sha512-OIpVvjCcZNP5SdhcNupnsI1zo5Y9Vpm+k/F1gfG5kXrtctlrwanisakweJtE0uA0OpLukRfOQae+Fg0M5Debhg==" + }, + "level-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.0.tgz", + "integrity": "sha512-AmY4HCp9h3OiU19uG+3YWkdELgy05OTP/r23aNHaQKWv8DO787yZgsEuGVkoph40uwN+YdUKnANlrxSsoOaaxg==", + "requires": { + "errno": "~0.1.1" + } + }, + "level-iterator-stream": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz", + "integrity": "sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig==", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.5", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "leveldown": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-4.0.1.tgz", + "integrity": "sha512-ZlBKVSsglPIPJnz4ggB8o2R0bxDxbsMzuQohbfgoFMVApyTE118DK5LNRG0cRju6rt3OkGxe0V6UYACGlq/byg==", + "requires": { + "abstract-leveldown": "~5.0.0", + "bindings": "~1.3.0", + "fast-future": "~1.0.2", + "nan": "~2.10.0", + "prebuild-install": "^4.0.0" + } + }, + "levelup": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-3.0.1.tgz", + "integrity": "sha512-TrrLDPC/BfP35ei2uK+L6Cc7kpI1NxIChwp+BUB6jrHG3A8gtrr9jx1UZ9bi2w1O6VN7jYO4LUoq1iKRP5AREg==", + "requires": { + "deferred-leveldown": "~4.0.0", + "level-errors": "~2.0.0", + "level-iterator-stream": "~2.0.0", + "xtend": "~4.0.0" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "leven": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz", + "integrity": "sha1-kUS27ryl8dBoAWnxpncNzqYLdcM=" + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash.keys": "^3.0.0" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=" + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "requires": { + "lodash._baseassign": "^3.0.0", + "lodash._basecreate": "^3.0.0", + "lodash._isiterateecall": "^3.0.0" + } + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "requires": { + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + } + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=" + }, + "lodash.template": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "requires": { + "lodash._reinterpolate": "~3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "requires": { + "lodash._reinterpolate": "~3.0.0" + } + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "^2.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "longest-streak": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", + "integrity": "sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA==" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "markdown-escapes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.2.tgz", + "integrity": "sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA==" + }, + "markdown-table": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.2.tgz", + "integrity": "sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw==" + }, + "marked": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" + }, + "math-random": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", + "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=" + }, + "mathml-tag-names": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz", + "integrity": "sha512-3Zs9P/0zzwTob2pdgT0CHZuMbnSUSp8MB1bddfm+HDmnFWHGT4jvEZRf+2RuPoa+cjdn/z25SEt5gFTqdhvJAg==" + }, + "mdast-util-compact": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz", + "integrity": "sha1-zbX4TitqLTEU3zO9BdnLMuPECDo=", + "requires": { + "unist-util-modify-children": "^1.0.0", + "unist-util-visit": "^1.1.0" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge2": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.2.tgz", + "integrity": "sha512-bgM8twH86rWni21thii6WCMQMRMmwqqdW3sGWi9IipnVAszdLXRjwDwAnyrVXo6DuP3AjRMMttZKUB48QWIFGg==" + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "minidump": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/minidump/-/minidump-0.9.0.tgz", + "integrity": "sha1-Ei6d8kTzCPNEnvunpOLDIfQmwfk=" + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "mkpath": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz", + "integrity": "sha1-dVSm+Nhxg0zJe1RisSLEwSTW3pE=" + }, + "mksnapshot": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mksnapshot/-/mksnapshot-0.3.1.tgz", + "integrity": "sha1-JQHAVldDbXQs6Vik/5LHfkDdN+Y=", + "requires": { + "decompress-zip": "0.3.0", + "fs-extra": "0.26.7", + "request": "^2.79.0" + }, + "dependencies": { + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + } + } + }, + "mocha": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "he": "1.1.1", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "requires": { + "glob": "^6.0.1" + } + } + } + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "natives": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.4.tgz", + "integrity": "sha512-Q29yeg9aFKwhLVdkTAejM/HvYG0Y1Am1+HUkFQGn5k2j8GS+v60TVmZh6nujpEAj/qql+wGUrlryO8bF+b1jEg==", + "optional": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-abi": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.4.3.tgz", + "integrity": "sha512-b656V5C0628gOOA2kwcpNA/bxdlqYF9FvxJ+qqVX0ctdXNVZpS8J6xEUYir3WAKc7U0BH/NRlSpNbGsy+azjeg==", + "requires": { + "semver": "^5.4.1" + }, + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + } + } + }, + "noop-logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", + "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.5.tgz", + "integrity": "sha1-jZJPFClg4Xd+f/4XBUNjHMfLAt8=", + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=" + }, + "npm": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.2.0.tgz", + "integrity": "sha512-GnlNsOnxwVJX4WSfyQY0gY3LnUX2cc46XU0eu1g+WSuZgDRUGmw8tuptitJu6byp0RWGT8ZEAKajblwdhQHN8A==", + "requires": { + "JSONStream": "^1.3.3", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "aproba": "~1.2.0", + "archy": "~1.0.0", + "bin-links": "^1.1.2", + "bluebird": "~3.5.1", + "byte-size": "^4.0.3", + "cacache": "^11.0.2", + "call-limit": "~1.1.0", + "chownr": "~1.0.1", + "cli-columns": "^3.1.2", + "cli-table3": "^0.5.0", + "cmd-shim": "~2.0.2", + "columnify": "~1.5.4", + "config-chain": "~1.1.11", + "debuglog": "*", + "detect-indent": "~5.0.0", + "detect-newline": "^2.1.0", + "dezalgo": "~1.0.3", + "editor": "~1.0.0", + "figgy-pudding": "^3.1.0", + "find-npm-prefix": "^1.0.2", + "fs-vacuum": "~1.2.10", + "fs-write-stream-atomic": "~1.0.10", + "gentle-fs": "^2.0.1", + "glob": "~7.1.2", + "graceful-fs": "~4.1.11", + "has-unicode": "~2.0.1", + "hosted-git-info": "^2.6.0", + "iferr": "^1.0.0", + "imurmurhash": "*", + "inflight": "~1.0.6", + "inherits": "~2.0.3", + "ini": "^1.3.5", + "init-package-json": "^1.10.3", + "is-cidr": "^2.0.6", + "json-parse-better-errors": "^1.0.2", + "lazy-property": "~1.0.0", + "libcipm": "^2.0.0", + "libnpmhook": "^4.0.1", + "libnpx": "^10.2.0", + "lock-verify": "^2.0.2", + "lockfile": "^1.0.4", + "lodash._baseindexof": "*", + "lodash._baseuniq": "~4.6.0", + "lodash._bindcallback": "*", + "lodash._cacheindexof": "*", + "lodash._createcache": "*", + "lodash._getnative": "*", + "lodash.clonedeep": "~4.5.0", + "lodash.restparam": "*", + "lodash.union": "~4.6.0", + "lodash.uniq": "~4.5.0", + "lodash.without": "~4.4.0", + "lru-cache": "^4.1.3", + "meant": "~1.0.1", + "mississippi": "^3.0.0", + "mkdirp": "~0.5.1", + "move-concurrently": "^1.0.1", + "node-gyp": "^3.7.0", + "nopt": "~4.0.1", + "normalize-package-data": "~2.4.0", + "npm-audit-report": "^1.3.1", + "npm-cache-filename": "~1.0.2", + "npm-install-checks": "~3.0.0", + "npm-lifecycle": "^2.0.3", + "npm-package-arg": "^6.1.0", + "npm-packlist": "~1.1.10", + "npm-pick-manifest": "^2.1.0", + "npm-profile": "^3.0.2", + "npm-registry-client": "^8.5.1", + "npm-registry-fetch": "^1.1.0", + "npm-user-validate": "~1.0.0", + "npmlog": "~4.1.2", + "once": "~1.4.0", + "opener": "~1.4.3", + "osenv": "^0.1.5", + "pacote": "^8.1.6", + "path-is-inside": "~1.0.2", + "promise-inflight": "~1.0.1", + "qrcode-terminal": "^0.12.0", + "query-string": "^6.1.0", + "qw": "~1.0.1", + "read": "~1.0.7", + "read-cmd-shim": "~1.0.1", + "read-installed": "~4.0.3", + "read-package-json": "^2.0.13", + "read-package-tree": "^5.2.1", + "readable-stream": "^2.3.6", + "readdir-scoped-modules": "*", + "request": "^2.81.0", + "retry": "^0.12.0", + "rimraf": "~2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.5.0", + "sha": "~2.0.1", + "slide": "~1.1.6", + "sorted-object": "~2.0.1", + "sorted-union-stream": "~2.1.3", + "ssri": "^6.0.0", + "tar": "^4.4.4", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "uid-number": "0.0.6", + "umask": "~1.1.0", + "unique-filename": "~1.1.0", + "unpipe": "~1.0.0", + "update-notifier": "^2.5.0", + "uuid": "^3.3.2", + "validate-npm-package-license": "^3.0.3", + "validate-npm-package-name": "~3.0.0", + "which": "^1.3.1", + "worker-farm": "^1.6.0", + "wrappy": "~1.0.2", + "write-file-atomic": "^2.3.0" + }, + "dependencies": { + "JSONStream": { + "version": "1.3.3", + "bundled": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.1.1", + "bundled": true + }, + "agent-base": { + "version": "4.2.0", + "bundled": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.4.1", + "bundled": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ansi-align": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.0.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true + }, + "archy": { + "version": "1.0.0", + "bundled": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true + }, + "aws4": { + "version": "1.7.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "1.1.2", + "bundled": true, + "requires": { + "bluebird": "^3.5.0", + "cmd-shim": "^2.0.2", + "gentle-fs": "^2.0.0", + "graceful-fs": "^4.1.11", + "write-file-atomic": "^2.3.0" + } + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "3.5.1", + "bundled": true + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "requires": { + "hoek": "2.x.x" + } + }, + "boxen": { + "version": "1.3.0", + "bundled": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.0.0", + "bundled": true + }, + "builtin-modules": { + "version": "1.1.1", + "bundled": true + }, + "builtins": { + "version": "1.0.3", + "bundled": true + }, + "byline": { + "version": "5.0.0", + "bundled": true + }, + "byte-size": { + "version": "4.0.3", + "bundled": true + }, + "cacache": { + "version": "11.0.2", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "figgy-pudding": "^3.1.0", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.0", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "call-limit": { + "version": "1.1.0", + "bundled": true + }, + "camelcase": { + "version": "4.1.0", + "bundled": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true + }, + "chalk": { + "version": "2.4.1", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "1.0.1", + "bundled": true + }, + "ci-info": { + "version": "1.1.3", + "bundled": true + }, + "cidr-regex": { + "version": "2.0.9", + "bundled": true, + "requires": { + "ip-regex": "^2.1.0" + } + }, + "cli-boxes": { + "version": "1.0.0", + "bundled": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.5.0", + "bundled": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + } + }, + "cliui": { + "version": "4.1.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true + }, + "cmd-shim": { + "version": "2.0.2", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "~0.5.0" + } + }, + "co": { + "version": "4.6.0", + "bundled": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "color-convert": { + "version": "1.9.1", + "bundled": true, + "requires": { + "color-name": "^1.1.1" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "colors": { + "version": "1.3.0", + "bundled": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.6", + "bundled": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "concat-stream": { + "version": "1.6.2", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.11", + "bundled": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.2", + "bundled": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "copy-concurrently": { + "version": "1.0.5", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "create-error-class": { + "version": "3.0.2", + "bundled": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "bundled": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "requires": { + "boom": "2.x.x" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "bundled": true + }, + "cyclist": { + "version": "0.2.2", + "bundled": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "debug": { + "version": "3.1.0", + "bundled": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "bundled": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "decode-uri-component": { + "version": "0.2.0", + "bundled": true + }, + "deep-extend": { + "version": "0.5.1", + "bundled": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true + }, + "detect-indent": { + "version": "5.0.0", + "bundled": true + }, + "detect-newline": { + "version": "2.1.0", + "bundled": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "dot-prop": { + "version": "4.2.0", + "bundled": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "dotenv": { + "version": "5.0.1", + "bundled": true + }, + "duplexer3": { + "version": "0.1.4", + "bundled": true + }, + "duplexify": { + "version": "3.6.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true, + "requires": { + "jsbn": "~0.1.0" + } + }, + "editor": { + "version": "1.0.0", + "bundled": true + }, + "encoding": { + "version": "0.1.12", + "bundled": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "err-code": { + "version": "1.1.2", + "bundled": true + }, + "errno": { + "version": "0.1.7", + "bundled": true, + "requires": { + "prr": "~1.0.1" + } + }, + "es6-promise": { + "version": "4.2.4", + "bundled": true + }, + "es6-promisify": { + "version": "5.0.0", + "bundled": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "bundled": true + }, + "execa": { + "version": "0.7.0", + "bundled": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "extend": { + "version": "3.0.1", + "bundled": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true + }, + "figgy-pudding": { + "version": "3.1.0", + "bundled": true + }, + "find-npm-prefix": { + "version": "1.0.2", + "bundled": true + }, + "find-up": { + "version": "2.1.0", + "bundled": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.0.3", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" + } + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-vacuum": { + "version": "1.2.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "path-is-inside": "^1.0.1", + "rimraf": "^2.5.2" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "genfun": { + "version": "4.0.1", + "bundled": true + }, + "gentle-fs": { + "version": "2.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.2", + "fs-vacuum": "^1.2.10", + "graceful-fs": "^4.1.11", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "path-is-inside": "^1.0.2", + "read-cmd-shim": "^1.0.1", + "slide": "^1.1.6" + }, + "dependencies": { + "iferr": { + "version": "0.1.5", + "bundled": true + } + } + }, + "get-caller-file": { + "version": "1.0.2", + "bundled": true + }, + "get-stream": { + "version": "3.0.0", + "bundled": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "bundled": true, + "requires": { + "ini": "^1.3.4" + } + }, + "got": { + "version": "6.7.1", + "bundled": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "requires": { + "ajv": "^4.9.1", + "har-schema": "^1.0.5" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "bundled": true, + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + } + } + }, + "has-flag": { + "version": "3.0.0", + "bundled": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "requires": { + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" + } + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "hosted-git-info": { + "version": "2.6.0", + "bundled": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "bundled": true + }, + "http-proxy-agent": { + "version": "2.1.0", + "bundled": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + } + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "requires": { + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.1", + "bundled": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "bundled": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "iferr": { + "version": "1.0.0", + "bundled": true + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-lazy": { + "version": "2.1.0", + "bundled": true + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.5", + "bundled": true + }, + "init-package-json": { + "version": "1.10.3", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "1 || 2", + "semver": "2.x || 3.x || 4 || 5", + "validate-npm-package-license": "^3.0.1", + "validate-npm-package-name": "^3.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "bundled": true + }, + "ip": { + "version": "1.1.5", + "bundled": true + }, + "ip-regex": { + "version": "2.1.0", + "bundled": true + }, + "is-builtin-module": { + "version": "1.0.0", + "bundled": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-ci": { + "version": "1.1.0", + "bundled": true, + "requires": { + "ci-info": "^1.0.0" + } + }, + "is-cidr": { + "version": "2.0.6", + "bundled": true, + "requires": { + "cidr-regex": "^2.0.8" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "bundled": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "bundled": true + }, + "is-obj": { + "version": "1.0.1", + "bundled": true + }, + "is-path-inside": { + "version": "1.0.1", + "bundled": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "bundled": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "bundled": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "latest-version": { + "version": "3.1.0", + "bundled": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "lazy-property": { + "version": "1.0.0", + "bundled": true + }, + "lcid": { + "version": "1.0.0", + "bundled": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "libcipm": { + "version": "2.0.0", + "bundled": true, + "requires": { + "bin-links": "^1.1.2", + "bluebird": "^3.5.1", + "find-npm-prefix": "^1.0.2", + "graceful-fs": "^4.1.11", + "lock-verify": "^2.0.2", + "npm-lifecycle": "^2.0.3", + "npm-logical-tree": "^1.2.1", + "npm-package-arg": "^6.1.0", + "pacote": "^8.1.6", + "protoduck": "^5.0.0", + "read-package-json": "^2.0.13", + "rimraf": "^2.6.2", + "worker-farm": "^1.6.0" + } + }, + "libnpmhook": { + "version": "4.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "^3.1.0", + "npm-registry-fetch": "^3.0.0" + }, + "dependencies": { + "npm-registry-fetch": { + "version": "3.1.1", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "figgy-pudding": "^3.1.0", + "lru-cache": "^4.1.2", + "make-fetch-happen": "^4.0.0", + "npm-package-arg": "^6.0.0" + } + } + } + }, + "libnpx": { + "version": "10.2.0", + "bundled": true, + "requires": { + "dotenv": "^5.0.1", + "npm-package-arg": "^6.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.0", + "update-notifier": "^2.3.0", + "which": "^1.3.0", + "y18n": "^4.0.0", + "yargs": "^11.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lock-verify": { + "version": "2.0.2", + "bundled": true, + "requires": { + "npm-package-arg": "^5.1.2 || 6", + "semver": "^5.4.1" + } + }, + "lockfile": { + "version": "1.0.4", + "bundled": true, + "requires": { + "signal-exit": "^3.0.2" + } + }, + "lodash._baseindexof": { + "version": "3.1.0", + "bundled": true + }, + "lodash._baseuniq": { + "version": "4.6.0", + "bundled": true, + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._bindcallback": { + "version": "3.0.1", + "bundled": true + }, + "lodash._cacheindexof": { + "version": "3.0.2", + "bundled": true + }, + "lodash._createcache": { + "version": "3.1.2", + "bundled": true, + "requires": { + "lodash._getnative": "^3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "bundled": true + }, + "lodash._getnative": { + "version": "3.9.1", + "bundled": true + }, + "lodash._root": { + "version": "3.0.1", + "bundled": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "bundled": true + }, + "lodash.restparam": { + "version": "3.6.1", + "bundled": true + }, + "lodash.union": { + "version": "4.6.0", + "bundled": true + }, + "lodash.uniq": { + "version": "4.5.0", + "bundled": true + }, + "lodash.without": { + "version": "4.4.0", + "bundled": true + }, + "lowercase-keys": { + "version": "1.0.1", + "bundled": true + }, + "lru-cache": { + "version": "4.1.3", + "bundled": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "bundled": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-fetch-happen": { + "version": "4.0.1", + "bundled": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + } + }, + "meant": { + "version": "1.0.1", + "bundled": true + }, + "mem": { + "version": "1.1.0", + "bundled": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "mime-db": { + "version": "1.33.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.18", + "bundled": true, + "requires": { + "mime-db": "~1.33.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "minipass": { + "version": "2.3.3", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "minizlib": { + "version": "1.1.0", + "bundled": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "3.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true + }, + "mute-stream": { + "version": "0.0.7", + "bundled": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "bundled": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-gyp": { + "version": "3.7.0", + "bundled": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": ">=2.9.0 <2.82.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "nopt": { + "version": "3.0.6", + "bundled": true, + "requires": { + "abbrev": "1" + } + }, + "semver": { + "version": "5.3.0", + "bundled": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "1.3.1", + "bundled": true, + "requires": { + "cli-table3": "^0.5.0", + "console-control-strings": "^1.1.0" + } + }, + "npm-bundled": { + "version": "1.0.3", + "bundled": true + }, + "npm-cache-filename": { + "version": "1.0.2", + "bundled": true + }, + "npm-install-checks": { + "version": "3.0.0", + "bundled": true, + "requires": { + "semver": "^2.3.0 || 3.x || 4 || 5" + } + }, + "npm-lifecycle": { + "version": "2.0.3", + "bundled": true, + "requires": { + "byline": "^5.0.0", + "graceful-fs": "^4.1.11", + "node-gyp": "^3.6.2", + "resolve-from": "^4.0.0", + "slide": "^1.1.6", + "uid-number": "0.0.6", + "umask": "^1.1.0", + "which": "^1.3.0" + } + }, + "npm-logical-tree": { + "version": "1.2.1", + "bundled": true + }, + "npm-package-arg": { + "version": "6.1.0", + "bundled": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.1.0", + "bundled": true, + "requires": { + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-profile": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "^1.1.2 || 2", + "make-fetch-happen": "^2.5.0 || 3 || 4" + } + }, + "npm-registry-client": { + "version": "8.5.1", + "bundled": true, + "requires": { + "concat-stream": "^1.5.2", + "graceful-fs": "^4.1.6", + "normalize-package-data": "~1.0.1 || ^2.0.0", + "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "npmlog": "2 || ^3.1.0 || ^4.0.0", + "once": "^1.3.3", + "request": "^2.74.0", + "retry": "^0.10.0", + "safe-buffer": "^5.1.1", + "semver": "2 >=2.2.1 || 3.x || 4 || 5", + "slide": "^1.1.3", + "ssri": "^5.2.4" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-registry-fetch": { + "version": "1.1.0", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "figgy-pudding": "^2.0.1", + "lru-cache": "^4.1.2", + "make-fetch-happen": "^3.0.0", + "npm-package-arg": "^6.0.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "cacache": { + "version": "10.0.4", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + }, + "dependencies": { + "mississippi": { + "version": "2.0.0", + "bundled": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + } + } + }, + "figgy-pudding": { + "version": "2.0.1", + "bundled": true + }, + "make-fetch-happen": { + "version": "3.0.0", + "bundled": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^10.0.4", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.0", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^3.0.1", + "ssri": "^5.2.4" + } + }, + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "smart-buffer": { + "version": "1.1.15", + "bundled": true + }, + "socks": { + "version": "1.1.10", + "bundled": true, + "requires": { + "ip": "^1.1.4", + "smart-buffer": "^1.0.13" + } + }, + "socks-proxy-agent": { + "version": "3.0.1", + "bundled": true, + "requires": { + "agent-base": "^4.1.0", + "socks": "^1.1.10" + } + }, + "ssri": { + "version": "5.3.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.0", + "bundled": true + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.4.3", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true + }, + "os-locale": { + "version": "2.1.0", + "bundled": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-limit": { + "version": "1.2.0", + "bundled": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "bundled": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "bundled": true + }, + "package-json": { + "version": "4.0.1", + "bundled": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "pacote": { + "version": "8.1.6", + "bundled": true, + "requires": { + "bluebird": "^3.5.1", + "cacache": "^11.0.2", + "get-stream": "^3.0.0", + "glob": "^7.1.2", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.3", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.10", + "npm-pick-manifest": "^2.1.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.0", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.5.0", + "ssri": "^6.0.0", + "tar": "^4.4.3", + "unique-filename": "^1.1.0", + "which": "^1.3.0" + } + }, + "parallel-transform": { + "version": "1.1.0", + "bundled": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "path-is-inside": { + "version": "1.0.2", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true + }, + "pify": { + "version": "3.0.0", + "bundled": true + }, + "prepend-http": { + "version": "1.0.4", + "bundled": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true + }, + "promise-retry": { + "version": "1.1.1", + "bundled": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + }, + "dependencies": { + "retry": { + "version": "0.10.1", + "bundled": true + } + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "requires": { + "read": "1" + } + }, + "proto-list": { + "version": "1.2.4", + "bundled": true + }, + "protoduck": { + "version": "5.0.0", + "bundled": true, + "requires": { + "genfun": "^4.0.1" + } + }, + "prr": { + "version": "1.0.1", + "bundled": true + }, + "pseudomap": { + "version": "1.0.2", + "bundled": true + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "bundled": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "1.4.1", + "bundled": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true + }, + "qs": { + "version": "6.4.0", + "bundled": true + }, + "query-string": { + "version": "6.1.0", + "bundled": true, + "requires": { + "decode-uri-component": "^0.2.0", + "strict-uri-encode": "^2.0.0" + } + }, + "qw": { + "version": "1.0.1", + "bundled": true + }, + "rc": { + "version": "1.2.7", + "bundled": true, + "requires": { + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true + } + } + }, + "read": { + "version": "1.0.7", + "bundled": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "1.0.1", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2" + } + }, + "read-installed": { + "version": "4.0.3", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "^4.1.2", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + } + }, + "read-package-json": { + "version": "2.0.13", + "bundled": true, + "requires": { + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "json-parse-better-errors": "^1.0.1", + "normalize-package-data": "^2.0.0", + "slash": "^1.0.0" + } + }, + "read-package-tree": { + "version": "5.2.1", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "once": "^1.3.0", + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "bundled": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "registry-auth-token": { + "version": "3.3.2", + "bundled": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "bundled": true, + "requires": { + "rc": "^1.0.1" + } + }, + "request": { + "version": "2.81.0", + "bundled": true, + "requires": { + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.1.1", + "har-validator": "~4.2.1", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "oauth-sign": "~0.8.1", + "performance-now": "^0.2.0", + "qs": "~6.4.0", + "safe-buffer": "^5.0.1", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "1.0.1", + "bundled": true + }, + "resolve-from": { + "version": "4.0.0", + "bundled": true + }, + "retry": { + "version": "0.12.0", + "bundled": true + }, + "rimraf": { + "version": "2.6.2", + "bundled": true, + "requires": { + "glob": "^7.0.5" + } + }, + "run-queue": { + "version": "1.0.3", + "bundled": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true + }, + "semver": { + "version": "5.5.0", + "bundled": true + }, + "semver-diff": { + "version": "2.1.0", + "bundled": true, + "requires": { + "semver": "^5.0.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "sha": { + "version": "2.0.1", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.2", + "readable-stream": "^2.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true + }, + "slash": { + "version": "1.0.0", + "bundled": true + }, + "slide": { + "version": "1.1.6", + "bundled": true + }, + "smart-buffer": { + "version": "4.0.1", + "bundled": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "requires": { + "hoek": "2.x.x" + } + }, + "socks": { + "version": "2.2.0", + "bundled": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, + "sorted-object": { + "version": "2.0.1", + "bundled": true + }, + "sorted-union-stream": { + "version": "2.1.3", + "bundled": true, + "requires": { + "from2": "^1.3.0", + "stream-iterate": "^1.1.0" + }, + "dependencies": { + "from2": { + "version": "1.3.0", + "bundled": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "~1.1.10" + } + }, + "isarray": { + "version": "0.0.1", + "bundled": true + }, + "readable-stream": { + "version": "1.1.14", + "bundled": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "bundled": true + } + } + }, + "spdx-correct": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "bundled": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "bundled": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "bundled": true + }, + "sshpk": { + "version": "1.14.2", + "bundled": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "ssri": { + "version": "6.0.0", + "bundled": true + }, + "stream-each": { + "version": "1.2.2", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-iterate": { + "version": "1.2.0", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "stream-shift": "^1.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "bundled": true + }, + "strict-uri-encode": { + "version": "2.0.0", + "bundled": true + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringstream": { + "version": "0.0.6", + "bundled": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true + }, + "supports-color": { + "version": "5.4.0", + "bundled": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "4.4.4", + "bundled": true, + "requires": { + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.3", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "bundled": true + } + } + }, + "term-size": { + "version": "1.2.0", + "bundled": true, + "requires": { + "execa": "^0.7.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true + }, + "through": { + "version": "2.3.8", + "bundled": true + }, + "through2": { + "version": "2.0.3", + "bundled": true, + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "bundled": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true + }, + "tough-cookie": { + "version": "2.3.4", + "bundled": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "bundled": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true + }, + "umask": { + "version": "1.1.0", + "bundled": true + }, + "unique-filename": { + "version": "1.1.0", + "bundled": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.0", + "bundled": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "bundled": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "bundled": true + }, + "unzip-response": { + "version": "2.0.1", + "bundled": true + }, + "update-notifier": { + "version": "2.5.0", + "bundled": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "url-parse-lax": { + "version": "1.0.0", + "bundled": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "util-extend": { + "version": "1.0.3", + "bundled": true + }, + "uuid": { + "version": "3.3.2", + "bundled": true + }, + "validate-npm-package-license": { + "version": "3.0.3", + "bundled": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true + } + } + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "requires": { + "string-width": "^1.0.2" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "widest-line": { + "version": "2.0.0", + "bundled": true, + "requires": { + "string-width": "^2.1.1" + } + }, + "worker-farm": { + "version": "1.6.0", + "bundled": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "bundled": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "bundled": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "write-file-atomic": { + "version": "2.3.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "bundled": true + }, + "xtend": { + "version": "4.0.1", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yallist": { + "version": "2.1.2", + "bundled": true + }, + "yargs": { + "version": "11.0.0", + "bundled": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "bundled": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "bundled": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "npm-run-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz", + "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=", + "requires": { + "path-key": "^1.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nugget": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", + "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", + "requires": { + "debug": "^2.1.3", + "minimist": "^1.1.0", + "pretty-bytes": "^1.0.2", + "progress-stream": "^1.1.0", + "request": "^2.45.0", + "single-line-log": "^1.1.2", + "throttleit": "0.0.2" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "output-file-sync": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.2.tgz", + "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", + "requires": { + "graceful-fs": "^4.1.4", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "parse-entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.2.tgz", + "integrity": "sha512-5N9lmQ7tmxfXf+hO3X6KRG6w7uYO/HL9fHalSySTdyn63C3WNvTM/1R8tn1u1larNcEbo3Slcy2bsVDQqvEpUg==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "passwd-user": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/passwd-user/-/passwd-user-2.1.0.tgz", + "integrity": "sha1-+tnbauJS+LCI4MXezSCn2gxdnx4=", + "requires": { + "execa": "^0.4.0", + "pify": "^2.3.0" + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz", + "integrity": "sha1-1aiZjrce83p0w06w2eum6HjuoIE=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pegjs": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.9.0.tgz", + "integrity": "sha1-9q76LjzlYWkgjlIXnf5B+JFBo2k=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" + }, + "plist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz", + "integrity": "sha1-CEtQk93JJQbiWfh0uNmxr7jHlZM=", + "requires": { + "base64-js": "0.0.8", + "util-deprecate": "1.0.2", + "xmlbuilder": "4.0.0", + "xmldom": "0.1.x" + }, + "dependencies": { + "base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "xmlbuilder": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.0.0.tgz", + "integrity": "sha1-mLj2UcowqmJANvEn0RzGbce5B6M=", + "requires": { + "lodash": "^3.5.0" + } + } + } + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-html": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.28.0.tgz", + "integrity": "sha512-H+ucbGVR+lsZySspOApeQU9yC6Q3t75lwJYa3Im93fKAUt5DScKOSErShC0aC7USdn2jsT1LxubcC5vYu/VJYw==", + "requires": { + "htmlparser2": "^3.9.2" + } + }, + "postcss-less": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-2.0.0.tgz", + "integrity": "sha512-pPNsVnpCB13nBMOcl5GVh8JGmB0JGFjqkLUDzKdVpptFFKEe9wFdEzvh2j4lD2AD+7qcrUfw9Ta+oi5+Fw7jjQ==", + "requires": { + "postcss": "^5.2.16" + }, + "dependencies": { + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + } + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "postcss-markdown": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-0.28.0.tgz", + "integrity": "sha512-F0Vc8eHKDKTmensntXpd35LSAoXXtykhPY+IRfn4AnN4m+irav3QawmtSWLhsmbElKna8l1/HObYnbiM/Wok9Q==", + "requires": { + "remark": "^9.0.0", + "unist-util-find-all-after": "^1.0.2" + } + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=" + }, + "postcss-reporter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-5.0.0.tgz", + "integrity": "sha512-rBkDbaHAu5uywbCR2XE8a25tats3xSOsGNx6mppK6Q9kSFGKc/FyAzfci+fWM2l+K402p1D0pNcfDGxeje5IKg==", + "requires": { + "chalk": "^2.0.1", + "lodash": "^4.17.4", + "log-symbols": "^2.0.0", + "postcss": "^6.0.8" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=" + }, + "postcss-safe-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-3.0.1.tgz", + "integrity": "sha1-t1Pv9sfArqXoN1++TN6L+QY/8UI=", + "requires": { + "postcss": "^6.0.6" + } + }, + "postcss-sass": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.3.2.tgz", + "integrity": "sha512-0HgxikiZ07VKYr98KT+k7/rAzyMgZlP+3+R8vUti56T2dPdhW0OhPGDQzddxY/N2iDtBVZQqCHRDA09j5I6EWg==", + "requires": { + "gonzales-pe": "4.2.3", + "postcss": "6.0.22" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "postcss": { + "version": "6.0.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", + "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-scss": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-1.0.6.tgz", + "integrity": "sha512-4EFYGHcEw+H3E06PT/pQQri06u/1VIIPjeJQaM8skB80vZuXMhp4cSNV5azmdNkontnOID/XYWEvEEELLFB1ww==", + "requires": { + "postcss": "^6.0.23" + } + }, + "postcss-selector-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", + "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "requires": { + "dot-prop": "^4.1.1", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-syntax": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.28.0.tgz", + "integrity": "sha512-9W3T1fSE9QWKyW6s84kZapv0BP5uvj7mNBp34kwI93uGWULzZjaKv4xR4phubBD53cRgaM/qnvquVK1KLsl+Kg==" + }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" + }, + "pragma-singleton": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pragma-singleton/-/pragma-singleton-1.0.3.tgz", + "integrity": "sha1-aJQxe7jUcVflneKkoAnbfm9j4w4=" + }, + "prebuild-install": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-4.0.0.tgz", + "integrity": "sha512-7tayxeYboJX0RbVzdnKyGl2vhQRWr6qfClEXDhOkXjuaOKCw2q8aiuFhONRYVsG/czia7KhpykIlI2S2VaPunA==", + "requires": { + "detect-libc": "^1.0.3", + "expand-template": "^1.0.2", + "github-from-package": "0.0.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "node-abi": "^2.2.0", + "noop-logger": "^0.1.1", + "npmlog": "^4.0.1", + "os-homedir": "^1.0.1", + "pump": "^2.0.1", + "rc": "^1.1.6", + "simple-get": "^2.7.0", + "tar-fs": "^1.13.0", + "tunnel-agent": "^0.6.0", + "which-pm-runs": "^1.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "pretty-bytes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", + "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.1.0" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" + }, + "progress-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", + "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", + "requires": { + "speedometer": "~0.1.2", + "through2": "~0.2.3" + } + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "publish-release": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/publish-release/-/publish-release-1.6.0.tgz", + "integrity": "sha512-t+NFXTQN/VDTg9yJ8Uv5ZWQ7Ud1T5W1tPW+bmuo4g6uYVQTVNiwwRF6Td3EtXFTOafpEXJQEZqGG7IvIJwLwIg==", + "requires": { + "async": "^0.9.0", + "ghauth": "^2.0.0", + "github-url-to-object": "^1.4.2", + "inquirer": "^0.8.2", + "lodash": "^3.6.0", + "mime": "^1.3.4", + "minimist": "^1.1.1", + "pkginfo": "^0.3.0", + "pretty-bytes": "^1.0.4", + "progress-stream": "^1.0.1", + "request": "^2.54.0", + "single-line-log": "^0.4.1", + "string-editor": "^0.1.0" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" + }, + "single-line-log": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", + "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=" + } + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "queue": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-1.0.2.tgz", + "integrity": "sha1-LZr55hyaGuRVem842FtTTq/yBYE=" + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=" + }, + "random-seed": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/random-seed/-/random-seed-0.3.0.tgz", + "integrity": "sha1-2UXy4fOPSejViRNDG4v2u5N1Vs0=", + "requires": { + "json-stringify-safe": "^5.0.1" + } + }, + "randomatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz", + "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + } + } + }, + "rcedit": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-0.5.1.tgz", + "integrity": "sha1-0L3PXSgKnRwp2m8RjMzizhU87x0=" + }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-installed": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-3.1.3.tgz", + "integrity": "sha1-wJCSoTwhF/IoQsrRaATzsFkSnRE=", + "requires": { + "debuglog": "^1.0.1", + "graceful-fs": "2 || 3", + "read-package-json": "1", + "readdir-scoped-modules": "^1.0.0", + "semver": "2 || 3 || 4", + "slide": "~1.1.3", + "util-extend": "^1.0.1" + }, + "dependencies": { + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "optional": true, + "requires": { + "natives": "^1.1.0" + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + } + } + }, + "read-package-json": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-1.3.3.tgz", + "integrity": "sha1-73nf2kbhZTdu6KV++/7dTRsCm6Q=", + "requires": { + "glob": "^5.0.3", + "graceful-fs": "2 || 3", + "json-parse-helpfulerror": "^1.0.2", + "normalize-package-data": "^1.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "optional": true, + "requires": { + "natives": "^1.1.0" + } + }, + "normalize-package-data": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-1.0.3.tgz", + "integrity": "sha1-i+lVuJB6+XXxpFhOqLubQUkjEvU=", + "requires": { + "github-url-from-git": "^1.3.0", + "github-url-from-username-repo": "^1.0.0", + "semver": "2 || 3 || 4" + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, + "readdir-scoped-modules": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz", + "integrity": "sha1-n6+jfShr5dksuuve4DDcm19AZ0c=", + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "readline2": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", + "integrity": "sha1-mUQ7pug7gw7zBRv9fcJBqCco1Wg=", + "requires": { + "mute-stream": "0.0.4", + "strip-ansi": "^2.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz", + "integrity": "sha1-QchHGUZGN15qGl0Qw8oFTvn8mA0=" + }, + "mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha1-qSGZYKbV1dBGWXruUSUsZlX3F34=" + }, + "strip-ansi": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", + "integrity": "sha1-32LBqpTtLxFOHQ8h/R1QSCt5pg4=", + "requires": { + "ansi-regex": "^1.0.0" + } + } + } + }, + "recast": { + "version": "0.10.33", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.33.tgz", + "integrity": "sha1-lCgI96oBbx+nFCxGHX5XBKqo1pc=", + "requires": { + "ast-types": "0.8.12", + "esprima-fb": "~15001.1001.0-dev-harmony-fb", + "private": "~0.1.5", + "source-map": "~0.5.0" + }, + "dependencies": { + "ast-types": { + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.12.tgz", + "integrity": "sha1-oNkOQ1G7iHcWyD/WN+v4GK9K38w=" + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" + }, + "regenerator": { + "version": "0.8.40", + "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.40.tgz", + "integrity": "sha1-oORXxY69uuV1yfjNdRJ+k3VkNdg=", + "requires": { + "commoner": "~0.10.3", + "defs": "~1.1.0", + "esprima-fb": "~15001.1001.0-dev-harmony-fb", + "private": "~0.1.5", + "recast": "0.10.33", + "through": "~2.3.8" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexpu/-/regexpu-1.3.0.tgz", + "integrity": "sha1-5TTcmRqeWEYFDJjebX3UpVyeoW0=", + "requires": { + "esprima": "^2.6.0", + "recast": "^0.10.10", + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + } + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "requires": { + "jsesc": "~0.5.0" + } + }, + "remark": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-9.0.0.tgz", + "integrity": "sha512-amw8rGdD5lHbMEakiEsllmkdBP+/KpjW/PRK6NSGPZKCQowh0BT4IWXDAkRMyG3SB9dKPXWMviFjNusXzXNn3A==", + "requires": { + "remark-parse": "^5.0.0", + "remark-stringify": "^5.0.0", + "unified": "^6.0.0" + } + }, + "remark-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", + "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "remark-stringify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-5.0.0.tgz", + "integrity": "sha512-Ws5MdA69ftqQ/yhRF9XhVV29mhxbfGhbz0Rx5bQH+oJcNhhSM6nCu1EpLod+DjrFGrU0BMPs+czVmJZU7xiS7w==", + "requires": { + "ccount": "^1.0.0", + "is-alphanumeric": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "longest-streak": "^2.0.1", + "markdown-escapes": "^1.0.0", + "markdown-table": "^1.1.0", + "mdast-util-compact": "^1.0.0", + "parse-entities": "^1.0.2", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "stringify-entities": "^1.0.1", + "unherit": "^1.0.4", + "xtend": "^4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "integrity": "sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + } + }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "requires": { + "path-parse": "^1.0.5" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rgb2hex": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.1.8.tgz", + "integrity": "sha512-kPH3Zm3UrBIfJv17AtJJGLRxak+Hvvz6SnsTBIajqB2Zbh+A4EEjkMWKkmGhms0cJlzOOjZcu1LX5K3vnON7ug==" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "^7.0.5" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "requires": { + "once": "^1.3.0" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" + }, + "run-series": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.8.tgz", + "integrity": "sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg==" + }, + "rx": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz", + "integrity": "sha1-Ia3H2A8CACr1Da6X/Z2/JIdV9WY=" + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "season": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/season/-/season-5.3.0.tgz", + "integrity": "sha1-KC05fmQW9EkjKHvVVFCtCAuV22U=", + "requires": { + "cson-parser": "1.0.9", + "fs-plus": "2.x", + "optimist": "~0.4.0" + }, + "dependencies": { + "optimist": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.4.0.tgz", + "integrity": "sha1-y47Dfy/jqphky2eidSUOfhliCiU=", + "requires": { + "wordwrap": "~0.0.2" + } + } + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shelljs": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz", + "integrity": "sha1-7GIRvtGSBEIIj+D3Cyg3Iy7SyKg=" + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" + }, + "simple-fmt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz", + "integrity": "sha1-GRv1ZqWeZTBILLJatTtKjchcOms=" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-is": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=" + }, + "single-line-log": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", + "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", + "requires": { + "string-width": "^1.0.1" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + } + }, + "sntp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", + "optional": true, + "requires": { + "hoek": "0.9.x" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz", + "integrity": "sha1-6lo5AKHByyUJagrozFwrSxDe09w=", + "requires": { + "source-map": "0.1.32" + }, + "dependencies": { + "source-map": { + "version": "0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "integrity": "sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" + }, + "specificity": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.2.tgz", + "integrity": "sha512-Nc/QN/A425Qog7j9aHmwOrlwX2e7pNI47ciwxwy4jOlvbbMHkNNJchit+FX+UjF3IAdiaaV5BKeWuDUnws6G1A==" + }, + "speedometer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", + "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", + "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "standard": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/standard/-/standard-8.4.0.tgz", + "integrity": "sha1-SDNS5D+us1om6OwWOZTlE4wxtlA=", + "requires": { + "eslint": "~3.7.1", + "eslint-config-standard": "6.2.0", + "eslint-config-standard-jsx": "3.2.0", + "eslint-plugin-promise": "~3.0.0", + "eslint-plugin-react": "~6.4.1", + "eslint-plugin-standard": "~2.0.1", + "standard-engine": "~5.1.0" + } + }, + "standard-engine": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-5.1.1.tgz", + "integrity": "sha1-y3derhxQz6jnarJUVt0SKvfzR4g=", + "requires": { + "deglob": "^2.0.0", + "find-root": "^1.0.0", + "get-stdin": "^5.0.1", + "home-or-tmp": "^2.0.0", + "minimist": "^1.1.0", + "pkg-config": "^1.0.1" + }, + "dependencies": { + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=" + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + } + } + }, + "state-toggle": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.1.tgz", + "integrity": "sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og==" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "string-editor": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/string-editor/-/string-editor-0.1.2.tgz", + "integrity": "sha1-9f8bWsSu16xsL7jeI20VUbIPYdA=", + "requires": { + "editor": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "stringify-entities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", + "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==", + "requires": { + "character-entities-html4": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "stringmap": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz", + "integrity": "sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE=" + }, + "stringset": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz", + "integrity": "sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=" + }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=" + }, + "stylelint": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-9.3.0.tgz", + "integrity": "sha512-u59pWTlrdwjqriJtTvO1a0wRK1mfbQQp7jLt27SX4zl2HmtVHOM/I1wd43xHTvUJZDKp1PTOpqRAamU3gFvmOA==", + "requires": { + "autoprefixer": "^8.0.0", + "balanced-match": "^1.0.0", + "chalk": "^2.4.1", + "cosmiconfig": "^5.0.0", + "debug": "^3.0.0", + "execall": "^1.0.0", + "file-entry-cache": "^2.0.0", + "get-stdin": "^6.0.0", + "globby": "^8.0.0", + "globjoin": "^0.1.4", + "html-tags": "^2.0.0", + "ignore": "^3.3.3", + "import-lazy": "^3.1.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.6.0", + "lodash": "^4.17.4", + "log-symbols": "^2.0.0", + "mathml-tag-names": "^2.0.1", + "meow": "^5.0.0", + "micromatch": "^2.3.11", + "normalize-selector": "^0.2.0", + "pify": "^3.0.0", + "postcss": "^6.0.16", + "postcss-html": "^0.28.0", + "postcss-less": "^2.0.0", + "postcss-markdown": "^0.28.0", + "postcss-media-query-parser": "^0.2.3", + "postcss-reporter": "^5.0.0", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^3.0.1", + "postcss-sass": "^0.3.0", + "postcss-scss": "^1.0.2", + "postcss-selector-parser": "^3.1.0", + "postcss-syntax": "^0.28.0", + "postcss-value-parser": "^3.3.0", + "resolve-from": "^4.0.0", + "signal-exit": "^3.0.2", + "specificity": "^0.3.1", + "string-width": "^2.1.0", + "style-search": "^0.1.0", + "sugarss": "^1.0.0", + "svg-tags": "^1.0.0", + "table": "^4.0.1" + }, + "dependencies": { + "ajv": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" + } + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globby": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", + "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=" + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "requires": { + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=" + } + } + }, + "stylelint-config-recommended": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz", + "integrity": "sha512-ajMbivOD7JxdsnlS5945KYhvt7L/HwN6YeYF2BH6kE4UCLJR0YvXMf+2j7nQpJyYLZx9uZzU5G1ZOSBiWAc6yA==" + }, + "stylelint-config-standard": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-18.2.0.tgz", + "integrity": "sha512-07x0TaSIzvXlbOioUU4ORkCIM07kyIuojkbSVCyFWNVgXMXYHfhnQSCkqu+oHWJf3YADAnPGWzdJ53NxkoJ7RA==", + "requires": { + "stylelint-config-recommended": "^2.1.0" + } + }, + "sugarss": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-1.0.1.tgz", + "integrity": "sha512-3qgLZytikQQEVn1/FrhY7B68gPUUGY3R1Q1vTiD5xT+Ti1DP/8iZuwFet9ONs5+bmL8pZoDQ6JrQHVgrNlK6mA==", + "requires": { + "postcss": "^6.0.14" + } + }, + "sumchecker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz", + "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", + "requires": { + "debug": "^2.2.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=" + }, + "sync-request": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", + "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", + "requires": { + "concat-stream": "^1.4.7", + "http-response-object": "^1.0.1", + "then-request": "^2.0.1" + } + }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "requires": { + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", + "slice-ansi": "0.0.4", + "string-width": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" + } + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "tar-fs": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz", + "integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==", + "requires": { + "chownr": "^1.0.1", + "mkdirp": "^0.5.1", + "pump": "^1.0.0", + "tar-stream": "^1.1.2" + }, + "dependencies": { + "pump": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", + "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "tar-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", + "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.1.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "tello": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tello/-/tello-1.0.7.tgz", + "integrity": "sha512-N/EvP7dLmiNQwg0NFY1igz69Fj6G8RGM2AuVSpJfDWYb831w9Ary81/jwRhgIarFDH6deK7jytHyYMo6FtHbiA==", + "requires": { + "atomdoc": "1.0.6", + "optimist": "~0.6", + "underscore": "~1.6" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "temp": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz", + "integrity": "sha1-4Ma8TSa5AxJEEOT+2BEDAU38H1k=", + "requires": { + "os-tmpdir": "^1.0.0", + "rimraf": "~2.2.6" + }, + "dependencies": { + "rimraf": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", + "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, + "then-request": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", + "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", + "requires": { + "caseless": "~0.11.0", + "concat-stream": "^1.4.7", + "http-basic": "^2.5.1", + "http-response-object": "^1.1.0", + "promise": "^7.1.1", + "qs": "^6.1.0" + }, + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + } + } + }, + "throttleit": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "requires": { + "readable-stream": "~1.1.9", + "xtend": "~2.1.1" + } + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + } + } + } + }, + "touch": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz", + "integrity": "sha1-Ua7z1ElXHU8oel2Hyci0kYGg2x0=", + "requires": { + "nopt": "~1.0.10" + }, + "dependencies": { + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", + "requires": { + "abbrev": "1" + } + } + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "trim-trailing-lines": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz", + "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg==" + }, + "trough": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.2.tgz", + "integrity": "sha512-FHkoUZvG6Egrv9XZAyYGKEyb1JMsFphgPjoczkZC2y6W93U1jswcVURB8MUvtsahEPEVACyxD47JAL63vF4JsQ==" + }, + "try-resolve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz", + "integrity": "sha1-z95vq9ctY+V5fPqrhzq76OcA6RI=" + }, + "tryor": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz", + "integrity": "sha1-gUXkynyv9ArN48z5Rui4u3W0Fys=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "underscore-plus": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/underscore-plus/-/underscore-plus-1.6.8.tgz", + "integrity": "sha512-88PrCeMKeAAC1L4xjSiiZ3Fg6kZOYrLpLGVPPeqKq/662DfQe/KTSKdSR/Q/tucKNnfW2MNAUGSCkDf8HmXC5Q==", + "requires": { + "underscore": "~1.8.3" + }, + "dependencies": { + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + } + } + }, + "underscore.string": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", + "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=", + "requires": { + "sprintf-js": "^1.0.3", + "util-deprecate": "^1.0.2" + } + }, + "unherit": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", + "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==", + "requires": { + "inherits": "^2.0.1", + "xtend": "^4.0.1" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } + }, + "unified": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", + "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^2.0.0", + "x-is-string": "^0.1.0" + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "unist-util-find-all-after": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.2.tgz", + "integrity": "sha512-nDl79mKpffXojLpCimVXnxhlH/jjaTnDuScznU9J4jjsaUtBdDbxmlc109XtcqxY4SDO0SwzngsxxW8DIISt1w==", + "requires": { + "unist-util-is": "^2.0.0" + } + }, + "unist-util-is": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz", + "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw==" + }, + "unist-util-modify-children": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-1.1.2.tgz", + "integrity": "sha512-GRi04yhng1WqBf5RBzPkOtWAadcZS2gvuOgNn/cyJBYNxtTuyYqTKN0eg4rC1YJwGnzrqfRB3dSKm8cNCjNirg==", + "requires": { + "array-iterate": "^1.0.0" + } + }, + "unist-util-remove-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz", + "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" + }, + "unist-util-visit": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.1.tgz", + "integrity": "sha512-0fdB9EQJU0tho5tK0VzOJzAQpPv2LyLZ030b10GxuzAWEfvd54mpY7BMjQ1L69k2YNvL+SvxRzH0yUIehOO8aA==", + "requires": { + "unist-util-is": "^2.1.1" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util-extend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz", + "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vfile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", + "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "requires": { + "is-buffer": "^1.1.4", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-location": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.3.tgz", + "integrity": "sha512-zM5/l4lfw1CBoPx3Jimxoc5RNDAHHpk6AM6LM0pTIkm5SUSsx8ZekZ0PVdf0WEZ7kjlhSt7ZlqbRL6Cd6dBs6A==" + }, + "vfile-message": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.1.tgz", + "integrity": "sha512-vSGCkhNvJzO6VcWC6AlJW4NtYOVtS+RgCaqFIYUjoGIlHnFL+i0LbtYvonDWOMcB97uTPT4PRsyYY7REWC9vug==", + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + }, + "walkdir": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.12.tgz", + "integrity": "sha512-HFhaD4mMWPzFSqhpyDG48KDdrjfn409YQuVW7ckZYhW4sE87mYtWifdB/+73RA7+p4s4K18n5Jfx1kHthE1gBw==" + }, + "webdriverio": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-2.4.5.tgz", + "integrity": "sha1-wD7ajhp+tCMDhYjm5z8nCyx03gs=", + "requires": { + "archiver": "~0.6.1", + "async": "^0.9.0", + "chainit": "^2.1.1", + "css-parse": "^1.7.0", + "css-value": "0.0.1", + "deepmerge": "~0.2.7", + "pragma-singleton": "~1.0.3", + "q": "^1.1.2", + "request": "~2.34.0", + "rgb2hex": "^0.1.0", + "url": "^0.10.1", + "wgxpath": "^0.23.0" + }, + "dependencies": { + "asn1": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", + "optional": true + }, + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", + "optional": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "aws-sign2": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", + "optional": true + }, + "combined-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", + "optional": true, + "requires": { + "delayed-stream": "0.0.5" + } + }, + "delayed-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", + "optional": true + }, + "forever-agent": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=" + }, + "form-data": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", + "optional": true, + "requires": { + "async": "~0.9.0", + "combined-stream": "~0.0.4", + "mime": "~1.2.11" + } + }, + "http-signature": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", + "optional": true, + "requires": { + "asn1": "0.1.11", + "assert-plus": "^0.1.5", + "ctype": "0.5.3" + } + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + }, + "oauth-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", + "optional": true + }, + "qs": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", + "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=" + }, + "request": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.34.0.tgz", + "integrity": "sha1-tdi5UmrdSi1GKfTUFxJFc5lkRa4=", + "requires": { + "aws-sign2": "~0.5.0", + "forever-agent": "~0.5.0", + "form-data": "~0.1.0", + "hawk": "~1.0.0", + "http-signature": "~0.10.0", + "json-stringify-safe": "~5.0.0", + "mime": "~1.2.9", + "node-uuid": "~1.4.0", + "oauth-sign": "~0.3.0", + "qs": "~0.6.0", + "tough-cookie": ">=0.12.0", + "tunnel-agent": "~0.3.0" + } + }, + "tunnel-agent": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", + "integrity": "sha1-rWgbaPUyGtKCfEz7G31d8s/pQu4=", + "optional": true + } + } + }, + "wgxpath": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/wgxpath/-/wgxpath-0.23.0.tgz", + "integrity": "sha1-2z/IOJ2BhOluunA3SJc1wTYiep8=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "requires": { + "mkdirp": "^0.5.1" + } + }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + } + } + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "requires": { + "fd-slicer": "~1.0.1" + } + }, + "zip-stream": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-0.2.3.tgz", + "integrity": "sha1-rvCVN2z+E4lZqBNBmB0mM4tG2NM=", + "requires": { + "debug": "~0.7.4", + "lodash.defaults": "~2.4.1", + "readable-stream": "~1.0.24" + }, + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + } + } + } + } +} diff --git a/script/package.json b/script/package.json index 78025d223..874ceed88 100644 --- a/script/package.json +++ b/script/package.json @@ -2,36 +2,41 @@ "name": "atom-build-scripts", "description": "Atom build scripts", "dependencies": { + "7zip-bin": "^4.0.2", "async": "2.0.1", + "aws-sdk": "^2.5.2", "babel-core": "5.8.38", "coffeelint": "1.15.7", "colors": "1.1.2", - "csslint": "1.0.2", "donna": "1.0.16", - "electron-chromedriver": "~1.7", - "electron-link": "0.1.2", - "electron-mksnapshot": "~1.7", + "electron-chromedriver": "~2.0", + "electron-link": "0.2.2", + "electron-mksnapshot": "~2.0", "electron-packager": "7.3.0", - "electron-winstaller": "2.6.3", + "electron-winstaller": "2.6.4", "fs-admin": "^0.1.5", "fs-extra": "0.30.0", "glob": "7.0.3", "joanna": "0.0.10", "klaw-sync": "^1.1.2", "legal-eagle": "0.14.0", + "lodash.startcase": "4.4.0", "lodash.template": "4.4.0", "minidump": "0.9.0", "mkdirp": "0.5.1", "normalize-package-data": "2.3.5", - "npm": "5.3.0", + "npm": "6.2.0", "passwd-user": "2.1.0", "pegjs": "0.9.0", + "publish-release": "^1.6.0", "random-seed": "^0.3.0", "season": "5.3.0", "semver": "5.3.0", "standard": "8.4.0", + "stylelint": "^9.0.0", + "stylelint-config-standard": "^18.1.0", "sync-request": "3.0.1", - "tello": "1.0.5", + "tello": "1.0.7", "webdriverio": "2.4.5", "yargs": "4.8.1" } diff --git a/script/publish-release b/script/publish-release new file mode 100644 index 000000000..2e9115c4d --- /dev/null +++ b/script/publish-release @@ -0,0 +1,65 @@ +#!/usr/bin/env node + +'use strict' + +const path = require('path') +const glob = require('glob') +const publishRelease = require('publish-release') +const uploadToS3 = require('./lib/upload-to-s3') +const CONFIG = require('./config') + +const yargs = require('yargs') +const argv = yargs + .usage('Usage: $0 [options]') + .help('help') + .describe('assets-path', 'Path to the folder where all release assets are stored') + .describe('s3-path', 'Indicates the S3 path in which the assets should be uploaded') + .describe('create-github-release', 'Creates a GitHub release for this build, draft if release branch or public if Nightly') + .wrap(yargs.terminalWidth()) + .argv + +const assetsPath = argv.assetsPath || path.join(CONFIG.repositoryRootPath, 'out') +const assetsPattern = '/**/*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*|atom-api.json)' +const assets = glob.sync(assetsPattern, { root: assetsPath, nodir: true }) +const bucketPath = argv.s3Path || `releases/v${CONFIG.computedAppVersion}/` + +if (!assets || assets.length === 0) { + console.error(`No assets found under specified path: ${assetsPath}`) + process.exit(1) +} + +console.log(`Uploading ${assets.length} release assets for ${CONFIG.computedAppVersion} to S3 under '${bucketPath}'`) + +uploadToS3( + process.env.ATOM_RELEASES_S3_KEY, + process.env.ATOM_RELEASES_S3_SECRET, + process.env.ATOM_RELEASES_S3_BUCKET, + bucketPath, + assets).then( + () => { + if (argv.createGithubRelease) { + console.log(`Creating GitHub release v${CONFIG.computedAppVersion}`) + publishRelease({ + token: process.env.GITHUB_TOKEN, + owner: 'atom', + repo: CONFIG.channel !== 'nightly' ? 'atom' : 'atom-nightly-releases', + name: CONFIG.computedAppVersion, + tag: `v${CONFIG.computedAppVersion}`, + draft: false, + prerelease: CONFIG.channel !== 'stable', + reuseRelease: true, + skipIfPublished: true, + assets + }, function (err, release) { + if (err) { + console.error("An error occurred while publishing the release:\n\n", err) + } else { + console.log("Release published successfully: ", release.html_url) + } + }) + } else { + console.log("Skipping GitHub release creation") + } + }).catch((err) => { + console.error('An error occurred while uploading the release:', err) + }) diff --git a/script/publish-release.cmd b/script/publish-release.cmd new file mode 100644 index 000000000..46e077a3c --- /dev/null +++ b/script/publish-release.cmd @@ -0,0 +1,5 @@ +@IF EXIST "%~dp0\node.exe" ( + "%~dp0\node.exe" "%~dp0\publish-release" %* +) ELSE ( + node "%~dp0\publish-release" %* +) diff --git a/script/test b/script/test index c6c3a6a61..64688daa1 100755 --- a/script/test +++ b/script/test @@ -22,7 +22,7 @@ if (process.platform === 'darwin') { assert(executablePaths.length === 1, `More than one application to run tests against was found. ${executablePaths.join(',')}`) executablePath = path.join(executablePaths[0], 'Contents', 'MacOS', path.basename(executablePaths[0], '.app')) } else if (process.platform === 'linux') { - const executablePaths = glob.sync(path.join(CONFIG.buildOutputPath, '**', 'atom')) + const executablePaths = glob.sync(path.join(CONFIG.buildOutputPath, 'atom-*', 'atom')) assert(executablePaths.length === 1, `More than one application to run tests against was found. ${executablePaths.join(',')}`) executablePath = executablePaths[0] } else if (process.platform === 'win32') { diff --git a/script/vsts/README.md b/script/vsts/README.md new file mode 100644 index 000000000..0e956d8d0 --- /dev/null +++ b/script/vsts/README.md @@ -0,0 +1,65 @@ +# Atom Release Build Documentation + +## Overview + +This folder contains build configuration and scripts for automating Atom's +release pipeline using [Visual Studio Team Services](https://azure.microsoft.com/en-us/services/visual-studio-team-services/). +VSTS allows us to leverage [multi-phase jobs](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-jobs.md) to generate Atom installation packages +on Windows, macOS, and Linux and then publish a new release automatically once +the build completes successfully. + +## Nightly Release Build + +Our scheduled nightly release uses a mutli-phase job to automatically generate Atom +Nightly installation packages and then publish them to GitHub and atom.io. + +The [Atom Nightly build definition](https://github.visualstudio.com/Atom/_build/index?context=mine&path=%5C&definitionId=1&_a=completed) +is configured with the [`nightly-release.yml`](nightly-release.yml) file. More +information on VSTS' YAML configuration format can be found in their [Getting Started](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted.md) +documentation. + +### Versioning Phase + +In this phase, we run [`script/vsts/generate-version.js`](generate-version.js) to +determine the version of the next Atom Nightly release. This script consults the +GitHub v3 API to get the list of releases on the [`atom/atom-nightly-releases`](https://github.com/atom/atom-nightly-releases) +repo. We look for the most recent, non-draft release and then parse its version +number (e.g. `1.30.0-nightly4`) to extract the base version and the monotonically-increasing +nightly release number. + +Once we have the version and release number, we compare the base version number +(`1.30.0`) against the one in `package.json` of the latest commit in the local +repo. If those versions are the same, we increment the release number (`1.30.0-nightly5`). +If those versions are different, we use `0` for the release number to start a +new series of Nightly releases for the new version (`1.31.0-nightly0`). + +Once the release version has been determined, it is set as our custom `ReleaseVersion` +[output variable](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-outputvariables.md) +by writing out a special string to `stdout` which is recognized by VSTS. This +variable will be used in later build steps. + +If any part of the build process fails from this point forward, the same version +number *should* be chosen in the next build unless the base version number has +been changed in `master`. + +### OS-specific Build Phases + +In this part of the build, we use [phase templates](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-templates.md) +for [Windows](windows.yml), [macOS](macos.yml), and [Linux](linux.yml) to build +Atom simultaneously across those platforms and then run the Atom test suite to +verify the builds. If build, test, and linting come back clean, we take the build +assets generated in the `out` folder on each OS and then stage them as build artifacts. + +For each OS build, we refer to the `ReleaseVersion` variable, set in the previous +phase, to configure the `ATOM_RELEASE_VERSION` environment variable to override +the version contained in Atom's `package.json`. + +### Publish Phase + +If all three OS builds have completed successfully, the publish phase will launch the +[`script/publish-release`](../publish-release) script to collect the release +artifacts created from those builds and then upload them to the S3 bucket from +which Atom release assets are served. If the upload process is successful, a new +release will be created on the `atom/atom-nightly-releases` repo using the +`ReleaseVersion` with a `v` prefix as the tag name. The release assets will also +be uploaded to the GitHub release at this time. diff --git a/script/vsts/get-release-version.js b/script/vsts/get-release-version.js new file mode 100644 index 000000000..13fbfbea5 --- /dev/null +++ b/script/vsts/get-release-version.js @@ -0,0 +1,59 @@ +const path = require('path') +const request = require('request-promise-native') + +const repositoryRootPath = path.resolve(__dirname, '..', '..') +const appMetadata = require(path.join(repositoryRootPath, 'package.json')) + +const yargs = require('yargs') +const argv = yargs + .usage('Usage: $0 [options]') + .help('help') + .describe('nightly', 'Indicates that a nightly version should be produced') + .wrap(yargs.terminalWidth()) + .argv + +async function getReleaseVersion () { + let releaseVersion = appMetadata.version + if (argv.nightly) { + const releases = await request({ + url: 'https://api.github.com/repos/atom/atom-nightly-releases/releases', + headers: {'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'Atom Release Build'}, + json: true + }) + + let releaseNumber = 0 + const baseVersion = appMetadata.version.split('-')[0] + if (releases && releases.length > 0) { + const latestRelease = releases.find(r => !r.draft) + const versionMatch = latestRelease.tag_name.match(/^v?(\d+\.\d+\.\d+)-nightly(\d+)$/) + + if (versionMatch && versionMatch[1] === baseVersion) { + releaseNumber = parseInt(versionMatch[2]) + 1 + } + } + + releaseVersion = `${baseVersion}-nightly${releaseNumber}` + } + + // Set our ReleaseVersion build variable and update VSTS' build number to + // include the version. Writing these strings to stdout causes VSTS to set + // the associated variables. + console.log(`##vso[task.setvariable variable=ReleaseVersion;isOutput=true]${releaseVersion}`) + if (!process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER) { + // Only set the build number on non-PR builds as it causes build errors when + // non-admins send PRs to the repo + console.log(`##vso[build.updatebuildnumber]${releaseVersion}+${process.env.BUILD_BUILDNUMBER}`) + } + + // Write out some variables that indicate whether artifacts should be uploaded + const buildBranch = process.env.BUILD_SOURCEBRANCHNAME + const isReleaseBranch = process.env.IS_RELEASE_BRANCH || argv.nightly || buildBranch.match(/\d\.\d+-releases/) !== null + const isSignedZipBranch = + process.env.IS_SIGNED_ZIP_BRANCH || + buildBranch.startsWith('electron-') || + buildBranch === 'master' && !process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER + console.log(`##vso[task.setvariable variable=IsReleaseBranch;isOutput=true]${isReleaseBranch}`) + console.log(`##vso[task.setvariable variable=IsSignedZipBranch;isOutput=true]${isSignedZipBranch}`) +} + +getReleaseVersion() diff --git a/script/vsts/nightly-release.yml b/script/vsts/nightly-release.yml new file mode 100644 index 000000000..ef68ef335 --- /dev/null +++ b/script/vsts/nightly-release.yml @@ -0,0 +1,58 @@ +phases: + +- phase: GetReleaseVersion + steps: + # This has to be done separately because VSTS inexplicably + # exits the script block after `npm install` completes. + - script: | + cd script\vsts + npm install + displayName: npm install + - script: node script\vsts\get-release-version.js --nightly + name: Version + +# Import OS-specific build definitions +- template: platforms/windows.yml +- template: platforms/macos.yml +- template: platforms/linux.yml + +- phase: Release + queue: Hosted # Need this for Python 2.7 + + dependsOn: + - GetReleaseVersion + - Windows + - Linux + - macOS + + variables: + ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ] + + steps: + - task: NodeTool@0 + inputs: + versionSpec: 8.9.3 + displayName: Install Node.js 8.9.3 + + # This has to be done separately because VSTS inexplicably + # exits the script block after `npm install` completes. + - script: | + cd script + npm install + displayName: npm install + + - task: DownloadBuildArtifacts@0 + inputs: + itemPattern: '**' + downloadType: 'specific' + displayName: Download Release Artifacts + + - script: | + $(Build.SourcesDirectory)\script\publish-release.cmd --create-github-release --assets-path "$(System.ArtifactsDirectory)" + env: + GITHUB_TOKEN: $(GITHUB_TOKEN) + ATOM_RELEASE_VERSION: $(ReleaseVersion) + ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY) + ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET) + ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET) + displayName: Create Nightly Release diff --git a/script/vsts/package-lock.json b/script/vsts/package-lock.json new file mode 100644 index 000000000..18d2190b1 --- /dev/null +++ b/script/vsts/package-lock.json @@ -0,0 +1,704 @@ +{ + "name": "atom-release-scripts", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", + "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "0.2.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.6", + "mime-types": "2.1.18" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "requires": { + "builtin-modules": "1.1.1" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "1.33.0" + } + }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "requires": { + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.3" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "1.3.2" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "2.0.4" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.3.0" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.10" + } + }, + "request-promise-native": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", + "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", + "requires": { + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.4" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "requires": { + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "0.2.1" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "uuid": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.0.tgz", + "integrity": "sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw==" + }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "requires": { + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.3", + "lodash.assign": "4.2.0", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "window-size": "0.2.0", + "y18n": "3.2.1", + "yargs-parser": "2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "3.0.0", + "lodash.assign": "4.2.0" + } + } + } +} diff --git a/script/vsts/package.json b/script/vsts/package.json new file mode 100644 index 000000000..4e0b450d5 --- /dev/null +++ b/script/vsts/package.json @@ -0,0 +1,9 @@ +{ + "name": "atom-release-scripts", + "description": "Atom release scripts", + "dependencies": { + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "yargs": "4.8.1" + } +} diff --git a/script/vsts/platforms/linux.yml b/script/vsts/platforms/linux.yml new file mode 100644 index 000000000..51e7e5001 --- /dev/null +++ b/script/vsts/platforms/linux.yml @@ -0,0 +1,63 @@ +phases: +- phase: Linux + dependsOn: GetReleaseVersion + variables: + ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ] + queue: + name: Hosted Ubuntu 1604 + timeoutInMinutes: 180 + + steps: + - task: NodeTool@0 + inputs: + versionSpec: 8.9.3 + displayName: Install Node.js 8.9.3 + + - script: npm install --global npm@6.2.0 + displayName: Update npm + + - script: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends build-essential xvfb clang-3.5 fakeroot git libsecret-1-dev rpm libx11-dev libxkbfile-dev xz-utils xorriso zsync libxss1 libgconf2-4 libgtk-3-0 + displayName: Install apt dependencies + + - script: | + script/build --create-debian-package --create-rpm-package --compress-artifacts + env: + ATOM_RELEASE_VERSION: $(ReleaseVersion) + displayName: Build Atom + + - script: script/lint + displayName: Run linter + + - script: | + sudo /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 + export DISPLAY=':99.0' + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + script/test + env: + CI: true + CI_PROVIDER: VSTS + displayName: Run tests + condition: and(succeeded(), ne(variables['Atom.SkipTests'], 'true')) + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom.x86_64.rpm + ArtifactName: atom.x86_64.rpm + ArtifactType: Container + displayName: Upload atom.x84_64.rpm + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom-amd64.deb + ArtifactName: atom-amd64.deb + ArtifactType: Container + displayName: Upload atom-amd64.deb + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom-amd64.tar.gz + ArtifactName: atom-amd64.tar.gz + ArtifactType: Container + displayName: Upload atom-amd64.tar.gz diff --git a/script/vsts/platforms/macos.yml b/script/vsts/platforms/macos.yml new file mode 100644 index 000000000..e7443860e --- /dev/null +++ b/script/vsts/platforms/macos.yml @@ -0,0 +1,72 @@ +phases: +- phase: macOS + dependsOn: GetReleaseVersion + variables: + ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ] + IsReleaseBranch: $[ dependencies.GetReleaseVersion.outputs['Version.IsReleaseBranch'] ] + IsSignedZipBranch: $[ dependencies.GetReleaseVersion.outputs['Version.IsSignedZipBranch'] ] + queue: + name: Hosted macOS Preview + timeoutInMinutes: 180 + + steps: + - task: NodeTool@0 + inputs: + versionSpec: 8.9.3 + displayName: Install Node.js 8.9.3 + + - script: npm install --global npm@6.2.0 + displayName: Update npm + + - script: | + if [ $IS_RELEASE_BRANCH == "true" ] || [ $IS_SIGNED_ZIP_BRANCH == "true" ]; then + script/build --code-sign --compress-artifacts + else + script/build --compress-artifacts + fi + displayName: Build Atom + env: + IS_RELEASE_BRANCH: $(IsReleaseBranch) + IS_SIGNED_ZIP_BRANCH: $(IsSignedZipBranch) + ATOM_RELEASE_VERSION: $(ReleaseVersion) + ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL: $(ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL) + ATOM_MAC_CODE_SIGNING_CERT_PASSWORD: $(ATOM_MAC_CODE_SIGNING_CERT_PASSWORD) + ATOM_MAC_CODE_SIGNING_KEYCHAIN: $(ATOM_MAC_CODE_SIGNING_KEYCHAIN) + ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD: $(ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD) + + - script: script/lint + displayName: Run linter + + - script: | + osascript -e 'tell application "System Events" to keystroke "x"' # clear screen saver + caffeinate -s script/test # Run with caffeinate to prevent screen saver + env: + CI: true + CI_PROVIDER: VSTS + displayName: Run tests + condition: and(succeeded(), ne(variables['Atom.SkipTests'], 'true')) + + - script: | + cp $(Build.SourcesDirectory)/out/*.zip $(Build.ArtifactStagingDirectory) + displayName: Stage Artifacts + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/atom-mac.zip + ArtifactName: atom-mac.zip + ArtifactType: Container + displayName: Upload atom-mac.zip + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.ArtifactStagingDirectory)/atom-mac-symbols.zip + ArtifactName: atom-mac-symbols.zip + ArtifactType: Container + displayName: Upload atom-mac-symbols.zip + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/docs/output/atom-api.json + ArtifactName: atom-api.json + ArtifactType: Container + displayName: Upload atom-api.json diff --git a/script/vsts/platforms/windows.yml b/script/vsts/platforms/windows.yml new file mode 100644 index 000000000..aba06b2a3 --- /dev/null +++ b/script/vsts/platforms/windows.yml @@ -0,0 +1,93 @@ +phases: +- phase: Windows + dependsOn: GetReleaseVersion + variables: + ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ] + IsReleaseBranch: $[ dependencies.GetReleaseVersion.outputs['Version.IsReleaseBranch'] ] + IsSignedZipBranch: $[ dependencies.GetReleaseVersion.outputs['Version.IsSignedZipBranch'] ] + queue: + name: Hosted + timeoutInMinutes: 180 + parallel: 2 + matrix: + x64: + buildArch: x64 + # TODO: x86 is currently not supported on VSTS + # x86: + # buildArch: x86 + + steps: + - task: NodeTool@0 + inputs: + versionSpec: 8.9.3 + displayName: Install Node.js 8.9.3 + + - script: | + ECHO Installing npm-windows-upgrade + npm install --global --production npm-windows-upgrade + ECHO Upgrading npm + npm-windows-upgrade --no-spinner --no-prompt --npm-version 6.2.0 + + - script: | + IF NOT EXIST C:\tmp MKDIR C:\tmp + SET SQUIRREL_TEMP=C:\tmp + IF [%IS_RELEASE_BRANCH%]==[true] ( + ECHO Creating production artifacts for release branch %BUILD_SOURCEBRANCHNAME% + script\build.cmd --code-sign --compress-artifacts --create-windows-installer + ) ELSE ( + IF [%IS_SIGNED_ZIP_BRANCH%]==[true] ( + ECHO Creating signed CI artifacts for branch %BUILD_SOURCEBRANCHNAME% + script\build.cmd --code-sign --compress-artifacts + ) ELSE ( + ECHO Pull request build, no code signing will be performed + script\build.cmd --compress-artifacts + ) + ) + env: + ATOM_RELEASE_VERSION: $(ReleaseVersion) + ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL: $(ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL) + ATOM_WIN_CODE_SIGNING_CERT_PASSWORD: $(ATOM_WIN_CODE_SIGNING_CERT_PASSWORD) + IS_RELEASE_BRANCH: $(IsReleaseBranch) + IS_SIGNED_ZIP_BRANCH: $(IsSignedZipBranch) + displayName: Build Atom + + - script: script\lint.cmd + displayName: Run linter + + - script: script\test.cmd + env: + CI: true + CI_PROVIDER: VSTS + displayName: Run tests + condition: and(succeeded(), ne(variables['Atom.SkipTests'], 'true')) + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom-x64-windows.zip + ArtifactName: atom-x64-windows.zip + ArtifactType: Container + displayName: Upload atom-x64-windows.zip + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/AtomSetup-x64.exe + ArtifactName: AtomSetup-x64.exe + ArtifactType: Container + displayName: Upload AtomSetup-x64.exe + condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true')) + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/atom-x64-$(ReleaseVersion)-full.nupkg + ArtifactName: atom-x64-$(ReleaseVersion)-full.nupkg + ArtifactType: Container + displayName: Upload atom-x64-$(ReleaseVersion)-full.nupkg + condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true')) + + - task: PublishBuildArtifacts@1 + inputs: + PathtoPublish: $(Build.SourcesDirectory)/out/RELEASES-x64 + ArtifactName: RELEASES-x64 + ArtifactType: Container + displayName: Upload RELEASES-x64 + condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true')) diff --git a/script/vsts/pull-requests.yml b/script/vsts/pull-requests.yml new file mode 100644 index 000000000..8f17bacff --- /dev/null +++ b/script/vsts/pull-requests.yml @@ -0,0 +1,19 @@ +trigger: none # No CI builds, only PR builds + +phases: + +- phase: GetReleaseVersion + steps: + # This has to be done separately because VSTS inexplicably + # exits the script block after `npm install` completes. + - script: | + cd script\vsts + npm install + displayName: npm install + - script: node script\vsts\get-release-version.js + name: Version + +# Import OS-specific build definitions +- template: platforms/windows.yml +- template: platforms/macos.yml +- template: platforms/linux.yml diff --git a/script/vsts/release-branch-build.yml b/script/vsts/release-branch-build.yml new file mode 100644 index 000000000..cbbf73ffb --- /dev/null +++ b/script/vsts/release-branch-build.yml @@ -0,0 +1,64 @@ +trigger: +- master +- 1.* # VSTS only supports wildcards at the end + +phases: + +- phase: GetReleaseVersion + steps: + # This has to be done separately because VSTS inexplicably + # exits the script block after `npm install` completes. + - script: | + cd script\vsts + npm install + displayName: npm install + - script: node script\vsts\get-release-version.js + name: Version + +# Import OS-specific build definitions +- template: platforms/windows.yml +- template: platforms/macos.yml +- template: platforms/linux.yml + +- phase: UploadArtifacts + queue: Hosted # Need this for Python 2.7 + + dependsOn: + - GetReleaseVersion + - Windows + - Linux + - macOS + + variables: + ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ] + IsReleaseBranch: $[ dependencies.GetReleaseVersion.outputs['Version.IsReleaseBranch'] ] + IsSignedZipBranch: $[ dependencies.GetReleaseVersion.outputs['Version.IsSignedZipBranch'] ] + + steps: + - task: NodeTool@0 + inputs: + versionSpec: 8.9.3 + displayName: Install Node.js 8.9.3 + + # This has to be done separately because VSTS inexplicably + # exits the script block after `npm install` completes. + - script: | + cd script + npm install + displayName: npm install + + - task: DownloadBuildArtifacts@0 + inputs: + itemPattern: '**' + downloadType: 'specific' + displayName: Download Release Artifacts + + - script: | + $(Build.SourcesDirectory)\script\publish-release.cmd --assets-path "$(System.ArtifactsDirectory)" --s3-path "vsts-artifacts/$(Build.BuildId)/" + env: + ATOM_RELEASE_VERSION: $(ReleaseVersion) + ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY) + ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET) + ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET) + displayName: Upload CI Artifacts to S3 + condition: and(succeeded(), eq(variables['IsSignedZipBranch'], 'true')) diff --git a/spec/atom-environment-spec.js b/spec/atom-environment-spec.js index 7dc07dafd..37f7de72c 100644 --- a/spec/atom-environment-spec.js +++ b/spec/atom-environment-spec.js @@ -1,9 +1,9 @@ const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') const _ = require('underscore-plus') +const fs = require('fs') const path = require('path') const temp = require('temp').track() const AtomEnvironment = require('../src/atom-environment') -const StorageFolder = require('../src/storage-folder') describe('AtomEnvironment', () => { afterEach(() => { @@ -471,15 +471,28 @@ describe('AtomEnvironment', () => { await atom.workspace.open() }) - it('automatically restores the saved state into the current environment', () => { - const state = {} - spyOn(atom.workspace, 'open') - spyOn(atom, 'restoreStateIntoThisEnvironment') + it('automatically restores the saved state into the current environment', async () => { + const projectPath = temp.mkdirSync() + const filePath1 = path.join(projectPath, 'file-1') + const filePath2 = path.join(projectPath, 'file-2') + const filePath3 = path.join(projectPath, 'file-3') + fs.writeFileSync(filePath1, 'abc') + fs.writeFileSync(filePath2, 'def') + fs.writeFileSync(filePath3, 'ghi') - atom.attemptRestoreProjectStateForPaths(state, [__dirname], [__filename]) - expect(atom.restoreStateIntoThisEnvironment).toHaveBeenCalledWith(state) - expect(atom.workspace.open.callCount).toBe(1) - expect(atom.workspace.open).toHaveBeenCalledWith(__filename) + const env1 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) + env1.project.setPaths([projectPath]) + await env1.workspace.open(filePath1) + await env1.workspace.open(filePath2) + await env1.workspace.open(filePath3) + const env1State = env1.serialize() + env1.destroy() + + const env2 = new AtomEnvironment({applicationDelegate: atom.applicationDelegate}) + await env2.attemptRestoreProjectStateForPaths(env1State, [projectPath], [filePath2]) + const restoredURIs = env2.workspace.getPaneItems().map(p => p.getURI()) + expect(restoredURIs).toEqual([filePath1, filePath2, filePath3]) + env2.destroy() }) describe('when a dock has a non-text editor', () => { @@ -733,29 +746,6 @@ describe('AtomEnvironment', () => { }) }) - describe('::updateAvailable(info) (called via IPC from browser process)', () => { - let subscription - - afterEach(() => { - if (subscription) subscription.dispose() - }) - - it('invokes onUpdateAvailable listeners', async () => { - if (process.platform !== 'darwin') return // Test tied to electron autoUpdater, we use something else on Linux and Win32 - - const updateAvailablePromise = new Promise(resolve => { - subscription = atom.onUpdateAvailable(resolve) - }) - - atom.listenForUpdates() - const {autoUpdater} = require('electron').remote - autoUpdater.emit('update-downloaded', null, 'notes', 'version') - - const {releaseVersion} = await updateAvailablePromise - expect(releaseVersion).toBe('version') - }) - }) - describe('::getReleaseChannel()', () => { let version diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index a522d9298..9af21a7dd 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -33,6 +33,8 @@ formatStackTrace = (spec, message='', stackTrace) -> line = line.trim() # at jasmine.Spec. (path:1:2) -> at path:1:2 .replace(/^at jasmine\.Spec\. \(([^)]+)\)/, 'at $1') + # at jasmine.Spec.it (path:1:2) -> at path:1:2 + .replace(/^at jasmine\.Spec\.f*it \(([^)]+)\)/, 'at $1') # at it (path:1:2) -> at path:1:2 .replace(/^at f*it \(([^)]+)\)/, 'at $1') # at spec/file-test.js -> at file-test.js diff --git a/spec/auto-update-manager-spec.js b/spec/auto-update-manager-spec.js index 780c9816b..ab9e0ed70 100644 --- a/spec/auto-update-manager-spec.js +++ b/spec/auto-update-manager-spec.js @@ -1,11 +1,8 @@ -'use babel' - -import AutoUpdateManager from '../src/auto-update-manager' -import {remote} from 'electron' +const AutoUpdateManager = require('../src/auto-update-manager') +const {remote} = require('electron') const electronAutoUpdater = remote.require('electron').autoUpdater describe('AutoUpdateManager (renderer)', () => { - if (process.platform !== 'darwin') return // Tests are tied to electron autoUpdater, we use something else on Linux and Win32 let autoUpdateManager diff --git a/spec/babel-spec.coffee b/spec/babel-spec.coffee index 400e5c03e..9e9195783 100644 --- a/spec/babel-spec.coffee +++ b/spec/babel-spec.coffee @@ -42,6 +42,11 @@ describe "Babel transpiler support", -> transpiled = require('./fixtures/babel/flow-comment.js') expect(transpiled(3)).toBe 4 + describe 'when a .js file starts with // @flow', -> + it "transpiles it using babel", -> + transpiled = require('./fixtures/babel/flow-slash-comment.js') + expect(transpiled(3)).toBe 4 + describe "when a .js file does not start with 'use babel';", -> it "does not transpile it using babel", -> spyOn(console, 'error') diff --git a/spec/command-installer-spec.js b/spec/command-installer-spec.js index 6a2a31e77..b303d4954 100644 --- a/spec/command-installer-spec.js +++ b/spec/command-installer-spec.js @@ -1,6 +1,7 @@ const path = require('path') const fs = require('fs-plus') const temp = require('temp').track() +const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers'); const CommandInstaller = require('../src/command-installer') describe('CommandInstaller on #darwin', () => { @@ -56,8 +57,8 @@ describe('CommandInstaller on #darwin', () => { const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm']) installer = new CommandInstaller(appDelegate) installer.initialize('2.0.2') - spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback()) - spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback()) + spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(undefined, 'atom')) + spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(undefined, 'apm')) installer.installShellCommandsInteractively() @@ -140,4 +141,41 @@ describe('CommandInstaller on #darwin', () => { }) }) }) + + describe('when using a nightly version of atom', () => { + beforeEach(() => { + installer = new CommandInstaller() + installer.initialize('2.2.0-nightly0') + }) + + it("symlinks the atom command as 'atom-nightly'", () => { + const installedAtomPath = path.join(installationPath, 'atom-nightly') + expect(fs.isFileSync(installedAtomPath)).toBeFalsy() + + waitsFor(done => { + installer.installAtomCommand(false, error => { + expect(error).toBeNull() + expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath)) + expect(fs.isExecutableSync(installedAtomPath)).toBe(true) + expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false) + done() + }) + }) + }) + + it("symlinks the apm command as 'apm-nightly'", () => { + const installedApmPath = path.join(installationPath, 'apm-nightly') + expect(fs.isFileSync(installedApmPath)).toBeFalsy() + + waitsFor(done => { + installer.installApmCommand(false, error => { + expect(error).toBeNull() + expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath)) + expect(fs.isExecutableSync(installedApmPath)).toBeTruthy() + expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe(false) + done() + }) + }) + }) + }) }) diff --git a/spec/config-file-spec.js b/spec/config-file-spec.js new file mode 100644 index 000000000..ae5e05fe6 --- /dev/null +++ b/spec/config-file-spec.js @@ -0,0 +1,121 @@ +const {it, fit, ffit, beforeEach, afterEach, conditionPromise} = require('./async-spec-helpers') +const fs = require('fs-plus') +const path = require('path') +const temp = require('temp').track() +const dedent = require('dedent') +const ConfigFile = require('../src/config-file') + +describe('ConfigFile', () => { + let filePath, configFile, subscription + + beforeEach(async () => { + jasmine.useRealClock() + const tempDir = fs.realpathSync(temp.mkdirSync()) + filePath = path.join(tempDir, 'the-config.cson') + }) + + afterEach(() => { + subscription.dispose() + }) + + describe('when the file does not exist', () => { + it('returns an empty object from .get()', async () => { + configFile = new ConfigFile(filePath) + subscription = await configFile.watch() + expect(configFile.get()).toEqual({}) + }) + }) + + describe('when the file is empty', () => { + it('returns an empty object from .get()', async () => { + writeFileSync(filePath, '') + configFile = new ConfigFile(filePath) + subscription = await configFile.watch() + expect(configFile.get()).toEqual({}) + }) + }) + + describe('when the file is updated with valid CSON', () => { + it('notifies onDidChange observers with the data', async () => { + configFile = new ConfigFile(filePath) + subscription = await configFile.watch() + + const event = new Promise(resolve => configFile.onDidChange(resolve)) + + writeFileSync(filePath, dedent ` + '*': + foo: 'bar' + + 'javascript': + foo: 'baz' + `) + + expect(await event).toEqual({ + '*': {foo: 'bar'}, + 'javascript': {foo: 'baz'} + }) + + expect(configFile.get()).toEqual({ + '*': {foo: 'bar'}, + 'javascript': {foo: 'baz'} + }) + }) + }) + + describe('when the file is updated with invalid CSON', () => { + it('notifies onDidError observers', async () => { + configFile = new ConfigFile(filePath) + subscription = await configFile.watch() + + const message = new Promise(resolve => configFile.onDidError(resolve)) + + writeFileSync(filePath, dedent ` + um what? + `, 2) + + expect(await message).toContain('Failed to load `the-config.cson`') + + const event = new Promise(resolve => configFile.onDidChange(resolve)) + + writeFileSync(filePath, dedent ` + '*': + foo: 'bar' + + 'javascript': + foo: 'baz' + `, 4) + + expect(await event).toEqual({ + '*': {foo: 'bar'}, + 'javascript': {foo: 'baz'} + }) + }) + }) + + describe('ConfigFile.at()', () => { + let path0, path1 + + beforeEach(() => { + path0 = filePath + path1 = path.join(fs.realpathSync(temp.mkdirSync()), 'the-config.cson') + + configFile = ConfigFile.at(path0) + }) + + it('returns an existing ConfigFile', () => { + const cf = ConfigFile.at(path0) + expect(cf).toEqual(configFile) + }) + + it('creates a new ConfigFile for unrecognized paths', () => { + const cf = ConfigFile.at(path1) + expect(cf).not.toEqual(configFile) + }) + }) +}) + +function writeFileSync (filePath, content, seconds = 2) { + const utime = (Date.now() / 1000) + seconds + fs.writeFileSync(filePath, content) + fs.utimesSync(filePath, utime, utime) +} diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee deleted file mode 100644 index 090bc7a29..000000000 --- a/spec/config-spec.coffee +++ /dev/null @@ -1,1807 +0,0 @@ -path = require 'path' -temp = require('temp').track() -CSON = require 'season' -fs = require 'fs-plus' - -describe "Config", -> - dotAtomPath = null - - beforeEach -> - spyOn(atom.config, "load") - spyOn(atom.config, "save") - spyOn(console, 'warn') - dotAtomPath = temp.path('atom-spec-config') - atom.config.configDirPath = dotAtomPath - atom.config.enablePersistence = true - atom.config.settingsLoaded = true - atom.config.pendingOperations = [] - atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson") - - afterEach -> - atom.config.enablePersistence = false - fs.removeSync(dotAtomPath) - - describe ".get(keyPath, {scope, sources, excludeSources})", -> - it "allows a key path's value to be read", -> - expect(atom.config.set("foo.bar.baz", 42)).toBe true - expect(atom.config.get("foo.bar.baz")).toBe 42 - expect(atom.config.get("foo.quux")).toBeUndefined() - - it "returns a deep clone of the key path's value", -> - atom.config.set('value', array: [1, b: 2, 3]) - retrievedValue = atom.config.get('value') - retrievedValue.array[0] = 4 - retrievedValue.array[1].b = 2.1 - expect(atom.config.get('value')).toEqual(array: [1, b: 2, 3]) - - it "merges defaults into the returned value if both the assigned value and the default value are objects", -> - atom.config.setDefaults("foo.bar", baz: 1, ok: 2) - atom.config.set("foo.bar", baz: 3) - expect(atom.config.get("foo.bar")).toEqual {baz: 3, ok: 2} - - atom.config.setDefaults("other", baz: 1) - atom.config.set("other", 7) - expect(atom.config.get("other")).toBe 7 - - atom.config.set("bar.baz", a: 3) - atom.config.setDefaults("bar", baz: 7) - expect(atom.config.get("bar.baz")).toEqual {a: 3} - - describe "when a 'sources' option is specified", -> - it "only retrieves values from the specified sources", -> - atom.config.set("x.y", 1, scopeSelector: ".foo", source: "a") - atom.config.set("x.y", 2, scopeSelector: ".foo", source: "b") - atom.config.set("x.y", 3, scopeSelector: ".foo", source: "c") - atom.config.setSchema("x.y", type: "integer", default: 4) - - expect(atom.config.get("x.y", sources: ["a"], scope: [".foo"])).toBe 1 - expect(atom.config.get("x.y", sources: ["b"], scope: [".foo"])).toBe 2 - expect(atom.config.get("x.y", sources: ["c"], scope: [".foo"])).toBe 3 - # Schema defaults never match a specific source. We could potentially add a special "schema" source. - expect(atom.config.get("x.y", sources: ["x"], scope: [".foo"])).toBeUndefined() - - expect(atom.config.get(null, sources: ['a'], scope: [".foo"]).x.y).toBe 1 - - describe "when an 'excludeSources' option is specified", -> - it "only retrieves values from the specified sources", -> - atom.config.set("x.y", 0) - atom.config.set("x.y", 1, scopeSelector: ".foo", source: "a") - atom.config.set("x.y", 2, scopeSelector: ".foo", source: "b") - atom.config.set("x.y", 3, scopeSelector: ".foo", source: "c") - atom.config.setSchema("x.y", type: "integer", default: 4) - - expect(atom.config.get("x.y", excludeSources: ["a"], scope: [".foo"])).toBe 3 - expect(atom.config.get("x.y", excludeSources: ["c"], scope: [".foo"])).toBe 2 - expect(atom.config.get("x.y", excludeSources: ["b", "c"], scope: [".foo"])).toBe 1 - expect(atom.config.get("x.y", excludeSources: ["b", "c", "a"], scope: [".foo"])).toBe 0 - expect(atom.config.get("x.y", excludeSources: ["b", "c", "a", atom.config.getUserConfigPath()], scope: [".foo"])).toBe 4 - expect(atom.config.get("x.y", excludeSources: [atom.config.getUserConfigPath()])).toBe 4 - - describe "when a 'scope' option is given", -> - it "returns the property with the most specific scope selector", -> - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee .string.quoted.double.coffee") - atom.config.set("foo.bar.baz", 22, scopeSelector: ".source .string.quoted.double") - atom.config.set("foo.bar.baz", 11, scopeSelector: ".source") - - expect(atom.config.get("foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"])).toBe 42 - expect(atom.config.get("foo.bar.baz", scope: [".source.js", ".string.quoted.double.js"])).toBe 22 - expect(atom.config.get("foo.bar.baz", scope: [".source.js", ".variable.assignment.js"])).toBe 11 - expect(atom.config.get("foo.bar.baz", scope: [".text"])).toBeUndefined() - - it "favors the most recently added properties in the event of a specificity tie", -> - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee .string.quoted.single") - atom.config.set("foo.bar.baz", 22, scopeSelector: ".source.coffee .string.quoted.double") - - expect(atom.config.get("foo.bar.baz", scope: [".source.coffee", ".string.quoted.single"])).toBe 42 - expect(atom.config.get("foo.bar.baz", scope: [".source.coffee", ".string.quoted.single.double"])).toBe 22 - - describe 'when there are global defaults', -> - it 'falls back to the global when there is no scoped property specified', -> - atom.config.setDefaults("foo", hasDefault: 'ok') - expect(atom.config.get("foo.hasDefault", scope: [".source.coffee", ".string.quoted.single"])).toBe 'ok' - - describe 'when package settings are added after user settings', -> - it "returns the user's setting because the user's setting has higher priority", -> - atom.config.set("foo.bar.baz", 100, scopeSelector: ".source.coffee") - atom.config.set("foo.bar.baz", 1, scopeSelector: ".source.coffee", source: "some-package") - expect(atom.config.get("foo.bar.baz", scope: [".source.coffee"])).toBe 100 - - describe "when the first component of the scope descriptor matches a legacy scope alias", -> - it "falls back to properties defined for the legacy scope if no value is found for the original scope descriptor", -> - atom.config.addLegacyScopeAlias('javascript', '.source.js') - atom.config.set('foo', 100, scopeSelector: '.source.js') - atom.config.set('foo', 200, scopeSelector: 'javascript for_statement') - - expect(atom.config.get('foo', scope: ['javascript', 'for_statement', 'identifier'])).toBe(200) - expect(atom.config.get('foo', scope: ['javascript', 'function', 'identifier'])).toBe(100) - - describe ".getAll(keyPath, {scope, sources, excludeSources})", -> - it "reads all of the values for a given key-path", -> - expect(atom.config.set("foo", 41)).toBe true - expect(atom.config.set("foo", 43, scopeSelector: ".a .b")).toBe true - expect(atom.config.set("foo", 42, scopeSelector: ".a")).toBe true - expect(atom.config.set("foo", 44, scopeSelector: ".a .b.c")).toBe true - - expect(atom.config.set("foo", -44, scopeSelector: ".d")).toBe true - - expect(atom.config.getAll("foo", scope: [".a", ".b.c"])).toEqual [ - {scopeSelector: '.a .b.c', value: 44} - {scopeSelector: '.a .b', value: 43} - {scopeSelector: '.a', value: 42} - {scopeSelector: '*', value: 41} - ] - - it "includes the schema's default value", -> - atom.config.setSchema("foo", type: 'number', default: 40) - expect(atom.config.set("foo", 43, scopeSelector: ".a .b")).toBe true - expect(atom.config.getAll("foo", scope: [".a", ".b.c"])).toEqual [ - {scopeSelector: '.a .b', value: 43} - {scopeSelector: '*', value: 40} - ] - - describe "when the first component of the scope descriptor matches a legacy scope alias", -> - it "includes the values defined for the legacy scope", -> - atom.config.addLegacyScopeAlias('javascript', '.source.js') - - expect(atom.config.set('foo', 41)).toBe true - expect(atom.config.set('foo', 42, scopeSelector: 'javascript')).toBe true - expect(atom.config.set('foo', 43, scopeSelector: '.source.js')).toBe true - - expect(atom.config.getAll('foo', scope: ['javascript'])).toEqual([ - {scopeSelector: 'javascript', value: 42}, - {scopeSelector: '.js.source', value: 43}, - {scopeSelector: '*', value: 41} - ]) - - describe ".set(keyPath, value, {source, scopeSelector})", -> - it "allows a key path's value to be written", -> - expect(atom.config.set("foo.bar.baz", 42)).toBe true - expect(atom.config.get("foo.bar.baz")).toBe 42 - - it "saves the user's config to disk after it stops changing", -> - atom.config.set("foo.bar.baz", 42) - advanceClock(50) - expect(atom.config.save).not.toHaveBeenCalled() - atom.config.set("foo.bar.baz", 43) - advanceClock(50) - expect(atom.config.save).not.toHaveBeenCalled() - atom.config.set("foo.bar.baz", 44) - advanceClock(150) - expect(atom.config.save).toHaveBeenCalled() - - it "does not save when a non-default 'source' is given", -> - atom.config.set("foo.bar.baz", 42, source: 'some-other-source', scopeSelector: '.a') - advanceClock(500) - expect(atom.config.save).not.toHaveBeenCalled() - - it "does not allow a 'source' option without a 'scopeSelector'", -> - expect(-> atom.config.set("foo", 1, source: [".source.ruby"])).toThrow() - - describe "when the key-path is null", -> - it "sets the root object", -> - expect(atom.config.set(null, editor: tabLength: 6)).toBe true - expect(atom.config.get("editor.tabLength")).toBe 6 - expect(atom.config.set(null, editor: tabLength: 8, scopeSelector: ['.source.js'])).toBe true - expect(atom.config.get("editor.tabLength", scope: ['.source.js'])).toBe 8 - - describe "when the value equals the default value", -> - it "does not store the value in the user's config", -> - atom.config.setSchema "foo", - type: 'object' - properties: - same: - type: 'number' - default: 1 - changes: - type: 'number' - default: 1 - sameArray: - type: 'array' - default: [1, 2, 3] - sameObject: - type: 'object' - default: {a: 1, b: 2} - null: - type: '*' - default: null - undefined: - type: '*' - default: undefined - expect(atom.config.settings.foo).toBeUndefined() - - atom.config.set('foo.same', 1) - atom.config.set('foo.changes', 2) - atom.config.set('foo.sameArray', [1, 2, 3]) - atom.config.set('foo.null', undefined) - atom.config.set('foo.undefined', null) - atom.config.set('foo.sameObject', {b: 2, a: 1}) - - userConfigPath = atom.config.getUserConfigPath() - - expect(atom.config.get("foo.same", sources: [userConfigPath])).toBeUndefined() - - expect(atom.config.get("foo.changes")).toBe 2 - expect(atom.config.get("foo.changes", sources: [userConfigPath])).toBe 2 - - atom.config.set('foo.changes', 1) - expect(atom.config.get("foo.changes", sources: [userConfigPath])).toBeUndefined() - - describe "when a 'scopeSelector' is given", -> - it "sets the value and overrides the others", -> - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee .string.quoted.double.coffee") - atom.config.set("foo.bar.baz", 22, scopeSelector: ".source .string.quoted.double") - atom.config.set("foo.bar.baz", 11, scopeSelector: ".source") - - expect(atom.config.get("foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"])).toBe 42 - - expect(atom.config.set("foo.bar.baz", 100, scopeSelector: ".source.coffee .string.quoted.double.coffee")).toBe true - expect(atom.config.get("foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"])).toBe 100 - - describe ".unset(keyPath, {source, scopeSelector})", -> - beforeEach -> - atom.config.setSchema 'foo', - type: 'object' - properties: - bar: - type: 'object' - properties: - baz: - type: 'integer' - default: 0 - ok: - type: 'integer' - default: 0 - quux: - type: 'integer' - default: 0 - - it "sets the value of the key path to its default", -> - atom.config.setDefaults('a', b: 3) - atom.config.set('a.b', 4) - expect(atom.config.get('a.b')).toBe 4 - atom.config.unset('a.b') - expect(atom.config.get('a.b')).toBe 3 - - atom.config.set('a.c', 5) - expect(atom.config.get('a.c')).toBe 5 - atom.config.unset('a.c') - expect(atom.config.get('a.c')).toBeUndefined() - - it "calls ::save()", -> - atom.config.setDefaults('a', b: 3) - atom.config.set('a.b', 4) - atom.config.save.reset() - - atom.config.unset('a.c') - advanceClock(500) - expect(atom.config.save.callCount).toBe 1 - - describe "when no 'scopeSelector' is given", -> - describe "when a 'source' but no key-path is given", -> - it "removes all scoped settings with the given source", -> - atom.config.set("foo.bar.baz", 1, scopeSelector: ".a", source: "source-a") - atom.config.set("foo.bar.quux", 2, scopeSelector: ".b", source: "source-a") - expect(atom.config.get("foo.bar", scope: [".a.b"])).toEqual(baz: 1, quux: 2) - - atom.config.unset(null, source: "source-a") - expect(atom.config.get("foo.bar", scope: [".a"])).toEqual(baz: 0, ok: 0) - - describe "when a 'source' and a key-path is given", -> - it "removes all scoped settings with the given source and key-path", -> - atom.config.set("foo.bar.baz", 1) - atom.config.set("foo.bar.baz", 2, scopeSelector: ".a", source: "source-a") - atom.config.set("foo.bar.baz", 3, scopeSelector: ".a.b", source: "source-b") - expect(atom.config.get("foo.bar.baz", scope: [".a.b"])).toEqual(3) - - atom.config.unset("foo.bar.baz", source: "source-b") - expect(atom.config.get("foo.bar.baz", scope: [".a.b"])).toEqual(2) - expect(atom.config.get("foo.bar.baz")).toEqual(1) - - describe "when no 'source' is given", -> - it "removes all scoped and unscoped properties for that key-path", -> - atom.config.setDefaults("foo.bar", baz: 100) - - atom.config.set("foo.bar", {baz: 1, ok: 2}, scopeSelector: ".a") - atom.config.set("foo.bar", {baz: 11, ok: 12}, scopeSelector: ".b") - atom.config.set("foo.bar", {baz: 21, ok: 22}) - - atom.config.unset("foo.bar.baz") - - expect(atom.config.get("foo.bar.baz", scope: [".a"])).toBe 100 - expect(atom.config.get("foo.bar.baz", scope: [".b"])).toBe 100 - expect(atom.config.get("foo.bar.baz")).toBe 100 - - expect(atom.config.get("foo.bar.ok", scope: [".a"])).toBe 2 - expect(atom.config.get("foo.bar.ok", scope: [".b"])).toBe 12 - expect(atom.config.get("foo.bar.ok")).toBe 22 - - describe "when a 'scopeSelector' is given", -> - it "restores the global default when no scoped default set", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55 - - atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 10 - - it "restores the scoped default when a scoped default is set", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee", source: "some-source") - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - atom.config.set('foo.bar.ok', 100, scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55 - - atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 42 - expect(atom.config.get('foo.bar.ok', scope: ['.source.coffee'])).toBe 100 - - it "calls ::save()", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - atom.config.save.reset() - - atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee') - advanceClock(150) - expect(atom.config.save.callCount).toBe 1 - - it "allows removing settings for a specific source and scope selector", -> - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee', source: "source-a") - atom.config.set('foo.bar.baz', 65, scopeSelector: '.source.coffee', source: "source-b") - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 65 - - atom.config.unset('foo.bar.baz', source: "source-b", scopeSelector: ".source.coffee") - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee', '.string'])).toBe 55 - - it "allows removing all settings for a specific source", -> - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee', source: "source-a") - atom.config.set('foo.bar.baz', 65, scopeSelector: '.source.coffee', source: "source-b") - atom.config.set('foo.bar.ok', 65, scopeSelector: '.source.coffee', source: "source-b") - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 65 - - atom.config.unset(null, source: "source-b", scopeSelector: ".source.coffee") - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee', '.string'])).toBe 55 - expect(atom.config.get('foo.bar.ok', scope: ['.source.coffee', '.string'])).toBe 0 - - it "does not call ::save or add a scoped property when no value has been set", -> - # see https://github.com/atom/atom/issues/4175 - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 10 - - expect(atom.config.save).not.toHaveBeenCalled() - - scopedProperties = atom.config.scopedSettingsStore.propertiesForSource('user-config') - expect(scopedProperties['.coffee.source']).toBeUndefined() - - it "removes the scoped value when it was the only set value on the object", -> - spyOn(CSON, 'writeFileSync') - atom.config.save.andCallThrough() - - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - atom.config.set('foo.bar.ok', 20, scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55 - - advanceClock(150) - CSON.writeFileSync.reset() - - atom.config.unset('foo.bar.baz', scopeSelector: '.source.coffee') - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 10 - expect(atom.config.get('foo.bar.ok', scope: ['.source.coffee'])).toBe 20 - - advanceClock(150) - expect(CSON.writeFileSync).toHaveBeenCalled() - properties = CSON.writeFileSync.mostRecentCall.args[1] - expect(properties['.coffee.source']).toEqual - foo: - bar: - ok: 20 - CSON.writeFileSync.reset() - - atom.config.unset('foo.bar.ok', scopeSelector: '.source.coffee') - - advanceClock(150) - expect(CSON.writeFileSync).toHaveBeenCalled() - properties = CSON.writeFileSync.mostRecentCall.args[1] - expect(properties['.coffee.source']).toBeUndefined() - - it "does not call ::save when the value is already at the default", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set('foo.bar.baz', 55) - atom.config.save.reset() - - atom.config.unset('foo.bar.ok', scopeSelector: '.source.coffee') - expect(atom.config.save).not.toHaveBeenCalled() - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55 - - describe ".onDidChange(keyPath, {scope})", -> - [observeHandler, observeSubscription] = [] - - describe 'when a keyPath is specified', -> - beforeEach -> - observeHandler = jasmine.createSpy("observeHandler") - atom.config.set("foo.bar.baz", "value 1") - observeSubscription = atom.config.onDidChange "foo.bar.baz", observeHandler - - it "does not fire the given callback with the current value at the keypath", -> - expect(observeHandler).not.toHaveBeenCalled() - - it "fires the callback every time the observed value changes", -> - atom.config.set('foo.bar.baz', "value 2") - expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 2', oldValue: 'value 1'}) - observeHandler.reset() - - observeHandler.andCallFake -> throw new Error("oops") - expect(-> atom.config.set('foo.bar.baz', "value 1")).toThrow("oops") - expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 1', oldValue: 'value 2'}) - observeHandler.reset() - - # Regression: exception in earlier handler shouldn't put observer - # into a bad state. - atom.config.set('something.else', "new value") - expect(observeHandler).not.toHaveBeenCalled() - - describe 'when a keyPath is not specified', -> - beforeEach -> - observeHandler = jasmine.createSpy("observeHandler") - atom.config.set("foo.bar.baz", "value 1") - observeSubscription = atom.config.onDidChange observeHandler - - it "does not fire the given callback initially", -> - expect(observeHandler).not.toHaveBeenCalled() - - it "fires the callback every time any value changes", -> - observeHandler.reset() # clear the initial call - atom.config.set('foo.bar.baz', "value 2") - expect(observeHandler).toHaveBeenCalled() - expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe("value 2") - expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe("value 1") - - observeHandler.reset() - atom.config.set('foo.bar.baz', "value 1") - expect(observeHandler).toHaveBeenCalled() - expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe("value 1") - expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe("value 2") - - observeHandler.reset() - atom.config.set('foo.bar.int', 1) - expect(observeHandler).toHaveBeenCalled() - expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.int).toBe(1) - expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.int).toBe(undefined) - - describe "when a 'scope' is given", -> - it 'calls the supplied callback when the value at the descriptor/keypath changes', -> - changeSpy = jasmine.createSpy('onDidChange callback') - atom.config.onDidChange "foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"], changeSpy - - atom.config.set("foo.bar.baz", 12) - expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12}) - changeSpy.reset() - - atom.config.set("foo.bar.baz", 22, scopeSelector: ".source .string.quoted.double", source: "a") - expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: 22}) - changeSpy.reset() - - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee .string.quoted.double.coffee", source: "b") - expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 42}) - changeSpy.reset() - - atom.config.unset(null, scopeSelector: ".source.coffee .string.quoted.double.coffee", source: "b") - expect(changeSpy).toHaveBeenCalledWith({oldValue: 42, newValue: 22}) - changeSpy.reset() - - atom.config.unset(null, scopeSelector: ".source .string.quoted.double", source: "a") - expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 12}) - changeSpy.reset() - - atom.config.set("foo.bar.baz", undefined) - expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: undefined}) - changeSpy.reset() - - describe ".observe(keyPath, {scope})", -> - [observeHandler, observeSubscription] = [] - - beforeEach -> - observeHandler = jasmine.createSpy("observeHandler") - atom.config.set("foo.bar.baz", "value 1") - observeSubscription = atom.config.observe("foo.bar.baz", observeHandler) - - it "fires the given callback with the current value at the keypath", -> - expect(observeHandler).toHaveBeenCalledWith("value 1") - - it "fires the callback every time the observed value changes", -> - observeHandler.reset() # clear the initial call - atom.config.set('foo.bar.baz', "value 2") - expect(observeHandler).toHaveBeenCalledWith("value 2") - - observeHandler.reset() - atom.config.set('foo.bar.baz', "value 1") - expect(observeHandler).toHaveBeenCalledWith("value 1") - advanceClock(100) # complete pending save that was requested in ::set - - observeHandler.reset() - atom.config.loadUserConfig() - expect(observeHandler).toHaveBeenCalledWith(undefined) - - it "fires the callback when the observed value is deleted", -> - observeHandler.reset() # clear the initial call - atom.config.set('foo.bar.baz', undefined) - expect(observeHandler).toHaveBeenCalledWith(undefined) - - it "fires the callback when the full key path goes into and out of existence", -> - observeHandler.reset() # clear the initial call - atom.config.set("foo.bar", undefined) - expect(observeHandler).toHaveBeenCalledWith(undefined) - - observeHandler.reset() - atom.config.set("foo.bar.baz", "i'm back") - expect(observeHandler).toHaveBeenCalledWith("i'm back") - - it "does not fire the callback once the subscription is disposed", -> - observeHandler.reset() # clear the initial call - observeSubscription.dispose() - atom.config.set('foo.bar.baz', "value 2") - expect(observeHandler).not.toHaveBeenCalled() - - it 'does not fire the callback for a similarly named keyPath', -> - bazCatHandler = jasmine.createSpy("bazCatHandler") - observeSubscription = atom.config.observe "foo.bar.bazCat", bazCatHandler - - bazCatHandler.reset() - atom.config.set('foo.bar.baz', "value 10") - expect(bazCatHandler).not.toHaveBeenCalled() - - describe "when a 'scope' is given", -> - otherHandler = null - - beforeEach -> - observeSubscription.dispose() - otherHandler = jasmine.createSpy('otherHandler') - - it "allows settings to be observed in a specific scope", -> - atom.config.observe("foo.bar.baz", scope: [".some.scope"], observeHandler) - atom.config.observe("foo.bar.baz", scope: [".another.scope"], otherHandler) - - atom.config.set('foo.bar.baz', "value 2", scopeSelector: ".some") - expect(observeHandler).toHaveBeenCalledWith("value 2") - expect(otherHandler).not.toHaveBeenCalledWith("value 2") - - it 'calls the callback when properties with more specific selectors are removed', -> - changeSpy = jasmine.createSpy() - atom.config.observe("foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"], changeSpy) - expect(changeSpy).toHaveBeenCalledWith("value 1") - changeSpy.reset() - - atom.config.set("foo.bar.baz", 12) - expect(changeSpy).toHaveBeenCalledWith(12) - changeSpy.reset() - - atom.config.set("foo.bar.baz", 22, scopeSelector: ".source .string.quoted.double", source: "a") - expect(changeSpy).toHaveBeenCalledWith(22) - changeSpy.reset() - - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee .string.quoted.double.coffee", source: "b") - expect(changeSpy).toHaveBeenCalledWith(42) - changeSpy.reset() - - atom.config.unset(null, scopeSelector: ".source.coffee .string.quoted.double.coffee", source: "b") - expect(changeSpy).toHaveBeenCalledWith(22) - changeSpy.reset() - - atom.config.unset(null, scopeSelector: ".source .string.quoted.double", source: "a") - expect(changeSpy).toHaveBeenCalledWith(12) - changeSpy.reset() - - atom.config.set("foo.bar.baz", undefined) - expect(changeSpy).toHaveBeenCalledWith(undefined) - changeSpy.reset() - - describe ".transact(callback)", -> - changeSpy = null - - beforeEach -> - changeSpy = jasmine.createSpy('onDidChange callback') - atom.config.onDidChange("foo.bar.baz", changeSpy) - - it "allows only one change event for the duration of the given callback", -> - atom.config.transact -> - atom.config.set("foo.bar.baz", 1) - atom.config.set("foo.bar.baz", 2) - atom.config.set("foo.bar.baz", 3) - - expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) - - it "does not emit an event if no changes occur while paused", -> - atom.config.transact -> - expect(changeSpy).not.toHaveBeenCalled() - - describe ".transactAsync(callback)", -> - changeSpy = null - - beforeEach -> - changeSpy = jasmine.createSpy('onDidChange callback') - atom.config.onDidChange("foo.bar.baz", changeSpy) - - it "allows only one change event for the duration of the given promise if it gets resolved", -> - promiseResult = null - transactionPromise = atom.config.transactAsync -> - atom.config.set("foo.bar.baz", 1) - atom.config.set("foo.bar.baz", 2) - atom.config.set("foo.bar.baz", 3) - Promise.resolve("a result") - - waitsForPromise -> transactionPromise.then (r) -> promiseResult = r - - runs -> - expect(promiseResult).toBe("a result") - expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) - - it "allows only one change event for the duration of the given promise if it gets rejected", -> - promiseError = null - transactionPromise = atom.config.transactAsync -> - atom.config.set("foo.bar.baz", 1) - atom.config.set("foo.bar.baz", 2) - atom.config.set("foo.bar.baz", 3) - Promise.reject("an error") - - waitsForPromise -> transactionPromise.catch (e) -> promiseError = e - - runs -> - expect(promiseError).toBe("an error") - expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) - - it "allows only one change event even when the given callback throws", -> - error = new Error("Oops!") - promiseError = null - transactionPromise = atom.config.transactAsync -> - atom.config.set("foo.bar.baz", 1) - atom.config.set("foo.bar.baz", 2) - atom.config.set("foo.bar.baz", 3) - throw error - - waitsForPromise -> transactionPromise.catch (e) -> promiseError = e - - runs -> - expect(promiseError).toBe(error) - expect(changeSpy.callCount).toBe(1) - expect(changeSpy.argsForCall[0][0]).toEqual(newValue: 3, oldValue: undefined) - - describe ".getSources()", -> - it "returns an array of all of the config's source names", -> - expect(atom.config.getSources()).toEqual([]) - - atom.config.set("a.b", 1, scopeSelector: ".x1", source: "source-1") - atom.config.set("a.c", 1, scopeSelector: ".x1", source: "source-1") - atom.config.set("a.b", 2, scopeSelector: ".x2", source: "source-2") - atom.config.set("a.b", 1, scopeSelector: ".x3", source: "source-3") - - expect(atom.config.getSources()).toEqual([ - "source-1" - "source-2" - "source-3" - ]) - - describe "Internal Methods", -> - describe ".save()", -> - CSON = require 'season' - - beforeEach -> - spyOn(CSON, 'writeFileSync') - jasmine.unspy atom.config, 'save' - - describe "when ~/.atom/config.json exists", -> - it "writes any non-default properties to ~/.atom/config.json", -> - atom.config.set("a.b.c", 1) - atom.config.set("a.b.d", 2) - atom.config.set("x.y.z", 3) - atom.config.setDefaults("a.b", e: 4, f: 5) - - CSON.writeFileSync.reset() - atom.config.save() - - expect(CSON.writeFileSync.argsForCall[0][0]).toBe atom.config.configFilePath - writtenConfig = CSON.writeFileSync.argsForCall[0][1] - expect(writtenConfig).toEqual '*': atom.config.settings - - it 'writes properties in alphabetical order', -> - atom.config.set('foo', 1) - atom.config.set('bar', 2) - atom.config.set('baz.foo', 3) - atom.config.set('baz.bar', 4) - - CSON.writeFileSync.reset() - atom.config.save() - - expect(CSON.writeFileSync.argsForCall[0][0]).toBe atom.config.configFilePath - writtenConfig = CSON.writeFileSync.argsForCall[0][1] - expect(writtenConfig).toEqual '*': atom.config.settings - - expectedKeys = ['bar', 'baz', 'foo'] - foundKeys = (key for key of writtenConfig['*'] when key in expectedKeys) - expect(foundKeys).toEqual expectedKeys - expectedKeys = ['bar', 'foo'] - foundKeys = (key for key of writtenConfig['*']['baz'] when key in expectedKeys) - expect(foundKeys).toEqual expectedKeys - - describe "when ~/.atom/config.json doesn't exist", -> - it "writes any non-default properties to ~/.atom/config.cson", -> - atom.config.set("a.b.c", 1) - atom.config.set("a.b.d", 2) - atom.config.set("x.y.z", 3) - atom.config.setDefaults("a.b", e: 4, f: 5) - - CSON.writeFileSync.reset() - atom.config.save() - - expect(CSON.writeFileSync.argsForCall[0][0]).toBe path.join(atom.config.configDirPath, "atom.config.cson") - writtenConfig = CSON.writeFileSync.argsForCall[0][1] - expect(writtenConfig).toEqual '*': atom.config.settings - - describe "when scoped settings are defined", -> - it 'writes out explicitly set config settings', -> - atom.config.set('foo.bar', 'ruby', scopeSelector: '.source.ruby') - atom.config.set('foo.omg', 'wow', scopeSelector: '.source.ruby') - atom.config.set('foo.bar', 'coffee', scopeSelector: '.source.coffee') - - CSON.writeFileSync.reset() - atom.config.save() - - writtenConfig = CSON.writeFileSync.argsForCall[0][1] - expect(writtenConfig).toEqualJson - '*': - atom.config.settings - '.ruby.source': - foo: - bar: 'ruby' - omg: 'wow' - '.coffee.source': - foo: - bar: 'coffee' - - describe "when an error is thrown writing the file to disk", -> - addErrorHandler = null - beforeEach -> - atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() - - it "creates a notification", -> - jasmine.unspy CSON, 'writeFileSync' - spyOn(CSON, 'writeFileSync').andCallFake -> - error = new Error() - error.code = 'EPERM' - error.path = atom.config.getUserConfigPath() - throw error - - save = -> atom.config.save() - expect(save).not.toThrow() - expect(addErrorHandler.callCount).toBe 1 - - describe ".loadUserConfig()", -> - beforeEach -> - expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy() - atom.config.setSchema 'foo', - type: 'object' - properties: - bar: - type: 'string' - default: 'def' - int: - type: 'integer' - default: 12 - - afterEach -> - fs.removeSync(dotAtomPath) - - describe "when the config file contains scoped settings", -> - beforeEach -> - fs.writeFileSync atom.config.configFilePath, """ - '*': - foo: - bar: 'baz' - - '.source.ruby': - foo: - bar: 'more-specific' - """ - atom.config.loadUserConfig() - - it "updates the config data based on the file contents", -> - expect(atom.config.get("foo.bar")).toBe 'baz' - expect(atom.config.get("foo.bar", scope: ['.source.ruby'])).toBe 'more-specific' - - describe "when the config file does not conform to the schema", -> - beforeEach -> - fs.writeFileSync atom.config.configFilePath, """ - '*': - foo: - bar: 'omg' - int: 'baz' - '.source.ruby': - foo: - bar: 'scoped' - int: 'nope' - """ - - it "validates and does not load the incorrect values", -> - atom.config.loadUserConfig() - expect(atom.config.get("foo.int")).toBe 12 - expect(atom.config.get("foo.bar")).toBe 'omg' - expect(atom.config.get("foo.int", scope: ['.source.ruby'])).toBe 12 - expect(atom.config.get("foo.bar", scope: ['.source.ruby'])).toBe 'scoped' - - describe "when the config file contains valid cson", -> - beforeEach -> - fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'") - - it "updates the config data based on the file contents", -> - atom.config.loadUserConfig() - expect(atom.config.get("foo.bar")).toBe 'baz' - - it "notifies observers for updated keypaths on load", -> - observeHandler = jasmine.createSpy("observeHandler") - observeSubscription = atom.config.observe "foo.bar", observeHandler - - atom.config.loadUserConfig() - - expect(observeHandler).toHaveBeenCalledWith 'baz' - - describe "when the config file contains invalid cson", -> - addErrorHandler = null - beforeEach -> - atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() - fs.writeFileSync(atom.config.configFilePath, "{{{{{") - - it "logs an error to the console and does not overwrite the config file on a subsequent save", -> - atom.config.loadUserConfig() - expect(addErrorHandler.callCount).toBe 1 - atom.config.set("hair", "blonde") # trigger a save - expect(atom.config.save).not.toHaveBeenCalled() - - describe "when the config file does not exist", -> - it "creates it with an empty object", -> - fs.makeTreeSync(atom.config.configDirPath) - atom.config.loadUserConfig() - expect(fs.existsSync(atom.config.configFilePath)).toBe true - expect(CSON.readFileSync(atom.config.configFilePath)).toEqual {} - - describe "when the config file contains values that do not adhere to the schema", -> - beforeEach -> - fs.writeFileSync atom.config.configFilePath, """ - foo: - bar: 'baz' - int: 'bad value' - """ - atom.config.loadUserConfig() - - it "updates the only the settings that have values matching the schema", -> - expect(atom.config.get("foo.bar")).toBe 'baz' - expect(atom.config.get("foo.int")).toBe 12 - - expect(console.warn).toHaveBeenCalled() - expect(console.warn.mostRecentCall.args[0]).toContain "foo.int" - - describe "when there is a pending save", -> - it "does not change the config settings", -> - fs.writeFileSync atom.config.configFilePath, "'*': foo: bar: 'baz'" - - atom.config.set("foo.bar", "quux") - atom.config.loadUserConfig() - expect(atom.config.get("foo.bar")).toBe "quux" - - advanceClock(100) - expect(atom.config.save.callCount).toBe 1 - - expect(atom.config.get("foo.bar")).toBe "quux" - atom.config.loadUserConfig() - expect(atom.config.get("foo.bar")).toBe "baz" - - describe "when the config file fails to load", -> - addErrorHandler = null - - beforeEach -> - atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() - spyOn(fs, "makeTreeSync").andCallFake -> - error = new Error() - error.code = 'EPERM' - throw error - - it "creates a notification and does not try to save later changes to disk", -> - load = -> atom.config.loadUserConfig() - expect(load).not.toThrow() - expect(addErrorHandler.callCount).toBe 1 - - atom.config.set("foo.bar", "baz") - advanceClock(100) - expect(atom.config.save).not.toHaveBeenCalled() - expect(atom.config.get("foo.bar")).toBe "baz" - - describe ".observeUserConfig()", -> - updatedHandler = null - - writeConfigFile = (data, secondsInFuture = 0) -> - fs.writeFileSync(atom.config.configFilePath, data) - - future = (Date.now() / 1000) + secondsInFuture - fs.utimesSync(atom.config.configFilePath, future, future) - - beforeEach -> - jasmine.useRealClock() - - atom.config.setSchema 'foo', - type: 'object' - properties: - bar: - type: 'string' - default: 'def' - baz: - type: 'string' - scoped: - type: 'boolean' - int: - type: 'integer' - default: 12 - - expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy() - writeConfigFile """ - '*': - foo: - bar: 'baz' - scoped: false - '.source.ruby': - foo: - scoped: true - """ - atom.config.loadUserConfig() - - waitsForPromise -> atom.config.observeUserConfig() - - runs -> - updatedHandler = jasmine.createSpy "updatedHandler" - atom.config.onDidChange updatedHandler - - afterEach -> - atom.config.unobserveUserConfig() - fs.removeSync(dotAtomPath) - - describe "when the config file changes to contain valid cson", -> - - it "updates the config data", -> - writeConfigFile "foo: { bar: 'quux', baz: 'bar'}", 2 - - waitsFor 'update event', -> updatedHandler.callCount > 0 - - runs -> - expect(atom.config.get('foo.bar')).toBe 'quux' - expect(atom.config.get('foo.baz')).toBe 'bar' - - it "does not fire a change event for paths that did not change", -> - atom.config.onDidChange 'foo.bar', noChangeSpy = jasmine.createSpy "unchanged" - - writeConfigFile "foo: { bar: 'baz', baz: 'ok'}", 2 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - runs -> - expect(noChangeSpy).not.toHaveBeenCalled() - expect(atom.config.get('foo.bar')).toBe 'baz' - expect(atom.config.get('foo.baz')).toBe 'ok' - - describe "when the default value is a complex value", -> - beforeEach -> - atom.config.setSchema 'foo.bar', - type: 'array' - items: - type: 'string' - - updatedHandler.reset() - writeConfigFile "foo: { bar: ['baz', 'ok']}", 4 - waitsFor 'update event', -> updatedHandler.callCount > 0 - runs -> updatedHandler.reset() - - it "does not fire a change event for paths that did not change", -> - noChangeSpy = jasmine.createSpy "unchanged" - atom.config.onDidChange('foo.bar', noChangeSpy) - - writeConfigFile "foo: { bar: ['baz', 'ok'], baz: 'another'}", 2 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - runs -> - expect(noChangeSpy).not.toHaveBeenCalled() - expect(atom.config.get('foo.bar')).toEqual ['baz', 'ok'] - expect(atom.config.get('foo.baz')).toBe 'another' - - describe "when scoped settings are used", -> - it "fires a change event for scoped settings that are removed", -> - scopedSpy = jasmine.createSpy() - atom.config.onDidChange('foo.scoped', scope: ['.source.ruby'], scopedSpy) - - writeConfigFile """ - '*': - foo: - scoped: false - """, 2 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - runs -> - expect(scopedSpy).toHaveBeenCalled() - expect(atom.config.get('foo.scoped', scope: ['.source.ruby'])).toBe false - - it "does not fire a change event for paths that did not change", -> - noChangeSpy = jasmine.createSpy "no change" - atom.config.onDidChange('foo.scoped', scope: ['.source.ruby'], noChangeSpy) - - writeConfigFile """ - '*': - foo: - bar: 'baz' - '.source.ruby': - foo: - scoped: true - """, 2 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - runs -> - expect(noChangeSpy).not.toHaveBeenCalled() - expect(atom.config.get('foo.bar', scope: ['.source.ruby'])).toBe 'baz' - expect(atom.config.get('foo.scoped', scope: ['.source.ruby'])).toBe true - - describe "when the config file changes to omit a setting with a default", -> - it "resets the setting back to the default", -> - writeConfigFile "foo: { baz: 'new'}", 2 - waitsFor 'update event', -> updatedHandler.callCount > 0 - runs -> - expect(atom.config.get('foo.bar')).toBe 'def' - expect(atom.config.get('foo.baz')).toBe 'new' - - describe "when the config file changes to be empty", -> - beforeEach -> - updatedHandler.reset() - writeConfigFile "", 4 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - it "resets all settings back to the defaults", -> - expect(updatedHandler.callCount).toBe 1 - expect(atom.config.get('foo.bar')).toBe 'def' - atom.config.set("hair", "blonde") # trigger a save - waitsFor 'save', -> atom.config.save.callCount > 0 - - describe "when the config file subsequently changes again to contain configuration", -> - beforeEach -> - updatedHandler.reset() - writeConfigFile "foo: bar: 'newVal'", 2 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - it "sets the setting to the value specified in the config file", -> - expect(atom.config.get('foo.bar')).toBe 'newVal' - - describe "when the config file changes to contain invalid cson", -> - addErrorHandler = null - beforeEach -> - atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy "error handler" - writeConfigFile "}}}", 4 - waitsFor "error to be logged", -> addErrorHandler.callCount > 0 - - it "logs a warning and does not update config data", -> - expect(updatedHandler.callCount).toBe 0 - expect(atom.config.get('foo.bar')).toBe 'baz' - - atom.config.set("hair", "blonde") # trigger a save - expect(atom.config.save).not.toHaveBeenCalled() - - describe "when the config file subsequently changes again to contain valid cson", -> - beforeEach -> - updatedHandler.reset() - writeConfigFile "foo: bar: 'newVal'", 6 - waitsFor 'update event', -> updatedHandler.callCount > 0 - - it "updates the config data and resumes saving", -> - atom.config.set("hair", "blonde") - waitsFor 'save', -> atom.config.save.callCount > 0 - - describe ".initializeConfigDirectory()", -> - beforeEach -> - if fs.existsSync(dotAtomPath) - fs.removeSync(dotAtomPath) - - atom.config.configDirPath = dotAtomPath - - afterEach -> - fs.removeSync(dotAtomPath) - - describe "when the configDirPath doesn't exist", -> - it "copies the contents of dot-atom to ~/.atom", -> - return if process.platform is 'win32' # Flakey test on Win32 - initializationDone = false - jasmine.unspy(window, "setTimeout") - atom.config.initializeConfigDirectory -> - initializationDone = true - - waitsFor -> initializationDone - - runs -> - expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy() - expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy() - expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy() - expect(fs.isFileSync(path.join(atom.config.configDirPath, 'init.coffee'))).toBeTruthy() - expect(fs.isFileSync(path.join(atom.config.configDirPath, 'styles.less'))).toBeTruthy() - - describe ".pushAtKeyPath(keyPath, value)", -> - it "pushes the given value to the array at the key path and updates observers", -> - atom.config.set("foo.bar.baz", ["a"]) - observeHandler = jasmine.createSpy "observeHandler" - atom.config.observe "foo.bar.baz", observeHandler - observeHandler.reset() - - expect(atom.config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2 - expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"] - expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz") - - describe ".unshiftAtKeyPath(keyPath, value)", -> - it "unshifts the given value to the array at the key path and updates observers", -> - atom.config.set("foo.bar.baz", ["b"]) - observeHandler = jasmine.createSpy "observeHandler" - atom.config.observe "foo.bar.baz", observeHandler - observeHandler.reset() - - expect(atom.config.unshiftAtKeyPath("foo.bar.baz", "a")).toBe 2 - expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"] - expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz") - - describe ".removeAtKeyPath(keyPath, value)", -> - it "removes the given value from the array at the key path and updates observers", -> - atom.config.set("foo.bar.baz", ["a", "b", "c"]) - observeHandler = jasmine.createSpy "observeHandler" - atom.config.observe "foo.bar.baz", observeHandler - observeHandler.reset() - - expect(atom.config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"] - expect(atom.config.get("foo.bar.baz")).toEqual ["a", "c"] - expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz") - - describe ".setDefaults(keyPath, defaults)", -> - it "assigns any previously-unassigned keys to the object at the key path", -> - atom.config.set("foo.bar.baz", a: 1) - atom.config.setDefaults("foo.bar.baz", a: 2, b: 3, c: 4) - expect(atom.config.get("foo.bar.baz.a")).toBe 1 - expect(atom.config.get("foo.bar.baz.b")).toBe 3 - expect(atom.config.get("foo.bar.baz.c")).toBe 4 - - atom.config.setDefaults("foo.quux", x: 0, y: 1) - expect(atom.config.get("foo.quux.x")).toBe 0 - expect(atom.config.get("foo.quux.y")).toBe 1 - - it "emits an updated event", -> - updatedCallback = jasmine.createSpy('updated') - atom.config.onDidChange('foo.bar.baz.a', updatedCallback) - expect(updatedCallback.callCount).toBe 0 - atom.config.setDefaults("foo.bar.baz", a: 2) - expect(updatedCallback.callCount).toBe 1 - - describe ".setSchema(keyPath, schema)", -> - it 'creates a properly nested schema', -> - schema = - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - - atom.config.setSchema('foo.bar', schema) - - expect(atom.config.getSchema('foo')).toEqual - type: 'object' - properties: - bar: - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - - it 'sets defaults specified by the schema', -> - schema = - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - anObject: - type: 'object' - properties: - nestedInt: - type: 'integer' - default: 24 - nestedObject: - type: 'object' - properties: - superNestedInt: - type: 'integer' - default: 36 - - atom.config.setSchema('foo.bar', schema) - expect(atom.config.get("foo.bar.anInt")).toBe 12 - expect(atom.config.get("foo.bar.anObject")).toEqual - nestedInt: 24 - nestedObject: - superNestedInt: 36 - - expect(atom.config.get("foo")).toEqual { - bar: - anInt: 12 - anObject: - nestedInt: 24 - nestedObject: - superNestedInt: 36 - } - atom.config.set("foo.bar.anObject.nestedObject.superNestedInt", 37) - expect(atom.config.get("foo")).toEqual { - bar: - anInt: 12 - anObject: - nestedInt: 24 - nestedObject: - superNestedInt: 37 - } - - it 'can set a non-object schema', -> - schema = - type: 'integer' - default: 12 - - atom.config.setSchema('foo.bar.anInt', schema) - expect(atom.config.get("foo.bar.anInt")).toBe 12 - expect(atom.config.getSchema('foo.bar.anInt')).toEqual - type: 'integer' - default: 12 - - it "allows the schema to be retrieved via ::getSchema", -> - schema = - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - - atom.config.setSchema('foo.bar', schema) - - expect(atom.config.getSchema('foo.bar')).toEqual - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - - expect(atom.config.getSchema('foo.bar.anInt')).toEqual - type: 'integer' - default: 12 - - expect(atom.config.getSchema('foo.baz')).toEqual {type: 'any'} - expect(atom.config.getSchema('foo.bar.anInt.baz')).toBe(null) - - it "respects the schema for scoped settings", -> - schema = - type: 'string' - default: 'ok' - scopes: - '.source.js': - default: 'omg' - atom.config.setSchema('foo.bar.str', schema) - - expect(atom.config.get('foo.bar.str')).toBe 'ok' - expect(atom.config.get('foo.bar.str', scope: ['.source.js'])).toBe 'omg' - expect(atom.config.get('foo.bar.str', scope: ['.source.coffee'])).toBe 'ok' - - describe 'when a schema is added after config values have been set', -> - schema = null - beforeEach -> - schema = - type: 'object' - properties: - int: - type: 'integer' - default: 2 - str: - type: 'string' - default: 'def' - - it "respects the new schema when values are set", -> - expect(atom.config.set('foo.bar.str', 'global')).toBe true - expect(atom.config.set('foo.bar.str', 'scoped', scopeSelector: '.source.js')).toBe true - expect(atom.config.get('foo.bar.str')).toBe 'global' - expect(atom.config.get('foo.bar.str', scope: ['.source.js'])).toBe 'scoped' - - expect(atom.config.set('foo.bar.noschema', 'nsGlobal')).toBe true - expect(atom.config.set('foo.bar.noschema', 'nsScoped', scopeSelector: '.source.js')).toBe true - expect(atom.config.get('foo.bar.noschema')).toBe 'nsGlobal' - expect(atom.config.get('foo.bar.noschema', scope: ['.source.js'])).toBe 'nsScoped' - - expect(atom.config.set('foo.bar.int', 'nope')).toBe true - expect(atom.config.set('foo.bar.int', 'notanint', scopeSelector: '.source.js')).toBe true - expect(atom.config.set('foo.bar.int', 23, scopeSelector: '.source.coffee')).toBe true - expect(atom.config.get('foo.bar.int')).toBe 'nope' - expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 'notanint' - expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23 - - atom.config.setSchema('foo.bar', schema) - - expect(atom.config.get('foo.bar.str')).toBe 'global' - expect(atom.config.get('foo.bar.str', scope: ['.source.js'])).toBe 'scoped' - expect(atom.config.get('foo.bar.noschema')).toBe 'nsGlobal' - expect(atom.config.get('foo.bar.noschema', scope: ['.source.js'])).toBe 'nsScoped' - - expect(atom.config.get('foo.bar.int')).toBe 2 - expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 2 - expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23 - - it "sets all values that adhere to the schema", -> - expect(atom.config.set('foo.bar.int', 10)).toBe true - expect(atom.config.set('foo.bar.int', 15, scopeSelector: '.source.js')).toBe true - expect(atom.config.set('foo.bar.int', 23, scopeSelector: '.source.coffee')).toBe true - expect(atom.config.get('foo.bar.int')).toBe 10 - expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 15 - expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23 - - atom.config.setSchema('foo.bar', schema) - - expect(atom.config.get('foo.bar.int')).toBe 10 - expect(atom.config.get('foo.bar.int', scope: ['.source.js'])).toBe 15 - expect(atom.config.get('foo.bar.int', scope: ['.source.coffee'])).toBe 23 - - describe 'when the value has an "integer" type', -> - beforeEach -> - schema = - type: 'integer' - default: 12 - atom.config.setSchema('foo.bar.anInt', schema) - - it 'coerces a string to an int', -> - atom.config.set('foo.bar.anInt', '123') - expect(atom.config.get('foo.bar.anInt')).toBe 123 - - it 'does not allow infinity', -> - atom.config.set('foo.bar.anInt', Infinity) - expect(atom.config.get('foo.bar.anInt')).toBe 12 - - it 'coerces a float to an int', -> - atom.config.set('foo.bar.anInt', 12.3) - expect(atom.config.get('foo.bar.anInt')).toBe 12 - - it 'will not set non-integers', -> - atom.config.set('foo.bar.anInt', null) - expect(atom.config.get('foo.bar.anInt')).toBe 12 - - atom.config.set('foo.bar.anInt', 'nope') - expect(atom.config.get('foo.bar.anInt')).toBe 12 - - describe 'when the minimum and maximum keys are used', -> - beforeEach -> - schema = - type: 'integer' - minimum: 10 - maximum: 20 - default: 12 - atom.config.setSchema('foo.bar.anInt', schema) - - it 'keeps the specified value within the specified range', -> - atom.config.set('foo.bar.anInt', '123') - expect(atom.config.get('foo.bar.anInt')).toBe 20 - - atom.config.set('foo.bar.anInt', '1') - expect(atom.config.get('foo.bar.anInt')).toBe 10 - - describe 'when the value has an "integer" and "string" type', -> - beforeEach -> - schema = - type: ['integer', 'string'] - default: 12 - atom.config.setSchema('foo.bar.anInt', schema) - - it 'can coerce an int, and fallback to a string', -> - atom.config.set('foo.bar.anInt', '123') - expect(atom.config.get('foo.bar.anInt')).toBe 123 - - atom.config.set('foo.bar.anInt', 'cats') - expect(atom.config.get('foo.bar.anInt')).toBe 'cats' - - describe 'when the value has an "string" and "boolean" type', -> - beforeEach -> - schema = - type: ['string', 'boolean'] - default: 'def' - atom.config.setSchema('foo.bar', schema) - - it 'can set a string, a boolean, and revert back to the default', -> - atom.config.set('foo.bar', 'ok') - expect(atom.config.get('foo.bar')).toBe 'ok' - - atom.config.set('foo.bar', false) - expect(atom.config.get('foo.bar')).toBe false - - atom.config.set('foo.bar', undefined) - expect(atom.config.get('foo.bar')).toBe 'def' - - describe 'when the value has a "number" type', -> - beforeEach -> - schema = - type: 'number' - default: 12.1 - atom.config.setSchema('foo.bar.aFloat', schema) - - it 'coerces a string to a float', -> - atom.config.set('foo.bar.aFloat', '12.23') - expect(atom.config.get('foo.bar.aFloat')).toBe 12.23 - - it 'will not set non-numbers', -> - atom.config.set('foo.bar.aFloat', null) - expect(atom.config.get('foo.bar.aFloat')).toBe 12.1 - - atom.config.set('foo.bar.aFloat', 'nope') - expect(atom.config.get('foo.bar.aFloat')).toBe 12.1 - - describe 'when the minimum and maximum keys are used', -> - beforeEach -> - schema = - type: 'number' - minimum: 11.2 - maximum: 25.4 - default: 12.1 - atom.config.setSchema('foo.bar.aFloat', schema) - - it 'keeps the specified value within the specified range', -> - atom.config.set('foo.bar.aFloat', '123.2') - expect(atom.config.get('foo.bar.aFloat')).toBe 25.4 - - atom.config.set('foo.bar.aFloat', '1.0') - expect(atom.config.get('foo.bar.aFloat')).toBe 11.2 - - describe 'when the value has a "boolean" type', -> - beforeEach -> - schema = - type: 'boolean' - default: true - atom.config.setSchema('foo.bar.aBool', schema) - - it 'coerces various types to a boolean', -> - atom.config.set('foo.bar.aBool', 'true') - expect(atom.config.get('foo.bar.aBool')).toBe true - atom.config.set('foo.bar.aBool', 'false') - expect(atom.config.get('foo.bar.aBool')).toBe false - atom.config.set('foo.bar.aBool', 'TRUE') - expect(atom.config.get('foo.bar.aBool')).toBe true - atom.config.set('foo.bar.aBool', 'FALSE') - expect(atom.config.get('foo.bar.aBool')).toBe false - atom.config.set('foo.bar.aBool', 1) - expect(atom.config.get('foo.bar.aBool')).toBe false - atom.config.set('foo.bar.aBool', 0) - expect(atom.config.get('foo.bar.aBool')).toBe false - atom.config.set('foo.bar.aBool', {}) - expect(atom.config.get('foo.bar.aBool')).toBe false - atom.config.set('foo.bar.aBool', null) - expect(atom.config.get('foo.bar.aBool')).toBe false - - it 'reverts back to the default value when undefined is passed to set', -> - atom.config.set('foo.bar.aBool', 'false') - expect(atom.config.get('foo.bar.aBool')).toBe false - - atom.config.set('foo.bar.aBool', undefined) - expect(atom.config.get('foo.bar.aBool')).toBe true - - describe 'when the value has an "string" type', -> - beforeEach -> - schema = - type: 'string' - default: 'ok' - atom.config.setSchema('foo.bar.aString', schema) - - it 'allows strings', -> - atom.config.set('foo.bar.aString', 'yep') - expect(atom.config.get('foo.bar.aString')).toBe 'yep' - - it 'will only set strings', -> - expect(atom.config.set('foo.bar.aString', 123)).toBe false - expect(atom.config.get('foo.bar.aString')).toBe 'ok' - - expect(atom.config.set('foo.bar.aString', true)).toBe false - expect(atom.config.get('foo.bar.aString')).toBe 'ok' - - expect(atom.config.set('foo.bar.aString', null)).toBe false - expect(atom.config.get('foo.bar.aString')).toBe 'ok' - - expect(atom.config.set('foo.bar.aString', [])).toBe false - expect(atom.config.get('foo.bar.aString')).toBe 'ok' - - expect(atom.config.set('foo.bar.aString', nope: 'nope')).toBe false - expect(atom.config.get('foo.bar.aString')).toBe 'ok' - - it 'does not allow setting children of that key-path', -> - expect(atom.config.set('foo.bar.aString.something', 123)).toBe false - expect(atom.config.get('foo.bar.aString')).toBe 'ok' - - describe 'when the schema has a "maximumLength" key', -> - it "trims the string to be no longer than the specified maximum", -> - schema = - type: 'string' - default: 'ok' - maximumLength: 3 - atom.config.setSchema('foo.bar.aString', schema) - atom.config.set('foo.bar.aString', 'abcdefg') - expect(atom.config.get('foo.bar.aString')).toBe 'abc' - - describe 'when the value has an "object" type', -> - beforeEach -> - schema = - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - nestedObject: - type: 'object' - properties: - nestedBool: - type: 'boolean' - default: false - atom.config.setSchema('foo.bar', schema) - - it 'converts and validates all the children', -> - atom.config.set 'foo.bar', - anInt: '23' - nestedObject: - nestedBool: 'true' - expect(atom.config.get('foo.bar')).toEqual - anInt: 23 - nestedObject: - nestedBool: true - - it 'will set only the values that adhere to the schema', -> - expect(atom.config.set 'foo.bar', - anInt: 'nope' - nestedObject: - nestedBool: true - ).toBe true - expect(atom.config.get('foo.bar.anInt')).toEqual 12 - expect(atom.config.get('foo.bar.nestedObject.nestedBool')).toEqual true - - describe "when the value has additionalProperties set to false", -> - it 'does not allow other properties to be set on the object', -> - atom.config.setSchema('foo.bar', - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - additionalProperties: false - ) - - expect(atom.config.set('foo.bar', {anInt: 5, somethingElse: 'ok'})).toBe true - expect(atom.config.get('foo.bar.anInt')).toBe 5 - expect(atom.config.get('foo.bar.somethingElse')).toBeUndefined() - - expect(atom.config.set('foo.bar.somethingElse', {anInt: 5})).toBe false - expect(atom.config.get('foo.bar.somethingElse')).toBeUndefined() - - describe 'when the value has an additionalProperties schema', -> - it 'validates properties of the object against that schema', -> - atom.config.setSchema('foo.bar', - type: 'object' - properties: - anInt: - type: 'integer' - default: 12 - additionalProperties: - type: 'string' - ) - - expect(atom.config.set('foo.bar', {anInt: 5, somethingElse: 'ok'})).toBe true - expect(atom.config.get('foo.bar.anInt')).toBe 5 - expect(atom.config.get('foo.bar.somethingElse')).toBe 'ok' - - expect(atom.config.set('foo.bar.somethingElse', 7)).toBe false - expect(atom.config.get('foo.bar.somethingElse')).toBe 'ok' - - expect(atom.config.set('foo.bar', {anInt: 6, somethingElse: 7})).toBe true - expect(atom.config.get('foo.bar.anInt')).toBe 6 - expect(atom.config.get('foo.bar.somethingElse')).toBe undefined - - describe 'when the value has an "array" type', -> - beforeEach -> - schema = - type: 'array' - default: [1, 2, 3] - items: - type: 'integer' - atom.config.setSchema('foo.bar', schema) - - it 'converts an array of strings to an array of ints', -> - atom.config.set 'foo.bar', ['2', '3', '4'] - expect(atom.config.get('foo.bar')).toEqual [2, 3, 4] - - it 'does not allow setting children of that key-path', -> - expect(atom.config.set('foo.bar.child', 123)).toBe false - expect(atom.config.set('foo.bar.child.grandchild', 123)).toBe false - expect(atom.config.get('foo.bar')).toEqual [1, 2, 3] - - describe 'when the value has a "color" type', -> - beforeEach -> - schema = - type: 'color' - default: 'white' - atom.config.setSchema('foo.bar.aColor', schema) - - it 'returns a Color object', -> - color = atom.config.get('foo.bar.aColor') - expect(color.toHexString()).toBe '#ffffff' - expect(color.toRGBAString()).toBe 'rgba(255, 255, 255, 1)' - - color.red = 0 - color.green = 0 - color.blue = 0 - color.alpha = 0 - atom.config.set('foo.bar.aColor', color) - - color = atom.config.get('foo.bar.aColor') - expect(color.toHexString()).toBe '#000000' - expect(color.toRGBAString()).toBe 'rgba(0, 0, 0, 0)' - - color.red = 300 - color.green = -200 - color.blue = -1 - color.alpha = 'not see through' - atom.config.set('foo.bar.aColor', color) - - color = atom.config.get('foo.bar.aColor') - expect(color.toHexString()).toBe '#ff0000' - expect(color.toRGBAString()).toBe 'rgba(255, 0, 0, 1)' - - color.red = 11 - color.green = 11 - color.blue = 124 - color.alpha = 1 - atom.config.set('foo.bar.aColor', color) - - color = atom.config.get('foo.bar.aColor') - expect(color.toHexString()).toBe '#0b0b7c' - expect(color.toRGBAString()).toBe 'rgba(11, 11, 124, 1)' - - it 'coerces various types to a color object', -> - atom.config.set('foo.bar.aColor', 'red') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} - atom.config.set('foo.bar.aColor', '#020') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 34, blue: 0, alpha: 1} - atom.config.set('foo.bar.aColor', '#abcdef') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 171, green: 205, blue: 239, alpha: 1} - atom.config.set('foo.bar.aColor', 'rgb(1,2,3)') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 1, green: 2, blue: 3, alpha: 1} - atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 4, green: 5, blue: 6, alpha: .7} - atom.config.set('foo.bar.aColor', 'hsl(120,100%,50%)') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: 1} - atom.config.set('foo.bar.aColor', 'hsla(120,100%,50%,0.3)') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 255, blue: 0, alpha: .3} - atom.config.set('foo.bar.aColor', {red: 100, green: 255, blue: 2, alpha: .5}) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 100, green: 255, blue: 2, alpha: .5} - atom.config.set('foo.bar.aColor', {red: 255}) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} - atom.config.set('foo.bar.aColor', {red: 1000}) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 0, blue: 0, alpha: 1} - atom.config.set('foo.bar.aColor', {red: 'dark'}) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 0, green: 0, blue: 0, alpha: 1} - - it 'reverts back to the default value when undefined is passed to set', -> - atom.config.set('foo.bar.aColor', undefined) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} - - it 'will not set non-colors', -> - atom.config.set('foo.bar.aColor', null) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} - - atom.config.set('foo.bar.aColor', 'nope') - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} - - atom.config.set('foo.bar.aColor', 30) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} - - atom.config.set('foo.bar.aColor', false) - expect(atom.config.get('foo.bar.aColor')).toEqual {red: 255, green: 255, blue: 255, alpha: 1} - - it "returns a clone of the Color when returned in a parent object", -> - color1 = atom.config.get('foo.bar').aColor - color2 = atom.config.get('foo.bar').aColor - expect(color1.toRGBAString()).toBe 'rgba(255, 255, 255, 1)' - expect(color2.toRGBAString()).toBe 'rgba(255, 255, 255, 1)' - expect(color1).not.toBe color2 - expect(color1).toEqual color2 - - describe 'when the `enum` key is used', -> - beforeEach -> - schema = - type: 'object' - properties: - str: - type: 'string' - default: 'ok' - enum: ['ok', 'one', 'two'] - int: - type: 'integer' - default: 2 - enum: [2, 3, 5] - arr: - type: 'array' - default: ['one', 'two'] - items: - type: 'string' - enum: ['one', 'two', 'three'] - str_options: - type: 'string' - default: 'one' - enum: [ - value: 'one', description: 'One' - 'two', - value: 'three', description: 'Three' - ] - - atom.config.setSchema('foo.bar', schema) - - it 'will only set a string when the string is in the enum values', -> - expect(atom.config.set('foo.bar.str', 'nope')).toBe false - expect(atom.config.get('foo.bar.str')).toBe 'ok' - - expect(atom.config.set('foo.bar.str', 'one')).toBe true - expect(atom.config.get('foo.bar.str')).toBe 'one' - - it 'will only set an integer when the integer is in the enum values', -> - expect(atom.config.set('foo.bar.int', '400')).toBe false - expect(atom.config.get('foo.bar.int')).toBe 2 - - expect(atom.config.set('foo.bar.int', '3')).toBe true - expect(atom.config.get('foo.bar.int')).toBe 3 - - it 'will only set an array when the array values are in the enum values', -> - expect(atom.config.set('foo.bar.arr', ['one', 'five'])).toBe true - expect(atom.config.get('foo.bar.arr')).toEqual ['one'] - - expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe true - expect(atom.config.get('foo.bar.arr')).toEqual ['two', 'three'] - - it 'will honor the enum when specified as an array', -> - expect(atom.config.set('foo.bar.str_options', 'one')).toBe true - expect(atom.config.get('foo.bar.str_options')).toEqual 'one' - - expect(atom.config.set('foo.bar.str_options', 'two')).toBe true - expect(atom.config.get('foo.bar.str_options')).toEqual 'two' - - expect(atom.config.set('foo.bar.str_options', 'One')).toBe false - expect(atom.config.get('foo.bar.str_options')).toEqual 'two' - - describe "when .set/.unset is called prior to .loadUserConfig", -> - beforeEach -> - atom.config.settingsLoaded = false - fs.writeFileSync atom.config.configFilePath, """ - '*': - foo: - bar: 'baz' - do: - ray: 'me' - """ - - it "ensures that early set and unset calls are replayed after the config is loaded from disk", -> - atom.config.unset 'foo.bar' - atom.config.set 'foo.qux', 'boo' - - expect(atom.config.get('foo.bar')).toBeUndefined() - expect(atom.config.get('foo.qux')).toBe 'boo' - expect(atom.config.get('do.ray')).toBeUndefined() - - advanceClock 100 - expect(atom.config.save).not.toHaveBeenCalled() - - atom.config.loadUserConfig() - - advanceClock 100 - waitsFor -> atom.config.save.callCount > 0 - - runs -> - expect(atom.config.get('foo.bar')).toBeUndefined() - expect(atom.config.get('foo.qux')).toBe 'boo' - expect(atom.config.get('do.ray')).toBe 'me' diff --git a/spec/config-spec.js b/spec/config-spec.js new file mode 100644 index 000000000..53cf9fffc --- /dev/null +++ b/spec/config-spec.js @@ -0,0 +1,1893 @@ +describe('Config', () => { + let savedSettings + + beforeEach(() => { + spyOn(console, 'warn') + atom.config.settingsLoaded = true + + savedSettings = [] + atom.config.saveCallback = function (settings) { + savedSettings.push(settings) + } + }) + + describe('.get(keyPath, {scope, sources, excludeSources})', () => { + it("allows a key path's value to be read", () => { + expect(atom.config.set('foo.bar.baz', 42)).toBe(true) + expect(atom.config.get('foo.bar.baz')).toBe(42) + expect(atom.config.get('foo.quux')).toBeUndefined() + }) + + it("returns a deep clone of the key path's value", () => { + atom.config.set('value', {array: [1, {b: 2}, 3]}) + const retrievedValue = atom.config.get('value') + retrievedValue.array[0] = 4 + retrievedValue.array[1].b = 2.1 + expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]}) + }) + + it('merges defaults into the returned value if both the assigned value and the default value are objects', () => { + atom.config.setDefaults('foo.bar', {baz: 1, ok: 2}) + atom.config.set('foo.bar', {baz: 3}) + expect(atom.config.get('foo.bar')).toEqual({baz: 3, ok: 2}) + + atom.config.setDefaults('other', {baz: 1}) + atom.config.set('other', 7) + expect(atom.config.get('other')).toBe(7) + + atom.config.set('bar.baz', {a: 3}) + atom.config.setDefaults('bar', {baz: 7}) + expect(atom.config.get('bar.baz')).toEqual({a: 3}) + }) + + describe("when a 'sources' option is specified", () => + it('only retrieves values from the specified sources', () => { + atom.config.set('x.y', 1, {scopeSelector: '.foo', source: 'a'}) + atom.config.set('x.y', 2, {scopeSelector: '.foo', source: 'b'}) + atom.config.set('x.y', 3, {scopeSelector: '.foo', source: 'c'}) + atom.config.setSchema('x.y', {type: 'integer', default: 4}) + + expect(atom.config.get('x.y', {sources: ['a'], scope: ['.foo']})).toBe(1) + expect(atom.config.get('x.y', {sources: ['b'], scope: ['.foo']})).toBe(2) + expect(atom.config.get('x.y', {sources: ['c'], scope: ['.foo']})).toBe(3) + // Schema defaults never match a specific source. We could potentially add a special "schema" source. + expect(atom.config.get('x.y', {sources: ['x'], scope: ['.foo']})).toBeUndefined() + + expect(atom.config.get(null, {sources: ['a'], scope: ['.foo']}).x.y).toBe(1) + }) + ) + + describe("when an 'excludeSources' option is specified", () => + it('only retrieves values from the specified sources', () => { + atom.config.set('x.y', 0) + atom.config.set('x.y', 1, {scopeSelector: '.foo', source: 'a'}) + atom.config.set('x.y', 2, {scopeSelector: '.foo', source: 'b'}) + atom.config.set('x.y', 3, {scopeSelector: '.foo', source: 'c'}) + atom.config.setSchema('x.y', {type: 'integer', default: 4}) + + expect(atom.config.get('x.y', {excludeSources: ['a'], scope: ['.foo']})).toBe(3) + expect(atom.config.get('x.y', {excludeSources: ['c'], scope: ['.foo']})).toBe(2) + expect(atom.config.get('x.y', {excludeSources: ['b', 'c'], scope: ['.foo']})).toBe(1) + expect(atom.config.get('x.y', {excludeSources: ['b', 'c', 'a'], scope: ['.foo']})).toBe(0) + expect(atom.config.get('x.y', {excludeSources: ['b', 'c', 'a', atom.config.getUserConfigPath()], scope: ['.foo']})).toBe(4) + expect(atom.config.get('x.y', {excludeSources: [atom.config.getUserConfigPath()]})).toBe(4) + }) + ) + + describe("when a 'scope' option is given", () => { + it('returns the property with the most specific scope selector', () => { + atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee'}) + atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double'}) + atom.config.set('foo.bar.baz', 11, {scopeSelector: '.source'}) + + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']})).toBe(42) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.js', '.string.quoted.double.js']})).toBe(22) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.js', '.variable.assignment.js']})).toBe(11) + expect(atom.config.get('foo.bar.baz', {scope: ['.text']})).toBeUndefined() + }) + + it('favors the most recently added properties in the event of a specificity tie', () => { + atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.single'}) + atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source.coffee .string.quoted.double'}) + + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.single']})).toBe(42) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.single.double']})).toBe(22) + }) + + describe('when there are global defaults', () => + it('falls back to the global when there is no scoped property specified', () => { + atom.config.setDefaults('foo', {hasDefault: 'ok'}) + expect(atom.config.get('foo.hasDefault', {scope: ['.source.coffee', '.string.quoted.single']})).toBe('ok') + }) + ) + + describe('when package settings are added after user settings', () => + it("returns the user's setting because the user's setting has higher priority", () => { + atom.config.set('foo.bar.baz', 100, {scopeSelector: '.source.coffee'}) + atom.config.set('foo.bar.baz', 1, {scopeSelector: '.source.coffee', source: 'some-package'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(100) + }) + ) + }) + }) + + describe('.getAll(keyPath, {scope, sources, excludeSources})', () => { + it('reads all of the values for a given key-path', () => { + expect(atom.config.set('foo', 41)).toBe(true) + expect(atom.config.set('foo', 43, {scopeSelector: '.a .b'})).toBe(true) + expect(atom.config.set('foo', 42, {scopeSelector: '.a'})).toBe(true) + expect(atom.config.set('foo', 44, {scopeSelector: '.a .b.c'})).toBe(true) + + expect(atom.config.set('foo', -44, {scopeSelector: '.d'})).toBe(true) + + expect(atom.config.getAll('foo', {scope: ['.a', '.b.c']})).toEqual([ + {scopeSelector: '.a .b.c', value: 44}, + {scopeSelector: '.a .b', value: 43}, + {scopeSelector: '.a', value: 42}, + {scopeSelector: '*', value: 41} + ]) + }) + + it("includes the schema's default value", () => { + atom.config.setSchema('foo', {type: 'number', default: 40}) + expect(atom.config.set('foo', 43, {scopeSelector: '.a .b'})).toBe(true) + expect(atom.config.getAll('foo', {scope: ['.a', '.b.c']})).toEqual([ + {scopeSelector: '.a .b', value: 43}, + {scopeSelector: '*', value: 40} + ]) + }) + }) + + describe('.set(keyPath, value, {source, scopeSelector})', () => { + it("allows a key path's value to be written", () => { + expect(atom.config.set('foo.bar.baz', 42)).toBe(true) + expect(atom.config.get('foo.bar.baz')).toBe(42) + }) + + it("saves the user's config to disk after it stops changing", () => { + atom.config.set('foo.bar.baz', 42) + expect(savedSettings.length).toBe(0) + atom.config.set('foo.bar.baz', 43) + expect(savedSettings.length).toBe(0) + atom.config.set('foo.bar.baz', 44) + advanceClock(10) + expect(savedSettings.length).toBe(1) + }) + + it("does not save when a non-default 'source' is given", () => { + atom.config.set('foo.bar.baz', 42, {source: 'some-other-source', scopeSelector: '.a'}) + advanceClock(500) + expect(savedSettings.length).toBe(0) + }) + + it("does not allow a 'source' option without a 'scopeSelector'", () => { + expect(() => atom.config.set('foo', 1, {source: ['.source.ruby']})).toThrow() + }) + + describe('when the key-path is null', () => + it('sets the root object', () => { + expect(atom.config.set(null, {editor: {tabLength: 6}})).toBe(true) + expect(atom.config.get('editor.tabLength')).toBe(6) + expect(atom.config.set(null, {editor: {tabLength: 8, scopeSelector: ['.source.js']}})).toBe(true) + expect(atom.config.get('editor.tabLength', {scope: ['.source.js']})).toBe(8) + }) + ) + + describe('when the value equals the default value', () => + it("does not store the value in the user's config", () => { + atom.config.setSchema('foo', { + type: 'object', + properties: { + same: { + type: 'number', + default: 1 + }, + changes: { + type: 'number', + default: 1 + }, + sameArray: { + type: 'array', + default: [1, 2, 3] + }, + sameObject: { + type: 'object', + default: {a: 1, b: 2} + }, + null: { + type: '*', + default: null + }, + undefined: { + type: '*', + default: undefined + } + } + } + ) + expect(atom.config.settings.foo).toBeUndefined() + + atom.config.set('foo.same', 1) + atom.config.set('foo.changes', 2) + atom.config.set('foo.sameArray', [1, 2, 3]) + atom.config.set('foo.null', undefined) + atom.config.set('foo.undefined', null) + atom.config.set('foo.sameObject', {b: 2, a: 1}) + + const userConfigPath = atom.config.getUserConfigPath() + + expect(atom.config.get('foo.same', {sources: [userConfigPath]})).toBeUndefined() + + expect(atom.config.get('foo.changes')).toBe(2) + expect(atom.config.get('foo.changes', {sources: [userConfigPath]})).toBe(2) + + atom.config.set('foo.changes', 1) + expect(atom.config.get('foo.changes', {sources: [userConfigPath]})).toBeUndefined() + }) + ) + + describe("when a 'scopeSelector' is given", () => + it('sets the value and overrides the others', () => { + atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee'}) + atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double'}) + atom.config.set('foo.bar.baz', 11, {scopeSelector: '.source'}) + + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']})).toBe(42) + + expect(atom.config.set('foo.bar.baz', 100, {scopeSelector: '.source.coffee .string.quoted.double.coffee'})).toBe(true) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']})).toBe(100) + }) + ) + }) + + describe('.unset(keyPath, {source, scopeSelector})', () => { + beforeEach(() => + atom.config.setSchema('foo', { + type: 'object', + properties: { + bar: { + type: 'object', + properties: { + baz: { + type: 'integer', + default: 0 + }, + ok: { + type: 'integer', + default: 0 + } + } + }, + quux: { + type: 'integer', + default: 0 + } + } + } + ) + ) + + it('sets the value of the key path to its default', () => { + atom.config.setDefaults('a', {b: 3}) + atom.config.set('a.b', 4) + expect(atom.config.get('a.b')).toBe(4) + atom.config.unset('a.b') + expect(atom.config.get('a.b')).toBe(3) + + atom.config.set('a.c', 5) + expect(atom.config.get('a.c')).toBe(5) + atom.config.unset('a.c') + expect(atom.config.get('a.c')).toBeUndefined() + }) + + it('calls ::save()', () => { + atom.config.setDefaults('a', {b: 3}) + atom.config.set('a.b', 4) + savedSettings.length = 0 + + atom.config.unset('a.c') + advanceClock(500) + expect(savedSettings.length).toBe(1) + }) + + describe("when no 'scopeSelector' is given", () => { + describe("when a 'source' but no key-path is given", () => + it('removes all scoped settings with the given source', () => { + atom.config.set('foo.bar.baz', 1, {scopeSelector: '.a', source: 'source-a'}) + atom.config.set('foo.bar.quux', 2, {scopeSelector: '.b', source: 'source-a'}) + expect(atom.config.get('foo.bar', {scope: ['.a.b']})).toEqual({baz: 1, quux: 2}) + + atom.config.unset(null, {source: 'source-a'}) + expect(atom.config.get('foo.bar', {scope: ['.a']})).toEqual({baz: 0, ok: 0}) + }) + ) + + describe("when a 'source' and a key-path is given", () => + it('removes all scoped settings with the given source and key-path', () => { + atom.config.set('foo.bar.baz', 1) + atom.config.set('foo.bar.baz', 2, {scopeSelector: '.a', source: 'source-a'}) + atom.config.set('foo.bar.baz', 3, {scopeSelector: '.a.b', source: 'source-b'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.a.b']})).toEqual(3) + + atom.config.unset('foo.bar.baz', {source: 'source-b'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.a.b']})).toEqual(2) + expect(atom.config.get('foo.bar.baz')).toEqual(1) + }) + ) + + describe("when no 'source' is given", () => + it('removes all scoped and unscoped properties for that key-path', () => { + atom.config.setDefaults('foo.bar', {baz: 100}) + + atom.config.set('foo.bar', {baz: 1, ok: 2}, {scopeSelector: '.a'}) + atom.config.set('foo.bar', {baz: 11, ok: 12}, {scopeSelector: '.b'}) + atom.config.set('foo.bar', {baz: 21, ok: 22}) + + atom.config.unset('foo.bar.baz') + + expect(atom.config.get('foo.bar.baz', {scope: ['.a']})).toBe(100) + expect(atom.config.get('foo.bar.baz', {scope: ['.b']})).toBe(100) + expect(atom.config.get('foo.bar.baz')).toBe(100) + + expect(atom.config.get('foo.bar.ok', {scope: ['.a']})).toBe(2) + expect(atom.config.get('foo.bar.ok', {scope: ['.b']})).toBe(12) + expect(atom.config.get('foo.bar.ok')).toBe(22) + }) + ) + }) + + describe("when a 'scopeSelector' is given", () => { + it('restores the global default when no scoped default set', () => { + atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + + atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(10) + }) + + it('restores the scoped default when a scoped default is set', () => { + atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee', source: 'some-source'}) + atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) + atom.config.set('foo.bar.ok', 100, {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + + atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(42) + expect(atom.config.get('foo.bar.ok', {scope: ['.source.coffee']})).toBe(100) + }) + + it('calls ::save()', () => { + atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) + savedSettings.length = 0 + + atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) + advanceClock(150) + expect(savedSettings.length).toBe(1) + }) + + it('allows removing settings for a specific source and scope selector', () => { + atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee', source: 'source-a'}) + atom.config.set('foo.bar.baz', 65, {scopeSelector: '.source.coffee', source: 'source-b'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(65) + + atom.config.unset('foo.bar.baz', {source: 'source-b', scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string']})).toBe(55) + }) + + it('allows removing all settings for a specific source', () => { + atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee', source: 'source-a'}) + atom.config.set('foo.bar.baz', 65, {scopeSelector: '.source.coffee', source: 'source-b'}) + atom.config.set('foo.bar.ok', 65, {scopeSelector: '.source.coffee', source: 'source-b'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(65) + + atom.config.unset(null, {source: 'source-b', scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee', '.string']})).toBe(55) + expect(atom.config.get('foo.bar.ok', {scope: ['.source.coffee', '.string']})).toBe(0) + }) + + it('does not call ::save or add a scoped property when no value has been set', () => { + // see https://github.com/atom/atom/issues/4175 + atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(10) + + expect(savedSettings.length).toBe(0) + + const scopedProperties = atom.config.scopedSettingsStore.propertiesForSource('user-config') + expect(scopedProperties['.coffee.source']).toBeUndefined() + }) + + it('removes the scoped value when it was the only set value on the object', () => { + atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.set('foo.bar.baz', 55, {scopeSelector: '.source.coffee'}) + atom.config.set('foo.bar.ok', 20, {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + + advanceClock(150) + savedSettings.length = 0 + + atom.config.unset('foo.bar.baz', {scopeSelector: '.source.coffee'}) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(10) + expect(atom.config.get('foo.bar.ok', {scope: ['.source.coffee']})).toBe(20) + + advanceClock(150) + expect(savedSettings[0]['.coffee.source']).toEqual({ + foo: { + bar: { + ok: 20 + } + } + }) + + atom.config.unset('foo.bar.ok', {scopeSelector: '.source.coffee'}) + + advanceClock(150) + expect(savedSettings.length).toBe(2) + expect(savedSettings[1]['.coffee.source']).toBeUndefined() + }) + + it('does not call ::save when the value is already at the default', () => { + atom.config.setDefaults('foo', {bar: {baz: 10}}) + atom.config.set('foo.bar.baz', 55) + advanceClock(150) + savedSettings.length = 0 + + atom.config.unset('foo.bar.ok', {scopeSelector: '.source.coffee'}) + advanceClock(150) + expect(savedSettings.length).toBe(0) + expect(atom.config.get('foo.bar.baz', {scope: ['.source.coffee']})).toBe(55) + }) + }) + }) + + describe('.onDidChange(keyPath, {scope})', () => { + let observeHandler = [] + + describe('when a keyPath is specified', () => { + beforeEach(() => { + observeHandler = jasmine.createSpy('observeHandler') + atom.config.set('foo.bar.baz', 'value 1') + atom.config.onDidChange('foo.bar.baz', observeHandler) + }) + + it('does not fire the given callback with the current value at the keypath', () => expect(observeHandler).not.toHaveBeenCalled()) + + it('fires the callback every time the observed value changes', () => { + atom.config.set('foo.bar.baz', 'value 2') + expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 2', oldValue: 'value 1'}) + observeHandler.reset() + observeHandler.andCallFake(() => { throw new Error('oops') }) + expect(() => atom.config.set('foo.bar.baz', 'value 1')).toThrow('oops') + expect(observeHandler).toHaveBeenCalledWith({newValue: 'value 1', oldValue: 'value 2'}) + observeHandler.reset() + + // Regression: exception in earlier handler shouldn't put observer + // into a bad state. + atom.config.set('something.else', 'new value') + expect(observeHandler).not.toHaveBeenCalled() + }) + }) + + describe('when a keyPath is not specified', () => { + beforeEach(() => { + observeHandler = jasmine.createSpy('observeHandler') + atom.config.set('foo.bar.baz', 'value 1') + atom.config.onDidChange(observeHandler) + }) + + it('does not fire the given callback initially', () => expect(observeHandler).not.toHaveBeenCalled()) + + it('fires the callback every time any value changes', () => { + observeHandler.reset() // clear the initial call + atom.config.set('foo.bar.baz', 'value 2') + expect(observeHandler).toHaveBeenCalled() + expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe('value 2') + expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe('value 1') + + observeHandler.reset() + atom.config.set('foo.bar.baz', 'value 1') + expect(observeHandler).toHaveBeenCalled() + expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.baz).toBe('value 1') + expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.baz).toBe('value 2') + + observeHandler.reset() + atom.config.set('foo.bar.int', 1) + expect(observeHandler).toHaveBeenCalled() + expect(observeHandler.mostRecentCall.args[0].newValue.foo.bar.int).toBe(1) + expect(observeHandler.mostRecentCall.args[0].oldValue.foo.bar.int).toBe(undefined) + }) + }) + + describe("when a 'scope' is given", () => + it('calls the supplied callback when the value at the descriptor/keypath changes', () => { + const changeSpy = jasmine.createSpy('onDidChange callback') + atom.config.onDidChange('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']}, changeSpy) + + atom.config.set('foo.bar.baz', 12) + expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12}) + changeSpy.reset() + + atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double', source: 'a'}) + expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: 22}) + changeSpy.reset() + + atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) + expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 42}) + changeSpy.reset() + + atom.config.unset(null, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) + expect(changeSpy).toHaveBeenCalledWith({oldValue: 42, newValue: 22}) + changeSpy.reset() + + atom.config.unset(null, {scopeSelector: '.source .string.quoted.double', source: 'a'}) + expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 12}) + changeSpy.reset() + + atom.config.set('foo.bar.baz', undefined) + expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: undefined}) + changeSpy.reset() + }) + ) + }) + + describe('.observe(keyPath, {scope})', () => { + let [observeHandler, observeSubscription] = [] + + beforeEach(() => { + observeHandler = jasmine.createSpy('observeHandler') + atom.config.set('foo.bar.baz', 'value 1') + observeSubscription = atom.config.observe('foo.bar.baz', observeHandler) + }) + + it('fires the given callback with the current value at the keypath', () => expect(observeHandler).toHaveBeenCalledWith('value 1')) + + it('fires the callback every time the observed value changes', () => { + observeHandler.reset() // clear the initial call + atom.config.set('foo.bar.baz', 'value 2') + expect(observeHandler).toHaveBeenCalledWith('value 2') + + observeHandler.reset() + atom.config.set('foo.bar.baz', 'value 1') + expect(observeHandler).toHaveBeenCalledWith('value 1') + advanceClock(100) // complete pending save that was requested in ::set + + observeHandler.reset() + atom.config.resetUserSettings({foo: {}}) + expect(observeHandler).toHaveBeenCalledWith(undefined) + }) + + it('fires the callback when the observed value is deleted', () => { + observeHandler.reset() // clear the initial call + atom.config.set('foo.bar.baz', undefined) + expect(observeHandler).toHaveBeenCalledWith(undefined) + }) + + it('fires the callback when the full key path goes into and out of existence', () => { + observeHandler.reset() // clear the initial call + atom.config.set('foo.bar', undefined) + expect(observeHandler).toHaveBeenCalledWith(undefined) + + observeHandler.reset() + atom.config.set('foo.bar.baz', "i'm back") + expect(observeHandler).toHaveBeenCalledWith("i'm back") + }) + + it('does not fire the callback once the subscription is disposed', () => { + observeHandler.reset() // clear the initial call + observeSubscription.dispose() + atom.config.set('foo.bar.baz', 'value 2') + expect(observeHandler).not.toHaveBeenCalled() + }) + + it('does not fire the callback for a similarly named keyPath', () => { + const bazCatHandler = jasmine.createSpy('bazCatHandler') + observeSubscription = atom.config.observe('foo.bar.bazCat', bazCatHandler) + + bazCatHandler.reset() + atom.config.set('foo.bar.baz', 'value 10') + expect(bazCatHandler).not.toHaveBeenCalled() + }) + + describe("when a 'scope' is given", () => { + let otherHandler = null + + beforeEach(() => { + observeSubscription.dispose() + otherHandler = jasmine.createSpy('otherHandler') + }) + + it('allows settings to be observed in a specific scope', () => { + atom.config.observe('foo.bar.baz', {scope: ['.some.scope']}, observeHandler) + atom.config.observe('foo.bar.baz', {scope: ['.another.scope']}, otherHandler) + + atom.config.set('foo.bar.baz', 'value 2', {scopeSelector: '.some'}) + expect(observeHandler).toHaveBeenCalledWith('value 2') + expect(otherHandler).not.toHaveBeenCalledWith('value 2') + }) + + it('calls the callback when properties with more specific selectors are removed', () => { + const changeSpy = jasmine.createSpy() + atom.config.observe('foo.bar.baz', {scope: ['.source.coffee', '.string.quoted.double.coffee']}, changeSpy) + expect(changeSpy).toHaveBeenCalledWith('value 1') + changeSpy.reset() + + atom.config.set('foo.bar.baz', 12) + expect(changeSpy).toHaveBeenCalledWith(12) + changeSpy.reset() + + atom.config.set('foo.bar.baz', 22, {scopeSelector: '.source .string.quoted.double', source: 'a'}) + expect(changeSpy).toHaveBeenCalledWith(22) + changeSpy.reset() + + atom.config.set('foo.bar.baz', 42, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) + expect(changeSpy).toHaveBeenCalledWith(42) + changeSpy.reset() + + atom.config.unset(null, {scopeSelector: '.source.coffee .string.quoted.double.coffee', source: 'b'}) + expect(changeSpy).toHaveBeenCalledWith(22) + changeSpy.reset() + + atom.config.unset(null, {scopeSelector: '.source .string.quoted.double', source: 'a'}) + expect(changeSpy).toHaveBeenCalledWith(12) + changeSpy.reset() + + atom.config.set('foo.bar.baz', undefined) + expect(changeSpy).toHaveBeenCalledWith(undefined) + changeSpy.reset() + }) + }) + }) + + describe('.transact(callback)', () => { + let changeSpy = null + + beforeEach(() => { + changeSpy = jasmine.createSpy('onDidChange callback') + atom.config.onDidChange('foo.bar.baz', changeSpy) + }) + + it('allows only one change event for the duration of the given callback', () => { + atom.config.transact(() => { + atom.config.set('foo.bar.baz', 1) + atom.config.set('foo.bar.baz', 2) + atom.config.set('foo.bar.baz', 3) + }) + + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + }) + + it('does not emit an event if no changes occur while paused', () => { + atom.config.transact(() => {}) + expect(changeSpy).not.toHaveBeenCalled() + }) + }) + + describe('.transactAsync(callback)', () => { + let changeSpy = null + + beforeEach(() => { + changeSpy = jasmine.createSpy('onDidChange callback') + atom.config.onDidChange('foo.bar.baz', changeSpy) + }) + + it('allows only one change event for the duration of the given promise if it gets resolved', () => { + let promiseResult = null + const transactionPromise = atom.config.transactAsync(() => { + atom.config.set('foo.bar.baz', 1) + atom.config.set('foo.bar.baz', 2) + atom.config.set('foo.bar.baz', 3) + return Promise.resolve('a result') + }) + + waitsForPromise(() => transactionPromise.then(result => { + promiseResult = result + })) + + runs(() => { + expect(promiseResult).toBe('a result') + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + }) + }) + + it('allows only one change event for the duration of the given promise if it gets rejected', () => { + let promiseError = null + const transactionPromise = atom.config.transactAsync(() => { + atom.config.set('foo.bar.baz', 1) + atom.config.set('foo.bar.baz', 2) + atom.config.set('foo.bar.baz', 3) + return Promise.reject(new Error('an error')) + }) + + waitsForPromise(() => transactionPromise.catch(error => { + promiseError = error + })) + + runs(() => { + expect(promiseError.message).toBe('an error') + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + }) + }) + + it('allows only one change event even when the given callback throws', () => { + const error = new Error('Oops!') + let promiseError = null + const transactionPromise = atom.config.transactAsync(() => { + atom.config.set('foo.bar.baz', 1) + atom.config.set('foo.bar.baz', 2) + atom.config.set('foo.bar.baz', 3) + throw error + }) + + waitsForPromise(() => transactionPromise.catch(e => { + promiseError = e + })) + + runs(() => { + expect(promiseError).toBe(error) + expect(changeSpy.callCount).toBe(1) + expect(changeSpy.argsForCall[0][0]).toEqual({newValue: 3, oldValue: undefined}) + }) + }) + }) + + describe('.getSources()', () => { + it("returns an array of all of the config's source names", () => { + expect(atom.config.getSources()).toEqual([]) + + atom.config.set('a.b', 1, {scopeSelector: '.x1', source: 'source-1'}) + atom.config.set('a.c', 1, {scopeSelector: '.x1', source: 'source-1'}) + atom.config.set('a.b', 2, {scopeSelector: '.x2', source: 'source-2'}) + atom.config.set('a.b', 1, {scopeSelector: '.x3', source: 'source-3'}) + + expect(atom.config.getSources()).toEqual([ + 'source-1', + 'source-2', + 'source-3' + ]) + }) + }) + + describe('.save()', () => { + it('calls the save callback with any non-default properties', () => { + atom.config.set('a.b.c', 1) + atom.config.set('a.b.d', 2) + atom.config.set('x.y.z', 3) + atom.config.setDefaults('a.b', {e: 4, f: 5}) + + atom.config.save() + expect(savedSettings).toEqual([{'*': atom.config.settings}]) + }) + + it('serializes properties in alphabetical order', () => { + atom.config.set('foo', 1) + atom.config.set('bar', 2) + atom.config.set('baz.foo', 3) + atom.config.set('baz.bar', 4) + + savedSettings.length = 0 + atom.config.save() + + const writtenConfig = savedSettings[0] + expect(writtenConfig).toEqual({'*': atom.config.settings}) + + let expectedKeys = ['bar', 'baz', 'foo'] + let foundKeys = [] + for (const key in writtenConfig['*']) { + if ((expectedKeys).includes(key)) { + foundKeys.push(key) + } + } + expect(foundKeys).toEqual(expectedKeys) + expectedKeys = ['bar', 'foo'] + foundKeys = [] + for (const key in writtenConfig['*']['baz']) { + if ((expectedKeys).includes(key)) { + foundKeys.push(key) + } + } + expect(foundKeys).toEqual(expectedKeys) + }) + + describe('when scoped settings are defined', () => { + it('serializes any explicitly set config settings', () => { + atom.config.set('foo.bar', 'ruby', {scopeSelector: '.source.ruby'}) + atom.config.set('foo.omg', 'wow', {scopeSelector: '.source.ruby'}) + atom.config.set('foo.bar', 'coffee', {scopeSelector: '.source.coffee'}) + + savedSettings.length = 0 + atom.config.save() + + const writtenConfig = savedSettings[0] + expect(writtenConfig).toEqualJson({ + '*': + atom.config.settings, + '.ruby.source': { + foo: { + bar: 'ruby', + omg: 'wow' + } + }, + '.coffee.source': { + foo: { + bar: 'coffee' + } + } + }) + }) + }) + }) + + describe('.resetUserSettings()', () => { + beforeEach(() => { + atom.config.setSchema('foo', { + type: 'object', + properties: { + bar: { + type: 'string', + default: 'def' + }, + int: { + type: 'integer', + default: 12 + } + } + }) + }) + + describe('when the config file contains scoped settings', () => { + it('updates the config data based on the file contents', () => { + atom.config.resetUserSettings({ + '*': { + foo: { + bar: 'baz' + } + }, + + '.source.ruby': { + foo: { + bar: 'more-specific' + } + } + }) + expect(atom.config.get('foo.bar')).toBe('baz') + expect(atom.config.get('foo.bar', {scope: ['.source.ruby']})).toBe('more-specific') + }) + }) + + describe('when the config file does not conform to the schema', () => { + it('validates and does not load the incorrect values', () => { + atom.config.resetUserSettings({ + '*': { + foo: { + bar: 'omg', + int: 'baz' + } + }, + '.source.ruby': { + foo: { + bar: 'scoped', + int: 'nope' + } + } + }) + expect(atom.config.get('foo.int')).toBe(12) + expect(atom.config.get('foo.bar')).toBe('omg') + expect(atom.config.get('foo.int', {scope: ['.source.ruby']})).toBe(12) + expect(atom.config.get('foo.bar', {scope: ['.source.ruby']})).toBe('scoped') + }) + }) + + it('updates the config data based on the file contents', () => { + atom.config.resetUserSettings({foo: {bar: 'baz'}}) + expect(atom.config.get('foo.bar')).toBe('baz') + }) + + it('notifies observers for updated keypaths on load', () => { + const observeHandler = jasmine.createSpy('observeHandler') + atom.config.observe('foo.bar', observeHandler) + atom.config.resetUserSettings({foo: {bar: 'baz'}}) + expect(observeHandler).toHaveBeenCalledWith('baz') + }) + + describe('when the config file contains values that do not adhere to the schema', () => { + it('updates the only the settings that have values matching the schema', () => { + atom.config.resetUserSettings({ + foo: { + bar: 'baz', + int: 'bad value' + } + }) + expect(atom.config.get('foo.bar')).toBe('baz') + expect(atom.config.get('foo.int')).toBe(12) + expect(console.warn).toHaveBeenCalled() + expect(console.warn.mostRecentCall.args[0]).toContain('foo.int') + }) + }) + + it('does not fire a change event for paths that did not change', () => { + atom.config.resetUserSettings({ + foo: {bar: 'baz', int: 3} + }) + + const noChangeSpy = jasmine.createSpy('unchanged') + atom.config.onDidChange('foo.bar', (noChangeSpy)) + + atom.config.resetUserSettings({ + foo: {bar: 'baz', int: 4} + }) + + expect(noChangeSpy).not.toHaveBeenCalled() + expect(atom.config.get('foo.bar')).toBe('baz') + expect(atom.config.get('foo.int')).toBe(4) + }) + + it('does not fire a change event for paths whose non-primitive values did not change', () => { + atom.config.setSchema('foo.bar', { + type: 'array', + items: { + type: 'string' + } + }) + + atom.config.resetUserSettings({ + foo: {bar: ['baz', 'quux'], int: 2} + }) + + const noChangeSpy = jasmine.createSpy('unchanged') + atom.config.onDidChange('foo.bar', (noChangeSpy)) + + atom.config.resetUserSettings({ + foo: {bar: ['baz', 'quux'], int: 2} + }) + + expect(noChangeSpy).not.toHaveBeenCalled() + expect(atom.config.get('foo.bar')).toEqual(['baz', 'quux']) + }) + + describe('when a setting with a default is removed', () => { + it('resets the setting back to the default', () => { + atom.config.resetUserSettings({ + foo: {bar: ['baz', 'quux'], int: 2} + }) + + const events = [] + atom.config.onDidChange('foo.int', event => events.push(event)) + + atom.config.resetUserSettings({ + foo: {bar: ['baz', 'quux']} + }) + + expect(events.length).toBe(1) + expect(events[0]).toEqual({oldValue: 2, newValue: 12}) + }) + }) + }) + + describe('.pushAtKeyPath(keyPath, value)', () => { + it('pushes the given value to the array at the key path and updates observers', () => { + atom.config.set('foo.bar.baz', ['a']) + const observeHandler = jasmine.createSpy('observeHandler') + atom.config.observe('foo.bar.baz', observeHandler) + observeHandler.reset() + + expect(atom.config.pushAtKeyPath('foo.bar.baz', 'b')).toBe(2) + expect(atom.config.get('foo.bar.baz')).toEqual(['a', 'b']) + expect(observeHandler).toHaveBeenCalledWith(atom.config.get('foo.bar.baz')) + }) + }) + + describe('.unshiftAtKeyPath(keyPath, value)', () => { + it('unshifts the given value to the array at the key path and updates observers', () => { + atom.config.set('foo.bar.baz', ['b']) + const observeHandler = jasmine.createSpy('observeHandler') + atom.config.observe('foo.bar.baz', observeHandler) + observeHandler.reset() + + expect(atom.config.unshiftAtKeyPath('foo.bar.baz', 'a')).toBe(2) + expect(atom.config.get('foo.bar.baz')).toEqual(['a', 'b']) + expect(observeHandler).toHaveBeenCalledWith(atom.config.get('foo.bar.baz')) + }) + }) + + describe('.removeAtKeyPath(keyPath, value)', () => { + it('removes the given value from the array at the key path and updates observers', () => { + atom.config.set('foo.bar.baz', ['a', 'b', 'c']) + const observeHandler = jasmine.createSpy('observeHandler') + atom.config.observe('foo.bar.baz', observeHandler) + observeHandler.reset() + + expect(atom.config.removeAtKeyPath('foo.bar.baz', 'b')).toEqual(['a', 'c']) + expect(atom.config.get('foo.bar.baz')).toEqual(['a', 'c']) + expect(observeHandler).toHaveBeenCalledWith(atom.config.get('foo.bar.baz')) + }) + }) + + describe('.setDefaults(keyPath, defaults)', () => { + it('assigns any previously-unassigned keys to the object at the key path', () => { + atom.config.set('foo.bar.baz', {a: 1}) + atom.config.setDefaults('foo.bar.baz', {a: 2, b: 3, c: 4}) + expect(atom.config.get('foo.bar.baz.a')).toBe(1) + expect(atom.config.get('foo.bar.baz.b')).toBe(3) + expect(atom.config.get('foo.bar.baz.c')).toBe(4) + + atom.config.setDefaults('foo.quux', {x: 0, y: 1}) + expect(atom.config.get('foo.quux.x')).toBe(0) + expect(atom.config.get('foo.quux.y')).toBe(1) + }) + + it('emits an updated event', () => { + const updatedCallback = jasmine.createSpy('updated') + atom.config.onDidChange('foo.bar.baz.a', updatedCallback) + expect(updatedCallback.callCount).toBe(0) + atom.config.setDefaults('foo.bar.baz', {a: 2}) + expect(updatedCallback.callCount).toBe(1) + }) + }) + + describe('.setSchema(keyPath, schema)', () => { + it('creates a properly nested schema', () => { + const schema = { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + } + } + } + + atom.config.setSchema('foo.bar', schema) + + expect(atom.config.getSchema('foo')).toEqual({ + type: 'object', + properties: { + bar: { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + } + } + } + } + }) + }) + + it('sets defaults specified by the schema', () => { + const schema = { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + }, + anObject: { + type: 'object', + properties: { + nestedInt: { + type: 'integer', + default: 24 + }, + nestedObject: { + type: 'object', + properties: { + superNestedInt: { + type: 'integer', + default: 36 + } + } + } + } + } + } + } + + atom.config.setSchema('foo.bar', schema) + expect(atom.config.get('foo.bar.anInt')).toBe(12) + expect(atom.config.get('foo.bar.anObject')).toEqual({ + nestedInt: 24, + nestedObject: { + superNestedInt: 36 + } + }) + + expect(atom.config.get('foo')).toEqual({ + bar: { + anInt: 12, + anObject: { + nestedInt: 24, + nestedObject: { + superNestedInt: 36 + } + } + } + }) + atom.config.set('foo.bar.anObject.nestedObject.superNestedInt', 37) + expect(atom.config.get('foo')).toEqual({ + bar: { + anInt: 12, + anObject: { + nestedInt: 24, + nestedObject: { + superNestedInt: 37 + } + } + } + }) + }) + + it('can set a non-object schema', () => { + const schema = { + type: 'integer', + default: 12 + } + + atom.config.setSchema('foo.bar.anInt', schema) + expect(atom.config.get('foo.bar.anInt')).toBe(12) + expect(atom.config.getSchema('foo.bar.anInt')).toEqual({ + type: 'integer', + default: 12 + }) + }) + + it('allows the schema to be retrieved via ::getSchema', () => { + const schema = { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + } + } + } + + atom.config.setSchema('foo.bar', schema) + + expect(atom.config.getSchema('foo.bar')).toEqual({ + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + } + } + }) + + expect(atom.config.getSchema('foo.bar.anInt')).toEqual({ + type: 'integer', + default: 12 + }) + + expect(atom.config.getSchema('foo.baz')).toEqual({type: 'any'}) + expect(atom.config.getSchema('foo.bar.anInt.baz')).toBe(null) + }) + + it('respects the schema for scoped settings', () => { + const schema = { + type: 'string', + default: 'ok', + scopes: { + '.source.js': { + default: 'omg' + } + } + } + atom.config.setSchema('foo.bar.str', schema) + + expect(atom.config.get('foo.bar.str')).toBe('ok') + expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('omg') + expect(atom.config.get('foo.bar.str', {scope: ['.source.coffee']})).toBe('ok') + }) + + describe('when a schema is added after config values have been set', () => { + let schema = null + beforeEach(() => { + schema = { + type: 'object', + properties: { + int: { + type: 'integer', + default: 2 + }, + str: { + type: 'string', + default: 'def' + } + } + } + }) + + it('respects the new schema when values are set', () => { + expect(atom.config.set('foo.bar.str', 'global')).toBe(true) + expect(atom.config.set('foo.bar.str', 'scoped', {scopeSelector: '.source.js'})).toBe(true) + expect(atom.config.get('foo.bar.str')).toBe('global') + expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped') + + expect(atom.config.set('foo.bar.noschema', 'nsGlobal')).toBe(true) + expect(atom.config.set('foo.bar.noschema', 'nsScoped', {scopeSelector: '.source.js'})).toBe(true) + expect(atom.config.get('foo.bar.noschema')).toBe('nsGlobal') + expect(atom.config.get('foo.bar.noschema', {scope: ['.source.js']})).toBe('nsScoped') + + expect(atom.config.set('foo.bar.int', 'nope')).toBe(true) + expect(atom.config.set('foo.bar.int', 'notanint', {scopeSelector: '.source.js'})).toBe(true) + expect(atom.config.set('foo.bar.int', 23, {scopeSelector: '.source.coffee'})).toBe(true) + expect(atom.config.get('foo.bar.int')).toBe('nope') + expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe('notanint') + expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + + atom.config.setSchema('foo.bar', schema) + + expect(atom.config.get('foo.bar.str')).toBe('global') + expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped') + expect(atom.config.get('foo.bar.noschema')).toBe('nsGlobal') + expect(atom.config.get('foo.bar.noschema', {scope: ['.source.js']})).toBe('nsScoped') + + expect(atom.config.get('foo.bar.int')).toBe(2) + expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe(2) + expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + }) + + it('sets all values that adhere to the schema', () => { + expect(atom.config.set('foo.bar.int', 10)).toBe(true) + expect(atom.config.set('foo.bar.int', 15, {scopeSelector: '.source.js'})).toBe(true) + expect(atom.config.set('foo.bar.int', 23, {scopeSelector: '.source.coffee'})).toBe(true) + expect(atom.config.get('foo.bar.int')).toBe(10) + expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe(15) + expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + + atom.config.setSchema('foo.bar', schema) + + expect(atom.config.get('foo.bar.int')).toBe(10) + expect(atom.config.get('foo.bar.int', {scope: ['.source.js']})).toBe(15) + expect(atom.config.get('foo.bar.int', {scope: ['.source.coffee']})).toBe(23) + }) + }) + + describe('when the value has an "integer" type', () => { + beforeEach(() => { + const schema = { + type: 'integer', + default: 12 + } + atom.config.setSchema('foo.bar.anInt', schema) + }) + + it('coerces a string to an int', () => { + atom.config.set('foo.bar.anInt', '123') + expect(atom.config.get('foo.bar.anInt')).toBe(123) + }) + + it('does not allow infinity', () => { + atom.config.set('foo.bar.anInt', Infinity) + expect(atom.config.get('foo.bar.anInt')).toBe(12) + }) + + it('coerces a float to an int', () => { + atom.config.set('foo.bar.anInt', 12.3) + expect(atom.config.get('foo.bar.anInt')).toBe(12) + }) + + it('will not set non-integers', () => { + atom.config.set('foo.bar.anInt', null) + expect(atom.config.get('foo.bar.anInt')).toBe(12) + + atom.config.set('foo.bar.anInt', 'nope') + expect(atom.config.get('foo.bar.anInt')).toBe(12) + }) + + describe('when the minimum and maximum keys are used', () => { + beforeEach(() => { + const schema = { + type: 'integer', + minimum: 10, + maximum: 20, + default: 12 + } + atom.config.setSchema('foo.bar.anInt', schema) + }) + + it('keeps the specified value within the specified range', () => { + atom.config.set('foo.bar.anInt', '123') + expect(atom.config.get('foo.bar.anInt')).toBe(20) + + atom.config.set('foo.bar.anInt', '1') + expect(atom.config.get('foo.bar.anInt')).toBe(10) + }) + }) + }) + + describe('when the value has an "integer" and "string" type', () => { + beforeEach(() => { + const schema = { + type: ['integer', 'string'], + default: 12 + } + atom.config.setSchema('foo.bar.anInt', schema) + }) + + it('can coerce an int, and fallback to a string', () => { + atom.config.set('foo.bar.anInt', '123') + expect(atom.config.get('foo.bar.anInt')).toBe(123) + + atom.config.set('foo.bar.anInt', 'cats') + expect(atom.config.get('foo.bar.anInt')).toBe('cats') + }) + }) + + describe('when the value has an "string" and "boolean" type', () => { + beforeEach(() => { + const schema = { + type: ['string', 'boolean'], + default: 'def' + } + atom.config.setSchema('foo.bar', schema) + }) + + it('can set a string, a boolean, and revert back to the default', () => { + atom.config.set('foo.bar', 'ok') + expect(atom.config.get('foo.bar')).toBe('ok') + + atom.config.set('foo.bar', false) + expect(atom.config.get('foo.bar')).toBe(false) + + atom.config.set('foo.bar', undefined) + expect(atom.config.get('foo.bar')).toBe('def') + }) + }) + + describe('when the value has a "number" type', () => { + beforeEach(() => { + const schema = { + type: 'number', + default: 12.1 + } + atom.config.setSchema('foo.bar.aFloat', schema) + }) + + it('coerces a string to a float', () => { + atom.config.set('foo.bar.aFloat', '12.23') + expect(atom.config.get('foo.bar.aFloat')).toBe(12.23) + }) + + it('will not set non-numbers', () => { + atom.config.set('foo.bar.aFloat', null) + expect(atom.config.get('foo.bar.aFloat')).toBe(12.1) + + atom.config.set('foo.bar.aFloat', 'nope') + expect(atom.config.get('foo.bar.aFloat')).toBe(12.1) + }) + + describe('when the minimum and maximum keys are used', () => { + beforeEach(() => { + const schema = { + type: 'number', + minimum: 11.2, + maximum: 25.4, + default: 12.1 + } + atom.config.setSchema('foo.bar.aFloat', schema) + }) + + it('keeps the specified value within the specified range', () => { + atom.config.set('foo.bar.aFloat', '123.2') + expect(atom.config.get('foo.bar.aFloat')).toBe(25.4) + + atom.config.set('foo.bar.aFloat', '1.0') + expect(atom.config.get('foo.bar.aFloat')).toBe(11.2) + }) + }) + }) + + describe('when the value has a "boolean" type', () => { + beforeEach(() => { + const schema = { + type: 'boolean', + default: true + } + atom.config.setSchema('foo.bar.aBool', schema) + }) + + it('coerces various types to a boolean', () => { + atom.config.set('foo.bar.aBool', 'true') + expect(atom.config.get('foo.bar.aBool')).toBe(true) + atom.config.set('foo.bar.aBool', 'false') + expect(atom.config.get('foo.bar.aBool')).toBe(false) + atom.config.set('foo.bar.aBool', 'TRUE') + expect(atom.config.get('foo.bar.aBool')).toBe(true) + atom.config.set('foo.bar.aBool', 'FALSE') + expect(atom.config.get('foo.bar.aBool')).toBe(false) + atom.config.set('foo.bar.aBool', 1) + expect(atom.config.get('foo.bar.aBool')).toBe(false) + atom.config.set('foo.bar.aBool', 0) + expect(atom.config.get('foo.bar.aBool')).toBe(false) + atom.config.set('foo.bar.aBool', {}) + expect(atom.config.get('foo.bar.aBool')).toBe(false) + atom.config.set('foo.bar.aBool', null) + expect(atom.config.get('foo.bar.aBool')).toBe(false) + }) + + it('reverts back to the default value when undefined is passed to set', () => { + atom.config.set('foo.bar.aBool', 'false') + expect(atom.config.get('foo.bar.aBool')).toBe(false) + + atom.config.set('foo.bar.aBool', undefined) + expect(atom.config.get('foo.bar.aBool')).toBe(true) + }) + }) + + describe('when the value has an "string" type', () => { + beforeEach(() => { + const schema = { + type: 'string', + default: 'ok' + } + atom.config.setSchema('foo.bar.aString', schema) + }) + + it('allows strings', () => { + atom.config.set('foo.bar.aString', 'yep') + expect(atom.config.get('foo.bar.aString')).toBe('yep') + }) + + it('will only set strings', () => { + expect(atom.config.set('foo.bar.aString', 123)).toBe(false) + expect(atom.config.get('foo.bar.aString')).toBe('ok') + + expect(atom.config.set('foo.bar.aString', true)).toBe(false) + expect(atom.config.get('foo.bar.aString')).toBe('ok') + + expect(atom.config.set('foo.bar.aString', null)).toBe(false) + expect(atom.config.get('foo.bar.aString')).toBe('ok') + + expect(atom.config.set('foo.bar.aString', [])).toBe(false) + expect(atom.config.get('foo.bar.aString')).toBe('ok') + + expect(atom.config.set('foo.bar.aString', {nope: 'nope'})).toBe(false) + expect(atom.config.get('foo.bar.aString')).toBe('ok') + }) + + it('does not allow setting children of that key-path', () => { + expect(atom.config.set('foo.bar.aString.something', 123)).toBe(false) + expect(atom.config.get('foo.bar.aString')).toBe('ok') + }) + + describe('when the schema has a "maximumLength" key', () => + it('trims the string to be no longer than the specified maximum', () => { + const schema = { + type: 'string', + default: 'ok', + maximumLength: 3 + } + atom.config.setSchema('foo.bar.aString', schema) + atom.config.set('foo.bar.aString', 'abcdefg') + expect(atom.config.get('foo.bar.aString')).toBe('abc') + }) + ) + }) + + describe('when the value has an "object" type', () => { + beforeEach(() => { + const schema = { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + }, + nestedObject: { + type: 'object', + properties: { + nestedBool: { + type: 'boolean', + default: false + } + } + } + } + } + atom.config.setSchema('foo.bar', schema) + }) + + it('converts and validates all the children', () => { + atom.config.set('foo.bar', { + anInt: '23', + nestedObject: { + nestedBool: 'true' + } + } + ) + expect(atom.config.get('foo.bar')).toEqual({ + anInt: 23, + nestedObject: { + nestedBool: true + } + }) + }) + + it('will set only the values that adhere to the schema', () => { + expect(atom.config.set('foo.bar', { + anInt: 'nope', + nestedObject: { + nestedBool: true + } + } + )).toBe(true) + expect(atom.config.get('foo.bar.anInt')).toEqual(12) + expect(atom.config.get('foo.bar.nestedObject.nestedBool')).toEqual(true) + }) + + describe('when the value has additionalProperties set to false', () => + it('does not allow other properties to be set on the object', () => { + atom.config.setSchema('foo.bar', { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + } + }, + additionalProperties: false + } + ) + + expect(atom.config.set('foo.bar', {anInt: 5, somethingElse: 'ok'})).toBe(true) + expect(atom.config.get('foo.bar.anInt')).toBe(5) + expect(atom.config.get('foo.bar.somethingElse')).toBeUndefined() + + expect(atom.config.set('foo.bar.somethingElse', {anInt: 5})).toBe(false) + expect(atom.config.get('foo.bar.somethingElse')).toBeUndefined() + }) + ) + + describe('when the value has an additionalProperties schema', () => + it('validates properties of the object against that schema', () => { + atom.config.setSchema('foo.bar', { + type: 'object', + properties: { + anInt: { + type: 'integer', + default: 12 + } + }, + additionalProperties: { + type: 'string' + } + } + ) + + expect(atom.config.set('foo.bar', {anInt: 5, somethingElse: 'ok'})).toBe(true) + expect(atom.config.get('foo.bar.anInt')).toBe(5) + expect(atom.config.get('foo.bar.somethingElse')).toBe('ok') + + expect(atom.config.set('foo.bar.somethingElse', 7)).toBe(false) + expect(atom.config.get('foo.bar.somethingElse')).toBe('ok') + + expect(atom.config.set('foo.bar', {anInt: 6, somethingElse: 7})).toBe(true) + expect(atom.config.get('foo.bar.anInt')).toBe(6) + expect(atom.config.get('foo.bar.somethingElse')).toBe(undefined) + }) + ) + }) + + describe('when the value has an "array" type', () => { + beforeEach(() => { + const schema = { + type: 'array', + default: [1, 2, 3], + items: { + type: 'integer' + } + } + atom.config.setSchema('foo.bar', schema) + }) + + it('converts an array of strings to an array of ints', () => { + atom.config.set('foo.bar', ['2', '3', '4']) + expect(atom.config.get('foo.bar')).toEqual([2, 3, 4]) + }) + + it('does not allow setting children of that key-path', () => { + expect(atom.config.set('foo.bar.child', 123)).toBe(false) + expect(atom.config.set('foo.bar.child.grandchild', 123)).toBe(false) + expect(atom.config.get('foo.bar')).toEqual([1, 2, 3]) + }) + }) + + describe('when the value has a "color" type', () => { + beforeEach(() => { + const schema = { + type: 'color', + default: 'white' + } + atom.config.setSchema('foo.bar.aColor', schema) + }) + + it('returns a Color object', () => { + let color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe('#ffffff') + expect(color.toRGBAString()).toBe('rgba(255, 255, 255, 1)') + + color.red = 0 + color.green = 0 + color.blue = 0 + color.alpha = 0 + atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe('#000000') + expect(color.toRGBAString()).toBe('rgba(0, 0, 0, 0)') + + color.red = 300 + color.green = -200 + color.blue = -1 + color.alpha = 'not see through' + atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe('#ff0000') + expect(color.toRGBAString()).toBe('rgba(255, 0, 0, 1)') + + color.red = 11 + color.green = 11 + color.blue = 124 + color.alpha = 1 + atom.config.set('foo.bar.aColor', color) + + color = atom.config.get('foo.bar.aColor') + expect(color.toHexString()).toBe('#0b0b7c') + expect(color.toRGBAString()).toBe('rgba(11, 11, 124, 1)') + }) + + it('coerces various types to a color object', () => { + atom.config.set('foo.bar.aColor', 'red') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 0, blue: 0, alpha: 1}) + atom.config.set('foo.bar.aColor', '#020') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 34, blue: 0, alpha: 1}) + atom.config.set('foo.bar.aColor', '#abcdef') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 171, green: 205, blue: 239, alpha: 1}) + atom.config.set('foo.bar.aColor', 'rgb(1,2,3)') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 1, green: 2, blue: 3, alpha: 1}) + atom.config.set('foo.bar.aColor', 'rgba(4,5,6,.7)') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 4, green: 5, blue: 6, alpha: 0.7}) + atom.config.set('foo.bar.aColor', 'hsl(120,100%,50%)') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 255, blue: 0, alpha: 1}) + atom.config.set('foo.bar.aColor', 'hsla(120,100%,50%,0.3)') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 255, blue: 0, alpha: 0.3}) + atom.config.set('foo.bar.aColor', {red: 100, green: 255, blue: 2, alpha: 0.5}) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 100, green: 255, blue: 2, alpha: 0.5}) + atom.config.set('foo.bar.aColor', {red: 255}) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 0, blue: 0, alpha: 1}) + atom.config.set('foo.bar.aColor', {red: 1000}) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 0, blue: 0, alpha: 1}) + atom.config.set('foo.bar.aColor', {red: 'dark'}) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 0, green: 0, blue: 0, alpha: 1}) + }) + + it('reverts back to the default value when undefined is passed to set', () => { + atom.config.set('foo.bar.aColor', undefined) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + }) + + it('will not set non-colors', () => { + atom.config.set('foo.bar.aColor', null) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + + atom.config.set('foo.bar.aColor', 'nope') + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + + atom.config.set('foo.bar.aColor', 30) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + + atom.config.set('foo.bar.aColor', false) + expect(atom.config.get('foo.bar.aColor')).toEqual({red: 255, green: 255, blue: 255, alpha: 1}) + }) + + it('returns a clone of the Color when returned in a parent object', () => { + const color1 = atom.config.get('foo.bar').aColor + const color2 = atom.config.get('foo.bar').aColor + expect(color1.toRGBAString()).toBe('rgba(255, 255, 255, 1)') + expect(color2.toRGBAString()).toBe('rgba(255, 255, 255, 1)') + expect(color1).not.toBe(color2) + expect(color1).toEqual(color2) + }) + }) + + describe('when the `enum` key is used', () => { + beforeEach(() => { + const schema = { + type: 'object', + properties: { + str: { + type: 'string', + default: 'ok', + enum: ['ok', 'one', 'two'] + }, + int: { + type: 'integer', + default: 2, + enum: [2, 3, 5] + }, + arr: { + type: 'array', + default: ['one', 'two'], + items: { + type: 'string', + enum: ['one', 'two', 'three'] + } + }, + str_options: { + type: 'string', + default: 'one', + enum: [ + {value: 'one', description: 'One'}, + 'two', + {value: 'three', description: 'Three'} + ] + } + } + } + + atom.config.setSchema('foo.bar', schema) + }) + + it('will only set a string when the string is in the enum values', () => { + expect(atom.config.set('foo.bar.str', 'nope')).toBe(false) + expect(atom.config.get('foo.bar.str')).toBe('ok') + + expect(atom.config.set('foo.bar.str', 'one')).toBe(true) + expect(atom.config.get('foo.bar.str')).toBe('one') + }) + + it('will only set an integer when the integer is in the enum values', () => { + expect(atom.config.set('foo.bar.int', '400')).toBe(false) + expect(atom.config.get('foo.bar.int')).toBe(2) + + expect(atom.config.set('foo.bar.int', '3')).toBe(true) + expect(atom.config.get('foo.bar.int')).toBe(3) + }) + + it('will only set an array when the array values are in the enum values', () => { + expect(atom.config.set('foo.bar.arr', ['one', 'five'])).toBe(true) + expect(atom.config.get('foo.bar.arr')).toEqual(['one']) + + expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe(true) + expect(atom.config.get('foo.bar.arr')).toEqual(['two', 'three']) + }) + + it('will honor the enum when specified as an array', () => { + expect(atom.config.set('foo.bar.str_options', 'one')).toBe(true) + expect(atom.config.get('foo.bar.str_options')).toEqual('one') + + expect(atom.config.set('foo.bar.str_options', 'two')).toBe(true) + expect(atom.config.get('foo.bar.str_options')).toEqual('two') + + expect(atom.config.set('foo.bar.str_options', 'One')).toBe(false) + expect(atom.config.get('foo.bar.str_options')).toEqual('two') + }) + }) + }) + + describe('when .set/.unset is called prior to .resetUserSettings', () => { + beforeEach(() => { + atom.config.settingsLoaded = false + }) + + it('ensures that early set and unset calls are replayed after the config is loaded from disk', () => { + atom.config.unset('foo.bar') + atom.config.set('foo.qux', 'boo') + + expect(atom.config.get('foo.bar')).toBeUndefined() + expect(atom.config.get('foo.qux')).toBe('boo') + expect(atom.config.get('do.ray')).toBeUndefined() + + advanceClock(100) + expect(savedSettings.length).toBe(0) + + atom.config.resetUserSettings({ + '*': { + foo: { + bar: 'baz' + }, + do: { + ray: 'me' + } + } + }) + + advanceClock(100) + expect(savedSettings.length).toBe(1) + expect(atom.config.get('foo.bar')).toBeUndefined() + expect(atom.config.get('foo.qux')).toBe('boo') + expect(atom.config.get('do.ray')).toBe('me') + }) + }) + + describe('project specific settings', () => { + describe('config.resetProjectSettings', () => { + it('gracefully handles invalid config objects', () => { + atom.config.resetProjectSettings({}) + expect(atom.config.get('foo.bar')).toBeUndefined() + }) + }) + + describe('config.get', () => { + const dummyPath = '/Users/dummy/path.json' + describe('project settings', () => { + it('returns a deep clone of the property value', () => { + atom.config.resetProjectSettings({'*': {'value': {array: [1, {b: 2}, 3]}}}, dummyPath) + const retrievedValue = atom.config.get('value') + retrievedValue.array[0] = 4 + retrievedValue.array[1].b = 2.1 + expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]}) + }) + + it('properly gets project settings', () => { + atom.config.resetProjectSettings({'*': {'foo': 'wei'}}, dummyPath) + expect(atom.config.get('foo')).toBe('wei') + atom.config.resetProjectSettings({'*': {'foo': {'bar': 'baz'}}}, dummyPath) + expect(atom.config.get('foo.bar')).toBe('baz') + }) + + it('gets project settings with higher priority than regular settings', () => { + atom.config.set('foo', 'bar') + atom.config.resetProjectSettings({'*': {'foo': 'baz'}}, dummyPath) + expect(atom.config.get('foo')).toBe('baz') + }) + + it('correctly gets nested and scoped properties for project settings', () => { + expect(atom.config.set('foo.bar.str', 'global')).toBe(true) + expect(atom.config.set('foo.bar.str', 'scoped', {scopeSelector: '.source.js'})).toBe(true) + expect(atom.config.get('foo.bar.str')).toBe('global') + expect(atom.config.get('foo.bar.str', {scope: ['.source.js']})).toBe('scoped') + }) + + it('returns a deep clone of the property value', () => { + atom.config.set('value', {array: [1, {b: 2}, 3]}) + const retrievedValue = atom.config.get('value') + retrievedValue.array[0] = 4 + retrievedValue.array[1].b = 2.1 + expect(atom.config.get('value')).toEqual({array: [1, {b: 2}, 3]}) + }) + + it('gets scoped values correctly', () => { + atom.config.set('foo', 'bam', {scope: ['second']}) + expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('bam') + atom.config.resetProjectSettings({'*': {'foo': 'baz'}, 'second': {'foo': 'bar'}}, dummyPath) + expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('baz') + atom.config.clearProjectSettings() + expect(atom.config.get('foo', {'scopeSelector': 'second'})).toBe('bam') + }) + + it('clears project settings correctly', () => { + atom.config.set('foo', 'bar') + expect(atom.config.get('foo')).toBe('bar') + atom.config.resetProjectSettings({'*': {'foo': 'baz'}, 'second': {'foo': 'bar'}}, dummyPath) + expect(atom.config.get('foo')).toBe('baz') + expect(atom.config.getSources().length).toBe(1) + atom.config.clearProjectSettings() + expect(atom.config.get('foo')).toBe('bar') + expect(atom.config.getSources().length).toBe(0) + }) + }) + }) + + describe('config.getAll', () => { + const dummyPath = '/Users/dummy/path.json' + it('gets settings in the same way .get would return them', () => { + atom.config.resetProjectSettings({'*': {'a': 'b'}}, dummyPath) + atom.config.set('a', 'f') + expect(atom.config.getAll('a')).toEqual([{ + scopeSelector: '*', + value: 'b' + }]) + }) + }) + }) +}) diff --git a/spec/context-menu-manager-spec.coffee b/spec/context-menu-manager-spec.coffee index 035d56a61..70ab4b3e7 100644 --- a/spec/context-menu-manager-spec.coffee +++ b/spec/context-menu-manager-spec.coffee @@ -333,3 +333,46 @@ describe "ContextMenuManager", -> } ] ]) + + describe "::templateForEvent(target) (sorting)", -> + it "applies simple sorting rules", -> + contextMenu.add('.parent': [{ + label: 'My Command', + command: "test:my-command", + after: ["test:my-other-command"] + }, { + label: 'My Other Command', + command: "test:my-other-command", + }]) + dispatchedEvent = {target: parent} + expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual([{ + label: 'My Other Command', + command: 'test:my-other-command', + }, { + label: 'My Command', + command: 'test:my-command', + after: ["test:my-other-command"] + }]) + + it "applies sorting rules recursively to submenus", -> + contextMenu.add('.parent': [{ + submenu: [{ + label: 'My Command', + command: "test:my-command", + after: ["test:my-other-command"] + }, { + label: 'My Other Command', + command: "test:my-other-command", + }] + }]) + dispatchedEvent = {target: parent} + expect(contextMenu.templateForEvent(dispatchedEvent)).toEqual([{ + submenu: [{ + label: 'My Other Command', + command: 'test:my-other-command', + }, { + label: 'My Command', + command: 'test:my-command', + after: ["test:my-other-command"] + }] + }]) diff --git a/spec/dock-spec.js b/spec/dock-spec.js index d4db460ae..4713347a8 100644 --- a/spec/dock-spec.js +++ b/spec/dock-spec.js @@ -3,6 +3,9 @@ const Grim = require('grim') import {it, fit, ffit, fffit, beforeEach, afterEach} from './async-spec-helpers' +import etch from 'etch' + +const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise describe('Dock', () => { describe('when a dock is activated', () => { @@ -157,8 +160,10 @@ describe('Dock', () => { const dockElement = dock.getElement() dock.setState({size: 300}) + await getNextUpdatePromise() expect(dockElement.offsetWidth).toBe(300) dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2})) + await getNextUpdatePromise() expect(dockElement.offsetWidth).toBe(item.getPreferredWidth()) }) @@ -178,8 +183,10 @@ describe('Dock', () => { const dockElement = dock.getElement() dock.setState({size: 300}) + await getNextUpdatePromise() expect(dockElement.offsetHeight).toBe(300) dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2})) + await getNextUpdatePromise() expect(dockElement.offsetHeight).toBe(item.getPreferredHeight()) }) @@ -201,11 +208,7 @@ describe('Dock', () => { const dockElement = atom.workspace.getBottomDock().getElement() dockElement.querySelector('.atom-dock-resize-handle').dispatchEvent(new MouseEvent('mousedown', {detail: 2})) expect(dockElement.offsetHeight).toBe(0) - - // There should still be a hoverable, absolutely-positioned element so users can reveal the - // toggle affordance even when fullscreened. - expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe(1) - + expect(dockElement.querySelector('.atom-dock-inner').offsetHeight).toBe(0) // The content should be masked away. expect(dockElement.querySelector('.atom-dock-mask').offsetHeight).toBe(0) }) @@ -314,7 +317,7 @@ describe('Dock', () => { }) describe('drag handling', () => { - it('expands docks to match the preferred size of the dragged item', () => { + it('expands docks to match the preferred size of the dragged item', async () => { jasmine.attachToDOM(atom.workspace.getElement()) const element = document.createElement('div') @@ -329,7 +332,8 @@ describe('Dock', () => { Object.defineProperty(dragEvent, 'target', {value: element}) atom.workspace.getElement().handleDragStart(dragEvent) - expect(atom.workspace.getLeftDock().wrapperElement.offsetWidth).toBe(144) + await getNextUpdatePromise() + expect(atom.workspace.getLeftDock().refs.wrapperElement.offsetWidth).toBe(144) }) it('does nothing when text nodes are dragged', () => { diff --git a/spec/fixtures/babel/flow-slash-comment.js b/spec/fixtures/babel/flow-slash-comment.js new file mode 100644 index 000000000..b73cc7833 --- /dev/null +++ b/spec/fixtures/babel/flow-slash-comment.js @@ -0,0 +1,4 @@ +// @flow + +const f: Function = v => v + 1 +module.exports = f diff --git a/spec/fixtures/git/master.git/config b/spec/fixtures/git/master.git/config index af107929f..c9da546d6 100644 --- a/spec/fixtures/git/master.git/config +++ b/spec/fixtures/git/master.git/config @@ -4,3 +4,6 @@ bare = false logallrefupdates = true ignorecase = true +[remote "origin"] + url = https://github.com/example-user/example-repo.git + fetch = +refs/heads/*:refs/remotes/origin/* diff --git a/spec/fixtures/packages/package-with-tree-sitter-grammar/grammars/some-language.cson b/spec/fixtures/packages/package-with-tree-sitter-grammar/grammars/some-language.cson index 5eb473456..f6b32e532 100644 --- a/spec/fixtures/packages/package-with-tree-sitter-grammar/grammars/some-language.cson +++ b/spec/fixtures/packages/package-with-tree-sitter-grammar/grammars/some-language.cson @@ -1,6 +1,6 @@ name: 'Some Language' -id: 'some-language' +scopeName: 'some-language' type: 'tree-sitter' diff --git a/spec/git-repository-spec.js b/spec/git-repository-spec.js index 61c80ee48..65548fb3b 100644 --- a/spec/git-repository-spec.js +++ b/spec/git-repository-spec.js @@ -15,11 +15,6 @@ describe('GitRepository', () => { afterEach(() => { if (repo && !repo.isDestroyed()) repo.destroy() - - // These tests sometimes lag at shutting down resources - try { - temp.cleanupSync() - } catch (error) {} }) describe('@open(path)', () => { diff --git a/spec/grammar-registry-spec.js b/spec/grammar-registry-spec.js index e6d815f8d..fb0a4fb1f 100644 --- a/spec/grammar-registry-spec.js +++ b/spec/grammar-registry-spec.js @@ -14,6 +14,7 @@ describe('GrammarRegistry', () => { beforeEach(() => { grammarRegistry = new GrammarRegistry({config: atom.config}) + expect(subscriptionCount(grammarRegistry)).toBe(1) }) describe('.assignLanguageMode(buffer, languageId)', () => { @@ -24,6 +25,7 @@ describe('GrammarRegistry', () => { const buffer = new TextBuffer() expect(grammarRegistry.assignLanguageMode(buffer, 'source.js')).toBe(true) expect(buffer.getLanguageMode().getLanguageId()).toBe('source.js') + expect(grammarRegistry.getAssignedLanguageId(buffer)).toBe('source.js') // Returns true if we found the grammar, even if it didn't change expect(grammarRegistry.assignLanguageMode(buffer, 'source.js')).toBe(true) @@ -47,18 +49,19 @@ describe('GrammarRegistry', () => { expect(grammarRegistry.assignLanguageMode(buffer, null)).toBe(true) expect(buffer.getLanguageMode().getLanguageId()).toBe('text.plain.null-grammar') + expect(grammarRegistry.getAssignedLanguageId(buffer)).toBe(null) }) }) }) describe('.grammarForId(languageId)', () => { - it('converts the language id to a text-mate language id when `core.useTreeSitterParsers` is false', () => { + it('returns a text-mate grammar when `core.useTreeSitterParsers` is false', () => { atom.config.set('core.useTreeSitterParsers', false) grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) - const grammar = grammarRegistry.grammarForId('javascript') + const grammar = grammarRegistry.grammarForId('source.js') expect(grammar instanceof FirstMate.Grammar).toBe(true) expect(grammar.scopeName).toBe('source.js') @@ -66,7 +69,7 @@ describe('GrammarRegistry', () => { expect(grammarRegistry.grammarForId('javascript')).toBe(undefined) }) - it('converts the language id to a tree-sitter language id when `core.useTreeSitterParsers` is true', () => { + it('returns a tree-sitter grammar when `core.useTreeSitterParsers` is true', () => { atom.config.set('core.useTreeSitterParsers', true) grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) @@ -74,7 +77,7 @@ describe('GrammarRegistry', () => { const grammar = grammarRegistry.grammarForId('source.js') expect(grammar instanceof TreeSitterGrammar).toBe(true) - expect(grammar.id).toBe('javascript') + expect(grammar.scopeName).toBe('source.js') grammarRegistry.removeGrammar(grammar) expect(grammarRegistry.grammarForId('source.js') instanceof FirstMate.Grammar).toBe(true) @@ -120,11 +123,11 @@ describe('GrammarRegistry', () => { buffer.setPath('test.js') grammarRegistry.maintainLanguageMode(buffer) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - expect(buffer.getLanguageMode().getLanguageId()).toBe('source.js') + const textMateGrammar = grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) + expect(buffer.getLanguageMode().grammar).toBe(textMateGrammar) grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) - expect(buffer.getLanguageMode().getLanguageId()).toBe('source.js') + expect(buffer.getLanguageMode().grammar).toBe(textMateGrammar) }) it('updates the buffer\'s grammar when a more appropriate tree-sitter grammar is added for its path', async () => { @@ -136,11 +139,11 @@ describe('GrammarRegistry', () => { buffer.setPath('test.js') grammarRegistry.maintainLanguageMode(buffer) - grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) - expect(buffer.getLanguageMode().getLanguageId()).toBe('javascript') + const treeSitterGrammar = grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + expect(buffer.getLanguageMode().grammar).toBe(treeSitterGrammar) grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/javascript.cson')) - expect(buffer.getLanguageMode().getLanguageId()).toBe('javascript') + expect(buffer.getLanguageMode().grammar).toBe(treeSitterGrammar) }) it('can be overridden by calling .assignLanguageMode', () => { @@ -205,16 +208,16 @@ describe('GrammarRegistry', () => { const disposable = grammarRegistry.maintainLanguageMode(buffer) expect(retainedBufferCount(grammarRegistry)).toBe(1) - expect(subscriptionCount(grammarRegistry)).toBe(2) + expect(subscriptionCount(grammarRegistry)).toBe(3) buffer.destroy() expect(retainedBufferCount(grammarRegistry)).toBe(0) - expect(subscriptionCount(grammarRegistry)).toBe(0) + expect(subscriptionCount(grammarRegistry)).toBe(1) expect(buffer.emitter.getTotalListenerCount()).toBe(0) disposable.dispose() expect(retainedBufferCount(grammarRegistry)).toBe(0) - expect(subscriptionCount(grammarRegistry)).toBe(0) + expect(subscriptionCount(grammarRegistry)).toBe(1) }) it('does not retain the buffer when the grammar registry is destroyed', () => { @@ -223,12 +226,12 @@ describe('GrammarRegistry', () => { const disposable = grammarRegistry.maintainLanguageMode(buffer) expect(retainedBufferCount(grammarRegistry)).toBe(1) - expect(subscriptionCount(grammarRegistry)).toBe(2) + expect(subscriptionCount(grammarRegistry)).toBe(3) grammarRegistry.clear() expect(retainedBufferCount(grammarRegistry)).toBe(0) - expect(subscriptionCount(grammarRegistry)).toBe(0) + expect(subscriptionCount(grammarRegistry)).toBe(1) expect(buffer.emitter.getTotalListenerCount()).toBe(0) }) }) @@ -280,32 +283,6 @@ describe('GrammarRegistry', () => { expect(atom.grammars.selectGrammar('/hu.git/config').name).toBe('Null Grammar') }) - describe('when the grammar has a contentRegExp field', () => { - it('favors grammars whose contentRegExp matches a prefix of the file\'s content', () => { - atom.grammars.addGrammar({ - id: 'javascript-1', - fileTypes: ['js'] - }) - atom.grammars.addGrammar({ - id: 'flow-javascript', - contentRegExp: new RegExp('//.*@flow'), - fileTypes: ['js'] - }) - atom.grammars.addGrammar({ - id: 'javascript-2', - fileTypes: ['js'] - }) - - const selectedGrammar = atom.grammars.selectGrammar('test.js', dedent` - // Copyright EvilCorp - // @flow - - module.exports = function () { return 1 + 1 } - `) - expect(selectedGrammar.id).toBe('flow-javascript') - }) - }) - it("uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", async () => { await atom.packages.activatePackage('language-javascript') await atom.packages.activatePackage('language-ruby') @@ -435,9 +412,127 @@ describe('GrammarRegistry', () => { grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) const grammar = grammarRegistry.selectGrammar('test.js') - expect(grammar.id).toBe('javascript') expect(grammar instanceof TreeSitterGrammar).toBe(true) }) + + it('only favors a tree-sitter grammar if it actually matches in some way (regression)', () => { + atom.config.set('core.useTreeSitterParsers', true) + grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + + const grammar = grammarRegistry.selectGrammar('test', '') + expect(grammar.name).toBe('Null Grammar') + }) + }) + + describe('tree-sitter grammars with content regexes', () => { + it('recognizes C++ header files', () => { + atom.config.set('core.useTreeSitterParsers', true) + grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson')) + grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson')) + grammarRegistry.loadGrammarSync(require.resolve('language-coffee-script/grammars/coffeescript.cson')) + + let grammar = grammarRegistry.selectGrammar('test.h', dedent ` + #include + + typedef struct { + void verb(); + } Noun; + `) + expect(grammar.name).toBe('C') + + grammar = grammarRegistry.selectGrammar('test.h', dedent ` + #include + + class Noun { + public: + void verb(); + }; + `) + expect(grammar.name).toBe('C++') + + // The word `class` only indicates C++ in `.h` files, not in all files. + grammar = grammarRegistry.selectGrammar('test.coffee', dedent ` + module.exports = + class Noun + verb: -> true + `) + expect(grammar.name).toBe('CoffeeScript') + }) + + it('recognizes C++ files that do not match the content regex (regression)', () => { + atom.config.set('core.useTreeSitterParsers', true) + grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-c.cson')) + grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/c++.cson')) + grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson')) + + let grammar = grammarRegistry.selectGrammar('test.cc', dedent ` + int a(); + `) + expect(grammar.name).toBe('C++') + }) + + it('does not apply content regexes from grammars without filetype or first line matches', () => { + atom.config.set('core.useTreeSitterParsers', true) + grammarRegistry.loadGrammarSync(require.resolve('language-c/grammars/tree-sitter-cpp.cson')) + + let grammar = grammarRegistry.selectGrammar('', dedent ` + class Foo + # this is ruby, not C++ + end + `) + + expect(grammar.name).toBe('Null Grammar') + }) + + it('recognizes shell scripts with shebang lines', () => { + atom.config.set('core.useTreeSitterParsers', true) + grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/shell-unix-bash.cson')) + grammarRegistry.loadGrammarSync(require.resolve('language-shellscript/grammars/tree-sitter-bash.cson')) + + let grammar = grammarRegistry.selectGrammar('test.h', dedent ` + #!/bin/bash + + echo "hi" + `) + expect(grammar.name).toBe('Shell Script') + expect(grammar instanceof TreeSitterGrammar).toBeTruthy() + + grammar = grammarRegistry.selectGrammar('test.h', dedent ` + # vim: set ft=bash + + echo "hi" + `) + expect(grammar.name).toBe('Shell Script') + expect(grammar instanceof TreeSitterGrammar).toBeTruthy() + + atom.config.set('core.useTreeSitterParsers', false) + grammar = grammarRegistry.selectGrammar('test.h', dedent ` + #!/bin/bash + + echo "hi" + `) + expect(grammar.name).toBe('Shell Script') + expect(grammar instanceof TreeSitterGrammar).toBeFalsy() + }) + + it('recognizes JavaScript files that use Flow', () => { + atom.config.set('core.useTreeSitterParsers', true) + grammarRegistry.loadGrammarSync(require.resolve('language-javascript/grammars/tree-sitter-javascript.cson')) + grammarRegistry.loadGrammarSync(require.resolve('language-typescript/grammars/tree-sitter-flow.cson')) + + let grammar = grammarRegistry.selectGrammar('test.js', dedent` + // Copyright something + // @flow + + module.exports = function () { return 1 + 1 } + `) + expect(grammar.name).toBe('Flow JavaScript') + + grammar = grammarRegistry.selectGrammar('test.js', dedent` + module.exports = function () { return 1 + 1 } + `) + expect(grammar.name).toBe('JavaScript') + }) }) }) @@ -450,6 +545,34 @@ describe('GrammarRegistry', () => { }) }) + describe('.addInjectionPoint(languageId, {type, language, content})', () => { + const injectionPoint = { + type: 'some_node_type', + language() { return 'some_language_name' }, + content(node) { return node } + } + + beforeEach(() => { + atom.config.set('core.useTreeSitterParsers', true) + }) + + it('adds an injection point to the grammar with the given id', async () => { + await atom.packages.activatePackage('language-javascript') + atom.grammars.addInjectionPoint('javascript', injectionPoint) + const grammar = atom.grammars.grammarForId('javascript') + expect(grammar.injectionPoints).toContain(injectionPoint) + }) + + describe('when called before a grammar with the given id is loaded', () => { + it('adds the injection point once the grammar is loaded', async () => { + atom.grammars.addInjectionPoint('javascript', injectionPoint) + await atom.packages.activatePackage('language-javascript') + const grammar = atom.grammars.grammarForId('javascript') + expect(grammar.injectionPoints).toContain(injectionPoint) + }) + }) + }) + describe('serialization', () => { it('persists editors\' grammar overrides', async () => { const buffer1 = new TextBuffer() diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 90a512692..16aef8e27 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -49,7 +49,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([filePath + ':3'])) + const [window] = await atomApplication.launch(parseCommandLine([filePath + ':3'])) await focusWindow(window) const cursorRow = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { @@ -66,7 +66,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([filePath + ':2:2'])) + const [window] = await atomApplication.launch(parseCommandLine([filePath + ':2:2'])) await focusWindow(window) const cursorPosition = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { @@ -83,7 +83,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath, '1\n2\n3\n4\n') const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([filePath + ':: '])) + const [window] = await atomApplication.launch(parseCommandLine([filePath + ':: '])) await focusWindow(window) const openedPath = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { @@ -99,11 +99,11 @@ describe('AtomApplication', function () { it('positions new windows at an offset distance from the previous window', async () => { const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([makeTempDir()])) + const [window1] = await atomApplication.launch(parseCommandLine([makeTempDir()])) await focusWindow(window1) window1.browserWindow.setBounds({width: 400, height: 400, x: 0, y: 0}) - const window2 = atomApplication.launch(parseCommandLine([makeTempDir()])) + const [window2] = await atomApplication.launch(parseCommandLine([makeTempDir()])) await focusWindow(window2) assert.notEqual(window1, window2) @@ -122,7 +122,7 @@ describe('AtomApplication', function () { fs.writeFileSync(existingDirCFilePath, 'this is an existing file') const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) await emitterEventPromise(window1, 'window:locations-opened') await focusWindow(window1) @@ -135,7 +135,7 @@ describe('AtomApplication', function () { // Reuses the window when opening *files*, even if they're in a different directory // Does not change the project paths when doing so. - const reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine([existingDirCFilePath])) assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { @@ -148,7 +148,7 @@ describe('AtomApplication', function () { assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath]) // Opens new windows when opening directories - const window2 = atomApplication.launch(parseCommandLine([dirCPath])) + const [window2] = await atomApplication.launch(parseCommandLine([dirCPath])) await emitterEventPromise(window2, 'window:locations-opened') assert.notEqual(window2, window1) await focusWindow(window2) @@ -163,7 +163,7 @@ describe('AtomApplication', function () { fs.writeFileSync(existingDirCFilePath, 'this is an existing file') const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath, 'new-file')])) await focusWindow(window1) let activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { @@ -175,7 +175,7 @@ describe('AtomApplication', function () { // When opening *files* with --add, reuses an existing window and adds // parent directory to the project - let reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add'])) + let [reusedWindow] = await atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add'])) assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { @@ -189,7 +189,7 @@ describe('AtomApplication', function () { // When opening *directories* with add reuses an existing window and adds // the directory to the project - reusedWindow = atomApplication.launch(parseCommandLine([dirBPath, '-a'])) + reusedWindow = (await atomApplication.launch(parseCommandLine([dirBPath, '-a'])))[0] assert.equal(reusedWindow, window1) assert.deepEqual(atomApplication.getAllWindows(), [window1]) @@ -202,7 +202,7 @@ describe('AtomApplication', function () { const atomApplication = buildAtomApplication() const nonExistentFilePath = path.join(tempDirPath, 'new-file') - const window1 = atomApplication.launch(parseCommandLine([nonExistentFilePath])) + const [window1] = await atomApplication.launch(parseCommandLine([nonExistentFilePath])) await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { atom.workspace.observeTextEditors(textEditor => { textEditor.insertText('Hello World!') @@ -214,7 +214,7 @@ describe('AtomApplication', function () { await window1.closedPromise // Restore unsaved state when opening the directory itself - const window2 = atomApplication.launch(parseCommandLine([tempDirPath])) + const [window2] = await atomApplication.launch(parseCommandLine([tempDirPath])) await window2.loadedPromise const window2Text = await evalInWebContents(window2.browserWindow.webContents, sendBackToMainProcess => { const textEditor = atom.workspace.getActiveTextEditor() @@ -228,7 +228,7 @@ describe('AtomApplication', function () { await window2.closedPromise // Restore unsaved state when opening a path to a non-existent file in the directory - const window3 = atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'another-non-existent-file')])) + const [window3] = await atomApplication.launch(parseCommandLine([path.join(tempDirPath, 'another-non-existent-file')])) await window3.loadedPromise const window3Texts = await evalInWebContents(window3.browserWindow.webContents, (sendBackToMainProcess, nonExistentFilePath) => { sendBackToMainProcess(atom.workspace.getTextEditors().map(editor => editor.getText())) @@ -243,7 +243,7 @@ describe('AtomApplication', function () { fs.mkdirSync(dirBSubdirPath) const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([dirAPath, dirBPath])) + const [window1] = await atomApplication.launch(parseCommandLine([dirAPath, dirBPath])) await focusWindow(window1) assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirBPath]) @@ -252,17 +252,17 @@ describe('AtomApplication', function () { it('reuses windows with no project paths to open directories', async () => { const tempDirPath = makeTempDir() const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) - const reusedWindow = atomApplication.launch(parseCommandLine([tempDirPath])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine([tempDirPath])) assert.equal(reusedWindow, window1) await conditionPromise(async () => (await getTreeViewRootDirectories(reusedWindow)).length > 0) }) it('opens a new window with a single untitled buffer when launched with no path, even if windows already exist', async () => { const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) const window1EditorTitle = await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { sendBackToMainProcess(atom.workspace.getActiveTextEditor().getTitle()) @@ -287,7 +287,7 @@ describe('AtomApplication', function () { season.writeFileSync(configPath, config) const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window1) // wait a bit just to make sure we don't pass due to querying the render process before it loads @@ -302,7 +302,7 @@ describe('AtomApplication', function () { it('opens an empty text editor and loads its parent directory in the tree-view when launched with a new file path', async () => { const atomApplication = buildAtomApplication() const newFilePath = path.join(makeTempDir(), 'new-file') - const window = atomApplication.launch(parseCommandLine([newFilePath])) + const [window] = await atomApplication.launch(parseCommandLine([newFilePath])) await focusWindow(window) const {editorTitle, editorText} = await evalInWebContents(window.browserWindow.webContents, sendBackToMainProcess => { atom.workspace.observeTextEditors(editor => { @@ -324,7 +324,7 @@ describe('AtomApplication', function () { atomApplication.config.set('core.disabledPackages', ['fuzzy-finder']) const remotePath = 'remote://server:3437/some/directory/path' - let window = atomApplication.launch(parseCommandLine([remotePath])) + let [window] = await atomApplication.launch(parseCommandLine([remotePath])) await focusWindow(window) await conditionPromise(async () => (await getProjectDirectories()).length > 0) @@ -350,9 +350,9 @@ describe('AtomApplication', function () { const tempDirPath2 = makeTempDir() const atomApplication1 = buildAtomApplication() - const app1Window1 = atomApplication1.launch(parseCommandLine([tempDirPath1])) + const [app1Window1] = await atomApplication1.launch(parseCommandLine([tempDirPath1])) await emitterEventPromise(app1Window1, 'window:locations-opened') - const app1Window2 = atomApplication1.launch(parseCommandLine([tempDirPath2])) + const [app1Window2] = await atomApplication1.launch(parseCommandLine([tempDirPath2])) await emitterEventPromise(app1Window2, 'window:locations-opened') await Promise.all([ @@ -361,7 +361,7 @@ describe('AtomApplication', function () { ]) const atomApplication2 = buildAtomApplication() - const [app2Window1, app2Window2] = atomApplication2.launch(parseCommandLine([])) + const [app2Window1, app2Window2] = await atomApplication2.launch(parseCommandLine([])) await Promise.all([ emitterEventPromise(app2Window1, 'window:locations-opened'), emitterEventPromise(app2Window2, 'window:locations-opened') @@ -373,9 +373,9 @@ describe('AtomApplication', function () { it('does not reopen any previously opened windows when launched with no path and `core.restorePreviousWindowsOnStart` is no', async () => { const atomApplication1 = buildAtomApplication() - const app1Window1 = atomApplication1.launch(parseCommandLine([makeTempDir()])) + const [app1Window1] = await atomApplication1.launch(parseCommandLine([makeTempDir()])) await focusWindow(app1Window1) - const app1Window2 = atomApplication1.launch(parseCommandLine([makeTempDir()])) + const [app1Window2] = await atomApplication1.launch(parseCommandLine([makeTempDir()])) await focusWindow(app1Window2) const configPath = path.join(process.env.ATOM_HOME, 'config.cson') @@ -385,7 +385,7 @@ describe('AtomApplication', function () { season.writeFileSync(configPath, config) const atomApplication2 = buildAtomApplication() - const app2Window = atomApplication2.launch(parseCommandLine([])) + const [app2Window] = await atomApplication2.launch(parseCommandLine([])) await focusWindow(app2Window) assert.deepEqual(app2Window.representedDirectoryPaths, []) }) @@ -405,10 +405,10 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened window is closed', async () => { - const window1 = atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) + const [window1] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) await focusWindow(window1) - const [window2] = atomApplication.launch(parseCommandLine(['--new-window', '--wait', '--pid', '102'])) + const [window2] = await atomApplication.launch(parseCommandLine(['--new-window', '--wait', '--pid', '102'])) await focusWindow(window2) assert.deepEqual(killedPids, []) @@ -424,7 +424,7 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened file in an existing window is closed', async () => { - const window = atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) + const [window] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101'])) await focusWindow(window) const filePath1 = temp.openSync('test').path @@ -432,7 +432,7 @@ describe('AtomApplication', function () { fs.writeFileSync(filePath1, 'File 1') fs.writeFileSync(filePath2, 'File 2') - const reusedWindow = atomApplication.launch(parseCommandLine(['--wait', '--pid', '102', filePath1, filePath2])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '102', filePath1, filePath2])) assert.equal(reusedWindow, window) const activeEditorPath = await evalInWebContents(window.browserWindow.webContents, send => { @@ -467,11 +467,11 @@ describe('AtomApplication', function () { }) it('kills the specified pid after a newly-opened directory in an existing window is closed', async () => { - const window = atomApplication.launch(parseCommandLine([])) + const [window] = await atomApplication.launch(parseCommandLine([])) await focusWindow(window) const dirPath1 = makeTempDir() - const reusedWindow = atomApplication.launch(parseCommandLine(['--wait', '--pid', '101', dirPath1])) + const [reusedWindow] = await atomApplication.launch(parseCommandLine(['--wait', '--pid', '101', dirPath1])) assert.equal(reusedWindow, window) assert.deepEqual(await getTreeViewRootDirectories(window), [dirPath1]) assert.deepEqual(killedPids, []) @@ -498,7 +498,7 @@ describe('AtomApplication', function () { if (process.platform === 'linux' || process.platform === 'win32') { it('quits the application', async () => { const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) + const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) await focusWindow(window) window.close() await window.closedPromise @@ -508,7 +508,7 @@ describe('AtomApplication', function () { } else if (process.platform === 'darwin') { it('leaves the application open', async () => { const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) + const [window] = await atomApplication.launch(parseCommandLine([path.join(makeTempDir("a"), 'file-a')])) await focusWindow(window) window.close() await window.closedPromise @@ -524,7 +524,7 @@ describe('AtomApplication', function () { const dirB = makeTempDir() const atomApplication = buildAtomApplication() - const window = atomApplication.launch(parseCommandLine([dirA, dirB])) + const [window] = await atomApplication.launch(parseCommandLine([dirA, dirB])) await emitterEventPromise(window, 'window:locations-opened') await focusWindow(window) assert.deepEqual(await getTreeViewRootDirectories(window), [dirA, dirB]) @@ -539,7 +539,7 @@ describe('AtomApplication', function () { // Window state should be saved when the project folder is removed const atomApplication2 = buildAtomApplication() - const [window2] = atomApplication2.launch(parseCommandLine([])) + const [window2] = await atomApplication2.launch(parseCommandLine([])) await emitterEventPromise(window2, 'window:locations-opened') await focusWindow(window2) assert.deepEqual(await getTreeViewRootDirectories(window2), [dirB]) @@ -556,7 +556,7 @@ describe('AtomApplication', function () { const atomApplication = buildAtomApplication() const launchOptions = parseCommandLine([]) launchOptions.urlsToOpen = ['atom://package-with-url-main/test'] - let windows = atomApplication.launch(launchOptions) + let [windows] = await atomApplication.launch(launchOptions) await windows[0].loadedPromise let reached = await evalInWebContents(windows[0].browserWindow.webContents, sendBackToMainProcess => { @@ -571,9 +571,9 @@ describe('AtomApplication', function () { const dirBPath = makeTempDir('b') const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath)])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath)])) await focusWindow(window1) - const window2 = atomApplication.launch(parseCommandLine([path.join(dirBPath)])) + const [window2] = await atomApplication.launch(parseCommandLine([path.join(dirBPath)])) await focusWindow(window2) const fileA = path.join(dirAPath, 'file-a') @@ -597,9 +597,9 @@ describe('AtomApplication', function () { const dirAPath = makeTempDir("a") const dirBPath = makeTempDir("b") const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath, 'file-a')])) + const [window1] = await atomApplication.launch(parseCommandLine([path.join(dirAPath, 'file-a')])) await focusWindow(window1) - const window2 = atomApplication.launch(parseCommandLine([path.join(dirBPath, 'file-b')])) + const [window2] = await atomApplication.launch(parseCommandLine([path.join(dirBPath, 'file-b')])) await focusWindow(window2) electron.app.quit() await new Promise(process.nextTick) @@ -612,8 +612,8 @@ describe('AtomApplication', function () { it('prevents quitting if user cancels when prompted to save an item', async () => { const atomApplication = buildAtomApplication() - const window1 = atomApplication.launch(parseCommandLine([])) - const window2 = atomApplication.launch(parseCommandLine([])) + const [window1] = await atomApplication.launch(parseCommandLine([])) + const [window2] = await atomApplication.launch(parseCommandLine([])) await Promise.all([window1.loadedPromise, window2.loadedPromise]) await evalInWebContents(window1.browserWindow.webContents, sendBackToMainProcess => { atom.workspace.getActiveTextEditor().insertText('unsaved text') diff --git a/spec/main-process/file-recovery-service.test.js b/spec/main-process/file-recovery-service.test.js index 618c30ab0..45c10c25b 100644 --- a/spec/main-process/file-recovery-service.test.js +++ b/spec/main-process/file-recovery-service.test.js @@ -1,21 +1,23 @@ -/** @babel */ - -import {dialog} from 'electron' -import FileRecoveryService from '../../src/main-process/file-recovery-service' -import fs from 'fs-plus' -import sinon from 'sinon' -import {escapeRegExp} from 'underscore-plus' +const {dialog} = require('electron') +const FileRecoveryService = require('../../src/main-process/file-recovery-service') +const fs = require('fs-plus') +const fsreal = require('fs') +const EventEmitter = require('events').EventEmitter +const sinon = require('sinon') +const {escapeRegExp} = require('underscore-plus') const temp = require('temp').track() describe("FileRecoveryService", () => { - let recoveryService, recoveryDirectory + let recoveryService, recoveryDirectory, spies beforeEach(() => { recoveryDirectory = temp.mkdirSync('atom-spec-file-recovery') recoveryService = new FileRecoveryService(recoveryDirectory) + spies = sinon.sandbox.create() }) afterEach(() => { + spies.restore() try { temp.cleanupSync() } catch (e) { @@ -24,38 +26,38 @@ describe("FileRecoveryService", () => { }) describe("when no crash happens during a save", () => { - it("creates a recovery file and deletes it after saving", () => { + it("creates a recovery file and deletes it after saving", async () => { const mockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "some content") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "changed") - recoveryService.didSavePath(mockWindow, filePath) + await recoveryService.didSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") fs.removeSync(filePath) }) - it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", () => { + it("creates only one recovery file when many windows attempt to save the same file, deleting it when the last one finishes saving it", async () => { const mockWindow = {} const anotherMockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "some content") - recoveryService.willSavePath(mockWindow, filePath) - recoveryService.willSavePath(anotherMockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "changed") - recoveryService.didSavePath(mockWindow, filePath) + await recoveryService.didSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") - recoveryService.didSavePath(anotherMockWindow, filePath) + await recoveryService.didSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) assert.equal(fs.readFileSync(filePath, 'utf8'), "changed") @@ -64,66 +66,75 @@ describe("FileRecoveryService", () => { }) describe("when a crash happens during a save", () => { - it("restores the created recovery file and deletes it", () => { + it("restores the created recovery file and deletes it", async () => { const mockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "some content") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "changed") - recoveryService.didCrashWindow(mockWindow) + await recoveryService.didCrashWindow(mockWindow) assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) assert.equal(fs.readFileSync(filePath, 'utf8'), "some content") fs.removeSync(filePath) }) - it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", () => { + it("restores the created recovery file when many windows attempt to save the same file and one of them crashes", async () => { const mockWindow = {} const anotherMockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "A") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) fs.writeFileSync(filePath, "B") - recoveryService.willSavePath(anotherMockWindow, filePath) + await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "C") - recoveryService.didCrashWindow(mockWindow) + await recoveryService.didCrashWindow(mockWindow) assert.equal(fs.readFileSync(filePath, 'utf8'), "A") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) fs.writeFileSync(filePath, "D") - recoveryService.willSavePath(mockWindow, filePath) + await recoveryService.willSavePath(mockWindow, filePath) fs.writeFileSync(filePath, "E") - recoveryService.willSavePath(anotherMockWindow, filePath) + await recoveryService.willSavePath(anotherMockWindow, filePath) assert.equal(fs.listTreeSync(recoveryDirectory).length, 1) fs.writeFileSync(filePath, "F") - recoveryService.didCrashWindow(anotherMockWindow) + await recoveryService.didCrashWindow(anotherMockWindow) assert.equal(fs.readFileSync(filePath, 'utf8'), "D") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) fs.removeSync(filePath) }) - it("emits a warning when a file can't be recovered", sinon.test(function () { + it("emits a warning when a file can't be recovered", async () => { const mockWindow = {} const filePath = temp.path() fs.writeFileSync(filePath, "content") - fs.chmodSync(filePath, 0444) let logs = [] - this.stub(console, 'log', (message) => logs.push(message)) - this.stub(dialog, 'showMessageBox') + spies.stub(console, 'log', (message) => logs.push(message)) + spies.stub(dialog, 'showMessageBox') - recoveryService.willSavePath(mockWindow, filePath) - recoveryService.didCrashWindow(mockWindow) + // Copy files to be recovered before mocking fs.createWriteStream + await recoveryService.willSavePath(mockWindow, filePath) + + // Stub out fs.createWriteStream so that we can return a fake error when + // attempting to copy the recovered file to its original location + var fakeEmitter = new EventEmitter() + var onStub = spies.stub(fakeEmitter, 'on') + onStub.withArgs('error').yields(new Error('Nope')).returns(fakeEmitter) + onStub.withArgs('open').returns(fakeEmitter) + spies.stub(fsreal, 'createWriteStream').withArgs(filePath).returns(fakeEmitter) + + await recoveryService.didCrashWindow(mockWindow) let recoveryFiles = fs.listTreeSync(recoveryDirectory) assert.equal(recoveryFiles.length, 1) assert.equal(logs.length, 1) @@ -131,16 +142,16 @@ describe("FileRecoveryService", () => { assert.match(logs[0], new RegExp(escapeRegExp(recoveryFiles[0]))) fs.removeSync(filePath) - })) + }) }) - it("doesn't create a recovery file when the file that's being saved doesn't exist yet", () => { + it("doesn't create a recovery file when the file that's being saved doesn't exist yet", async () => { const mockWindow = {} - recoveryService.willSavePath(mockWindow, "a-file-that-doesnt-exist") + await recoveryService.willSavePath(mockWindow, "a-file-that-doesnt-exist") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) - recoveryService.didSavePath(mockWindow, "a-file-that-doesnt-exist") + await recoveryService.didSavePath(mockWindow, "a-file-that-doesnt-exist") assert.equal(fs.listTreeSync(recoveryDirectory).length, 0) }) }) diff --git a/spec/menu-manager-spec.coffee b/spec/menu-manager-spec.coffee index 3bbd8b9da..c7b15aae6 100644 --- a/spec/menu-manager-spec.coffee +++ b/spec/menu-manager-spec.coffee @@ -58,10 +58,8 @@ describe "MenuManager", -> menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}] atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b' menu.update() - - waits 50 - - runs -> expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toEqual ['ctrl-b'] + advanceClock(1) + expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toEqual ['ctrl-b'] it "omits key bindings that are mapped to unset! in any context", -> # it would be nice to be smarter about omitting, but that would require a much @@ -69,10 +67,8 @@ describe "MenuManager", -> menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}] atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b' atom.keymaps.add 'test', 'atom-text-editor': 'ctrl-b': 'unset!' - - waits 50 - - runs -> expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() + advanceClock(1) + expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() it "omits key bindings that could conflict with AltGraph characters on macOS", -> Object.defineProperty process, 'platform', value: 'darwin' @@ -87,12 +83,10 @@ describe "MenuManager", -> 'alt-shift-C': 'c' 'alt-cmd-d': 'd' - waits 50 - - runs -> - expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() - expect(menu.sendToBrowserProcess.argsForCall[0][1]['c']).toBeUndefined() - expect(menu.sendToBrowserProcess.argsForCall[0][1]['d']).toEqual(['alt-cmd-d']) + advanceClock(1) + expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['c']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['d']).toEqual(['alt-cmd-d']) it "omits key bindings that could conflict with AltGraph characters on Windows", -> Object.defineProperty process, 'platform', value: 'win32' @@ -107,12 +101,10 @@ describe "MenuManager", -> 'ctrl-alt-shift-C': 'c' 'ctrl-alt-cmd-d': 'd' - waits 50 - - runs -> - expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() - expect(menu.sendToBrowserProcess.argsForCall[0][1]['c']).toBeUndefined() - expect(menu.sendToBrowserProcess.argsForCall[0][1]['d']).toEqual(['ctrl-alt-cmd-d']) + advanceClock(1) + expect(menu.sendToBrowserProcess.argsForCall[0][1]['b']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['c']).toBeUndefined() + expect(menu.sendToBrowserProcess.argsForCall[0][1]['d']).toEqual(['ctrl-alt-cmd-d']) it "updates the application menu when a keymap is reloaded", -> spyOn(menu, 'update') diff --git a/spec/menu-sort-helpers-spec.js b/spec/menu-sort-helpers-spec.js new file mode 100644 index 000000000..86f00b37e --- /dev/null +++ b/spec/menu-sort-helpers-spec.js @@ -0,0 +1,243 @@ +const {sortMenuItems} = require('../src/menu-sort-helpers') + +describe('contextMenu', () => { + describe('dedupes separators', () => { + it('preserves existing submenus', () => { + const items = [{ submenu: [] }] + expect(sortMenuItems(items)).toEqual(items) + }) + }) + + describe('dedupes separators', () => { + it('trims leading separators', () => { + const items = [{ type: 'separator' }, { command: 'core:one' }] + const expected = [{ command: 'core:one' }] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it('preserves separators at the begining of set two', () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, { command: 'core:two' } + ] + const expected = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it('trims trailing separators', () => { + const items = [{ command: 'core:one' }, { type: 'separator' }] + const expected = [{ command: 'core:one' }] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it('removes duplicate separators across sets', () => { + const items = [ + { command: 'core:one' }, { type: 'separator' }, + { type: 'separator' }, { command: 'core:two' } + ] + const expected = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + }) + + describe('can move an item to a different group by merging groups', () => { + it('can move a group of one item', () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' }, + { type: 'separator' }, + { command: 'core:three', after: ['core:one'] }, + { type: 'separator' } + ] + const expected = [ + { command: 'core:one' }, + { command: 'core:three', after: ['core:one'] }, + { type: 'separator' }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it("moves all items in the moving item's group", () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' }, + { type: 'separator' }, + { command: 'core:three', after: ['core:one'] }, + { command: 'core:four' }, + { type: 'separator' } + ] + const expected = [ + { command: 'core:one' }, + { command: 'core:three', after: ['core:one'] }, + { command: 'core:four' }, + { type: 'separator' }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it("ignores positions relative to commands that don't exist", () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' }, + { type: 'separator' }, + { command: 'core:three', after: ['core:does-not-exist'] }, + { command: 'core:four', after: ['core:one'] }, + { type: 'separator' } + ] + const expected = [ + { command: 'core:one' }, + { command: 'core:three', after: ['core:does-not-exist'] }, + { command: 'core:four', after: ['core:one'] }, + { type: 'separator' }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it('can handle recursive group merging', () => { + const items = [ + { command: 'core:one', after: ['core:three'] }, + { command: 'core:two', before: ['core:one'] }, + { command: 'core:three' } + ] + const expected = [ + { command: 'core:three' }, + { command: 'core:two', before: ['core:one'] }, + { command: 'core:one', after: ['core:three'] } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it('can merge multiple groups when given a list of before/after commands', () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' }, + { type: 'separator' }, + { command: 'core:three', after: ['core:one', 'core:two'] } + ] + const expected = [ + { command: 'core:two' }, + { command: 'core:one' }, + { command: 'core:three', after: ['core:one', 'core:two'] } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + + it('can merge multiple groups based on both before/after commands', () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' }, + { type: 'separator' }, + { command: 'core:three', after: ['core:one'], before: ['core:two'] } + ] + const expected = [ + { command: 'core:one' }, + { command: 'core:three', after: ['core:one'], before: ['core:two'] }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual(expected) + }) + }) + + describe('sorts items within their ultimate group', () => { + it('does a simple sort', () => { + const items = [ + { command: 'core:two', after: ['core:one'] }, + { command: 'core:one' } + ] + expect(sortMenuItems(items)).toEqual([ + { command: 'core:one' }, + { command: 'core:two', after: ['core:one'] } + ]) + }) + + it('resolves cycles by ignoring things that conflict', () => { + const items = [ + { command: 'core:two', after: ['core:one'] }, + { command: 'core:one', after: ['core:two'] } + ] + expect(sortMenuItems(items)).toEqual([ + { command: 'core:one', after: ['core:two'] }, + { command: 'core:two', after: ['core:one'] } + ]) + }) + }) + + describe('sorts groups', () => { + it('does a simple sort', () => { + const items = [ + { command: 'core:two', afterGroupContaining: ['core:one'] }, + { type: 'separator' }, + { command: 'core:one' } + ] + expect(sortMenuItems(items)).toEqual([ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two', afterGroupContaining: ['core:one'] } + ]) + }) + + it('resolves cycles by ignoring things that conflict', () => { + const items = [ + { command: 'core:two', afterGroupContaining: ['core:one'] }, + { type: 'separator' }, + { command: 'core:one', afterGroupContaining: ['core:two'] } + ] + expect(sortMenuItems(items)).toEqual([ + { command: 'core:one', afterGroupContaining: ['core:two'] }, + { type: 'separator' }, + { command: 'core:two', afterGroupContaining: ['core:one'] } + ]) + }) + + it('ignores references to commands that do not exist', () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { + command: 'core:two', + afterGroupContaining: ['core:does-not-exist'] + } + ] + expect(sortMenuItems(items)).toEqual([ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two', afterGroupContaining: ['core:does-not-exist'] } + ]) + }) + + it('only respects the first matching [before|after]GroupContaining rule in a given group', () => { + const items = [ + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:three', beforeGroupContaining: ['core:one'] }, + { command: 'core:four', afterGroupContaining: ['core:two'] }, + { type: 'separator' }, + { command: 'core:two' } + ] + expect(sortMenuItems(items)).toEqual([ + { command: 'core:three', beforeGroupContaining: ['core:one'] }, + { command: 'core:four', afterGroupContaining: ['core:two'] }, + { type: 'separator' }, + { command: 'core:one' }, + { type: 'separator' }, + { command: 'core:two' } + ]) + }) + }) +}) diff --git a/spec/package-manager-spec.js b/spec/package-manager-spec.js index b1ecf834d..dd87f85fa 100644 --- a/spec/package-manager-spec.js +++ b/spec/package-manager-spec.js @@ -30,13 +30,15 @@ describe('PackageManager', () => { expect(packageManger.packageDirPaths[0]).toBe(path.join(configDirPath, 'packages')) }) - it('adds regular package path and dev package path in dev mode', () => { + it('adds regular package path, dev package path, and Atom repo package path in dev mode and dev resource path is set', () => { const packageManger = new PackageManager({}) const configDirPath = path.join('~', 'someConfig') - packageManger.initialize({configDirPath, devMode: true}) - expect(packageManger.packageDirPaths.length).toBe(2) + const resourcePath = path.join('~', '/atom') + packageManger.initialize({configDirPath, resourcePath, devMode: true}) + expect(packageManger.packageDirPaths.length).toBe(3) expect(packageManger.packageDirPaths).toContain(path.join(configDirPath, 'packages')) expect(packageManger.packageDirPaths).toContain(path.join(configDirPath, 'dev', 'packages')) + expect(packageManger.packageDirPaths).toContain(path.join(resourcePath, 'packages')) }) }) @@ -1032,6 +1034,7 @@ describe('PackageManager', () => { }) it('loads any tree-sitter grammars defined in the package', async () => { + atom.config.set('core.useTreeSitterParsers', true) await atom.packages.activatePackage('package-with-tree-sitter-grammar') const grammar = atom.grammars.selectGrammar('test.somelang') expect(grammar.name).toBe('Some Language') diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee index 21c1d000a..22651b489 100644 --- a/spec/pane-container-element-spec.coffee +++ b/spec/pane-container-element-spec.coffee @@ -237,3 +237,30 @@ describe "PaneContainerElement", -> atom.commands.dispatch(rightPane.getElement(), 'pane:decrease-size') expect(leftPane.getFlexScale()).toBe 1/1.1 expect(rightPane.getFlexScale()).toBe 1/1.1 + + describe "when only a single pane is present", -> + [singlePane] = [] + + beforeEach -> + container = new PaneContainer(params) + singlePane = container.getActivePane() + + describe "when pane:increase-size is triggered", -> + it "does not increases the size of the pane", -> + expect(singlePane.getFlexScale()).toBe 1 + + atom.commands.dispatch(singlePane.getElement(), 'pane:increase-size') + expect(singlePane.getFlexScale()).toBe 1 + + atom.commands.dispatch(singlePane.getElement(), 'pane:increase-size') + expect(singlePane.getFlexScale()).toBe 1 + + describe "when pane:decrease-size is triggered", -> + it "does not decreases the size of the pane", -> + expect(singlePane.getFlexScale()).toBe 1 + + atom.commands.dispatch(singlePane.getElement(), 'pane:decrease-size') + expect(singlePane.getFlexScale()).toBe 1 + + atom.commands.dispatch(singlePane.getElement(), 'pane:decrease-size') + expect(singlePane.getFlexScale()).toBe 1 diff --git a/spec/pane-spec.js b/spec/pane-spec.js index 8ef274c2d..ddb92b96e 100644 --- a/spec/pane-spec.js +++ b/spec/pane-spec.js @@ -219,6 +219,34 @@ describe('Pane', () => { runs(() => expect(eventOrder).toEqual(['add', 'remove'])) }) + it('subscribes to be notified when item terminates its pending state', () => { + const fakeDisposable = { dispose: () => {} } + const spy = jasmine.createSpy('onDidTerminatePendingState').andReturn((fakeDisposable)) + + const pane = new Pane(paneParams({items: []})) + const item = { + getTitle: () => '', + onDidTerminatePendingState: spy + } + pane.addItem(item) + + expect(spy).toHaveBeenCalled() + }) + + it('subscribes to be notified when item is destroyed', () => { + const fakeDisposable = { dispose: () => {} } + const spy = jasmine.createSpy('onDidDestroy').andReturn((fakeDisposable)) + + const pane = new Pane(paneParams({items: []})) + const item = { + getTitle: () => '', + onDidDestroy: spy + } + pane.addItem(item) + + expect(spy).toHaveBeenCalled() + }) + describe('when using the old API of ::addItem(item, index)', () => { beforeEach(() => spyOn(Grim, 'deprecate')) diff --git a/spec/project-spec.js b/spec/project-spec.js index bd6bb1fa6..861a0f53a 100644 --- a/spec/project-spec.js +++ b/spec/project-spec.js @@ -274,6 +274,51 @@ describe('Project', () => { }) }) + describe('.replace', () => { + let projectSpecification, projectPath1, projectPath2 + beforeEach(() => { + atom.project.replace(null) + projectPath1 = temp.mkdirSync('project-path1') + projectPath2 = temp.mkdirSync('project-path2') + projectSpecification = { + paths: [projectPath1, projectPath2], + originPath: 'originPath', + config: { + 'baz': 'buzz' + } + } + }) + it('sets a project specification', () => { + expect(atom.config.get('baz')).toBeUndefined() + atom.project.replace(projectSpecification) + expect(atom.project.getPaths()).toEqual([projectPath1, projectPath2]) + expect(atom.config.get('baz')).toBe('buzz') + }) + + it('clears a project through replace with no params', () => { + expect(atom.config.get('baz')).toBeUndefined() + atom.project.replace(projectSpecification) + expect(atom.config.get('baz')).toBe('buzz') + expect(atom.project.getPaths()).toEqual([projectPath1, projectPath2]) + atom.project.replace() + expect(atom.config.get('baz')).toBeUndefined() + expect(atom.project.getPaths()).toEqual([]) + }) + + it('responds to change of project specification', () => { + let wasCalled = false + const callback = () => { + wasCalled = true + } + atom.project.onDidReplace(callback) + atom.project.replace(projectSpecification) + expect(wasCalled).toBe(true) + wasCalled = false + atom.project.replace() + expect(wasCalled).toBe(true) + }) + }) + describe('before and after saving a buffer', () => { let buffer beforeEach(() => @@ -924,6 +969,77 @@ describe('Project', () => { }) }) + describe('.observeRepositories()', () => { + it('invokes the observer with current and future repositories', () => { + const observed = [] + + const directory1 = temp.mkdirSync('git-repo1') + const gitDirPath1 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + fs.copySync(gitDirPath1, path.join(directory1, '.git')) + + const directory2 = temp.mkdirSync('git-repo2') + const gitDirPath2 = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'repo-with-submodules', 'git.git')) + fs.copySync(gitDirPath2, path.join(directory2, '.git')) + + atom.project.setPaths([directory1]) + + const disposable = atom.project.observeRepositories((repo) => observed.push(repo)) + expect(observed.length).toBe(1) + expect(observed[0].getReferenceTarget('refs/heads/master')).toBe('ef046e9eecaa5255ea5e9817132d4001724d6ae1') + + atom.project.addPath(directory2) + expect(observed.length).toBe(2) + expect(observed[1].getReferenceTarget('refs/heads/master')).toBe('d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5') + + disposable.dispose() + }) + }) + + describe('.onDidAddRepository()', () => { + it('invokes callback when a path is added and the path is the root of a repository', () => { + const observed = [] + const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo)) + + const projectRootPath = temp.mkdirSync() + const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git')) + + atom.project.addPath(projectRootPath) + expect(observed.length).toBe(1) + expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git') + + disposable.dispose() + }) + + it('invokes callback when a path is added and the path is subdirectory of a repository', () => { + const observed = [] + const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo)) + + const projectRootPath = temp.mkdirSync() + const fixtureRepoPath = fs.absolute(path.join(__dirname, 'fixtures', 'git', 'master.git')) + fs.copySync(fixtureRepoPath, path.join(projectRootPath, '.git')) + + const projectSubDirPath = path.join(projectRootPath, 'sub-dir') + fs.mkdirSync(projectSubDirPath) + + atom.project.addPath(projectSubDirPath) + expect(observed.length).toBe(1) + expect(observed[0].getOriginURL()).toEqual('https://github.com/example-user/example-repo.git') + + disposable.dispose() + }) + + it('does not invoke callback when a path is added and the path is not part of a repository', () => { + const observed = [] + const disposable = atom.project.onDidAddRepository((repo) => observed.push(repo)) + + atom.project.addPath(temp.mkdirSync('not-a-repository')) + expect(observed.length).toBe(0) + + disposable.dispose() + }) + }) + describe('.relativize(path)', () => { it('returns the path, relative to whichever root directory it is inside of', () => { atom.project.addPath(temp.mkdirSync('another-path')) diff --git a/spec/sample-with-comments.js b/spec/sample-with-comments.js deleted file mode 100644 index 66dc9051d..000000000 --- a/spec/sample-with-comments.js +++ /dev/null @@ -1 +0,0 @@ -undefined \ No newline at end of file diff --git a/spec/selection-spec.js b/spec/selection-spec.js index cb586da26..8afc67575 100644 --- a/spec/selection-spec.js +++ b/spec/selection-spec.js @@ -154,4 +154,118 @@ describe('Selection', () => { expect(editor.isFoldedAtBufferRow(0)).toBe(false) }) }) + + describe('within a read-only editor', () => { + beforeEach(() => { + editor.setReadOnly(true) + selection.setBufferRange([[0, 0], [0, 13]]) + }) + + const modifications = [ + { + name: 'insertText', + op: opts => selection.insertText('yes', opts) + }, + { + name: 'backspace', + op: opts => selection.backspace(opts) + }, + { + name: 'deleteToPreviousWordBoundary', + op: opts => selection.deleteToPreviousWordBoundary(opts) + }, + { + name: 'deleteToNextWordBoundary', + op: opts => selection.deleteToNextWordBoundary(opts) + }, + { + name: 'deleteToBeginningOfWord', + op: opts => selection.deleteToBeginningOfWord(opts) + }, + { + name: 'deleteToBeginningOfLine', + op: opts => selection.deleteToBeginningOfLine(opts) + }, + { + name: 'delete', + op: opts => selection.delete(opts) + }, + { + name: 'deleteToEndOfLine', + op: opts => selection.deleteToEndOfLine(opts) + }, + { + name: 'deleteToEndOfWord', + op: opts => selection.deleteToEndOfWord(opts) + }, + { + name: 'deleteToBeginningOfSubword', + op: opts => selection.deleteToBeginningOfSubword(opts) + }, + { + name: 'deleteToEndOfSubword', + op: opts => selection.deleteToEndOfSubword(opts) + }, + { + name: 'deleteSelectedText', + op: opts => selection.deleteSelectedText(opts) + }, + { + name: 'deleteLine', + op: opts => selection.deleteLine(opts) + }, + { + name: 'joinLines', + op: opts => selection.joinLines(opts) + }, + { + name: 'outdentSelectedRows', + op: opts => selection.outdentSelectedRows(opts) + }, + { + name: 'autoIndentSelectedRows', + op: opts => selection.autoIndentSelectedRows(opts) + }, + { + name: 'toggleLineComments', + op: opts => selection.toggleLineComments(opts) + }, + { + name: 'cutToEndOfLine', + op: opts => selection.cutToEndOfLine(false, opts) + }, + { + name: 'cutToEndOfBufferLine', + op: opts => selection.cutToEndOfBufferLine(false, opts) + }, + { + name: 'cut', + op: opts => selection.cut(false, false, opts.bypassReadOnly) + }, + { + name: 'indent', + op: opts => selection.indent(opts) + }, + { + name: 'indentSelectedRows', + op: opts => selection.indentSelectedRows(opts) + }, + ] + + describe('without bypassReadOnly', () => { + for (const {name, op} of modifications) { + it(`throws an error on ${name}`, () => { + expect(op).toThrow() + }) + } + }) + + describe('with bypassReadOnly', () => { + for (const {name, op} of modifications) { + it(`permits ${name}`, () => { + op({bypassReadOnly: true}) + }) + } + }) + }) }) diff --git a/spec/text-editor-component-spec.js b/spec/text-editor-component-spec.js index 97210dd41..66274a99a 100644 --- a/spec/text-editor-component-spec.js +++ b/spec/text-editor-component-spec.js @@ -26,6 +26,7 @@ document.registerElement('text-editor-component-test-element', { }) const editors = [] +let verticalScrollbarWidth, horizontalScrollbarHeight describe('TextEditorComponent', () => { beforeEach(() => { @@ -33,8 +34,15 @@ describe('TextEditorComponent', () => { // Force scrollbars to be visible regardless of local system configuration const scrollbarStyle = document.createElement('style') - scrollbarStyle.textContent = '::-webkit-scrollbar { -webkit-appearance: none }' + scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }' jasmine.attachToDOM(scrollbarStyle) + + if (verticalScrollbarWidth == null) { + const {component, element} = buildComponent({text: 'abcdefgh\n'.repeat(10), width: 30, height: 30}) + verticalScrollbarWidth = getVerticalScrollbarWidth(component) + horizontalScrollbarHeight = getHorizontalScrollbarHeight(component) + element.remove() + } }) afterEach(() => { @@ -104,7 +112,7 @@ describe('TextEditorComponent', () => { { expect(editor.getApproximateLongestScreenRow()).toBe(3) - const expectedWidth = Math.round( + const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(3, Infinity)).left + component.getBaseCharacterWidth() ) @@ -121,7 +129,7 @@ describe('TextEditorComponent', () => { // Capture the width of the lines before requesting the width of // longest line, because making that request forces a DOM update const actualWidth = element.querySelector('.lines').style.width - const expectedWidth = Math.round( + const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(6, Infinity)).left + component.getBaseCharacterWidth() ) @@ -184,8 +192,8 @@ describe('TextEditorComponent', () => { }) it('makes the content at least as tall as the scroll container client height', async () => { - const {component, element, editor} = buildComponent({text: 'a', height: 100}) - expect(component.refs.content.offsetHeight).toBe(100) + const {component, element, editor} = buildComponent({text: 'a'.repeat(100), width: 50, height: 100}) + expect(component.refs.content.offsetHeight).toBe(100 - getHorizontalScrollbarHeight(component)) editor.setText('a\n'.repeat(30)) await component.getNextUpdatePromise() @@ -201,7 +209,7 @@ describe('TextEditorComponent', () => { await setEditorHeightInLines(component, 6) // scroll to end - await setScrollTop(component, scrollContainer.scrollHeight - scrollContainer.clientHeight) + await setScrollTop(component, Infinity) expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() - 3) editor.update({scrollPastEnd: false}) @@ -211,7 +219,7 @@ describe('TextEditorComponent', () => { // Always allows at least 3 lines worth of overscroll if the editor is short await setEditorHeightInLines(component, 2) await editor.update({scrollPastEnd: true}) - await setScrollTop(component, scrollContainer.scrollHeight - scrollContainer.clientHeight) + await setScrollTop(component, Infinity) expect(component.getFirstVisibleRow()).toBe(editor.getScreenLineCount() + 1) }) @@ -296,31 +304,6 @@ describe('TextEditorComponent', () => { expect(lineNumberNodeForScreenRow(component, 0).querySelector('.foldable')).toBeNull() }) - it('gracefully handles folds that change the soft-wrap boundary by causing the vertical scrollbar to disappear (regression)', async () => { - const text = ('x'.repeat(100) + '\n') + 'y\n'.repeat(28) + ' z\n'.repeat(50) - const {component, element, editor} = buildComponent({text, height: 1000, width: 500}) - - element.addEventListener('scroll', (event) => { - event.stopPropagation() - }, true) - - editor.setSoftWrapped(true) - jasmine.attachToDOM(element) - await component.getNextUpdatePromise() - - const firstScreenLineLengthWithVerticalScrollbar = element.querySelector('.line').textContent.length - - setScrollTop(component, 620) - await component.getNextUpdatePromise() - - editor.foldBufferRow(28) - await component.getNextUpdatePromise() - - const firstLineElement = element.querySelector('.line') - expect(firstLineElement.dataset.screenRow).toBe('0') - expect(firstLineElement.textContent.length).toBeGreaterThan(firstScreenLineLengthWithVerticalScrollbar) - }) - it('shows the foldable icon on the last screen row of a buffer row that can be folded', async () => { const {component, element, editor} = buildComponent({text: 'abc\n de\nfghijklm\n no', softWrapped: true}) await setEditorWidthInCharacters(component, 5) @@ -361,18 +344,14 @@ describe('TextEditorComponent', () => { expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0) expect(getHorizontalScrollbarHeight(component)).toBe(0) expect(verticalScrollbar.style.visibility).toBe('') - expect(verticalScrollbar.style.bottom).toBe('0px') expect(horizontalScrollbar.style.visibility).toBe('hidden') - expect(component.refs.scrollbarCorner).toBeUndefined() editor.setText('a'.repeat(100)) await component.getNextUpdatePromise() expect(getVerticalScrollbarWidth(component)).toBe(0) expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0) expect(verticalScrollbar.style.visibility).toBe('hidden') - expect(horizontalScrollbar.style.right).toBe('0px') expect(horizontalScrollbar.style.visibility).toBe('') - expect(component.refs.scrollbarCorner).toBeUndefined() editor.setText('') await component.getNextUpdatePromise() @@ -380,37 +359,6 @@ describe('TextEditorComponent', () => { expect(getHorizontalScrollbarHeight(component)).toBe(0) expect(verticalScrollbar.style.visibility).toBe('hidden') expect(horizontalScrollbar.style.visibility).toBe('hidden') - expect(component.refs.scrollbarCorner).toBeUndefined() - - editor.setText(SAMPLE_TEXT) - await component.getNextUpdatePromise() - - // Does not show scrollbars if the content perfectly fits - element.style.width = component.getGutterContainerWidth() + component.getContentWidth() + 'px' - element.style.height = component.getContentHeight() + 'px' - await component.getNextUpdatePromise() - expect(getVerticalScrollbarWidth(component)).toBe(0) - expect(getHorizontalScrollbarHeight(component)).toBe(0) - expect(verticalScrollbar.style.visibility).toBe('hidden') - expect(horizontalScrollbar.style.visibility).toBe('hidden') - - // Shows scrollbars if the only reason we overflow is the presence of the - // scrollbar for the opposite axis. - element.style.width = component.getGutterContainerWidth() + component.getContentWidth() - 1 + 'px' - element.style.height = component.getContentHeight() + component.getHorizontalScrollbarHeight() - 1 + 'px' - await component.getNextUpdatePromise() - expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0) - expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0) - expect(verticalScrollbar.style.visibility).toBe('') - expect(horizontalScrollbar.style.visibility).toBe('') - - element.style.width = component.getGutterContainerWidth() + component.getContentWidth() + component.getVerticalScrollbarWidth() - 1 + 'px' - element.style.height = component.getContentHeight() - 1 + 'px' - await component.getNextUpdatePromise() - expect(getVerticalScrollbarWidth(component)).toBeGreaterThan(0) - expect(getHorizontalScrollbarHeight(component)).toBeGreaterThan(0) - expect(verticalScrollbar.style.visibility).toBe('') - expect(horizontalScrollbar.style.visibility).toBe('') }) describe('when scrollbar styles change or the editor element is detached and then reattached', () => { @@ -564,9 +512,20 @@ describe('TextEditorComponent', () => { it('gives cursors at the end of lines the width of an "x" character', async () => { const {component, element, editor} = buildComponent() + editor.setText('abcde') + await setEditorWidthInCharacters(component, 5.5) + editor.setCursorScreenPosition([0, Infinity]) await component.getNextUpdatePromise() expect(element.querySelector('.cursor').offsetWidth).toBe(Math.round(component.getBaseCharacterWidth())) + + // Clip cursor width when soft-wrap is on and the cursor is at the end of + // the line. This prevents the parent tile from disabling sub-pixel + // anti-aliasing. For some reason, adding overflow: hidden to the cursor + // container doesn't solve this issue so we're adding this workaround instead. + editor.setSoftWrapped(true) + await component.getNextUpdatePromise() + expect(element.querySelector('.cursor').offsetWidth).toBeLessThan(Math.round(component.getBaseCharacterWidth())) }) it('positions and sizes cursors correctly when they are located next to a fold marker', async () => { @@ -672,17 +631,6 @@ describe('TextEditorComponent', () => { expect(scrollContainer.clientWidth).toBe(scrollContainer.scrollWidth) }) - it('accounts for the width of the vertical scrollbar when soft-wrapping lines', async () => { - const {component, element, editor} = buildComponent({ - height: 200, - text: 'a'.repeat(300), - softWrapped: true - }) - await setEditorWidthInCharacters(component, 23) - expect(Math.floor(component.getScrollContainerClientWidth() / component.getBaseCharacterWidth())).toBe(20) - expect(editor.lineLengthForScreenRow(0)).toBe(20) - }) - it('correctly forces the display layer to index visible rows when resizing (regression)', async () => { const text = 'a'.repeat(30) + '\n' + 'b'.repeat(1000) const {component, element, editor} = buildComponent({height: 300, width: 800, attach: false, text}) @@ -707,7 +655,7 @@ describe('TextEditorComponent', () => { editor.setText('a') await component.getNextUpdatePromise() - expect(element.querySelector('.line').offsetWidth).toBe(scrollContainer.offsetWidth) + expect(element.querySelector('.line').offsetWidth).toBe(scrollContainer.offsetWidth - verticalScrollbarWidth) }) it('resizes based on the content when the autoHeight and/or autoWidth options are true', async () => { @@ -717,44 +665,39 @@ describe('TextEditorComponent', () => { const {gutterContainer, scrollContainer} = component.refs const initialWidth = element.offsetWidth const initialHeight = element.offsetHeight - expect(initialWidth).toBe(component.getGutterContainerWidth() + component.getContentWidth() + 2 * editorPadding) - expect(initialHeight).toBe(component.getContentHeight() + 2 * editorPadding) + expect(initialWidth).toBe( + component.getGutterContainerWidth() + + component.getContentWidth() + + verticalScrollbarWidth + + 2 * editorPadding + ) + expect(initialHeight).toBe( + component.getContentHeight() + + horizontalScrollbarHeight + + 2 * editorPadding + ) // When autoWidth is enabled, width adjusts to content editor.setCursorScreenPosition([6, Infinity]) editor.insertText('x'.repeat(50)) await component.getNextUpdatePromise() - expect(element.offsetWidth).toBe(component.getGutterContainerWidth() + component.getContentWidth() + 2 * editorPadding) + expect(element.offsetWidth).toBe( + component.getGutterContainerWidth() + + component.getContentWidth() + + verticalScrollbarWidth + + 2 * editorPadding + ) expect(element.offsetWidth).toBeGreaterThan(initialWidth) // When autoHeight is enabled, height adjusts to content editor.insertText('\n'.repeat(5)) await component.getNextUpdatePromise() - expect(element.offsetHeight).toBe(component.getContentHeight() + 2 * editorPadding) - expect(element.offsetHeight).toBeGreaterThan(initialHeight) - - // When a horizontal scrollbar is visible, autoHeight accounts for it - editor.update({autoWidth: false}) - await component.getNextUpdatePromise() - element.style.width = component.getGutterContainerWidth() + component.getContentHeight() - 20 + 'px' - await component.getNextUpdatePromise() - expect(component.canScrollHorizontally()).toBe(true) - expect(component.canScrollVertically()).toBe(false) - expect(element.offsetHeight).toBe(component.getContentHeight() + component.getHorizontalScrollbarHeight() + 2 * editorPadding) - - // When a vertical scrollbar is visible, autoWidth accounts for it - editor.update({autoWidth: true, autoHeight: false}) - await component.getNextUpdatePromise() - element.style.height = component.getContentHeight() - 20 - await component.getNextUpdatePromise() - expect(component.canScrollHorizontally()).toBe(false) - expect(component.canScrollVertically()).toBe(true) - expect(element.offsetWidth).toBe( - component.getGutterContainerWidth() + - component.getContentWidth() + - component.getVerticalScrollbarWidth() + + expect(element.offsetHeight).toBe( + component.getContentHeight() + + horizontalScrollbarHeight + 2 * editorPadding ) + expect(element.offsetHeight).toBeGreaterThan(initialHeight) }) it('does not render the line number gutter at all if the isLineNumberGutterVisible parameter is false', () => { @@ -875,6 +818,18 @@ describe('TextEditorComponent', () => { expect(element.className).toBe('editor a b') }) + it('does not blow away class names managed by the component when packages change the element class name', async () => { + assertDocumentFocused() + const {component, element, editor} = buildComponent({mini: true}) + element.classList.add('a', 'b') + element.focus() + await component.getNextUpdatePromise() + expect(element.className).toBe('editor mini a b is-focused') + element.className = 'a c d'; + await component.getNextUpdatePromise() + expect(element.className).toBe('a c d editor is-focused mini') + }) + it('ignores resize events when the editor is hidden', async () => { const {component, element, editor} = buildComponent({autoHeight: false}) element.style.height = 5 * component.getLineHeight() + 'px' @@ -921,7 +876,7 @@ describe('TextEditorComponent', () => { const initialSeed = Date.now() for (var i = 0; i < 20; i++) { let seed = initialSeed + i - // seed = 1507224195357 + // seed = 1520247533732 const failureMessage = 'Randomized test failed with seed: ' + seed const random = Random(seed) @@ -930,6 +885,12 @@ describe('TextEditorComponent', () => { editor.setSoftWrapped(Boolean(random(2))) await setEditorWidthInCharacters(component, random(20)) await setEditorHeightInLines(component, random(10)) + + element.style.fontSize = random(20) + 'px' + element.style.lineHeight = random.floatBetween(0.1, 2.0) + TextEditor.didUpdateStyles() + await component.getNextUpdatePromise() + element.focus() for (var j = 0; j < 5; j++) { @@ -1034,7 +995,6 @@ describe('TextEditorComponent', () => { it('does not render scrollbars', async () => { const {component, element, editor} = buildComponent({mini: true, autoHeight: false}) await setEditorWidthInCharacters(component, 10) - await setEditorHeightInLines(component, 1) editor.setText('x'.repeat(20) + 'y'.repeat(20)) await component.getNextUpdatePromise() @@ -1119,7 +1079,7 @@ describe('TextEditorComponent', () => { describe('autoscroll', () => { it('automatically scrolls vertically when the requested range is within the vertical scroll margin of the top or bottom', async () => { - const {component, editor} = buildComponent({height: 120}) + const {component, editor} = buildComponent({height: 120 + horizontalScrollbarHeight}) expect(component.getLastVisibleRow()).toBe(7) editor.scrollToScreenRange([[4, 0], [6, 0]]) @@ -1141,7 +1101,7 @@ describe('TextEditorComponent', () => { it('does not vertically autoscroll by more than half of the visible lines if the editor is shorter than twice the scroll margin', async () => { const {component, element, editor} = buildComponent({autoHeight: false}) - element.style.height = 5.5 * component.measurements.lineHeight + 'px' + element.style.height = 5.5 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px' await component.getNextUpdatePromise() expect(component.getLastVisibleRow()).toBe(5) const scrollMarginInLines = 2 @@ -1171,8 +1131,8 @@ describe('TextEditorComponent', () => { await component.getNextUpdatePromise() const actualScrollCenter = (component.getScrollTop() + component.getScrollBottom()) / 2 - const expectedScrollCenter = Math.round((4 + 7) / 2 * component.getLineHeight()) - expect(actualScrollCenter).toBe(expectedScrollCenter) + const expectedScrollCenter = (4 + 7) / 2 * component.getLineHeight() + expect(actualScrollCenter).toBeCloseTo(expectedScrollCenter, 0) }) it('automatically scrolls horizontally when the requested range is within the horizontal scroll margin of the right edge of the gutter or right edge of the scroll container', async () => { @@ -1185,29 +1145,27 @@ describe('TextEditorComponent', () => { editor.scrollToScreenRange([[1, 12], [2, 28]]) await component.getNextUpdatePromise() - let expectedScrollLeft = Math.round( + let expectedScrollLeft = clientLeftForCharacter(component, 1, 12) - lineNodeForScreenRow(component, 1).getBoundingClientRect().left - (editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) - ) - expect(component.getScrollLeft()).toBe(expectedScrollLeft) + expect(component.getScrollLeft()).toBeCloseTo(expectedScrollLeft, 0) editor.scrollToScreenRange([[1, 12], [2, 28]], {reversed: false}) await component.getNextUpdatePromise() - expectedScrollLeft = Math.round( + expectedScrollLeft = component.getGutterContainerWidth() + clientLeftForCharacter(component, 2, 28) - lineNodeForScreenRow(component, 2).getBoundingClientRect().left + (editor.horizontalScrollMargin * component.measurements.baseCharacterWidth) - component.getScrollContainerClientWidth() - ) - expect(component.getScrollLeft()).toBe(expectedScrollLeft) + expect(component.getScrollLeft()).toBeCloseTo(expectedScrollLeft, 0) }) it('does not horizontally autoscroll by more than half of the visible "base-width" characters if the editor is narrower than twice the scroll margin', async () => { const {component, editor} = buildComponent({autoHeight: false}) await setEditorWidthInCharacters(component, 1.5 * editor.horizontalScrollMargin) - const editorWidthInChars = component.getScrollContainerWidth() / component.getBaseCharacterWidth() + const editorWidthInChars = component.getScrollContainerClientWidth() / component.getBaseCharacterWidth() expect(Math.round(editorWidthInChars)).toBe(9) editor.scrollToScreenRange([[6, 10], [6, 15]]) @@ -1307,22 +1265,22 @@ describe('TextEditorComponent', () => { // Assigns the scrollTop based on the logical position when attached jasmine.attachToDOM(element) - expect(component.getScrollLeft()).toBe(Math.round(2 * component.getBaseCharacterWidth())) + expect(component.getScrollLeft()).toBeCloseTo(2 * component.getBaseCharacterWidth(), 0) // Allows the scrollTopRow to be updated while attached component.setScrollLeftColumn(4) - expect(component.getScrollLeft()).toBe(Math.round(4 * component.getBaseCharacterWidth())) + expect(component.getScrollLeft()).toBeCloseTo(4 * component.getBaseCharacterWidth(), 0) // Preserves the scrollTopRow when detached element.remove() - expect(component.getScrollLeft()).toBe(Math.round(4 * component.getBaseCharacterWidth())) + expect(component.getScrollLeft()).toBeCloseTo(4 * component.getBaseCharacterWidth(), 0) component.setScrollLeftColumn(6) - expect(component.getScrollLeft()).toBe(Math.round(6 * component.getBaseCharacterWidth())) + expect(component.getScrollLeft()).toBeCloseTo(6 * component.getBaseCharacterWidth(), 0) jasmine.attachToDOM(element) element.style.width = '60px' - expect(component.getScrollLeft()).toBe(Math.round(6 * component.getBaseCharacterWidth())) + expect(component.getScrollLeft()).toBeCloseTo(6 * component.getBaseCharacterWidth(), 0) }) }) @@ -1368,40 +1326,6 @@ describe('TextEditorComponent', () => { } }) - it('always scrolls by a minimum of 1, even when the delta is small or the scroll sensitivity is low', () => { - const scrollSensitivity = 10 - const {component, editor} = buildComponent({height: 50, width: 50, scrollSensitivity}) - - { - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: -3}) - expect(component.getScrollTop()).toBe(1) - expect(component.getScrollLeft()).toBe(0) - expect(component.refs.content.style.transform).toBe(`translate(0px, -1px)`) - } - - { - component.didMouseWheel({wheelDeltaX: -4, wheelDeltaY: 0}) - expect(component.getScrollTop()).toBe(1) - expect(component.getScrollLeft()).toBe(1) - expect(component.refs.content.style.transform).toBe(`translate(-1px, -1px)`) - } - - editor.update({scrollSensitivity: 100}) - { - component.didMouseWheel({wheelDeltaX: 0, wheelDeltaY: 0.3}) - expect(component.getScrollTop()).toBe(0) - expect(component.getScrollLeft()).toBe(1) - expect(component.refs.content.style.transform).toBe(`translate(-1px, 0px)`) - } - - { - component.didMouseWheel({wheelDeltaX: 0.1, wheelDeltaY: 0}) - expect(component.getScrollTop()).toBe(0) - expect(component.getScrollLeft()).toBe(0) - expect(component.refs.content.style.transform).toBe(`translate(0px, 0px)`) - } - }) - it('inverts deltaX and deltaY when holding shift on Windows and Linux', async () => { const scrollSensitivity = 50 const {component, editor} = buildComponent({height: 50, width: 50, scrollSensitivity}) @@ -2130,6 +2054,37 @@ describe('TextEditorComponent', () => { expect(decorationNode2.firstChild).toBeNull() expect(gutterB.getElement().firstChild.children.length).toBe(0) }) + + it('renders custom line number gutters', async () => { + const {component, editor} = buildComponent() + const gutterA = editor.addGutter({ + name: 'a', + priority: 1, + type: 'line-number', + class: 'a-number', + labelFn: ({bufferRow}) => `a - ${bufferRow}` + }) + const gutterB = editor.addGutter({ + name: 'b', + priority: 1, + type: 'line-number', + class: 'b-number', + labelFn: ({bufferRow}) => `b - ${bufferRow}` + }) + editor.setText('0000\n0001\n0002\n0003\n0004\n') + + await component.getNextUpdatePromise() + + const gutterAElement = gutterA.getElement() + const aNumbers = gutterAElement.querySelectorAll('div.line-number[data-buffer-row]') + const aLabels = Array.from(aNumbers, e => e.textContent) + expect(aLabels).toEqual(['a - 0', 'a - 1', 'a - 2', 'a - 3', 'a - 4', 'a - 5']) + + const gutterBElement = gutterB.getElement() + const bNumbers = gutterBElement.querySelectorAll('div.line-number[data-buffer-row]') + const bLabels = Array.from(bNumbers, e => e.textContent) + expect(bLabels).toEqual(['b - 0', 'b - 1', 'b - 2', 'b - 3', 'b - 4', 'b - 5']) + }) }) describe('block decorations', () => { @@ -2140,7 +2095,8 @@ describe('TextEditorComponent', () => { // render an editor that already contains some block decorations const {component, element} = buildComponent({editor, rowsPerTile: 3}) - await setEditorHeightInLines(component, 4) + element.style.height = 4 * component.getLineHeight() + horizontalScrollbarHeight + 'px' + await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(0) expect(component.getRenderedEndRow()).toBe(9) expect(component.getScrollHeight()).toBe( @@ -2355,7 +2311,7 @@ describe('TextEditorComponent', () => { component.element.style.width = ( component.getGutterContainerWidth() + component.getScrollContainerClientWidth() * 2 + - component.getVerticalScrollbarWidth() + verticalScrollbarWidth ) + 'px' await component.getNextUpdatePromise() expect(component.getRenderedStartRow()).toBe(0) @@ -2840,9 +2796,9 @@ describe('TextEditorComponent', () => { describe('mouse input', () => { describe('on the lines', () => { - describe('when there is only one cursor and no selection', () => { - it('positions the cursor on single-click or when middle/right-clicking', async () => { - for (const button of [0, 1, 2]) { + describe('when there is only one cursor', () => { + it('positions the cursor on single-click or when middle-clicking', async () => { + for (const button of [0, 1]) { const {component, element, editor} = buildComponent() const {lineHeight} = component.measurements @@ -2921,70 +2877,6 @@ describe('TextEditorComponent', () => { }) }) - describe('when there is more than one cursor', () => { - it('does not move the cursor when right-clicking', async () => { - const {component, element, editor} = buildComponent() - const {lineHeight} = component.measurements - - editor.setCursorScreenPosition([5, 17], {autoscroll: false}) - editor.addCursorAtScreenPosition([2, 4]) - component.didMouseDownOnContent({ - detail: 1, - button: 2, - clientX: clientLeftForCharacter(component, 0, 0) - 1, - clientY: clientTopForLine(component, 0) - 1 - }) - expect(editor.getCursorScreenPositions()).toEqual([Point.fromObject([5, 17]), Point.fromObject([2, 4])]) - }) - - it('does move the cursor when middle-clicking', async () => { - const {component, element, editor} = buildComponent() - const {lineHeight} = component.measurements - - editor.setCursorScreenPosition([5, 17], {autoscroll: false}) - editor.addCursorAtScreenPosition([2, 4]) - component.didMouseDownOnContent({ - detail: 1, - button: 1, - clientX: clientLeftForCharacter(component, 0, 0) - 1, - clientY: clientTopForLine(component, 0) - 1 - }) - expect(editor.getCursorScreenPositions()).toEqual([Point.fromObject([0, 0])]) - }) - }) - - describe('when there are non-empty selections', () => { - it('does not move the cursor when right-clicking', async () => { - const {component, element, editor} = buildComponent() - const {lineHeight} = component.measurements - - editor.setCursorScreenPosition([5, 17], {autoscroll: false}) - editor.selectRight(3) - component.didMouseDownOnContent({ - detail: 1, - button: 2, - clientX: clientLeftForCharacter(component, 0, 0) - 1, - clientY: clientTopForLine(component, 0) - 1 - }) - expect(editor.getSelectedScreenRange()).toEqual([[5, 17], [5, 20]]) - }) - - it('does move the cursor when middle-clicking', async () => { - const {component, element, editor} = buildComponent() - const {lineHeight} = component.measurements - - editor.setCursorScreenPosition([5, 17], {autoscroll: false}) - editor.selectRight(3) - component.didMouseDownOnContent({ - detail: 1, - button: 1, - clientX: clientLeftForCharacter(component, 0, 0) - 1, - clientY: clientTopForLine(component, 0) - 1 - }) - expect(editor.getSelectedScreenRange()).toEqual([[0, 0], [0, 0]]) - }) - }) - describe('when the input is for the primary mouse button', () => { it('selects words on double-click', () => { const {component, editor} = buildComponent() @@ -3056,7 +2948,7 @@ describe('TextEditorComponent', () => { [[1, 16], [1, 16]] ]) - // ctrl-click does not add cursors on macOS, but it *does* move the cursor + // ctrl-click does not add cursors on macOS, nor does it move the cursor component.didMouseDownOnContent( Object.assign(clientPositionForCharacter(component, 1, 4), { detail: 1, @@ -3065,7 +2957,7 @@ describe('TextEditorComponent', () => { }) ) expect(editor.getSelectedScreenRanges()).toEqual([ - [[1, 4], [1, 4]] + [[1, 16], [1, 16]] ]) // ctrl-click adds cursors on platforms *other* than macOS @@ -3374,6 +3266,31 @@ describe('TextEditorComponent', () => { }) expect(editor.lineTextForBufferRow(10)).toBe('var') }) + + it('does not paste into a read only editor when clicking the middle mouse button on Linux', async () => { + spyOn(electron.ipcRenderer, 'send').andCallFake(function (eventName, selectedText) { + if (eventName === 'write-text-to-selection-clipboard') { + clipboard.writeText(selectedText, 'selection') + } + }) + + const {component, editor} = buildComponent({platform: 'linux', readOnly: true}) + + // Select the word 'sort' on line 2 and copy to clipboard + editor.setSelectedBufferRange([[1, 6], [1, 10]]) + await conditionPromise(() => TextEditor.clipboard.read() === 'sort') + + // Middle-click in the buffer at line 11, column 1 + component.didMouseDownOnContent({ + button: 1, + clientX: clientLeftForCharacter(component, 10, 0), + clientY: clientTopForLine(component, 10) + }) + + // Ensure that the correct text was copied but not pasted + expect(TextEditor.clipboard.read()).toBe('sort') + expect(editor.lineTextForBufferRow(10)).toBe('') + }) }) describe('on the line number gutter', () => { @@ -3624,12 +3541,12 @@ describe('TextEditorComponent', () => { describe('on the scrollbars', () => { it('delegates the mousedown events to the parent component unless the mousedown was on the actual scrollbar', async () => { const {component, element, editor} = buildComponent({height: 100}) - await setEditorWidthInCharacters(component, 8.5) + await setEditorWidthInCharacters(component, 6) const verticalScrollbar = component.refs.verticalScrollbar const horizontalScrollbar = component.refs.horizontalScrollbar - const leftEdgeOfVerticalScrollbar = verticalScrollbar.element.getBoundingClientRect().right - getVerticalScrollbarWidth(component) - const topEdgeOfHorizontalScrollbar = horizontalScrollbar.element.getBoundingClientRect().bottom - getHorizontalScrollbarHeight(component) + const leftEdgeOfVerticalScrollbar = verticalScrollbar.element.getBoundingClientRect().right - verticalScrollbarWidth + const topEdgeOfHorizontalScrollbar = horizontalScrollbar.element.getBoundingClientRect().bottom - horizontalScrollbarHeight verticalScrollbar.didMouseDown({ button: 0, @@ -3980,7 +3897,7 @@ describe('TextEditorComponent', () => { // Capture the width of the lines before requesting the width of // longest line, because making that request forces a DOM update const actualWidth = element.querySelector('.lines').style.width - const expectedWidth = Math.round( + const expectedWidth = Math.ceil( component.pixelPositionForScreenPosition(Point(3, Infinity)).left + component.getBaseCharacterWidth() ) @@ -4191,7 +4108,7 @@ describe('TextEditorComponent', () => { it('assigns scrollTop on the component when calling setFirstVisibleScreenRow', async () => { const {component, element, editor} = buildComponent({rowsPerTile: 3, autoHeight: false}) - element.style.height = 4 * component.measurements.lineHeight + 'px' + element.style.height = 4 * component.measurements.lineHeight + horizontalScrollbarHeight + 'px' await component.getNextUpdatePromise() expect(component.getMaxScrollTop() / component.getLineHeight()).toBe(9) @@ -4218,17 +4135,17 @@ describe('TextEditorComponent', () => { element.style.width = 30 * component.getBaseCharacterWidth() + 'px' await component.getNextUpdatePromise() expect(editor.getFirstVisibleScreenColumn()).toBe(0) - expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(0 * component.getBaseCharacterWidth()) + expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(0) setScrollLeft(component, 5.5 * component.getBaseCharacterWidth()) expect(editor.getFirstVisibleScreenColumn()).toBe(5) await component.getNextUpdatePromise() - expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(Math.round(5.5 * component.getBaseCharacterWidth())) + expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo(5.5 * component.getBaseCharacterWidth(), -1) editor.setFirstVisibleScreenColumn(12) - expect(component.getScrollLeft()).toBe(Math.round(12 * component.getBaseCharacterWidth())) + expect(component.getScrollLeft()).toBeCloseTo(12 * component.getBaseCharacterWidth(), -1) await component.getNextUpdatePromise() - expect(component.refs.horizontalScrollbar.element.scrollLeft).toBe(Math.round(12 * component.getBaseCharacterWidth())) + expect(component.refs.horizontalScrollbar.element.scrollLeft).toBeCloseTo(12 * component.getBaseCharacterWidth(), -1) }) }) @@ -4329,7 +4246,7 @@ describe('TextEditorComponent', () => { function buildEditor (params = {}) { const text = params.text != null ? params.text : SAMPLE_TEXT const buffer = new TextBuffer({text}) - const editorParams = {buffer} + const editorParams = {buffer, readOnly: params.readOnly} if (params.height != null) params.autoHeight = false for (const paramName of ['mini', 'autoHeight', 'autoWidth', 'lineNumberGutterVisible', 'showLineNumbers', 'placeholderText', 'softWrapped', 'scrollSensitivity']) { if (params[paramName] != null) editorParams[paramName] = params[paramName] @@ -4375,6 +4292,7 @@ async function setEditorWidthInCharacters (component, widthInCharacters) { component.element.style.width = component.getGutterContainerWidth() + widthInCharacters * component.measurements.baseCharacterWidth + + verticalScrollbarWidth + 'px' await component.getNextUpdatePromise() } diff --git a/spec/text-editor-element-spec.js b/spec/text-editor-element-spec.js index 7ffdf374d..d6c33e7ad 100644 --- a/spec/text-editor-element-spec.js +++ b/spec/text-editor-element-spec.js @@ -9,7 +9,7 @@ describe('TextEditorElement', () => { jasmineContent = document.body.querySelector('#jasmine-content') // Force scrollbars to be visible regardless of local system configuration const scrollbarStyle = document.createElement('style') - scrollbarStyle.textContent = '::-webkit-scrollbar { -webkit-appearance: none }' + scrollbarStyle.textContent = 'atom-text-editor ::-webkit-scrollbar { -webkit-appearance: none }' jasmine.attachToDOM(scrollbarStyle) }) @@ -338,18 +338,20 @@ describe('TextEditorElement', () => { element.style.width = '200px' jasmine.attachToDOM(element) + const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight() + expect(element.getMaxScrollTop()).toBe(0) await editor.update({autoHeight: false}) - element.style.height = '100px' + element.style.height = 100 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() expect(element.getMaxScrollTop()).toBe(60) - element.style.height = '120px' + element.style.height = 120 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() expect(element.getMaxScrollTop()).toBe(40) - element.style.height = '200px' + element.style.height = 200 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() expect(element.getMaxScrollTop()).toBe(0) }) @@ -392,10 +394,13 @@ describe('TextEditorElement', () => { it('returns true if the given row range intersects the visible row range', async () => { const element = buildTextEditorElement() const editor = element.getModel() + const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight() + editor.update({autoHeight: false}) element.getModel().setText('x\n'.repeat(20)) - element.style.height = '120px' + element.style.height = 120 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() + element.setScrollTop(80) await element.getNextUpdatePromise() expect(element.getVisibleRowRange()).toEqual([4, 11]) @@ -412,9 +417,11 @@ describe('TextEditorElement', () => { it('returns a {top/left/width/height} object describing the rectangle between two screen positions, even if they are not on screen', async () => { const element = buildTextEditorElement() const editor = element.getModel() + const horizontalScrollbarHeight = element.component.getHorizontalScrollbarHeight() + editor.update({autoHeight: false}) element.getModel().setText('xxxxxxxxxxxxxxxxxxxxxx\n'.repeat(20)) - element.style.height = '120px' + element.style.height = 120 + horizontalScrollbarHeight + 'px' await element.getNextUpdatePromise() element.setScrollTop(80) await element.getNextUpdatePromise() diff --git a/spec/text-editor-registry-spec.js b/spec/text-editor-registry-spec.js index e3086a302..4c6d680eb 100644 --- a/spec/text-editor-registry-spec.js +++ b/spec/text-editor-registry-spec.js @@ -1,6 +1,7 @@ const TextEditorRegistry = require('../src/text-editor-registry') const TextEditor = require('../src/text-editor') const TextBuffer = require('text-buffer') +const {Point, Range} = TextBuffer const {it, fit, ffit, fffit} = require('./async-spec-helpers') const dedent = require('dedent') @@ -154,6 +155,45 @@ describe('TextEditorRegistry', function () { expect(editor.getEncoding()).toBe('utf8') }) + it('preserves editor settings that haven\'t changed between previous and current language modes', async function () { + await atom.packages.activatePackage('language-javascript') + + registry.maintainConfig(editor) + await initialPackageActivation + + expect(editor.getEncoding()).toBe('utf8') + editor.setEncoding('utf16le') + expect(editor.getEncoding()).toBe('utf16le') + + expect(editor.isSoftWrapped()).toBe(false) + editor.setSoftWrapped(true) + expect(editor.isSoftWrapped()).toBe(true) + + atom.grammars.assignLanguageMode(editor, 'source.js') + await initialPackageActivation + expect(editor.getEncoding()).toBe('utf16le') + expect(editor.isSoftWrapped()).toBe(true) + }) + + it('updates editor settings that have changed between previous and current language modes', async function () { + await atom.packages.activatePackage('language-javascript') + + registry.maintainConfig(editor) + await initialPackageActivation + + expect(editor.getEncoding()).toBe('utf8') + atom.config.set('core.fileEncoding', 'utf16be', {scopeSelector: '.text.plain.null-grammar'}) + atom.config.set('core.fileEncoding', 'utf16le', {scopeSelector: '.source.js'}) + expect(editor.getEncoding()).toBe('utf16be') + + editor.setEncoding('utf8') + expect(editor.getEncoding()).toBe('utf8') + + atom.grammars.assignLanguageMode(editor, 'source.js') + await initialPackageActivation + expect(editor.getEncoding()).toBe('utf16le') + }) + it('returns a disposable that can be used to stop the registry from updating the editor\'s config', async function () { await atom.packages.activatePackage('language-javascript') @@ -218,19 +258,19 @@ describe('TextEditorRegistry', function () { describe('when the "tabType" config setting is "auto"', function () { it('enables or disables soft tabs based on the editor\'s content', async function () { + await initialPackageActivation await atom.packages.activatePackage('language-javascript') atom.grammars.assignLanguageMode(editor, 'source.js') atom.config.set('editor.tabType', 'auto') - - registry.maintainConfig(editor) await initialPackageActivation + const languageMode = editor.getBuffer().getLanguageMode() editor.setText(dedent` { hello; } `) - editor.getBuffer().getLanguageMode().retokenizeLines() + let disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(true) editor.setText(dedent` @@ -238,18 +278,17 @@ describe('TextEditorRegistry', function () { hello; } `) - editor.getBuffer().getLanguageMode().retokenizeLines() + disposable.dispose() + disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(false) - editor.setText(dedent` + editor.setTextInBufferRange(new Range(Point.ZERO, Point.ZERO), dedent` /* * Comment with a leading space. */ - { - ${'\t'}hello; - } - ` + editor.getText()) - editor.getBuffer().getLanguageMode().retokenizeLines() + ` + '\n') + disposable.dispose() + disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(false) editor.setText(dedent` @@ -261,8 +300,8 @@ describe('TextEditorRegistry', function () { hello; } `) - - editor.getBuffer().getLanguageMode().retokenizeLines() + disposable.dispose() + disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(false) editor.setText(dedent` @@ -274,7 +313,8 @@ describe('TextEditorRegistry', function () { hello; } `) - editor.getBuffer().getLanguageMode().retokenizeLines() + disposable.dispose() + disposable = registry.maintainConfig(editor) expect(editor.getSoftTabs()).toBe(true) }) }) diff --git a/spec/text-editor-spec.js b/spec/text-editor-spec.js index 32883a01d..9041d3528 100644 --- a/spec/text-editor-spec.js +++ b/spec/text-editor-spec.js @@ -861,6 +861,15 @@ describe('TextEditor', () => { }) }) }) + + it("clears the goal column", () => { + editor.setText('first\n\nthird') + editor.setCursorScreenPosition([0, 3]) + editor.moveDown() + editor.moveToFirstCharacterOfLine() + editor.moveDown() + expect(editor.getCursorBufferPosition()).toEqual([2, 0]) + }) }) describe('.moveToBeginningOfWord()', () => { @@ -5193,6 +5202,111 @@ describe('TextEditor', () => { }) }) + describe('undo/redo restore selections of editor which initiated original change', () => { + let editor1, editor2 + + beforeEach(async () => { + editor1 = editor + editor2 = new TextEditor({buffer: editor1.buffer}) + + editor1.setText(dedent ` + aaaaaa + bbbbbb + cccccc + dddddd + eeeeee + `) + }) + + it('[editor.transact] restore selection of change-initiated-editor', () => { + editor1.setCursorBufferPosition([0, 0]); editor1.transact(() => editor1.insertText('1')) + editor2.setCursorBufferPosition([1, 0]); editor2.transact(() => editor2.insertText('2')) + editor1.setCursorBufferPosition([2, 0]); editor1.transact(() => editor1.insertText('3')) + editor2.setCursorBufferPosition([3, 0]); editor2.transact(() => editor2.insertText('4')) + + expect(editor1.getText()).toBe(dedent ` + 1aaaaaa + 2bbbbbb + 3cccccc + 4dddddd + eeeeee + `) + + editor2.setCursorBufferPosition([4, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 0]) + expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 1]) + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 1]) + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 1]) + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 1]) + expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + + editor1.setCursorBufferPosition([4, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 0]) + expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 1]) + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 1]) + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 1]) + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 1]) + expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + }) + + it('[manually group checkpoint] restore selection of change-initiated-editor', () => { + const transact = (editor, fn) => { + const checkpoint = editor.createCheckpoint() + fn() + editor.groupChangesSinceCheckpoint(checkpoint) + } + + editor1.setCursorBufferPosition([0, 0]); transact(editor1, () => editor1.insertText('1')) + editor2.setCursorBufferPosition([1, 0]); transact(editor2, () => editor2.insertText('2')) + editor1.setCursorBufferPosition([2, 0]); transact(editor1, () => editor1.insertText('3')) + editor2.setCursorBufferPosition([3, 0]); transact(editor2, () => editor2.insertText('4')) + + expect(editor1.getText()).toBe(dedent ` + 1aaaaaa + 2bbbbbb + 3cccccc + 4dddddd + eeeeee + `) + + editor2.setCursorBufferPosition([4, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 0]) + editor1.undo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 0]) + expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([0, 1]) + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([1, 1]) + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([2, 1]) + editor1.redo(); expect(editor1.getCursorBufferPosition()).toEqual([3, 1]) + expect(editor2.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + + editor1.setCursorBufferPosition([4, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 0]) + editor2.undo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 0]) + expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([0, 1]) + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([1, 1]) + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([2, 1]) + editor2.redo(); expect(editor2.getCursorBufferPosition()).toEqual([3, 1]) + expect(editor1.getCursorBufferPosition()).toEqual([4, 0]) // remain unchanged + }) + }) + describe('when the buffer is changed (via its direct api, rather than via than edit session)', () => { it('moves the cursor so it is in the same relative position of the buffer', () => { expect(editor.getCursorScreenPosition()).toEqual([0, 0]) @@ -5383,6 +5497,195 @@ describe('TextEditor', () => { }) }) }) + + describe('when readonly', () => { + beforeEach(() => { + editor.setReadOnly(true) + }) + + const modifications = [ + { + name: 'moveLineUp', + op: (opts = {}) => { + editor.setCursorBufferPosition([1, 0]) + editor.moveLineUp(opts) + } + }, + { + name: 'moveLineDown', + op: (opts = {}) => { + editor.setCursorBufferPosition([0, 0]) + editor.moveLineDown(opts) + } + }, + { + name: 'insertText', + op: (opts = {}) => { + editor.setSelectedBufferRange([[1, 0], [1, 2]]) + editor.insertText('xxx', opts) + } + }, + { + name: 'insertNewline', + op: (opts = {}) => { + editor.setCursorScreenPosition({row: 1, column: 0}) + editor.insertNewline(opts) + } + }, + { + name: 'insertNewlineBelow', + op: (opts = {}) => { + editor.setCursorBufferPosition([0, 2]) + editor.insertNewlineBelow(opts) + } + }, + { + name: 'insertNewlineAbove', + op: (opts = {}) => { + editor.setCursorBufferPosition([0]) + editor.insertNewlineAbove(opts) + } + }, + { + name: 'backspace', + op: (opts = {}) => { + editor.setCursorScreenPosition({row: 1, column: 7}) + editor.backspace(opts) + } + }, + { + name: 'deleteToPreviousWordBoundary', + op: (opts = {}) => { + editor.setCursorBufferPosition([0, 16]) + editor.deleteToPreviousWordBoundary(opts) + } + }, + { + name: 'deleteToNextWordBoundary', + op: (opts = {}) => { + editor.setCursorBufferPosition([0, 15]) + editor.deleteToNextWordBoundary(opts) + } + }, + { + name: 'deleteToBeginningOfWord', + op: (opts = {}) => { + editor.setCursorBufferPosition([1, 24]) + editor.deleteToBeginningOfWord(opts) + } + }, + { + name: 'deleteToEndOfLine', + op: (opts = {}) => { + editor.setCursorBufferPosition([1, 24]) + editor.deleteToEndOfLine(opts) + } + }, + { + name: 'deleteToBeginningOfLine', + op: (opts = {}) => { + editor.setCursorBufferPosition([1, 24]) + editor.deleteToBeginningOfLine(opts) + } + }, + { + name: 'delete', + op: (opts = {}) => { + editor.setCursorScreenPosition([1, 6]) + editor.delete(opts) + } + }, + { + name: 'deleteToEndOfWord', + op: (opts = {}) => { + editor.setCursorBufferPosition([1, 24]) + editor.deleteToEndOfWord(opts) + } + }, + { + name: 'indent', + op: (opts = {}) => { + editor.indent(opts) + } + }, + { + name: 'cutSelectedText', + op: (opts = {}) => { + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + editor.cutSelectedText(opts) + } + }, + { + name: 'cutToEndOfLine', + op: (opts = {}) => { + editor.setCursorBufferPosition([2, 20]) + editor.cutToEndOfLine(opts) + } + }, + { + name: 'cutToEndOfBufferLine', + op: (opts = {}) => { + editor.setCursorBufferPosition([2, 20]) + editor.cutToEndOfBufferLine(opts) + } + }, + { + name: 'pasteText', + op: (opts = {}) => { + editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]]) + atom.clipboard.write('first') + editor.pasteText(opts) + } + }, + { + name: 'indentSelectedRows', + op: (opts = {}) => { + editor.setSelectedBufferRange([[0, 3], [0, 3]]) + editor.indentSelectedRows(opts) + } + }, + { + name: 'outdentSelectedRows', + op: (opts = {}) => { + editor.setSelectedBufferRange([[1, 3], [1, 3]]) + editor.outdentSelectedRows(opts) + } + }, + { + name: 'autoIndentSelectedRows', + op: (opts = {}) => { + editor.setCursorBufferPosition([2, 0]) + editor.insertText('function() {\ninside=true\n}\n i=1\n', opts) + editor.getLastSelection().setBufferRange([[2, 0], [6, 0]]) + editor.autoIndentSelectedRows(opts) + } + }, + { + name: 'undo/redo', + op: (opts = {}) => { + editor.insertText('foo', opts) + editor.undo(opts) + editor.redo(opts) + } + } + ] + + describe('without bypassReadOnly', () => { + for (const {name, op} of modifications) { + it(`throws an error on ${name}`, () => { + expect(op).toThrow() + }) + } + }) + + describe('with bypassReadOnly', () => { + for (const {name, op} of modifications) { + it(`permits ${name}`, () => { + op({bypassReadOnly: true}) + }) + } + }) + }) }) describe('reading text', () => { @@ -6413,6 +6716,20 @@ describe('TextEditor', () => { const gutter = editor.addGutter(options) expect(editor.getGutters().length).toBe(2) expect(editor.getGutters()[1]).toBe(gutter) + expect(gutter.type).toBe('decorated') + }) + + it('can add a custom line-number gutter', () => { + expect(editor.getGutters().length).toBe(1) + const options = { + name: 'another-gutter', + priority: 2, + type: 'line-number' + } + const gutter = editor.addGutter(options) + expect(editor.getGutters().length).toBe(2) + expect(editor.getGutters()[1]).toBe(gutter) + expect(gutter.type).toBe('line-number') }) it("does not allow a custom gutter with the 'line-number' name.", () => expect(editor.addGutter.bind(editor, {name: 'line-number'})).toThrow()) @@ -6747,6 +7064,14 @@ describe('TextEditor', () => { editor.destroy() }) + describe('.scopeDescriptorForBufferPosition(position)', () => { + it('returns a default scope descriptor when no language mode is assigned', () => { + editor = new TextEditor({buffer: new TextBuffer()}) + const scopeDescriptor = editor.scopeDescriptorForBufferPosition([0, 0]) + expect(scopeDescriptor.getScopesArray()).toEqual(['text']) + }) + }) + describe('.shouldPromptToSave()', () => { beforeEach(async () => { editor = await atom.workspace.open('sample.js') diff --git a/spec/text-mate-language-mode-spec.js b/spec/text-mate-language-mode-spec.js index 2d02348cb..c6292a63b 100644 --- a/spec/text-mate-language-mode-spec.js +++ b/spec/text-mate-language-mode-spec.js @@ -912,6 +912,20 @@ describe('TextMateLanguageMode', () => { } `) + range = languageMode.getFoldableRangeContainingPoint(Point(7, 0), 2) + expect(simulateFold([range])).toBe(dedent ` + if (a) { + b(); + if (c) {⋯ + } + h() + } + i() + if (j) { + k() + } + `) + range = languageMode.getFoldableRangeContainingPoint(Point(1, Infinity), 2) expect(simulateFold([range])).toBe(dedent ` if (a) {⋯ diff --git a/spec/tooltip-manager-spec.js b/spec/tooltip-manager-spec.js index 3a6b56a1b..0cf3c3e64 100644 --- a/spec/tooltip-manager-spec.js +++ b/spec/tooltip-manager-spec.js @@ -213,6 +213,18 @@ describe('TooltipManager', () => { }) ) + describe('when a user types', () => + it('hides the tooltips', () => { + const disposable = manager.add(element, { title: 'Title' }) + hover(element, function () { + expect(document.body.querySelector('.tooltip')).not.toBeNull() + window.dispatchEvent(new CustomEvent('keydown')) + expect(document.body.querySelector('.tooltip')).toBeNull() + disposable.dispose() + }) + }) + ) + describe('findTooltips', () => { it('adds and remove tooltips correctly', () => { expect(manager.findTooltips(element).length).toBe(0) diff --git a/spec/tree-sitter-language-mode-spec.js b/spec/tree-sitter-language-mode-spec.js index ec38c1a06..58dae0241 100644 --- a/spec/tree-sitter-language-mode-spec.js +++ b/spec/tree-sitter-language-mode-spec.js @@ -10,6 +10,9 @@ const TreeSitterLanguageMode = require('../src/tree-sitter-language-mode') const cGrammarPath = require.resolve('language-c/grammars/tree-sitter-c.cson') const pythonGrammarPath = require.resolve('language-python/grammars/tree-sitter-python.cson') const jsGrammarPath = require.resolve('language-javascript/grammars/tree-sitter-javascript.cson') +const htmlGrammarPath = require.resolve('language-html/grammars/tree-sitter-html.cson') +const ejsGrammarPath = require.resolve('language-html/grammars/tree-sitter-ejs.cson') +const rubyGrammarPath = require.resolve('language-ruby/grammars/tree-sitter-ruby.cson') describe('TreeSitterLanguageMode', () => { let editor, buffer @@ -17,10 +20,11 @@ describe('TreeSitterLanguageMode', () => { beforeEach(async () => { editor = await atom.workspace.open('') buffer = editor.getBuffer() + editor.displayLayer.reset({foldCharacter: '…'}) }) describe('highlighting', () => { - it('applies the most specific scope mapping to each node in the syntax tree', () => { + it('applies the most specific scope mapping to each node in the syntax tree', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { @@ -31,8 +35,11 @@ describe('TreeSitterLanguageMode', () => { } }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText('aa.bbb = cc(d.eee());') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + expectTokensToEqual(editor, [[ {text: 'aa.', scopes: ['source']}, {text: 'bbb', scopes: ['source', 'property']}, @@ -44,7 +51,7 @@ describe('TreeSitterLanguageMode', () => { ]]) }) - it('can start or end multiple scopes at the same position', () => { + it('can start or end multiple scopes at the same position', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { @@ -57,8 +64,11 @@ describe('TreeSitterLanguageMode', () => { } }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText('a = bb.ccc();') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + expectTokensToEqual(editor, [[ {text: 'a', scopes: ['source', 'variable']}, {text: ' = ', scopes: ['source']}, @@ -70,7 +80,7 @@ describe('TreeSitterLanguageMode', () => { ]]) }) - it('can resume highlighting on a line that starts with whitespace', () => { + it('can resume highlighting on a line that starts with whitespace', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: { @@ -80,14 +90,17 @@ describe('TreeSitterLanguageMode', () => { } }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText('a\n .b();') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + expectTokensToEqual(editor, [ [ {text: 'a', scopes: ['variable']}, ], [ - {text: ' ', scopes: ['whitespace']}, + {text: ' ', scopes: ['leading-whitespace']}, {text: '.', scopes: []}, {text: 'b', scopes: ['function']}, {text: '();', scopes: []} @@ -95,8 +108,8 @@ describe('TreeSitterLanguageMode', () => { ]) }) - it('correctly skips over tokens with zero size', () => { - const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + it('correctly skips over tokens with zero size', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, cGrammarPath, { parser: 'tree-sitter-c', scopes: { 'primitive_type': 'type', @@ -104,13 +117,13 @@ describe('TreeSitterLanguageMode', () => { } }) - const languageMode = new TreeSitterLanguageMode({buffer, grammar}) - buffer.setLanguageMode(languageMode) buffer.setText('int main() {\n int a\n int b;\n}'); - editor.screenLineForScreenRow(0) + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + expect( - languageMode.document.rootNode.descendantForPosition(Point(1, 2), Point(1, 6)).toString() + languageMode.tree.rootNode.descendantForPosition(Point(1, 2), Point(1, 6)).toString() ).toBe('(declaration (primitive_type) (identifier) (MISSING))') expectTokensToEqual(editor, [ @@ -121,13 +134,13 @@ describe('TreeSitterLanguageMode', () => { {text: '() {', scopes: []} ], [ - {text: ' ', scopes: ['whitespace']}, + {text: ' ', scopes: ['leading-whitespace']}, {text: 'int', scopes: ['type']}, {text: ' ', scopes: []}, {text: 'a', scopes: ['variable']} ], [ - {text: ' ', scopes: ['whitespace']}, + {text: ' ', scopes: ['leading-whitespace']}, {text: 'int', scopes: ['type']}, {text: ' ', scopes: []}, {text: 'b', scopes: ['variable']}, @@ -138,14 +151,604 @@ describe('TreeSitterLanguageMode', () => { ] ]) }) + + it('updates lines\' highlighting when they are affected by distant changes', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'call_expression > identifier': 'function', + 'property_identifier': 'member' + } + }) + + buffer.setText('a(\nb,\nc\n') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + // missing closing paren + expectTokensToEqual(editor, [ + [{text: 'a(', scopes: []}], + [{text: 'b,', scopes: []}], + [{text: 'c', scopes: []}], + [{text: '', scopes: []}] + ]) + + buffer.append(')') + await nextHighlightingUpdate(languageMode) + expectTokensToEqual(editor, [ + [ + {text: 'a', scopes: ['function']}, + {text: '(', scopes: []} + ], + [{text: 'b,', scopes: []}], + [{text: 'c', scopes: []}], + [{text: ')', scopes: []}] + ]) + }) + + it('allows comma-separated selectors as scope mapping keys', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'identifier, call_expression > identifier': [ + {match: '^[A-Z]', scopes: 'constructor'} + ], + + 'call_expression > identifier': 'function' + } + }) + + buffer.setText(`a(B(new C))`) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'a', scopes: ['function']}, + {text: '(', scopes: []}, + {text: 'B', scopes: ['constructor']}, + {text: '(new ', scopes: []}, + {text: 'C', scopes: ['constructor']}, + {text: '))', scopes: []}, + ] + ]) + }) + + it('handles edits after tokens that end between CR and LF characters (regression)', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'comment': 'comment', + 'string': 'string', + 'property_identifier': 'property', + } + }) + + buffer.setText([ + '// abc', + '', + 'a("b").c' + ].join('\r\n')) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [{text: '// abc', scopes: ['comment']}], + [{text: '', scopes: []}], + [ + {text: 'a(', scopes: []}, + {text: '"b"', scopes: ['string']}, + {text: ').', scopes: []}, + {text: 'c', scopes: ['property']} + ] + ]) + + buffer.insert([2, 0], ' ') + expectTokensToEqual(editor, [ + [{text: '// abc', scopes: ['comment']}], + [{text: '', scopes: []}], + [ + {text: ' ', scopes: ['leading-whitespace']}, + {text: 'a(', scopes: []}, + {text: '"b"', scopes: ['string']}, + {text: ').', scopes: []}, + {text: 'c', scopes: ['property']} + ] + ]) + }) + + it('handles multi-line nodes with children on different lines (regression)', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'template_string': 'string', + '"${"': 'interpolation', + '"}"': 'interpolation' + } + }); + + buffer.setText('`\na${1}\nb${2}\n`;') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: '`', scopes: ['string']} + ], [ + {text: 'a', scopes: ['string']}, + {text: '${', scopes: ['string', 'interpolation']}, + {text: '1', scopes: ['string']}, + {text: '}', scopes: ['string', 'interpolation']} + ], [ + {text: 'b', scopes: ['string']}, + {text: '${', scopes: ['string', 'interpolation']}, + {text: '2', scopes: ['string']}, + {text: '}', scopes: ['string', 'interpolation']} + ], + [ + {text: '`', scopes: ['string']}, + {text: ';', scopes: []} + ] + ]) + }) + + it('handles folds inside of highlighted tokens', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'comment': 'comment', + 'call_expression > identifier': 'function', + } + }) + + buffer.setText(dedent ` + /* + * Hello + */ + + hello(); + `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + editor.foldBufferRange([[0, 2], [2, 0]]) + + expectTokensToEqual(editor, [ + [ + {text: '/*', scopes: ['comment']}, + {text: '…', scopes: ['fold-marker']}, + {text: ' */', scopes: ['comment']} + ], + [ + {text: '', scopes: []} + ], + [ + {text: 'hello', scopes: ['function']}, + {text: '();', scopes: []}, + ] + ]) + }) + + it('applies regex match rules when specified', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'identifier': [ + {match: '^(exports|document|window|global)$', scopes: 'global'}, + {match: '^[A-Z_]+$', scopes: 'constant'}, + {match: '^[A-Z]', scopes: 'constructor'}, + 'variable' + ], + } + }) + + buffer.setText(`exports.object = Class(SOME_CONSTANT, x)`) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'exports', scopes: ['global']}, + {text: '.object = ', scopes: []}, + {text: 'Class', scopes: ['constructor']}, + {text: '(', scopes: []}, + {text: 'SOME_CONSTANT', scopes: ['constant']}, + {text: ', ', scopes: []}, + {text: 'x', scopes: ['variable']}, + {text: ')', scopes: []}, + ] + ]) + }) + + it('handles nodes that start before their first child and end after their last child', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, rubyGrammarPath, { + parser: 'tree-sitter-ruby', + scopes: { + 'bare_string': 'string', + 'interpolation': 'embedded', + '"#{"': 'punctuation', + '"}"': 'punctuation', + } + }) + + // The bare string node `bc#{d}ef` has one child: the interpolation, and that child + // starts later and ends earlier than the bare string. + buffer.setText('a = %W( bc#{d}ef )') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'a = %W( ', scopes: []}, + {text: 'bc', scopes: ['string']}, + {text: '#{', scopes: ['string', 'embedded', 'punctuation']}, + {text: 'd', scopes: ['string', 'embedded']}, + {text: '}', scopes: ['string', 'embedded', 'punctuation']}, + {text: 'ef', scopes: ['string']}, + {text: ' )', scopes: []}, + ] + ]) + }) + + describe('when the buffer changes during a parse', () => { + it('immediately parses again when the current parse completes', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'identifier': 'variable', + 'call_expression > identifier': 'function', + 'new_expression > call_expression > identifier': 'constructor' + } + }) + + buffer.setText('abc;'); + + const languageMode = new TreeSitterLanguageMode({buffer, grammar, syncOperationLimit: 0}) + buffer.setLanguageMode(languageMode) + await nextHighlightingUpdate(languageMode) + await new Promise(process.nextTick) + + expectTokensToEqual(editor, [ + [ + {text: 'abc', scopes: ['variable']}, + {text: ';', scopes: []} + ], + ]) + + buffer.setTextInRange([[0, 3], [0, 3]], '()'); + expectTokensToEqual(editor, [ + [ + {text: 'abc()', scopes: ['variable']}, + {text: ';', scopes: []} + ], + ]) + + buffer.setTextInRange([[0, 0], [0, 0]], 'new '); + expectTokensToEqual(editor, [ + [ + {text: 'new ', scopes: []}, + {text: 'abc()', scopes: ['variable']}, + {text: ';', scopes: []} + ], + ]) + + await nextHighlightingUpdate(languageMode) + expectTokensToEqual(editor, [ + [ + {text: 'new ', scopes: []}, + {text: 'abc', scopes: ['function']}, + {text: '();', scopes: []} + ], + ]) + + await nextHighlightingUpdate(languageMode) + expectTokensToEqual(editor, [ + [ + {text: 'new ', scopes: []}, + {text: 'abc', scopes: ['constructor']}, + {text: '();', scopes: []} + ], + ]) + }) + }) + + describe('injectionPoints and injectionPatterns', () => { + let jsGrammar, htmlGrammar + + beforeEach(() => { + jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript', + scopes: { + 'property_identifier': 'property', + 'call_expression > identifier': 'function', + 'template_string': 'string', + 'template_substitution > "${"': 'interpolation', + 'template_substitution > "}"': 'interpolation' + }, + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: { + fragment: 'html', + tag_name: 'tag', + attribute_name: 'attr' + }, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + }) + }) + + it('highlights code inside of injection points', async () => { + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + buffer.setText('node.innerHTML = html `\na ${b}\n`;') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'node.', scopes: []}, + {text: 'innerHTML', scopes: ['property']}, + {text: ' = ', scopes: []}, + {text: 'html', scopes: ['function']}, + {text: ' ', scopes: []}, + {text: '`', scopes: ['string']}, + {text: '', scopes: ['string', 'html']} + ], [ + {text: 'a ', scopes: ['string', 'html']}, + {text: '${', scopes: ['string', 'html', 'interpolation']}, + {text: 'b', scopes: ['string', 'html']}, + {text: '}', scopes: ['string', 'html', 'interpolation']}, + {text: '<', scopes: ['string', 'html']}, + {text: 'img', scopes: ['string', 'html', 'tag']}, + {text: ' ', scopes: ['string', 'html']}, + {text: 'src', scopes: ['string', 'html', 'attr']}, + {text: '="d">', scopes: ['string', 'html']} + ], [ + {text: '`', scopes: ['string']}, + {text: ';', scopes: []}, + ], + ]) + + const range = buffer.findSync('html') + buffer.setTextInRange(range, 'xml') + await nextHighlightingUpdate(languageMode) + await nextHighlightingUpdate(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'node.', scopes: []}, + {text: 'innerHTML', scopes: ['property']}, + {text: ' = ', scopes: []}, + {text: 'xml', scopes: ['function']}, + {text: ' ', scopes: []}, + {text: '`', scopes: ['string']} + ], [ + {text: 'a ', scopes: ['string']}, + {text: '${', scopes: ['string', 'interpolation']}, + {text: 'b', scopes: ['string']}, + {text: '}', scopes: ['string', 'interpolation']}, + {text: '', scopes: ['string']}, + ], [ + {text: '`', scopes: ['string']}, + {text: ';', scopes: []}, + ], + ]) + }) + + it('highlights the content after injections', async () => { + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + buffer.setText('\n
\n
') + + const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: '<', scopes: ['html']}, + {text: 'script', scopes: ['html', 'tag']}, + {text: '>', scopes: ['html']}, + ], + [ + {text: 'hello', scopes: ['html', 'function']}, + {text: '();', scopes: ['html']}, + ], + [ + {text: '', scopes: ['html']}, + ], + [ + {text: '<', scopes: ['html']}, + {text: 'div', scopes: ['html', 'tag']}, + {text: '>', scopes: ['html']}, + ], + [ + {text: '', scopes: ['html']}, + ] + ]) + }) + + it('updates buffers highlighting when a grammar with injectionRegExp is added', async () => { + atom.grammars.addGrammar(jsGrammar) + + buffer.setText('node.innerHTML = html `\na ${b}\n`;') + const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'node.', scopes: []}, + {text: 'innerHTML', scopes: ['property']}, + {text: ' = ', scopes: []}, + {text: 'html', scopes: ['function']}, + {text: ' ', scopes: []}, + {text: '`', scopes: ['string']} + ], [ + {text: 'a ', scopes: ['string']}, + {text: '${', scopes: ['string', 'interpolation']}, + {text: 'b', scopes: ['string']}, + {text: '}', scopes: ['string', 'interpolation']}, + {text: '', scopes: ['string']}, + ], [ + {text: '`', scopes: ['string']}, + {text: ';', scopes: []}, + ], + ]) + + atom.grammars.addGrammar(htmlGrammar) + await nextHighlightingUpdate(languageMode) + expectTokensToEqual(editor, [ + [ + {text: 'node.', scopes: []}, + {text: 'innerHTML', scopes: ['property']}, + {text: ' = ', scopes: []}, + {text: 'html', scopes: ['function']}, + {text: ' ', scopes: []}, + {text: '`', scopes: ['string']}, + {text: '', scopes: ['string', 'html']} + ], [ + {text: 'a ', scopes: ['string', 'html']}, + {text: '${', scopes: ['string', 'html', 'interpolation']}, + {text: 'b', scopes: ['string', 'html']}, + {text: '}', scopes: ['string', 'html', 'interpolation']}, + {text: '<', scopes: ['string', 'html']}, + {text: 'img', scopes: ['string', 'html', 'tag']}, + {text: ' ', scopes: ['string', 'html']}, + {text: 'src', scopes: ['string', 'html', 'attr']}, + {text: '="d">', scopes: ['string', 'html']} + ], [ + {text: '`', scopes: ['string']}, + {text: ';', scopes: []}, + ], + ]) + }) + + it('handles injections that intersect', async () => { + const ejsGrammar = new TreeSitterGrammar(atom.grammars, ejsGrammarPath, { + id: 'ejs', + parser: 'tree-sitter-embedded-template', + scopes: { + '"<%="': 'directive', + '"%>"': 'directive', + }, + injectionPoints: [ + { + type: 'template', + language (node) { return 'javascript' }, + content (node) { return node.descendantsOfType('code') } + }, + { + type: 'template', + language (node) { return 'html' }, + content (node) { return node.descendantsOfType('content') } + } + ] + }) + + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText('\n\n') + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: ejsGrammar, + grammars: atom.grammars, + }) + buffer.setLanguageMode(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: '<', scopes: ['html']}, + {text: 'body', scopes: ['html', 'tag']}, + {text: '>', scopes: ['html']} + ], + [ + {text: '<', scopes: ['html']}, + {text: 'script', scopes: ['html', 'tag']}, + {text: '>', scopes: ['html']} + ], + [ + {text: 'b', scopes: ['html', 'function']}, + {text: '(', scopes: ['html']}, + {text: '<%=', scopes: ['html', 'directive']}, + {text: ' c.', scopes: ['html']}, + {text: 'd', scopes: ['html', 'property']}, + {text: ' ', scopes: ['html']}, + {text: '%>', scopes: ['html', 'directive']}, + {text: ')', scopes: ['html']}, + ], + [ + {text: '', scopes: ['html']} + ], + [ + {text: '', scopes: ['html']} + ], + ]) + }) + + it('notifies onDidTokenize listeners the first time all syntax highlighting is done', async () => { + const promise = new Promise(resolve => { + editor.onDidTokenize(event => { + expectTokensToEqual(editor, [ + [ + {text: '<', scopes: ['html']}, + {text: 'script', scopes: ['html', 'tag']}, + {text: '>', scopes: ['html']}, + ], + [ + {text: 'hello', scopes: ['html', 'function']}, + {text: '();', scopes: ['html']}, + ], + [ + {text: '', scopes: ['html']}, + ] + ]) + resolve() + }) + }) + + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + buffer.setText('') + + const languageMode = new TreeSitterLanguageMode({ + buffer, + grammar: htmlGrammar, + grammars: atom.grammars, + syncOperationLimit: 0 + }) + buffer.setLanguageMode(languageMode) + + await promise + }) + }) }) describe('folding', () => { - beforeEach(() => { - editor.displayLayer.reset({foldCharacter: '…'}) - }) - - it('can fold nodes that start and end with specified tokens', () => { + it('can fold nodes that start and end with specified tokens', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', folds: [ @@ -160,7 +763,6 @@ describe('TreeSitterLanguageMode', () => { ] }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText(dedent ` module.exports = class A { @@ -172,7 +774,8 @@ describe('TreeSitterLanguageMode', () => { } `) - editor.screenLineForScreenRow(0) + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(false) expect(editor.isFoldableAtBufferRow(1)).toBe(true) @@ -185,7 +788,7 @@ describe('TreeSitterLanguageMode', () => { expect(getDisplayText(editor)).toBe(dedent ` module.exports = class A { - getB (…) { + getB (c,…) { return this.f(g) } } @@ -195,12 +798,60 @@ describe('TreeSitterLanguageMode', () => { expect(getDisplayText(editor)).toBe(dedent ` module.exports = class A { - getB (…) {…} + getB (c,…) {…} } `) }) - it('can fold nodes of specified types', () => { + it('folds entire buffer rows when necessary to keep words on separate lines', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + folds: [ + { + start: {type: '{', index: 0}, + end: {type: '}', index: -1} + }, + { + start: {type: '(', index: 0}, + end: {type: ')', index: -1} + } + ] + }) + + buffer.setText(dedent ` + if (a) { + b + } else if (c) { + d + } else { + e + } + `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + // Avoid bringing the `else if...` up onto the same screen line as the preceding `if`. + editor.foldBufferRow(1) + editor.foldBufferRow(3) + expect(getDisplayText(editor)).toBe(dedent ` + if (a) {… + } else if (c) {… + } else { + e + } + `) + + // It's ok to bring the final `}` onto the same screen line as the preceding `else`. + editor.foldBufferRow(5) + expect(getDisplayText(editor)).toBe(dedent ` + if (a) {… + } else if (c) {… + } else {…} + `) + }) + + it('can fold nodes of specified types', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', folds: [ @@ -221,7 +872,6 @@ describe('TreeSitterLanguageMode', () => { ] }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText(dedent ` const element1 = { `) - editor.screenLineForScreenRow(0) + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(true) expect(editor.isFoldableAtBufferRow(1)).toBe(false) @@ -261,7 +912,7 @@ describe('TreeSitterLanguageMode', () => { `) }) - it('can fold entire nodes when no start or end parameters are specified', () => { + it('can fold entire nodes when no start or end parameters are specified', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', folds: [ @@ -271,7 +922,6 @@ describe('TreeSitterLanguageMode', () => { ] }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText(dedent ` /** * Important @@ -281,7 +931,8 @@ describe('TreeSitterLanguageMode', () => { */ `) - editor.screenLineForScreenRow(0) + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(true) expect(editor.isFoldableAtBufferRow(1)).toBe(false) @@ -304,7 +955,7 @@ describe('TreeSitterLanguageMode', () => { `) }) - it('tries each folding strategy for a given node in the order specified', () => { + it('tries each folding strategy for a given node in the order specified', async () => { const grammar = new TreeSitterGrammar(atom.grammars, cGrammarPath, { parser: 'tree-sitter-c', folds: [ @@ -330,8 +981,6 @@ describe('TreeSitterLanguageMode', () => { ] }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - buffer.setText(dedent ` #ifndef FOO_H_ #define FOO_H_ @@ -356,7 +1005,8 @@ describe('TreeSitterLanguageMode', () => { #endif `) - editor.screenLineForScreenRow(0) + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) editor.foldBufferRow(3) expect(getDisplayText(editor)).toBe(dedent ` @@ -417,8 +1067,127 @@ describe('TreeSitterLanguageMode', () => { `) }) + it('does not fold when the start and end parameters match the same child', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + parser: 'tree-sitter-html', + folds: [ + { + type: 'element', + start: {index: 0}, + end: {index: -1} + } + ] + }) + + buffer.setText(dedent ` + + + + + `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + // Void elements have only one child + expect(editor.isFoldableAtBufferRow(1)).toBe(false) + expect(editor.isFoldableAtBufferRow(2)).toBe(false) + + editor.foldBufferRow(0) + expect(getDisplayText(editor)).toBe(dedent ` + … + + `) + }) + + it('can target named vs anonymous nodes as fold boundaries', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, rubyGrammarPath, { + parser: 'tree-sitter-ruby', + folds: [ + { + type: 'elsif', + start: {index: 1}, + + // There are no double quotes around the `elsif` type. This indicates + // that we're targeting a *named* node in the syntax tree. The fold + // should end at the nested `elsif` node, not at the token that represents + // the literal string "elsif". + end: {type: ['else', 'elsif']} + }, + { + type: 'else', + + // There are double quotes around the `else` type. This indicates that + // we're targetting an *anonymous* node in the syntax tree. The fold + // should start at the token representing the literal string "else", + // not at an `else` node. + start: {type: '"else"'} + } + ] + }) + + buffer.setText(dedent ` + if a + b + elsif c + d + elsif e + f + else + g + end + `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + + expect(languageMode.tree.rootNode.toString()).toBe( + "(program (if (identifier) " + + "(identifier) " + + "(elsif (identifier) " + + "(identifier) " + + "(elsif (identifier) " + + "(identifier) " + + "(else " + + "(identifier))))))" + ) + + editor.foldBufferRow(2) + expect(getDisplayText(editor)).toBe(dedent ` + if a + b + elsif c… + elsif e + f + else + g + end + `) + + editor.foldBufferRow(4) + expect(getDisplayText(editor)).toBe(dedent ` + if a + b + elsif c… + elsif e… + else + g + end + `) + + editor.foldBufferRow(6) + expect(getDisplayText(editor)).toBe(dedent ` + if a + b + elsif c… + elsif e… + else… + end + `) + }) + describe('when folding a node that ends with a line break', () => { - it('ends the fold at the end of the previous line', () => { + it('ends the fold at the end of the previous line', async () => { const grammar = new TreeSitterGrammar(atom.grammars, pythonGrammarPath, { parser: 'tree-sitter-python', folds: [ @@ -429,8 +1198,6 @@ describe('TreeSitterLanguageMode', () => { ] }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - buffer.setText(dedent ` def ab(): print 'a' @@ -441,7 +1208,7 @@ describe('TreeSitterLanguageMode', () => { print 'd' `) - editor.screenLineForScreenRow(0) + buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) editor.foldBufferRow(0) expect(getDisplayText(editor)).toBe(dedent ` @@ -453,41 +1220,354 @@ describe('TreeSitterLanguageMode', () => { `) }) }) + + it('folds code in injected languages', async () => { + const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + folds: [{ + type: ['element', 'raw_element'], + start: {index: 0}, + end: {index: -1} + }], + injectionRegExp: 'html' + }) + + const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript', + scopes: {}, + folds: [{ + type: ['template_string'], + start: {index: 0}, + end: {index: -1}, + }, + { + start: {index: 0, type: '('}, + end: {index: -1, type: ')'} + }], + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText( + `a = html \` +
+ c\${def( + 1, + 2, + 3, + )}e\${f}g +
+ \` + ` + ) + const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + editor.foldBufferRow(2) + expect(getDisplayText(editor)).toBe( + `a = html \` +
+ c\${def(… + )}e\${f}g +
+ \` + ` + ) + + editor.foldBufferRow(1) + expect(getDisplayText(editor)).toBe( + `a = html \` +
… +
+ \` + ` + ) + + editor.foldBufferRow(0) + expect(getDisplayText(editor)).toBe( + `a = html \`…\` + ` + ) + }) }) describe('.scopeDescriptorForPosition', () => { - it('returns a scope descriptor representing the given position in the syntax tree', () => { + it('returns a scope descriptor representing the given position in the syntax tree', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { - id: 'javascript', - parser: 'tree-sitter-javascript' + scopeName: 'source.js', + parser: 'tree-sitter-javascript', + scopes: { + program: 'source.js', + property_identifier: 'property.name' + } }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - buffer.setText('foo({bar: baz});') - editor.screenLineForScreenRow(0) - expect(editor.scopeDescriptorForBufferPosition({row: 0, column: 6}).getScopesArray()).toEqual([ - 'javascript', - 'program', - 'expression_statement', - 'call_expression', - 'arguments', - 'object', - 'pair', - 'property_identifier' + buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) + expect(editor.scopeDescriptorForBufferPosition([0, 'foo({b'.length]).getScopesArray()).toEqual([ + 'source.js', + 'property.name' + ]) + expect(editor.scopeDescriptorForBufferPosition([0, 'foo({'.length]).getScopesArray()).toEqual([ + 'source.js', + 'property.name' + ]) + }) + + it('includes nodes in injected syntax trees', async () => { + const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'source.js', + parser: 'tree-sitter-javascript', + scopes: { + program: 'source.js', + template_string: 'string.quoted', + interpolation: 'meta.embedded', + property_identifier: 'property.name' + }, + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'text.html', + parser: 'tree-sitter-html', + scopes: { + fragment: 'text.html', + raw_element: 'script.tag' + }, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + }) + + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText(` +
+ +
+ `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + const position = buffer.findSync('name').start + expect(languageMode.scopeDescriptorForPosition(position).getScopesArray()).toEqual([ + 'text.html', + 'script.tag', + 'source.js', + 'string.quoted', + 'text.html', + 'property.name' ]) }) }) + describe('.bufferRangeForScopeAtPosition(selector?, position)', () => { + describe('when selector = null', () => { + it('returns the range of the smallest node at position', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript' + }) + + buffer.setText('foo({bar: baz});') + + buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) + expect(editor.bufferRangeForScopeAtPosition(null, [0, 6])).toEqual( + [[0, 5], [0, 8]] + ) + expect(editor.bufferRangeForScopeAtPosition(null, [0, 9])).toEqual( + [[0, 8], [0, 9]] + ) + }) + + it('includes nodes in injected syntax trees', async () => { + const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript', + scopes: {}, + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + }) + + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText(` +
+ +
+ `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + const nameProperty = buffer.findSync('name') + const {start} = nameProperty + const position = Object.assign({}, start, {column: start.column + 2}) + expect(languageMode.bufferRangeForScopeAtPosition(null, position)) + .toEqual(nameProperty) + }) + }) + + describe('with a selector', () => { + it('returns the range of the smallest matching node at position', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript' + }) + + buffer.setText('foo({bar: baz});') + + buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) + expect(editor.bufferRangeForScopeAtPosition('.property_identifier', [0, 6])).toEqual( + buffer.findSync('bar') + ) + expect(editor.bufferRangeForScopeAtPosition('.call_expression', [0, 6])).toEqual( + [[0, 0], [0, buffer.getText().length - 1]] + ) + expect(editor.bufferRangeForScopeAtPosition('.object', [0, 9])).toEqual( + buffer.findSync('{bar: baz}') + ) + }) + + it('includes nodes in injected syntax trees', async () => { + const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript', + scopes: {}, + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + }) + + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText(` +
+ +
+ `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + const nameProperty = buffer.findSync('name') + const {start} = nameProperty + const position = Object.assign({}, start, {column: start.column + 2}) + expect(languageMode.bufferRangeForScopeAtPosition('.property_identifier', position)) + .toEqual(nameProperty) + expect(languageMode.bufferRangeForScopeAtPosition('.element', position)) + .toEqual(buffer.findSync('\\${person\\.name}')) + }) + + it('accepts node-matching functions as selectors', async () => { + const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript', + scopes: {}, + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: {}, + injectionRegExp: 'html', + injectionPoints: [SCRIPT_TAG_INJECTION_POINT] + }) + + atom.grammars.addGrammar(jsGrammar) + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText(` +
+ +
+ `) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + const nameProperty = buffer.findSync('name') + const {start} = nameProperty + const position = Object.assign({}, start, {column: start.column + 2}) + const templateStringInCallExpression = node => + node.type === 'template_string' && node.parent.type === 'call_expression' + expect(languageMode.bufferRangeForScopeAtPosition(templateStringInCallExpression, position)) + .toEqual([[3, 19], [5, 15]]) + }) + }) + }) + + describe('.getSyntaxNodeAtPosition(position, where?)', () => { + it('returns the range of the smallest matching node at position', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript' + }) + + buffer.setText('foo(bar({x: 2}));') + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + expect(languageMode.getSyntaxNodeAtPosition([0, 6]).range).toEqual( + buffer.findSync('bar') + ) + const findFoo = node => + node.type === 'call_expression' && node.firstChild.text === 'foo' + expect(languageMode.getSyntaxNodeAtPosition([0, 6], findFoo).range).toEqual( + [[0, 0], [0, buffer.getText().length - 1]] + ) + }) + }) + describe('TextEditor.selectLargerSyntaxNode and .selectSmallerSyntaxNode', () => { - it('expands and contract the selection based on the syntax tree', () => { + it('expands and contracts the selection based on the syntax tree', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { parser: 'tree-sitter-javascript', scopes: {'program': 'source'} }) - buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) buffer.setText(dedent ` function a (b, c, d) { eee.f() @@ -495,7 +1575,7 @@ describe('TreeSitterLanguageMode', () => { } `) - editor.screenLineForScreenRow(0) + buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) editor.setCursorBufferPosition([1, 3]) editor.selectLargerSyntaxNode() @@ -520,9 +1600,69 @@ describe('TreeSitterLanguageMode', () => { editor.selectSmallerSyntaxNode() expect(editor.getSelectedBufferRange()).toEqual([[1, 3], [1, 3]]) }) + + it('handles injected languages', async () => { + const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + scopeName: 'javascript', + parser: 'tree-sitter-javascript', + scopes: { + 'property_identifier': 'property', + 'call_expression > identifier': 'function', + 'template_string': 'string', + 'template_substitution > "${"': 'interpolation', + 'template_substitution > "}"': 'interpolation' + }, + injectionRegExp: 'javascript', + injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] + }) + + const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { + scopeName: 'html', + parser: 'tree-sitter-html', + scopes: { + fragment: 'html', + tag_name: 'tag', + attribute_name: 'attr' + }, + injectionRegExp: 'html' + }) + + atom.grammars.addGrammar(htmlGrammar) + + buffer.setText('a = html ` c${def()}e${f}g `') + const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) + buffer.setLanguageMode(languageMode) + + editor.setCursorBufferPosition({row: 0, column: buffer.getText().indexOf('ef()')}) + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('def') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('def()') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('${def()}') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('c${def()}e${f}g') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('c${def()}e${f}g') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe(' c${def()}e${f}g ') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('` c${def()}e${f}g `') + editor.selectLargerSyntaxNode() + expect(editor.getSelectedText()).toBe('html ` c${def()}e${f}g `') + }) }) }) +function nextHighlightingUpdate (languageMode) { + return new Promise(resolve => { + const subscription = languageMode.onDidChangeHighlighting(() => { + subscription.dispose() + resolve() + }) + }) +} + function getDisplayText (editor) { return editor.displayLayer.getText() } @@ -533,7 +1673,14 @@ function expectTokensToEqual (editor, expectedTokenLines) { // Assert that the correct tokens are returned regardless of which row // the highlighting iterator starts on. for (let startRow = 0; startRow <= lastRow; startRow++) { - editor.displayLayer.clearSpatialIndex() + + // Clear the screen line cache between iterations, but not on the first + // iteration, so that the first iteration tests that the cache has been + // correctly invalidated by any changes. + if (startRow > 0) { + editor.displayLayer.clearSpatialIndex() + } + editor.displayLayer.getScreenLines(startRow, Infinity) const tokenLines = [] @@ -542,7 +1689,7 @@ function expectTokensToEqual (editor, expectedTokenLines) { text, scopes: scopes.map(scope => scope .split(' ') - .map(className => className.slice('syntax--'.length)) + .map(className => className.replace('syntax--', '')) .join(' ')) })) } @@ -557,4 +1704,26 @@ function expectTokensToEqual (editor, expectedTokenLines) { } } } + + // Fully populate the screen line cache again so that cache invalidation + // due to subsequent edits can be tested. + editor.displayLayer.getScreenLines(0, Infinity) +} + +const HTML_TEMPLATE_LITERAL_INJECTION_POINT = { + type: 'call_expression', + language (node) { + if (node.lastChild.type === 'template_string' && node.firstChild.type === 'identifier') { + return node.firstChild.text + } + }, + content (node) { + return node.lastChild + } +} + +const SCRIPT_TAG_INJECTION_POINT = { + type: 'raw_element', + language () { return 'javascript' }, + content (node) { return node.child(1) } } diff --git a/spec/update-process-env-spec.js b/spec/update-process-env-spec.js index e5a1cfd9c..f7948d998 100644 --- a/spec/update-process-env-spec.js +++ b/spec/update-process-env-spec.js @@ -36,7 +36,7 @@ describe('updateProcessEnv(launchEnv)', function () { }) describe('when the launch environment appears to come from a shell', function () { - it('updates process.env to match the launch environment', async function () { + it('updates process.env to match the launch environment because PWD is set', async function () { process.env = { WILL_BE_DELETED: 'hi', NODE_ENV: 'the-node-env', @@ -64,6 +64,65 @@ describe('updateProcessEnv(launchEnv)', function () { expect(process.env).toBe(initialProcessEnv) }) + it('updates process.env to match the launch environment because PROMPT is set', async function () { + process.env = { + WILL_BE_DELETED: 'hi', + NODE_ENV: 'the-node-env', + NODE_PATH: '/the/node/path', + ATOM_HOME: '/the/atom/home' + } + + const initialProcessEnv = process.env + + await updateProcessEnv({ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', PROMPT: '$P$G', KEY1: 'value1', KEY2: 'value2'}) + expect(process.env).toEqual({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PROMPT: '$P$G', + KEY1: 'value1', + KEY2: 'value2', + NODE_ENV: 'the-node-env', + NODE_PATH: '/the/node/path', + ATOM_HOME: '/the/atom/home' + }) + + // See #11302. On Windows, `process.env` is a magic object that offers + // case-insensitive environment variable matching, so we cannot replace it + // with another object. + expect(process.env).toBe(initialProcessEnv) + }) + + it('updates process.env to match the launch environment because PSModulePath is set', async function () { + process.env = { + WILL_BE_DELETED: 'hi', + NODE_ENV: 'the-node-env', + NODE_PATH: '/the/node/path', + ATOM_HOME: '/the/atom/home' + } + + const initialProcessEnv = process.env + + await updateProcessEnv({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PSModulePath: 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\', + KEY1: 'value1', + KEY2: 'value2' + }) + expect(process.env).toEqual({ + ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT: 'true', + PSModulePath: 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules\\', + KEY1: 'value1', + KEY2: 'value2', + NODE_ENV: 'the-node-env', + NODE_PATH: '/the/node/path', + ATOM_HOME: '/the/atom/home' + }) + + // See #11302. On Windows, `process.env` is a magic object that offers + // case-insensitive environment variable matching, so we cannot replace it + // with another object. + expect(process.env).toBe(initialProcessEnv) + }) + it('allows ATOM_HOME to be overwritten only if the new value is a valid path', async function () { let newAtomHomePath = temp.mkdirSync('atom-home') diff --git a/spec/workspace-element-spec.js b/spec/workspace-element-spec.js index 552e95b6d..7564f6931 100644 --- a/spec/workspace-element-spec.js +++ b/spec/workspace-element-spec.js @@ -1,11 +1,14 @@ /** @babel */ const {ipcRenderer} = require('electron') +const etch = require('etch') const path = require('path') const temp = require('temp').track() const {Disposable} = require('event-kit') const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers') +const getNextUpdatePromise = () => etch.getScheduler().nextUpdatePromise + describe('WorkspaceElement', () => { afterEach(() => { try { @@ -561,41 +564,46 @@ describe('WorkspaceElement', () => { expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) - workspaceElement.paneContainer.dispatchEvent(new MouseEvent('mouseleave')) - // --- Right Dock --- // Mouse over where the toggle button would be if the dock were hovered moveMouse({clientX: 440, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the dock moveMouse({clientX: 460, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonVisible(rightDock, 'icon-chevron-right') expectToggleButtonHidden(bottomDock) // Mouse over the toggle button moveMouse({clientX: 440, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonVisible(rightDock, 'icon-chevron-right') expectToggleButtonHidden(bottomDock) // Click the toggle button - rightDock.toggleButton.innerElement.click() + rightDock.refs.toggleButton.refs.innerElement.click() + await getNextUpdatePromise() expect(rightDock.isVisible()).toBe(false) expectToggleButtonHidden(rightDock) // Mouse to edge of the window moveMouse({clientX: 575, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonHidden(rightDock) - moveMouse({clientX: 600, clientY: 150}) + moveMouse({clientX: 598, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonVisible(rightDock, 'icon-chevron-left') // Click the toggle button again - rightDock.toggleButton.innerElement.click() + rightDock.refs.toggleButton.refs.innerElement.click() + await getNextUpdatePromise() expect(rightDock.isVisible()).toBe(true) expectToggleButtonVisible(rightDock, 'icon-chevron-right') @@ -603,35 +611,42 @@ describe('WorkspaceElement', () => { // Mouse over where the toggle button would be if the dock were hovered moveMouse({clientX: 160, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the dock moveMouse({clientX: 140, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonVisible(leftDock, 'icon-chevron-left') expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the toggle button moveMouse({clientX: 160, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonVisible(leftDock, 'icon-chevron-left') expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Click the toggle button - leftDock.toggleButton.innerElement.click() + leftDock.refs.toggleButton.refs.innerElement.click() + await getNextUpdatePromise() expect(leftDock.isVisible()).toBe(false) expectToggleButtonHidden(leftDock) // Mouse to edge of the window moveMouse({clientX: 25, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) - moveMouse({clientX: 0, clientY: 150}) + moveMouse({clientX: 2, clientY: 150}) + await getNextUpdatePromise() expectToggleButtonVisible(leftDock, 'icon-chevron-right') // Click the toggle button again - leftDock.toggleButton.innerElement.click() + leftDock.refs.toggleButton.refs.innerElement.click() + await getNextUpdatePromise() expect(leftDock.isVisible()).toBe(true) expectToggleButtonVisible(leftDock, 'icon-chevron-left') @@ -639,51 +654,58 @@ describe('WorkspaceElement', () => { // Mouse over where the toggle button would be if the dock were hovered moveMouse({clientX: 300, clientY: 190}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonHidden(bottomDock) // Mouse over the dock moveMouse({clientX: 300, clientY: 210}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonVisible(bottomDock, 'icon-chevron-down') // Mouse over the toggle button moveMouse({clientX: 300, clientY: 195}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) expectToggleButtonHidden(rightDock) expectToggleButtonVisible(bottomDock, 'icon-chevron-down') // Click the toggle button - bottomDock.toggleButton.innerElement.click() + bottomDock.refs.toggleButton.refs.innerElement.click() + await getNextUpdatePromise() expect(bottomDock.isVisible()).toBe(false) expectToggleButtonHidden(bottomDock) // Mouse to edge of the window moveMouse({clientX: 300, clientY: 290}) + await getNextUpdatePromise() expectToggleButtonHidden(leftDock) - moveMouse({clientX: 300, clientY: 300}) + moveMouse({clientX: 300, clientY: 299}) + await getNextUpdatePromise() expectToggleButtonVisible(bottomDock, 'icon-chevron-up') // Click the toggle button again - bottomDock.toggleButton.innerElement.click() + bottomDock.refs.toggleButton.refs.innerElement.click() + await getNextUpdatePromise() expect(bottomDock.isVisible()).toBe(true) expectToggleButtonVisible(bottomDock, 'icon-chevron-down') }) - function moveMouse(coordinates) { + function moveMouse (coordinates) { window.dispatchEvent(new MouseEvent('mousemove', coordinates)) advanceClock(100) } function expectToggleButtonHidden(dock) { - expect(dock.toggleButton.element).not.toHaveClass('atom-dock-toggle-button-visible') + expect(dock.refs.toggleButton.element).not.toHaveClass('atom-dock-toggle-button-visible') } function expectToggleButtonVisible(dock, iconClass) { - expect(dock.toggleButton.element).toHaveClass('atom-dock-toggle-button-visible') - expect(dock.toggleButton.iconElement).toHaveClass(iconClass) + expect(dock.refs.toggleButton.element).toHaveClass('atom-dock-toggle-button-visible') + expect(dock.refs.toggleButton.refs.iconElement).toHaveClass(iconClass) } }) @@ -873,27 +895,39 @@ describe('WorkspaceElement', () => { // No active item. Use first project directory. atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec')) + expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {}) ipcRenderer.send.reset() // Active item doesn't implement ::getPath(). Use first project directory. const item = document.createElement('div') atom.workspace.getActivePane().activateItem(item) atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec')) + expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {}) ipcRenderer.send.reset() // Active item has no path. Use first project directory. item.getPath = () => null atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec')) + expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[0], 'spec'), {}) ipcRenderer.send.reset() // Active item has path. Use project path for item path. item.getPath = () => path.join(projectPaths[1], 'a-file.txt') atom.commands.dispatch(workspaceElement, 'window:run-package-specs') - expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[1], 'spec')) + expect(ipcRenderer.send).toHaveBeenCalledWith('run-package-specs', path.join(projectPaths[1], 'spec'), {}) ipcRenderer.send.reset() }) + + it("passes additional options to the spec window", () => { + const workspaceElement = atom.workspace.getElement() + spyOn(ipcRenderer, 'send') + + const projectPath = temp.mkdirSync('dir1-') + atom.project.setPaths([projectPath]) + workspaceElement.runPackageSpecs({env: {ATOM_GITHUB_BABEL_ENV: 'coverage'}}) + + expect(ipcRenderer.send).toHaveBeenCalledWith( + 'run-package-specs', path.join(projectPath, 'spec'), {env: {ATOM_GITHUB_BABEL_ENV: 'coverage'}}) + }) }) }) diff --git a/spec/workspace-spec.js b/spec/workspace-spec.js index 4b115e594..091588a70 100644 --- a/spec/workspace-spec.js +++ b/spec/workspace-spec.js @@ -274,6 +274,21 @@ describe('Workspace', () => { }) }) + it('discovers existing editors that are still opening', () => { + let editor0 = null + let editor1 = null + + waitsForPromise(() => Promise.all([ + workspace.open('spartacus.txt').then(o0 => { editor0 = o0 }), + workspace.open('spartacus.txt').then(o1 => { editor1 = o1 }), + ])) + + runs(() => { + expect(editor0).toEqual(editor1) + expect(workspace.getActivePane().items).toEqual([editor0]) + }) + }) + it("uses the location specified by the model's `getDefaultLocation()` method", () => { const item = { getDefaultLocation: jasmine.createSpy().andReturn('right'), @@ -361,6 +376,28 @@ describe('Workspace', () => { }) }) + it('discovers existing editors that are still opening in an inactive pane', () => { + let editor0 = null + let editor1 = null + const pane0 = workspace.getActivePane() + const pane1 = workspace.getActivePane().splitRight() + + pane0.activate() + const promise0 = workspace.open('spartacus.txt', {searchAllPanes: true}).then(o0 => { editor0 = o0 }) + pane1.activate() + const promise1 = workspace.open('spartacus.txt', {searchAllPanes: true}).then(o1 => { editor1 = o1 }) + + waitsForPromise(() => Promise.all([promise0, promise1])) + + runs(() => { + expect(editor0).toBeDefined() + expect(editor1).toBeDefined() + + expect(editor0).toEqual(editor1) + expect(workspace.getActivePane().items).toEqual([editor0]) + }) + }) + it('activates the pane in the dock with the matching item', () => { const dock = atom.workspace.getRightDock() const ITEM_URI = 'atom://test' @@ -1236,21 +1273,57 @@ describe('Workspace', () => { describe('the grammar-used hook', () => { it('fires when opening a file or changing the grammar of an open file', async () => { - let resolveJavascriptGrammarUsed, resolveCoffeeScriptGrammarUsed - const javascriptGrammarUsed = new Promise(resolve => { resolveJavascriptGrammarUsed = resolve }) - const coffeescriptGrammarUsed = new Promise(resolve => { resolveCoffeeScriptGrammarUsed = resolve }) + await atom.packages.activatePackage('language-javascript') + await atom.packages.activatePackage('language-coffee-script') + + const observeTextEditorsSpy = jasmine.createSpy('observeTextEditors') + const javascriptGrammarUsed = jasmine.createSpy('javascript') + const coffeeScriptGrammarUsed = jasmine.createSpy('coffeescript') atom.packages.triggerDeferredActivationHooks() - atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', resolveJavascriptGrammarUsed) - atom.packages.onDidTriggerActivationHook('language-coffee-script:grammar-used', resolveCoffeeScriptGrammarUsed) + atom.packages.onDidTriggerActivationHook('language-javascript:grammar-used', () => { + atom.workspace.observeTextEditors(observeTextEditorsSpy) + javascriptGrammarUsed() + }) + atom.packages.onDidTriggerActivationHook('language-coffee-script:grammar-used', coffeeScriptGrammarUsed) + expect(javascriptGrammarUsed).not.toHaveBeenCalled() + expect(observeTextEditorsSpy).not.toHaveBeenCalled() const editor = await atom.workspace.open('sample.js', {autoIndent: false}) - await atom.packages.activatePackage('language-javascript') - await javascriptGrammarUsed + expect(javascriptGrammarUsed).toHaveBeenCalled() + expect(observeTextEditorsSpy.callCount).toBe(1) - await atom.packages.activatePackage('language-coffee-script') + expect(coffeeScriptGrammarUsed).not.toHaveBeenCalled() atom.grammars.assignLanguageMode(editor, 'source.coffee') - await coffeescriptGrammarUsed + expect(coffeeScriptGrammarUsed).toHaveBeenCalled() + }) + }) + + describe('the root-scope-used hook', () => { + it('fires when opening a file or changing the grammar of an open file', async () => { + await atom.packages.activatePackage('language-javascript') + await atom.packages.activatePackage('language-coffee-script') + + const observeTextEditorsSpy = jasmine.createSpy('observeTextEditors') + const javascriptGrammarUsed = jasmine.createSpy('javascript') + const coffeeScriptGrammarUsed = jasmine.createSpy('coffeescript') + + atom.packages.triggerDeferredActivationHooks() + atom.packages.onDidTriggerActivationHook('source.js:root-scope-used', () => { + atom.workspace.observeTextEditors(observeTextEditorsSpy) + javascriptGrammarUsed() + }) + atom.packages.onDidTriggerActivationHook('source.coffee:root-scope-used', coffeeScriptGrammarUsed) + + expect(javascriptGrammarUsed).not.toHaveBeenCalled() + expect(observeTextEditorsSpy).not.toHaveBeenCalled() + const editor = await atom.workspace.open('sample.js', {autoIndent: false}) + expect(javascriptGrammarUsed).toHaveBeenCalled() + expect(observeTextEditorsSpy.callCount).toBe(1) + + expect(coffeeScriptGrammarUsed).not.toHaveBeenCalled() + atom.grammars.assignLanguageMode(editor, 'source.coffee') + expect(coffeeScriptGrammarUsed).toHaveBeenCalled() }) }) diff --git a/src/application-delegate.js b/src/application-delegate.js index a6d701078..8d7981edb 100644 --- a/src/application-delegate.js +++ b/src/application-delegate.js @@ -1,10 +1,25 @@ const {ipcRenderer, remote, shell} = require('electron') const ipcHelpers = require('./ipc-helpers') -const {Disposable} = require('event-kit') +const {Emitter, Disposable} = require('event-kit') const getWindowLoadSettings = require('./get-window-load-settings') module.exports = class ApplicationDelegate { + constructor () { + this.pendingSettingsUpdateCount = 0 + this._ipcMessageEmitter = null + } + + ipcMessageEmitter () { + if (!this._ipcMessageEmitter) { + this._ipcMessageEmitter = new Emitter() + ipcRenderer.on('message', (event, message, detail) => { + this._ipcMessageEmitter.emit(message, detail) + }) + } + return this._ipcMessageEmitter + } + getWindowLoadSettings () { return getWindowLoadSettings() } open (params) { @@ -175,6 +190,25 @@ class ApplicationDelegate { return remote.systemPreferences.getUserDefault(key, type) } + async setUserSettings (config, configFilePath) { + this.pendingSettingsUpdateCount++ + try { + await ipcHelpers.call('set-user-settings', JSON.stringify(config), configFilePath) + } finally { + this.pendingSettingsUpdateCount-- + } + } + + onDidChangeUserSettings (callback) { + return this.ipcMessageEmitter().on('did-change-user-settings', detail => { + if (this.pendingSettingsUpdateCount === 0) callback(detail) + }) + } + + onDidFailToReadUserSettings (callback) { + return this.ipcMessageEmitter().on('did-fail-to-read-user-setting', callback) + } + confirm (options, callback) { if (typeof callback === 'function') { // Async version: pass options directly to Electron but set sane defaults @@ -205,7 +239,7 @@ class ApplicationDelegate { return chosen } else { const callback = buttons[buttonLabels[chosen]] - if (typeof callback === 'function') callback() + if (typeof callback === 'function') return callback() } } } @@ -218,7 +252,7 @@ class ApplicationDelegate { this.getCurrentWindow().showSaveDialog(options, callback) } else { // Sync - if (typeof params === 'string') { + if (typeof options === 'string') { options = {defaultPath: options} } return this.getCurrentWindow().showSaveDialog(options) @@ -230,24 +264,14 @@ class ApplicationDelegate { } onDidOpenLocations (callback) { - const outerCallback = (event, message, detail) => { - if (message === 'open-locations') callback(detail) - } - - ipcRenderer.on('message', outerCallback) - return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) + return this.ipcMessageEmitter().on('open-locations', callback) } onUpdateAvailable (callback) { - const outerCallback = (event, message, detail) => { - // TODO: Yes, this is strange that `onUpdateAvailable` is listening for - // `did-begin-downloading-update`. We currently have no mechanism to know - // if there is an update, so begin of downloading is a good proxy. - if (message === 'did-begin-downloading-update') callback(detail) - } - - ipcRenderer.on('message', outerCallback) - return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) + // TODO: Yes, this is strange that `onUpdateAvailable` is listening for + // `did-begin-downloading-update`. We currently have no mechanism to know + // if there is an update, so begin of downloading is a good proxy. + return this.ipcMessageEmitter().on('did-begin-downloading-update', callback) } onDidBeginDownloadingUpdate (callback) { @@ -255,40 +279,19 @@ class ApplicationDelegate { } onDidBeginCheckingForUpdate (callback) { - const outerCallback = (event, message, detail) => { - if (message === 'checking-for-update') callback(detail) - } - - ipcRenderer.on('message', outerCallback) - return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) + return this.ipcMessageEmitter().on('checking-for-update', callback) } onDidCompleteDownloadingUpdate (callback) { - const outerCallback = (event, message, detail) => { - // TODO: We could rename this event to `did-complete-downloading-update` - if (message === 'update-available') callback(detail) - } - - ipcRenderer.on('message', outerCallback) - return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) + return this.ipcMessageEmitter().on('update-available', callback) } onUpdateNotAvailable (callback) { - const outerCallback = (event, message, detail) => { - if (message === 'update-not-available') callback(detail) - } - - ipcRenderer.on('message', outerCallback) - return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) + return this.ipcMessageEmitter().on('update-not-available', callback) } onUpdateError (callback) { - const outerCallback = (event, message, detail) => { - if (message === 'update-error') callback(detail) - } - - ipcRenderer.on('message', outerCallback) - return new Disposable(() => ipcRenderer.removeListener('message', outerCallback)) + return this.ipcMessageEmitter().on('update-error', callback) } onApplicationMenuCommand (handler) { @@ -354,11 +357,11 @@ class ApplicationDelegate { } emitWillSavePath (path) { - return ipcRenderer.sendSync('will-save-path', path) + return ipcHelpers.call('will-save-path', path) } emitDidSavePath (path) { - return ipcRenderer.sendSync('did-save-path', path) + return ipcHelpers.call('did-save-path', path) } resolveProxy (requestId, url) { diff --git a/src/atom-environment.js b/src/atom-environment.js index 159464534..59e4da1f6 100644 --- a/src/atom-environment.js +++ b/src/atom-environment.js @@ -9,7 +9,6 @@ const fs = require('fs-plus') const {mapSourcePosition} = require('@atom/source-map-support') const WindowEventHandler = require('./window-event-handler') const StateStore = require('./state-store') -const StorageFolder = require('./storage-folder') const registerDefaultCommands = require('./register-default-commands') const {updateProcessEnv} = require('./update-process-env') const ConfigSchema = require('./config-schema') @@ -51,7 +50,6 @@ let nextId = 0 // // An instance of this class is always available as the `atom` global. class AtomEnvironment { - /* Section: Properties */ @@ -86,8 +84,11 @@ class AtomEnvironment { // Public: A {Config} instance this.config = new Config({ - notificationManager: this.notifications, - enablePersistence: this.enablePersistence + saveCallback: settings => { + if (this.enablePersistence) { + this.applicationDelegate.setUserSettings(settings, this.config.getUserConfigPath()) + } + } }) this.config.setSchema(null, {type: 'object', properties: _.clone(ConfigSchema)}) @@ -208,19 +209,23 @@ class AtomEnvironment { this.blobStore = params.blobStore this.configDirPath = params.configDirPath - const {devMode, safeMode, resourcePath, clearWindowState} = this.getLoadSettings() - - if (clearWindowState) { - this.getStorageFolder().clear() - this.stateStore.clear() - } + const {devMode, safeMode, resourcePath, userSettings, projectSpecification} = this.getLoadSettings() ConfigSchema.projectHome = { type: 'string', default: path.join(fs.getHomeDirectory(), 'github'), description: 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' } - this.config.initialize({configDirPath: this.configDirPath, resourcePath, projectHomeSchema: ConfigSchema.projectHome}) + + this.config.initialize({ + mainSource: this.enablePersistence && path.join(this.configDirPath, 'config.cson'), + projectHomeSchema: ConfigSchema.projectHome + }) + this.config.resetUserSettings(userSettings) + + if (projectSpecification != null && projectSpecification.config != null) { + this.project.replace(projectSpecification) + } this.menu.initialize({resourcePath}) this.contextMenu.initialize({resourcePath, devMode}) @@ -242,8 +247,6 @@ class AtomEnvironment { this.uriHandlerRegistry.registerHostHandler('core', CoreURIHandlers.create(this)) this.autoUpdater.initialize() - this.config.load() - this.protocolHandlerInstaller.initialize(this.config, this.notifications) this.themes.loadBaseStylesheets() @@ -373,8 +376,7 @@ class AtomEnvironment { if (this.project) this.project.destroy() this.project = null this.commands.clear() - this.stylesElement.remove() - this.config.unobserveUserConfig() + if (this.stylesElement) this.stylesElement.remove() this.autoUpdater.destroy() this.uriHandlerRegistry.destroy() @@ -485,21 +487,25 @@ class AtomEnvironment { // Public: Gets the release channel of the Atom application. // - // Returns the release channel as a {String}. Will return one of `dev`, `beta`, or `stable`. + // Returns the release channel as a {String}. Will return a specific release channel + // name like 'beta' or 'nightly' if one is found in the Atom version or 'stable' + // otherwise. getReleaseChannel () { - const version = this.getVersion() - if (version.includes('beta')) { - return 'beta' - } else if (version.includes('dev')) { - return 'dev' - } else { - return 'stable' + // This matches stable, dev (with or without commit hash) and any other + // release channel following the pattern '1.00.0-channel0' + const match = this.getVersion().match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/) + if (!match) { + return 'unrecognized' + } else if (match[2]) { + return match[2] } + + return 'stable' } // Public: Returns a {Boolean} that is `true` if the current version is an official release. isReleasedVersion () { - return !/\w{7}/.test(this.getVersion()) // Check if the release is a 7-character SHA prefix + return this.getReleaseChannel().match(/stable|beta|nightly/) != null } // Public: Get the time taken to completely load the current window. @@ -764,7 +770,11 @@ class AtomEnvironment { } // Call this method when establishing a real application window. - startEditorWindow () { + async startEditorWindow () { + if (this.getLoadSettings().clearWindowState) { + await this.stateStore.clear() + } + this.unloaded = false const updateProcessEnvPromise = this.updateProcessEnvAndTriggerHooks() @@ -779,6 +789,13 @@ class AtomEnvironment { if (error) console.warn(error.message) }) + this.disposables.add(this.applicationDelegate.onDidChangeUserSettings(settings => + this.config.resetUserSettings(settings) + )) + this.disposables.add(this.applicationDelegate.onDidFailToReadUserSettings(message => + this.notifications.addError(message) + )) + this.disposables.add(this.applicationDelegate.onDidOpenLocations(this.openLocations.bind(this))) this.disposables.add(this.applicationDelegate.onApplicationMenuCommand(this.dispatchApplicationMenuCommand.bind(this))) this.disposables.add(this.applicationDelegate.onContextMenuCommand(this.dispatchContextMenuCommand.bind(this))) @@ -1121,7 +1138,7 @@ class AtomEnvironment { } if (windowIsUnused()) { - this.restoreStateIntoThisEnvironment(state) + await this.restoreStateIntoThisEnvironment(state) return Promise.all(filesToOpen.map(file => this.workspace.open(file))) } else { let resolveDiscardStatePromise = null @@ -1264,11 +1281,6 @@ or use Pane::saveItemAs for programmatic saving.`) } } - getStorageFolder () { - if (!this.storageFolder) this.storageFolder = new StorageFolder(this.getConfigDirPath()) - return this.storageFolder - } - getConfigDirPath () { if (!this.configDirPath) this.configDirPath = process.env.ATOM_HOME return this.configDirPath diff --git a/src/atom-paths.js b/src/atom-paths.js index 39a768e91..d36ac25f5 100644 --- a/src/atom-paths.js +++ b/src/atom-paths.js @@ -1,5 +1,3 @@ -/** @babel */ - const fs = require('fs-plus') const path = require('path') diff --git a/src/auto-update-manager.js b/src/auto-update-manager.js index 111147f32..5e3a80129 100644 --- a/src/auto-update-manager.js +++ b/src/auto-update-manager.js @@ -1,8 +1,7 @@ -'use babel' +const {Emitter, CompositeDisposable} = require('event-kit') -import {Emitter, CompositeDisposable} from 'event-kit' - -export default class AutoUpdateManager { +module.exports = +class AutoUpdateManager { constructor ({applicationDelegate}) { this.applicationDelegate = applicationDelegate this.subscriptions = new CompositeDisposable() diff --git a/src/babel.js b/src/babel.js index a944f2e8c..8476a33c0 100644 --- a/src/babel.js +++ b/src/babel.js @@ -11,7 +11,8 @@ var PREFIXES = [ '/** @babel */', '"use babel"', '\'use babel\'', - '/* @flow */' + '/* @flow */', + '// @flow' ] var PREFIX_LENGTH = Math.max.apply(Math, PREFIXES.map(function (prefix) { diff --git a/src/buffered-node-process.js b/src/buffered-node-process.js index 86b0c5747..a33176e51 100644 --- a/src/buffered-node-process.js +++ b/src/buffered-node-process.js @@ -1,6 +1,4 @@ -/** @babel */ - -import BufferedProcess from './buffered-process' +const BufferedProcess = require('./buffered-process') // Extended: Like {BufferedProcess}, but accepts a Node script as the command // to run. @@ -12,7 +10,8 @@ import BufferedProcess from './buffered-process' // ```js // const {BufferedNodeProcess} = require('atom') // ``` -export default class BufferedNodeProcess extends BufferedProcess { +module.exports = +class BufferedNodeProcess extends BufferedProcess { // Public: Runs the given Node script by spawning a new child process. // diff --git a/src/buffered-process.js b/src/buffered-process.js index 339bf05c5..c82c78fac 100644 --- a/src/buffered-process.js +++ b/src/buffered-process.js @@ -1,9 +1,7 @@ -/** @babel */ - -import _ from 'underscore-plus' -import ChildProcess from 'child_process' -import {Emitter} from 'event-kit' -import path from 'path' +const _ = require('underscore-plus') +const ChildProcess = require('child_process') +const {Emitter} = require('event-kit') +const path = require('path') // Extended: A wrapper which provides standard error/output line buffering for // Node's ChildProcess. @@ -19,7 +17,8 @@ import path from 'path' // const exit = (code) => console.log("ps -ef exited with #{code}") // const process = new BufferedProcess({command, args, stdout, exit}) // ``` -export default class BufferedProcess { +module.exports = +class BufferedProcess { /* Section: Construction */ @@ -190,12 +189,12 @@ export default class BufferedProcess { output += data }) wmicProcess.stdout.on('close', () => { - const pidsToKill = output.split(/\s+/) - .filter((pid) => /^\d+$/.test(pid)) - .map((pid) => parseInt(pid)) - .filter((pid) => pid !== parentPid && pid > 0 && pid < Infinity) + for (let pid of output.split(/\s+/)) { + if (!/^\d{1,10}$/.test(pid)) continue + pid = parseInt(pid, 10) + + if (!pid || pid === parentPid) continue - for (let pid of pidsToKill) { try { process.kill(pid) } catch (error) {} diff --git a/src/color.js b/src/color.js index 2f2947e16..c183fb3e5 100644 --- a/src/color.js +++ b/src/color.js @@ -1,10 +1,9 @@ -/** @babel */ - let ParsedColor = null // Essential: A simple color class returned from {Config::get} when the value // at the key path is of type 'color'. -export default class Color { +module.exports = +class Color { // Essential: Parse a {String} or {Object} into a {Color}. // // * `value` A {String} such as `'white'`, `#ff00ff`, or @@ -89,6 +88,10 @@ export default class Color { return this.alpha === 1 ? this.toHexString() : this.toRGBAString() } + toString () { + return this.toRGBAString() + } + isEqual (color) { if (this === color) { return true diff --git a/src/command-installer.js b/src/command-installer.js index 85360da17..b432023ba 100644 --- a/src/command-installer.js +++ b/src/command-installer.js @@ -27,22 +27,36 @@ class CommandInstaller { }, () => {}) } - this.installAtomCommand(true, error => { + this.installAtomCommand(true, (error, atomCommandName) => { if (error) return showErrorDialog(error) - this.installApmCommand(true, error => { + this.installApmCommand(true, (error, apmCommandName) => { if (error) return showErrorDialog(error) this.applicationDelegate.confirm({ message: 'Commands installed.', - detail: 'The shell commands `atom` and `apm` are installed.' + detail: `The shell commands \`${atomCommandName}\` and \`${apmCommandName}\` are installed.` }, () => {}) }) }) } + getCommandNameForChannel (commandName) { + let channelMatch = this.appVersion.match(/beta|nightly/) + let channel = channelMatch ? channelMatch[0] : '' + + switch (channel) { + case 'beta': + return `${commandName}-beta` + case 'nightly': + return `${commandName}-nightly` + default: + return commandName + } + } + installAtomCommand (askForPrivilege, callback) { this.installCommand( path.join(this.getResourcesDirectory(), 'app', 'atom.sh'), - this.appVersion.includes('beta') ? 'atom-beta' : 'atom', + this.getCommandNameForChannel('atom'), askForPrivilege, callback ) @@ -51,7 +65,7 @@ class CommandInstaller { installApmCommand (askForPrivilege, callback) { this.installCommand( path.join(this.getResourcesDirectory(), 'app', 'apm', 'node_modules', '.bin', 'apm'), - this.appVersion.includes('beta') ? 'apm-beta' : 'apm', + this.getCommandNameForChannel('apm'), askForPrivilege, callback ) @@ -64,11 +78,11 @@ class CommandInstaller { fs.readlink(destinationPath, (error, realpath) => { if (error && error.code !== 'ENOENT') return callback(error) - if (realpath === commandPath) return callback() + if (realpath === commandPath) return callback(null, commandName) this.createSymlink(fs, commandPath, destinationPath, error => { if (error && error.code === 'EACCES' && askForPrivilege) { const fsAdmin = require('fs-admin') - this.createSymlink(fsAdmin, commandPath, destinationPath, callback) + this.createSymlink(fsAdmin, commandPath, destinationPath, (error) => { callback(error, commandName) }) } else { callback(error) } diff --git a/src/compile-cache.js b/src/compile-cache.js index a4f9ded1e..ea387a631 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -17,6 +17,7 @@ var packageTranspilationRegistry = new PackageTranspilationRegistry() var COMPILERS = { '.js': packageTranspilationRegistry.wrapTranspiler(require('./babel')), '.ts': packageTranspilationRegistry.wrapTranspiler(require('./typescript')), + '.tsx': packageTranspilationRegistry.wrapTranspiler(require('./typescript')), '.coffee': packageTranspilationRegistry.wrapTranspiler(require('./coffee-script')) } diff --git a/src/config-file.js b/src/config-file.js new file mode 100644 index 000000000..f8cba37ef --- /dev/null +++ b/src/config-file.js @@ -0,0 +1,145 @@ +const _ = require('underscore-plus') +const fs = require('fs-plus') +const dedent = require('dedent') +const {Emitter} = require('event-kit') +const {watchPath} = require('./path-watcher') +const CSON = require('season') +const Path = require('path') +const async = require('async') +const temp = require('temp') + +const EVENT_TYPES = new Set([ + 'created', + 'modified', + 'renamed' +]) + +module.exports = +class ConfigFile { + static at (path) { + if (!this._known) { + this._known = new Map() + } + + const existing = this._known.get(path) + if (existing) { + return existing + } + + const created = new ConfigFile(path) + this._known.set(path, created) + return created + } + + constructor (path) { + this.path = path + this.emitter = new Emitter() + this.value = {} + this.reloadCallbacks = [] + + // Use a queue to prevent multiple concurrent write to the same file. + const writeQueue = async.queue((data, callback) => { + (async () => { + try { + await writeCSONFileAtomically(this.path, data) + } catch (error) { + this.emitter.emit('did-error', dedent ` + Failed to write \`${Path.basename(this.path)}\`. + + ${error.message} + `) + } + callback() + })() + }) + + this.requestLoad = _.debounce(() => this.reload(), 200) + this.requestSave = _.debounce((data) => writeQueue.push(data), 200) + } + + get () { + return this.value + } + + update (value) { + return new Promise(resolve => { + this.requestSave(value) + this.reloadCallbacks.push(resolve) + }) + } + + async watch (callback) { + if (!fs.existsSync(this.path)) { + fs.makeTreeSync(Path.dirname(this.path)) + CSON.writeFileSync(this.path, {}, {flag: 'wx'}) + } + + await this.reload() + + try { + const watcher = await watchPath(this.path, {}, events => { + if (events.some(event => EVENT_TYPES.has(event.action))) this.requestLoad() + }) + return watcher + } catch (error) { + this.emitter.emit('did-error', dedent ` + Unable to watch path: \`${Path.basename(this.path)}\`. + + Make sure you have permissions to \`${this.path}\`. + On linux there are currently problems with watch sizes. + See [this document][watches] for more info. + + [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path\ + `) + } + } + + onDidChange (callback) { + return this.emitter.on('did-change', callback) + } + + onDidError (callback) { + return this.emitter.on('did-error', callback) + } + + reload () { + return new Promise(resolve => { + CSON.readFile(this.path, (error, data) => { + if (error) { + this.emitter.emit('did-error', `Failed to load \`${Path.basename(this.path)}\` - ${error.message}`) + } else { + this.value = data || {} + this.emitter.emit('did-change', this.value) + + for (const callback of this.reloadCallbacks) callback() + this.reloadCallbacks.length = 0 + } + resolve() + }) + }) + } +} + +function writeCSONFile (path, data) { + return new Promise((resolve, reject) => { + CSON.writeFile(path, data, error => { + if (error) reject(error) + else resolve() + }) + }) +} + +async function writeCSONFileAtomically (path, data) { + const tempPath = temp.path() + await writeCSONFile(tempPath, data) + await rename(tempPath, path) +} + +function rename (oldPath, newPath) { + return new Promise((resolve, reject) => { + fs.rename(oldPath, newPath, error => { + if (error) reject(error) + else resolve() + }) + }) +} diff --git a/src/config-schema.js b/src/config-schema.js index 18dc3d774..343726d2c 100644 --- a/src/config-schema.js +++ b/src/config-schema.js @@ -337,6 +337,14 @@ const configSchema = { value: 'native', description: 'Native operating system APIs' }, + { + value: 'experimental', + description: 'Experimental filesystem watching library' + }, + { + value: 'poll', + description: 'Polling' + }, { value: 'atom', description: 'Emulated with Atom events' @@ -346,7 +354,22 @@ const configSchema = { useTreeSitterParsers: { type: 'boolean', default: false, - description: 'Use the new Tree-sitter parsing system for supported languages' + description: 'Experimental: Use the new Tree-sitter parsing system for supported languages.' + }, + colorProfile: { + description: "Specify whether Atom should use the operating system's color profile (recommended) or an alternative color profile.
Changing this setting will require a relaunch of Atom to take effect.", + type: 'string', + default: 'default', + enum: [ + { + value: 'default', + description: 'Use color profile configured in the operating system' + }, + { + value: 'srgb', + description: 'Use sRGB color profile' + } + ] } } }, @@ -372,7 +395,7 @@ const configSchema = { // These can be used as globals or scoped, thus defaults. fontFamily: { type: 'string', - default: '', + default: 'Menlo, Consolas, DejaVu Sans Mono, monospace', description: 'The name of the font family used for editor text.' }, fontSize: { diff --git a/src/config.coffee b/src/config.coffee deleted file mode 100644 index 84e726700..000000000 --- a/src/config.coffee +++ /dev/null @@ -1,1353 +0,0 @@ -_ = require 'underscore-plus' -fs = require 'fs-plus' -{Emitter} = require 'event-kit' -CSON = require 'season' -path = require 'path' -async = require 'async' -{watchPath} = require './path-watcher' -{ - getValueAtKeyPath, setValueAtKeyPath, deleteValueAtKeyPath, - pushKeyPath, splitKeyPath, -} = require 'key-path-helpers' - -Color = require './color' -ScopedPropertyStore = require 'scoped-property-store' -ScopeDescriptor = require './scope-descriptor' - -# Essential: Used to access all of Atom's configuration details. -# -# An instance of this class is always available as the `atom.config` global. -# -# ## Getting and setting config settings. -# -# ```coffee -# # Note that with no value set, ::get returns the setting's default value. -# atom.config.get('my-package.myKey') # -> 'defaultValue' -# -# atom.config.set('my-package.myKey', 'value') -# atom.config.get('my-package.myKey') # -> 'value' -# ``` -# -# You may want to watch for changes. Use {::observe} to catch changes to the setting. -# -# ```coffee -# atom.config.set('my-package.myKey', 'value') -# atom.config.observe 'my-package.myKey', (newValue) -> -# # `observe` calls immediately and every time the value is changed -# console.log 'My configuration changed:', newValue -# ``` -# -# If you want a notification only when the value changes, use {::onDidChange}. -# -# ```coffee -# atom.config.onDidChange 'my-package.myKey', ({newValue, oldValue}) -> -# console.log 'My configuration changed:', newValue, oldValue -# ``` -# -# ### Value Coercion -# -# Config settings each have a type specified by way of a -# [schema](json-schema.org). For example we might an integer setting that only -# allows integers greater than `0`: -# -# ```coffee -# # When no value has been set, `::get` returns the setting's default value -# atom.config.get('my-package.anInt') # -> 12 -# -# # The string will be coerced to the integer 123 -# atom.config.set('my-package.anInt', '123') -# atom.config.get('my-package.anInt') # -> 123 -# -# # The string will be coerced to an integer, but it must be greater than 0, so is set to 1 -# atom.config.set('my-package.anInt', '-20') -# atom.config.get('my-package.anInt') # -> 1 -# ``` -# -# ## Defining settings for your package -# -# Define a schema under a `config` key in your package main. -# -# ```coffee -# module.exports = -# # Your config schema -# config: -# someInt: -# type: 'integer' -# default: 23 -# minimum: 1 -# -# activate: (state) -> # ... -# # ... -# ``` -# -# See [package docs](http://flight-manual.atom.io/hacking-atom/sections/package-word-count/) for -# more info. -# -# ## Config Schemas -# -# We use [json schema](http://json-schema.org) which allows you to define your value's -# default, the type it should be, etc. A simple example: -# -# ```coffee -# # We want to provide an `enableThing`, and a `thingVolume` -# config: -# enableThing: -# type: 'boolean' -# default: false -# thingVolume: -# type: 'integer' -# default: 5 -# minimum: 1 -# maximum: 11 -# ``` -# -# The type keyword allows for type coercion and validation. If a `thingVolume` is -# set to a string `'10'`, it will be coerced into an integer. -# -# ```coffee -# atom.config.set('my-package.thingVolume', '10') -# atom.config.get('my-package.thingVolume') # -> 10 -# -# # It respects the min / max -# atom.config.set('my-package.thingVolume', '400') -# atom.config.get('my-package.thingVolume') # -> 11 -# -# # If it cannot be coerced, the value will not be set -# atom.config.set('my-package.thingVolume', 'cats') -# atom.config.get('my-package.thingVolume') # -> 11 -# ``` -# -# ### Supported Types -# -# The `type` keyword can be a string with any one of the following. You can also -# chain them by specifying multiple in an an array. For example -# -# ```coffee -# config: -# someSetting: -# type: ['boolean', 'integer'] -# default: 5 -# -# # Then -# atom.config.set('my-package.someSetting', 'true') -# atom.config.get('my-package.someSetting') # -> true -# -# atom.config.set('my-package.someSetting', '12') -# atom.config.get('my-package.someSetting') # -> 12 -# ``` -# -# #### string -# -# Values must be a string. -# -# ```coffee -# config: -# someSetting: -# type: 'string' -# default: 'hello' -# ``` -# -# #### integer -# -# Values will be coerced into integer. Supports the (optional) `minimum` and -# `maximum` keys. -# -# ```coffee -# config: -# someSetting: -# type: 'integer' -# default: 5 -# minimum: 1 -# maximum: 11 -# ``` -# -# #### number -# -# Values will be coerced into a number, including real numbers. Supports the -# (optional) `minimum` and `maximum` keys. -# -# ```coffee -# config: -# someSetting: -# type: 'number' -# default: 5.3 -# minimum: 1.5 -# maximum: 11.5 -# ``` -# -# #### boolean -# -# Values will be coerced into a Boolean. `'true'` and `'false'` will be coerced into -# a boolean. Numbers, arrays, objects, and anything else will not be coerced. -# -# ```coffee -# config: -# someSetting: -# type: 'boolean' -# default: false -# ``` -# -# #### array -# -# Value must be an Array. The types of the values can be specified by a -# subschema in the `items` key. -# -# ```coffee -# config: -# someSetting: -# type: 'array' -# default: [1, 2, 3] -# items: -# type: 'integer' -# minimum: 1.5 -# maximum: 11.5 -# ``` -# -# #### color -# -# Values will be coerced into a {Color} with `red`, `green`, `blue`, and `alpha` -# properties that all have numeric values. `red`, `green`, `blue` will be in -# the range 0 to 255 and `value` will be in the range 0 to 1. Values can be any -# valid CSS color format such as `#abc`, `#abcdef`, `white`, -# `rgb(50, 100, 150)`, and `rgba(25, 75, 125, .75)`. -# -# ```coffee -# config: -# someSetting: -# type: 'color' -# default: 'white' -# ``` -# -# #### object / Grouping other types -# -# A config setting with the type `object` allows grouping a set of config -# settings. The group will be visually separated and has its own group headline. -# The sub options must be listed under a `properties` key. -# -# ```coffee -# config: -# someSetting: -# type: 'object' -# properties: -# myChildIntOption: -# type: 'integer' -# minimum: 1.5 -# maximum: 11.5 -# ``` -# -# ### Other Supported Keys -# -# #### enum -# -# All types support an `enum` key, which lets you specify all the values the -# setting can take. `enum` may be an array of allowed values (of the specified -# type), or an array of objects with `value` and `description` properties, where -# the `value` is an allowed value, and the `description` is a descriptive string -# used in the settings view. -# -# In this example, the setting must be one of the 4 integers: -# -# ```coffee -# config: -# someSetting: -# type: 'integer' -# default: 4 -# enum: [2, 4, 6, 8] -# ``` -# -# In this example, the setting must be either 'foo' or 'bar', which are -# presented using the provided descriptions in the settings pane: -# -# ```coffee -# config: -# someSetting: -# type: 'string' -# default: 'foo' -# enum: [ -# {value: 'foo', description: 'Foo mode. You want this.'} -# {value: 'bar', description: 'Bar mode. Nobody wants that!'} -# ] -# ``` -# -# Usage: -# -# ```coffee -# atom.config.set('my-package.someSetting', '2') -# atom.config.get('my-package.someSetting') # -> 2 -# -# # will not set values outside of the enum values -# atom.config.set('my-package.someSetting', '3') -# atom.config.get('my-package.someSetting') # -> 2 -# -# # If it cannot be coerced, the value will not be set -# atom.config.set('my-package.someSetting', '4') -# atom.config.get('my-package.someSetting') # -> 4 -# ``` -# -# #### title and description -# -# The settings view will use the `title` and `description` keys to display your -# config setting in a readable way. By default the settings view humanizes your -# config key, so `someSetting` becomes `Some Setting`. In some cases, this is -# confusing for users, and a more descriptive title is useful. -# -# Descriptions will be displayed below the title in the settings view. -# -# For a group of config settings the humanized key or the title and the -# description are used for the group headline. -# -# ```coffee -# config: -# someSetting: -# title: 'Setting Magnitude' -# description: 'This will affect the blah and the other blah' -# type: 'integer' -# default: 4 -# ``` -# -# __Note__: You should strive to be so clear in your naming of the setting that -# you do not need to specify a title or description! -# -# Descriptions allow a subset of -# [Markdown formatting](https://help.github.com/articles/github-flavored-markdown/). -# Specifically, you may use the following in configuration setting descriptions: -# -# * **bold** - `**bold**` -# * *italics* - `*italics*` -# * [links](https://atom.io) - `[links](https://atom.io)` -# * `code spans` - `\`code spans\`` -# * line breaks - `line breaks
` -# * ~~strikethrough~~ - `~~strikethrough~~` -# -# #### order -# -# The settings view orders your settings alphabetically. You can override this -# ordering with the order key. -# -# ```coffee -# config: -# zSetting: -# type: 'integer' -# default: 4 -# order: 1 -# aSetting: -# type: 'integer' -# default: 4 -# order: 2 -# ``` -# -# ## Manipulating values outside your configuration schema -# -# It is possible to manipulate(`get`, `set`, `observe` etc) values that do not -# appear in your configuration schema. For example, if the config schema of the -# package 'some-package' is -# -# ```coffee -# config: -# someSetting: -# type: 'boolean' -# default: false -# ``` -# -# You can still do the following -# -# ```coffee -# let otherSetting = atom.config.get('some-package.otherSetting') -# atom.config.set('some-package.stillAnotherSetting', otherSetting * 5) -# ``` -# -# In other words, if a function asks for a `key-path`, that path doesn't have to -# be described in the config schema for the package or any package. However, as -# highlighted in the best practices section, you are advised against doing the -# above. -# -# ## Best practices -# -# * Don't depend on (or write to) configuration keys outside of your keypath. -# -module.exports = -class Config - @schemaEnforcers = {} - - @addSchemaEnforcer: (typeName, enforcerFunction) -> - @schemaEnforcers[typeName] ?= [] - @schemaEnforcers[typeName].push(enforcerFunction) - - @addSchemaEnforcers: (filters) -> - for typeName, functions of filters - for name, enforcerFunction of functions - @addSchemaEnforcer(typeName, enforcerFunction) - return - - @executeSchemaEnforcers: (keyPath, value, schema) -> - error = null - types = schema.type - types = [types] unless Array.isArray(types) - for type in types - try - enforcerFunctions = @schemaEnforcers[type].concat(@schemaEnforcers['*']) - for enforcer in enforcerFunctions - # At some point in one's life, one must call upon an enforcer. - value = enforcer.call(this, keyPath, value, schema) - error = null - break - catch e - error = e - - throw error if error? - value - - # Created during initialization, available as `atom.config` - constructor: ({@notificationManager, @enablePersistence}={}) -> - @clear() - - initialize: ({@configDirPath, @resourcePath, projectHomeSchema}) -> - if @enablePersistence? - @configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson']) - @configFilePath ?= path.join(@configDirPath, 'config.cson') - - @schema.properties.core.properties.projectHome = projectHomeSchema - @defaultSettings.core.projectHome = projectHomeSchema.default - - clear: -> - @emitter = new Emitter - @schema = - type: 'object' - properties: {} - @defaultSettings = {} - @settings = {} - @scopedSettingsStore = new ScopedPropertyStore - - @settingsLoaded = false - @savePending = false - @configFileHasErrors = false - @transactDepth = 0 - @pendingOperations = [] - @legacyScopeAliases = {} - - @requestLoad = _.debounce => - @loadUserConfig() - , 100 - - debouncedSave = _.debounce => - @savePending = false - @save() - , 100 - @requestSave = => - @savePending = true - debouncedSave() - - shouldNotAccessFileSystem: -> not @enablePersistence - - ### - Section: Config Subscription - ### - - # Essential: Add a listener for changes to a given key path. This is different - # than {::onDidChange} in that it will immediately call your callback with the - # current value of the config entry. - # - # ### Examples - # - # You might want to be notified when the themes change. We'll watch - # `core.themes` for changes - # - # ```coffee - # atom.config.observe 'core.themes', (value) -> - # # do stuff with value - # ``` - # - # * `keyPath` {String} name of the key to observe - # * `options` (optional) {Object} - # * `scope` (optional) {ScopeDescriptor} describing a path from - # the root of the syntax tree to a token. Get one by calling - # {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples. - # See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) - # for more information. - # * `callback` {Function} to call when the value of the key changes. - # * `value` the new value of the key - # - # Returns a {Disposable} with the following keys on which you can call - # `.dispose()` to unsubscribe. - observe: -> - if arguments.length is 2 - [keyPath, callback] = arguments - else if arguments.length is 3 and (_.isString(arguments[0]) and _.isObject(arguments[1])) - [keyPath, options, callback] = arguments - scopeDescriptor = options.scope - else - console.error 'An unsupported form of Config::observe is being used. See https://atom.io/docs/api/latest/Config for details' - return - - if scopeDescriptor? - @observeScopedKeyPath(scopeDescriptor, keyPath, callback) - else - @observeKeyPath(keyPath, options ? {}, callback) - - # Essential: Add a listener for changes to a given key path. If `keyPath` is - # not specified, your callback will be called on changes to any key. - # - # * `keyPath` (optional) {String} name of the key to observe. Must be - # specified if `scopeDescriptor` is specified. - # * `options` (optional) {Object} - # * `scope` (optional) {ScopeDescriptor} describing a path from - # the root of the syntax tree to a token. Get one by calling - # {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples. - # See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) - # for more information. - # * `callback` {Function} to call when the value of the key changes. - # * `event` {Object} - # * `newValue` the new value of the key - # * `oldValue` the prior value of the key. - # - # Returns a {Disposable} with the following keys on which you can call - # `.dispose()` to unsubscribe. - onDidChange: -> - if arguments.length is 1 - [callback] = arguments - else if arguments.length is 2 - [keyPath, callback] = arguments - else - [keyPath, options, callback] = arguments - scopeDescriptor = options.scope - - if scopeDescriptor? - @onDidChangeScopedKeyPath(scopeDescriptor, keyPath, callback) - else - @onDidChangeKeyPath(keyPath, callback) - - ### - Section: Managing Settings - ### - - # Essential: Retrieves the setting for the given key. - # - # ### Examples - # - # You might want to know what themes are enabled, so check `core.themes` - # - # ```coffee - # atom.config.get('core.themes') - # ``` - # - # With scope descriptors you can get settings within a specific editor - # scope. For example, you might want to know `editor.tabLength` for ruby - # files. - # - # ```coffee - # atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 2 - # ``` - # - # This setting in ruby files might be different than the global tabLength setting - # - # ```coffee - # atom.config.get('editor.tabLength') # => 4 - # atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 2 - # ``` - # - # You can get the language scope descriptor via - # {TextEditor::getRootScopeDescriptor}. This will get the setting specifically - # for the editor's language. - # - # ```coffee - # atom.config.get('editor.tabLength', scope: @editor.getRootScopeDescriptor()) # => 2 - # ``` - # - # Additionally, you can get the setting at the specific cursor position. - # - # ```coffee - # scopeDescriptor = @editor.getLastCursor().getScopeDescriptor() - # atom.config.get('editor.tabLength', scope: scopeDescriptor) # => 2 - # ``` - # - # * `keyPath` The {String} name of the key to retrieve. - # * `options` (optional) {Object} - # * `sources` (optional) {Array} of {String} source names. If provided, only - # values that were associated with these sources during {::set} will be used. - # * `excludeSources` (optional) {Array} of {String} source names. If provided, - # values that were associated with these sources during {::set} will not - # be used. - # * `scope` (optional) {ScopeDescriptor} describing a path from - # the root of the syntax tree to a token. Get one by calling - # {editor.getLastCursor().getScopeDescriptor()} - # See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) - # for more information. - # - # Returns the value from Atom's default settings, the user's configuration - # file in the type specified by the configuration schema. - get: -> - if arguments.length > 1 - if typeof arguments[0] is 'string' or not arguments[0]? - [keyPath, options] = arguments - {scope} = options - else - [keyPath] = arguments - - if scope? - value = @getRawScopedValue(scope, keyPath, options) - value ? @getRawValue(keyPath, options) - else - @getRawValue(keyPath, options) - - # Extended: Get all of the values for the given key-path, along with their - # associated scope selector. - # - # * `keyPath` The {String} name of the key to retrieve - # * `options` (optional) {Object} see the `options` argument to {::get} - # - # Returns an {Array} of {Object}s with the following keys: - # * `scopeDescriptor` The {ScopeDescriptor} with which the value is associated - # * `value` The value for the key-path - getAll: (keyPath, options) -> - {scope} = options if options? - - if scope? - scopeDescriptor = ScopeDescriptor.fromObject(scope) - result = @scopedSettingsStore.getAll( - scopeDescriptor.getScopeChain(), - keyPath, - options - ) - if legacyScopeDescriptor = @getLegacyScopeDescriptor(scopeDescriptor) - result.push(@scopedSettingsStore.getAll( - legacyScopeDescriptor.getScopeChain(), - keyPath, - options - )...) - else - result = [] - - if globalValue = @getRawValue(keyPath, options) - result.push(scopeSelector: '*', value: globalValue) - - result - - # Essential: Sets the value for a configuration setting. - # - # This value is stored in Atom's internal configuration file. - # - # ### Examples - # - # You might want to change the themes programmatically: - # - # ```coffee - # atom.config.set('core.themes', ['atom-light-ui', 'atom-light-syntax']) - # ``` - # - # You can also set scoped settings. For example, you might want change the - # `editor.tabLength` only for ruby files. - # - # ```coffee - # atom.config.get('editor.tabLength') # => 4 - # atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 4 - # atom.config.get('editor.tabLength', scope: ['source.js']) # => 4 - # - # # Set ruby to 2 - # atom.config.set('editor.tabLength', 2, scopeSelector: '.source.ruby') # => true - # - # # Notice it's only set to 2 in the case of ruby - # atom.config.get('editor.tabLength') # => 4 - # atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 2 - # atom.config.get('editor.tabLength', scope: ['source.js']) # => 4 - # ``` - # - # * `keyPath` The {String} name of the key. - # * `value` The value of the setting. Passing `undefined` will revert the - # setting to the default value. - # * `options` (optional) {Object} - # * `scopeSelector` (optional) {String}. eg. '.source.ruby' - # See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) - # for more information. - # * `source` (optional) {String} The name of a file with which the setting - # is associated. Defaults to the user's config file. - # - # Returns a {Boolean} - # * `true` if the value was set. - # * `false` if the value was not able to be coerced to the type specified in the setting's schema. - set: -> - [keyPath, value, options] = arguments - - unless @settingsLoaded - @pendingOperations.push => @set.call(this, keyPath, value, options) - - scopeSelector = options?.scopeSelector - source = options?.source - shouldSave = options?.save ? true - - if source and not scopeSelector - throw new Error("::set with a 'source' and no 'sourceSelector' is not yet implemented!") - - source ?= @getUserConfigPath() - - unless value is undefined - try - value = @makeValueConformToSchema(keyPath, value) - catch e - return false - - if scopeSelector? - @setRawScopedValue(keyPath, value, source, scopeSelector) - else - @setRawValue(keyPath, value) - - if source is @getUserConfigPath() and shouldSave and not @configFileHasErrors and @settingsLoaded - @requestSave() - true - - # Essential: Restore the setting at `keyPath` to its default value. - # - # * `keyPath` The {String} name of the key. - # * `options` (optional) {Object} - # * `scopeSelector` (optional) {String}. See {::set} - # * `source` (optional) {String}. See {::set} - unset: (keyPath, options) -> - unless @settingsLoaded - @pendingOperations.push => @unset.call(this, keyPath, options) - - {scopeSelector, source} = options ? {} - source ?= @getUserConfigPath() - - if scopeSelector? - if keyPath? - settings = @scopedSettingsStore.propertiesForSourceAndSelector(source, scopeSelector) - if getValueAtKeyPath(settings, keyPath)? - @scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector) - setValueAtKeyPath(settings, keyPath, undefined) - settings = withoutEmptyObjects(settings) - @set(null, settings, {scopeSelector, source, priority: @priorityForSource(source)}) if settings? - if source is @getUserConfigPath() and not @configFileHasErrors and @settingsLoaded - @requestSave() - else - @scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector) - @emitChangeEvent() - else - for scopeSelector of @scopedSettingsStore.propertiesForSource(source) - @unset(keyPath, {scopeSelector, source}) - if keyPath? and source is @getUserConfigPath() - @set(keyPath, getValueAtKeyPath(@defaultSettings, keyPath)) - - # Extended: Get an {Array} of all of the `source` {String}s with which - # settings have been added via {::set}. - getSources: -> - _.uniq(_.pluck(@scopedSettingsStore.propertySets, 'source')).sort() - - # Extended: Retrieve the schema for a specific key path. The schema will tell - # you what type the keyPath expects, and other metadata about the config - # option. - # - # * `keyPath` The {String} name of the key. - # - # Returns an {Object} eg. `{type: 'integer', default: 23, minimum: 1}`. - # Returns `null` when the keyPath has no schema specified, but is accessible - # from the root schema. - getSchema: (keyPath) -> - keys = splitKeyPath(keyPath) - schema = @schema - for key in keys - if schema.type is 'object' - childSchema = schema.properties?[key] - unless childSchema? - if isPlainObject(schema.additionalProperties) - childSchema = schema.additionalProperties - else if schema.additionalProperties is false - return null - else - return {type: 'any'} - else - return null - schema = childSchema - schema - - # Extended: Get the {String} path to the config file being used. - getUserConfigPath: -> - @configFilePath - - # Extended: Suppress calls to handler functions registered with {::onDidChange} - # and {::observe} for the duration of `callback`. After `callback` executes, - # handlers will be called once if the value for their key-path has changed. - # - # * `callback` {Function} to execute while suppressing calls to handlers. - transact: (callback) -> - @beginTransaction() - try - callback() - finally - @endTransaction() - - addLegacyScopeAlias: (languageId, legacyScopeName) -> - @legacyScopeAliases[languageId] = legacyScopeName - - removeLegacyScopeAlias: (languageId) -> - delete @legacyScopeAliases[languageId] - - ### - Section: Internal methods used by core - ### - - # Private: Suppress calls to handler functions registered with {::onDidChange} - # and {::observe} for the duration of the {Promise} returned by `callback`. - # After the {Promise} is either resolved or rejected, handlers will be called - # once if the value for their key-path has changed. - # - # * `callback` {Function} that returns a {Promise}, which will be executed - # while suppressing calls to handlers. - # - # Returns a {Promise} that is either resolved or rejected according to the - # `{Promise}` returned by `callback`. If `callback` throws an error, a - # rejected {Promise} will be returned instead. - transactAsync: (callback) -> - @beginTransaction() - try - endTransaction = (fn) => (args...) => - @endTransaction() - fn(args...) - result = callback() - new Promise (resolve, reject) -> - result.then(endTransaction(resolve)).catch(endTransaction(reject)) - catch error - @endTransaction() - Promise.reject(error) - - beginTransaction: -> - @transactDepth++ - - endTransaction: -> - @transactDepth-- - @emitChangeEvent() - - pushAtKeyPath: (keyPath, value) -> - arrayValue = @get(keyPath) ? [] - result = arrayValue.push(value) - @set(keyPath, arrayValue) - result - - unshiftAtKeyPath: (keyPath, value) -> - arrayValue = @get(keyPath) ? [] - result = arrayValue.unshift(value) - @set(keyPath, arrayValue) - result - - removeAtKeyPath: (keyPath, value) -> - arrayValue = @get(keyPath) ? [] - result = _.remove(arrayValue, value) - @set(keyPath, arrayValue) - result - - setSchema: (keyPath, schema) -> - unless isPlainObject(schema) - throw new Error("Error loading schema for #{keyPath}: schemas can only be objects!") - - unless typeof schema.type? - throw new Error("Error loading schema for #{keyPath}: schema objects must have a type attribute") - - rootSchema = @schema - if keyPath - for key in splitKeyPath(keyPath) - rootSchema.type = 'object' - rootSchema.properties ?= {} - properties = rootSchema.properties - properties[key] ?= {} - rootSchema = properties[key] - - Object.assign rootSchema, schema - @transact => - @setDefaults(keyPath, @extractDefaultsFromSchema(schema)) - @setScopedDefaultsFromSchema(keyPath, schema) - @resetSettingsForSchemaChange() - - load: -> - @initializeConfigDirectory() - @loadUserConfig() - @observeUserConfig() - - ### - Section: Private methods managing the user's config file - ### - - initializeConfigDirectory: (done) -> - return if fs.existsSync(@configDirPath) or @shouldNotAccessFileSystem() - - fs.makeTreeSync(@configDirPath) - - queue = async.queue ({sourcePath, destinationPath}, callback) -> - fs.copy(sourcePath, destinationPath, callback) - queue.drain = done - - templateConfigDirPath = fs.resolve(@resourcePath, 'dot-atom') - onConfigDirFile = (sourcePath) => - relativePath = sourcePath.substring(templateConfigDirPath.length + 1) - destinationPath = path.join(@configDirPath, relativePath) - queue.push({sourcePath, destinationPath}) - fs.traverseTree(templateConfigDirPath, onConfigDirFile, ((path) -> true), (->)) - - loadUserConfig: -> - return if @shouldNotAccessFileSystem() - return if @savePending - - try - unless fs.existsSync(@configFilePath) - fs.makeTreeSync(path.dirname(@configFilePath)) - CSON.writeFileSync(@configFilePath, {}, {flag: 'wx'}) # fails if file exists - catch error - if error.code isnt 'EEXIST' - @configFileHasErrors = true - @notifyFailure("Failed to initialize `#{path.basename(@configFilePath)}`", error.stack) - return - - try - userConfig = CSON.readFileSync(@configFilePath) - userConfig = {} if userConfig is null - - unless isPlainObject(userConfig) - throw new Error("`#{path.basename(@configFilePath)}` must contain valid JSON or CSON") - - @resetUserSettings(userConfig) - @configFileHasErrors = false - catch error - @configFileHasErrors = true - message = "Failed to load `#{path.basename(@configFilePath)}`" - - detail = if error.location? - # stack is the output from CSON in this case - error.stack - else - # message will be EACCES permission denied, et al - error.message - - @notifyFailure(message, detail) - - observeUserConfig: -> - return if @shouldNotAccessFileSystem() - - try - @watchSubscriptionPromise ?= watchPath @configFilePath, {}, (events) => - for {action} in events - if action in ['created', 'modified', 'renamed'] and @watchSubscriptionPromise? - @requestLoad() - catch error - @notifyFailure """ - Unable to watch path: `#{path.basename(@configFilePath)}`. Make sure you have permissions to - `#{@configFilePath}`. On linux there are currently problems with watch - sizes. See [this document][watches] for more info. - [watches]:https://github.com/atom/atom/blob/master/docs/build-instructions/linux.md#typeerror-unable-to-watch-path - """ - - @watchSubscriptionPromise - - unobserveUserConfig: -> - @watchSubscriptionPromise?.then (watcher) -> watcher?.dispose() - @watchSubscriptionPromise = null - - notifyFailure: (errorMessage, detail) -> - @notificationManager?.addError(errorMessage, {detail, dismissable: true}) - - save: -> - return if @shouldNotAccessFileSystem() - - allSettings = {'*': @settings} - allSettings = Object.assign allSettings, @scopedSettingsStore.propertiesForSource(@getUserConfigPath()) - allSettings = sortObject(allSettings) - try - CSON.writeFileSync(@configFilePath, allSettings) - catch error - message = "Failed to save `#{path.basename(@configFilePath)}`" - detail = error.message - @notifyFailure(message, detail) - - ### - Section: Private methods managing global settings - ### - - resetUserSettings: (newSettings) -> - if newSettings.global? - newSettings['*'] = newSettings.global - delete newSettings.global - - if newSettings['*']? - scopedSettings = newSettings - newSettings = newSettings['*'] - delete scopedSettings['*'] - @resetUserScopedSettings(scopedSettings) - - @transact => - @settings = {} - @settingsLoaded = true - @set(key, value, save: false) for key, value of newSettings - if @pendingOperations.length - op() for op in @pendingOperations - @pendingOperations = [] - - getRawValue: (keyPath, options) -> - unless options?.excludeSources?.indexOf(@getUserConfigPath()) >= 0 - value = getValueAtKeyPath(@settings, keyPath) - unless options?.sources?.length > 0 - defaultValue = getValueAtKeyPath(@defaultSettings, keyPath) - - if value? - value = @deepClone(value) - @deepDefaults(value, defaultValue) if isPlainObject(value) and isPlainObject(defaultValue) - else - value = @deepClone(defaultValue) - - value - - setRawValue: (keyPath, value) -> - defaultValue = getValueAtKeyPath(@defaultSettings, keyPath) - if _.isEqual(defaultValue, value) - if keyPath? - deleteValueAtKeyPath(@settings, keyPath) - else - @settings = null - else - if keyPath? - setValueAtKeyPath(@settings, keyPath, value) - else - @settings = value - @emitChangeEvent() - - observeKeyPath: (keyPath, options, callback) -> - callback(@get(keyPath)) - @onDidChangeKeyPath keyPath, (event) -> callback(event.newValue) - - onDidChangeKeyPath: (keyPath, callback) -> - oldValue = @get(keyPath) - @emitter.on 'did-change', => - newValue = @get(keyPath) - unless _.isEqual(oldValue, newValue) - event = {oldValue, newValue} - oldValue = newValue - callback(event) - - isSubKeyPath: (keyPath, subKeyPath) -> - return false unless keyPath? and subKeyPath? - pathSubTokens = splitKeyPath(subKeyPath) - pathTokens = splitKeyPath(keyPath).slice(0, pathSubTokens.length) - _.isEqual(pathTokens, pathSubTokens) - - setRawDefault: (keyPath, value) -> - setValueAtKeyPath(@defaultSettings, keyPath, value) - @emitChangeEvent() - - setDefaults: (keyPath, defaults) -> - if defaults? and isPlainObject(defaults) - keys = splitKeyPath(keyPath) - @transact => - for key, childValue of defaults - continue unless defaults.hasOwnProperty(key) - @setDefaults(keys.concat([key]).join('.'), childValue) - else - try - defaults = @makeValueConformToSchema(keyPath, defaults) - @setRawDefault(keyPath, defaults) - catch e - console.warn("'#{keyPath}' could not set the default. Attempted default: #{JSON.stringify(defaults)}; Schema: #{JSON.stringify(@getSchema(keyPath))}") - return - - deepClone: (object) -> - if object instanceof Color - object.clone() - else if _.isArray(object) - object.map (value) => @deepClone(value) - else if isPlainObject(object) - _.mapObject object, (key, value) => [key, @deepClone(value)] - else - object - - deepDefaults: (target) -> - result = target - i = 0 - while ++i < arguments.length - object = arguments[i] - if isPlainObject(result) and isPlainObject(object) - for key in Object.keys(object) - result[key] = @deepDefaults(result[key], object[key]) - else - if not result? - result = @deepClone(object) - result - - # `schema` will look something like this - # - # ```coffee - # type: 'string' - # default: 'ok' - # scopes: - # '.source.js': - # default: 'omg' - # ``` - setScopedDefaultsFromSchema: (keyPath, schema) -> - if schema.scopes? and isPlainObject(schema.scopes) - scopedDefaults = {} - for scope, scopeSchema of schema.scopes - continue unless scopeSchema.hasOwnProperty('default') - scopedDefaults[scope] = {} - setValueAtKeyPath(scopedDefaults[scope], keyPath, scopeSchema.default) - @scopedSettingsStore.addProperties('schema-default', scopedDefaults) - - if schema.type is 'object' and schema.properties? and isPlainObject(schema.properties) - keys = splitKeyPath(keyPath) - for key, childValue of schema.properties - continue unless schema.properties.hasOwnProperty(key) - @setScopedDefaultsFromSchema(keys.concat([key]).join('.'), childValue) - - return - - extractDefaultsFromSchema: (schema) -> - if schema.default? - schema.default - else if schema.type is 'object' and schema.properties? and isPlainObject(schema.properties) - defaults = {} - properties = schema.properties or {} - defaults[key] = @extractDefaultsFromSchema(value) for key, value of properties - defaults - - makeValueConformToSchema: (keyPath, value, options) -> - if options?.suppressException - try - @makeValueConformToSchema(keyPath, value) - catch e - undefined - else - unless (schema = @getSchema(keyPath))? - throw new Error("Illegal key path #{keyPath}") if schema is false - @constructor.executeSchemaEnforcers(keyPath, value, schema) - - # When the schema is changed / added, there may be values set in the config - # that do not conform to the schema. This will reset make them conform. - resetSettingsForSchemaChange: (source=@getUserConfigPath()) -> - @transact => - @settings = @makeValueConformToSchema(null, @settings, suppressException: true) - selectorsAndSettings = @scopedSettingsStore.propertiesForSource(source) - @scopedSettingsStore.removePropertiesForSource(source) - for scopeSelector, settings of selectorsAndSettings - settings = @makeValueConformToSchema(null, settings, suppressException: true) - @setRawScopedValue(null, settings, source, scopeSelector) - return - - ### - Section: Private Scoped Settings - ### - - priorityForSource: (source) -> - if source is @getUserConfigPath() - 1000 - else - 0 - - emitChangeEvent: -> - @emitter.emit 'did-change' unless @transactDepth > 0 - - resetUserScopedSettings: (newScopedSettings) -> - source = @getUserConfigPath() - priority = @priorityForSource(source) - @scopedSettingsStore.removePropertiesForSource(source) - - for scopeSelector, settings of newScopedSettings - settings = @makeValueConformToSchema(null, settings, suppressException: true) - validatedSettings = {} - validatedSettings[scopeSelector] = withoutEmptyObjects(settings) - @scopedSettingsStore.addProperties(source, validatedSettings, {priority}) if validatedSettings[scopeSelector]? - - @emitChangeEvent() - - setRawScopedValue: (keyPath, value, source, selector, options) -> - if keyPath? - newValue = {} - setValueAtKeyPath(newValue, keyPath, value) - value = newValue - - settingsBySelector = {} - settingsBySelector[selector] = value - @scopedSettingsStore.addProperties(source, settingsBySelector, priority: @priorityForSource(source)) - @emitChangeEvent() - - getRawScopedValue: (scopeDescriptor, keyPath, options) -> - scopeDescriptor = ScopeDescriptor.fromObject(scopeDescriptor) - result = @scopedSettingsStore.getPropertyValue( - scopeDescriptor.getScopeChain(), - keyPath, - options - ) - - if result? - result - else if legacyScopeDescriptor = @getLegacyScopeDescriptor(scopeDescriptor) - @scopedSettingsStore.getPropertyValue( - legacyScopeDescriptor.getScopeChain(), - keyPath, - options - ) - - observeScopedKeyPath: (scope, keyPath, callback) -> - callback(@get(keyPath, {scope})) - @onDidChangeScopedKeyPath scope, keyPath, (event) -> callback(event.newValue) - - onDidChangeScopedKeyPath: (scope, keyPath, callback) -> - oldValue = @get(keyPath, {scope}) - @emitter.on 'did-change', => - newValue = @get(keyPath, {scope}) - unless _.isEqual(oldValue, newValue) - event = {oldValue, newValue} - oldValue = newValue - callback(event) - - getLegacyScopeDescriptor: (scopeDescriptor) -> - legacyAlias = @legacyScopeAliases[scopeDescriptor.scopes[0]] - if legacyAlias - scopes = scopeDescriptor.scopes.slice() - scopes[0] = legacyAlias - new ScopeDescriptor({scopes}) - -# Base schema enforcers. These will coerce raw input into the specified type, -# and will throw an error when the value cannot be coerced. Throwing the error -# will indicate that the value should not be set. -# -# Enforcers are run from most specific to least. For a schema with type -# `integer`, all the enforcers for the `integer` type will be run first, in -# order of specification. Then the `*` enforcers will be run, in order of -# specification. -Config.addSchemaEnforcers - 'any': - coerce: (keyPath, value, schema) -> - value - - 'integer': - coerce: (keyPath, value, schema) -> - value = parseInt(value) - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into an int") if isNaN(value) or not isFinite(value) - value - - 'number': - coerce: (keyPath, value, schema) -> - value = parseFloat(value) - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a number") if isNaN(value) or not isFinite(value) - value - - 'boolean': - coerce: (keyPath, value, schema) -> - switch typeof value - when 'string' - if value.toLowerCase() is 'true' - true - else if value.toLowerCase() is 'false' - false - else - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be a boolean or the string 'true' or 'false'") - when 'boolean' - value - else - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be a boolean or the string 'true' or 'false'") - - 'string': - validate: (keyPath, value, schema) -> - unless typeof value is 'string' - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be a string") - value - - validateMaximumLength: (keyPath, value, schema) -> - if typeof schema.maximumLength is 'number' and value.length > schema.maximumLength - value.slice(0, schema.maximumLength) - else - value - - 'null': - # null sort of isnt supported. It will just unset in this case - coerce: (keyPath, value, schema) -> - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be null") unless value in [undefined, null] - value - - 'object': - coerce: (keyPath, value, schema) -> - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be an object") unless isPlainObject(value) - return value unless schema.properties? - - defaultChildSchema = null - allowsAdditionalProperties = true - if isPlainObject(schema.additionalProperties) - defaultChildSchema = schema.additionalProperties - if schema.additionalProperties is false - allowsAdditionalProperties = false - - newValue = {} - for prop, propValue of value - childSchema = schema.properties[prop] ? defaultChildSchema - if childSchema? - try - newValue[prop] = @executeSchemaEnforcers(pushKeyPath(keyPath, prop), propValue, childSchema) - catch error - console.warn "Error setting item in object: #{error.message}" - else if allowsAdditionalProperties - # Just pass through un-schema'd values - newValue[prop] = propValue - else - console.warn "Illegal object key: #{keyPath}.#{prop}" - - newValue - - 'array': - coerce: (keyPath, value, schema) -> - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} must be an array") unless Array.isArray(value) - itemSchema = schema.items - if itemSchema? - newValue = [] - for item in value - try - newValue.push @executeSchemaEnforcers(keyPath, item, itemSchema) - catch error - console.warn "Error setting item in array: #{error.message}" - newValue - else - value - - 'color': - coerce: (keyPath, value, schema) -> - color = Color.parse(value) - unless color? - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} cannot be coerced into a color") - color - - '*': - coerceMinimumAndMaximum: (keyPath, value, schema) -> - return value unless typeof value is 'number' - if schema.minimum? and typeof schema.minimum is 'number' - value = Math.max(value, schema.minimum) - if schema.maximum? and typeof schema.maximum is 'number' - value = Math.min(value, schema.maximum) - value - - validateEnum: (keyPath, value, schema) -> - possibleValues = schema.enum - - if Array.isArray(possibleValues) - possibleValues = possibleValues.map (value) -> - if value.hasOwnProperty('value') then value.value else value - - return value unless possibleValues? and Array.isArray(possibleValues) and possibleValues.length - - for possibleValue in possibleValues - # Using `isEqual` for possibility of placing enums on array and object schemas - return value if _.isEqual(possibleValue, value) - - throw new Error("Validation failed at #{keyPath}, #{JSON.stringify(value)} is not one of #{JSON.stringify(possibleValues)}") - -isPlainObject = (value) -> - _.isObject(value) and not _.isArray(value) and not _.isFunction(value) and not _.isString(value) and not (value instanceof Color) - -sortObject = (value) -> - return value unless isPlainObject(value) - result = {} - for key in Object.keys(value).sort() - result[key] = sortObject(value[key]) - result - -withoutEmptyObjects = (object) -> - resultObject = undefined - if isPlainObject(object) - for key, value of object - newValue = withoutEmptyObjects(value) - if newValue? - resultObject ?= {} - resultObject[key] = newValue - else - resultObject = object - resultObject diff --git a/src/config.js b/src/config.js new file mode 100644 index 000000000..ce20db30c --- /dev/null +++ b/src/config.js @@ -0,0 +1,1484 @@ +const _ = require('underscore-plus') +const {Emitter} = require('event-kit') +const { + getValueAtKeyPath, setValueAtKeyPath, deleteValueAtKeyPath, + pushKeyPath, splitKeyPath +} = require('key-path-helpers') +const Color = require('./color') +const ScopedPropertyStore = require('scoped-property-store') +const ScopeDescriptor = require('./scope-descriptor') + +const schemaEnforcers = {} + +// Essential: Used to access all of Atom's configuration details. +// +// An instance of this class is always available as the `atom.config` global. +// +// ## Getting and setting config settings. +// +// ```coffee +// # Note that with no value set, ::get returns the setting's default value. +// atom.config.get('my-package.myKey') # -> 'defaultValue' +// +// atom.config.set('my-package.myKey', 'value') +// atom.config.get('my-package.myKey') # -> 'value' +// ``` +// +// You may want to watch for changes. Use {::observe} to catch changes to the setting. +// +// ```coffee +// atom.config.set('my-package.myKey', 'value') +// atom.config.observe 'my-package.myKey', (newValue) -> +// # `observe` calls immediately and every time the value is changed +// console.log 'My configuration changed:', newValue +// ``` +// +// If you want a notification only when the value changes, use {::onDidChange}. +// +// ```coffee +// atom.config.onDidChange 'my-package.myKey', ({newValue, oldValue}) -> +// console.log 'My configuration changed:', newValue, oldValue +// ``` +// +// ### Value Coercion +// +// Config settings each have a type specified by way of a +// [schema](json-schema.org). For example we might want an integer setting that only +// allows integers greater than `0`: +// +// ```coffee +// # When no value has been set, `::get` returns the setting's default value +// atom.config.get('my-package.anInt') # -> 12 +// +// # The string will be coerced to the integer 123 +// atom.config.set('my-package.anInt', '123') +// atom.config.get('my-package.anInt') # -> 123 +// +// # The string will be coerced to an integer, but it must be greater than 0, so is set to 1 +// atom.config.set('my-package.anInt', '-20') +// atom.config.get('my-package.anInt') # -> 1 +// ``` +// +// ## Defining settings for your package +// +// Define a schema under a `config` key in your package main. +// +// ```coffee +// module.exports = +// # Your config schema +// config: +// someInt: +// type: 'integer' +// default: 23 +// minimum: 1 +// +// activate: (state) -> # ... +// # ... +// ``` +// +// See [package docs](http://flight-manual.atom.io/hacking-atom/sections/package-word-count/) for +// more info. +// +// ## Config Schemas +// +// We use [json schema](http://json-schema.org) which allows you to define your value's +// default, the type it should be, etc. A simple example: +// +// ```coffee +// # We want to provide an `enableThing`, and a `thingVolume` +// config: +// enableThing: +// type: 'boolean' +// default: false +// thingVolume: +// type: 'integer' +// default: 5 +// minimum: 1 +// maximum: 11 +// ``` +// +// The type keyword allows for type coercion and validation. If a `thingVolume` is +// set to a string `'10'`, it will be coerced into an integer. +// +// ```coffee +// atom.config.set('my-package.thingVolume', '10') +// atom.config.get('my-package.thingVolume') # -> 10 +// +// # It respects the min / max +// atom.config.set('my-package.thingVolume', '400') +// atom.config.get('my-package.thingVolume') # -> 11 +// +// # If it cannot be coerced, the value will not be set +// atom.config.set('my-package.thingVolume', 'cats') +// atom.config.get('my-package.thingVolume') # -> 11 +// ``` +// +// ### Supported Types +// +// The `type` keyword can be a string with any one of the following. You can also +// chain them by specifying multiple in an an array. For example +// +// ```coffee +// config: +// someSetting: +// type: ['boolean', 'integer'] +// default: 5 +// +// # Then +// atom.config.set('my-package.someSetting', 'true') +// atom.config.get('my-package.someSetting') # -> true +// +// atom.config.set('my-package.someSetting', '12') +// atom.config.get('my-package.someSetting') # -> 12 +// ``` +// +// #### string +// +// Values must be a string. +// +// ```coffee +// config: +// someSetting: +// type: 'string' +// default: 'hello' +// ``` +// +// #### integer +// +// Values will be coerced into integer. Supports the (optional) `minimum` and +// `maximum` keys. +// +// ```coffee +// config: +// someSetting: +// type: 'integer' +// default: 5 +// minimum: 1 +// maximum: 11 +// ``` +// +// #### number +// +// Values will be coerced into a number, including real numbers. Supports the +// (optional) `minimum` and `maximum` keys. +// +// ```coffee +// config: +// someSetting: +// type: 'number' +// default: 5.3 +// minimum: 1.5 +// maximum: 11.5 +// ``` +// +// #### boolean +// +// Values will be coerced into a Boolean. `'true'` and `'false'` will be coerced into +// a boolean. Numbers, arrays, objects, and anything else will not be coerced. +// +// ```coffee +// config: +// someSetting: +// type: 'boolean' +// default: false +// ``` +// +// #### array +// +// Value must be an Array. The types of the values can be specified by a +// subschema in the `items` key. +// +// ```coffee +// config: +// someSetting: +// type: 'array' +// default: [1, 2, 3] +// items: +// type: 'integer' +// minimum: 1.5 +// maximum: 11.5 +// ``` +// +// #### color +// +// Values will be coerced into a {Color} with `red`, `green`, `blue`, and `alpha` +// properties that all have numeric values. `red`, `green`, `blue` will be in +// the range 0 to 255 and `value` will be in the range 0 to 1. Values can be any +// valid CSS color format such as `#abc`, `#abcdef`, `white`, +// `rgb(50, 100, 150)`, and `rgba(25, 75, 125, .75)`. +// +// ```coffee +// config: +// someSetting: +// type: 'color' +// default: 'white' +// ``` +// +// #### object / Grouping other types +// +// A config setting with the type `object` allows grouping a set of config +// settings. The group will be visually separated and has its own group headline. +// The sub options must be listed under a `properties` key. +// +// ```coffee +// config: +// someSetting: +// type: 'object' +// properties: +// myChildIntOption: +// type: 'integer' +// minimum: 1.5 +// maximum: 11.5 +// ``` +// +// ### Other Supported Keys +// +// #### enum +// +// All types support an `enum` key, which lets you specify all the values the +// setting can take. `enum` may be an array of allowed values (of the specified +// type), or an array of objects with `value` and `description` properties, where +// the `value` is an allowed value, and the `description` is a descriptive string +// used in the settings view. +// +// In this example, the setting must be one of the 4 integers: +// +// ```coffee +// config: +// someSetting: +// type: 'integer' +// default: 4 +// enum: [2, 4, 6, 8] +// ``` +// +// In this example, the setting must be either 'foo' or 'bar', which are +// presented using the provided descriptions in the settings pane: +// +// ```coffee +// config: +// someSetting: +// type: 'string' +// default: 'foo' +// enum: [ +// {value: 'foo', description: 'Foo mode. You want this.'} +// {value: 'bar', description: 'Bar mode. Nobody wants that!'} +// ] +// ``` +// +// Usage: +// +// ```coffee +// atom.config.set('my-package.someSetting', '2') +// atom.config.get('my-package.someSetting') # -> 2 +// +// # will not set values outside of the enum values +// atom.config.set('my-package.someSetting', '3') +// atom.config.get('my-package.someSetting') # -> 2 +// +// # If it cannot be coerced, the value will not be set +// atom.config.set('my-package.someSetting', '4') +// atom.config.get('my-package.someSetting') # -> 4 +// ``` +// +// #### title and description +// +// The settings view will use the `title` and `description` keys to display your +// config setting in a readable way. By default the settings view humanizes your +// config key, so `someSetting` becomes `Some Setting`. In some cases, this is +// confusing for users, and a more descriptive title is useful. +// +// Descriptions will be displayed below the title in the settings view. +// +// For a group of config settings the humanized key or the title and the +// description are used for the group headline. +// +// ```coffee +// config: +// someSetting: +// title: 'Setting Magnitude' +// description: 'This will affect the blah and the other blah' +// type: 'integer' +// default: 4 +// ``` +// +// __Note__: You should strive to be so clear in your naming of the setting that +// you do not need to specify a title or description! +// +// Descriptions allow a subset of +// [Markdown formatting](https://help.github.com/articles/github-flavored-markdown/). +// Specifically, you may use the following in configuration setting descriptions: +// +// * **bold** - `**bold**` +// * *italics* - `*italics*` +// * [links](https://atom.io) - `[links](https://atom.io)` +// * `code spans` - `\`code spans\`` +// * line breaks - `line breaks
` +// * ~~strikethrough~~ - `~~strikethrough~~` +// +// #### order +// +// The settings view orders your settings alphabetically. You can override this +// ordering with the order key. +// +// ```coffee +// config: +// zSetting: +// type: 'integer' +// default: 4 +// order: 1 +// aSetting: +// type: 'integer' +// default: 4 +// order: 2 +// ``` +// +// ## Manipulating values outside your configuration schema +// +// It is possible to manipulate(`get`, `set`, `observe` etc) values that do not +// appear in your configuration schema. For example, if the config schema of the +// package 'some-package' is +// +// ```coffee +// config: +// someSetting: +// type: 'boolean' +// default: false +// ``` +// +// You can still do the following +// +// ```coffee +// let otherSetting = atom.config.get('some-package.otherSetting') +// atom.config.set('some-package.stillAnotherSetting', otherSetting * 5) +// ``` +// +// In other words, if a function asks for a `key-path`, that path doesn't have to +// be described in the config schema for the package or any package. However, as +// highlighted in the best practices section, you are advised against doing the +// above. +// +// ## Best practices +// +// * Don't depend on (or write to) configuration keys outside of your keypath. +// +class Config { + static addSchemaEnforcer (typeName, enforcerFunction) { + if (schemaEnforcers[typeName] == null) { schemaEnforcers[typeName] = [] } + return schemaEnforcers[typeName].push(enforcerFunction) + } + + static addSchemaEnforcers (filters) { + for (let typeName in filters) { + const functions = filters[typeName] + for (let name in functions) { + const enforcerFunction = functions[name] + this.addSchemaEnforcer(typeName, enforcerFunction) + } + } + } + + static executeSchemaEnforcers (keyPath, value, schema) { + let error = null + let types = schema.type + if (!Array.isArray(types)) { types = [types] } + for (let type of types) { + try { + const enforcerFunctions = schemaEnforcers[type].concat(schemaEnforcers['*']) + for (let enforcer of enforcerFunctions) { + // At some point in one's life, one must call upon an enforcer. + value = enforcer.call(this, keyPath, value, schema) + } + error = null + break + } catch (e) { + error = e + } + } + + if (error != null) { throw error } + return value + } + + // Created during initialization, available as `atom.config` + constructor (params = {}) { + this.clear() + this.initialize(params) + } + + initialize ({saveCallback, mainSource, projectHomeSchema}) { + if (saveCallback) { + this.saveCallback = saveCallback + } + if (mainSource) this.mainSource = mainSource + if (projectHomeSchema) { + this.schema.properties.core.properties.projectHome = projectHomeSchema + this.defaultSettings.core.projectHome = projectHomeSchema.default + } + } + + clear () { + this.emitter = new Emitter() + this.schema = { + type: 'object', + properties: {} + } + + this.defaultSettings = {} + this.settings = {} + this.projectSettings = {} + this.projectFile = null + + this.scopedSettingsStore = new ScopedPropertyStore() + + this.settingsLoaded = false + this.transactDepth = 0 + this.pendingOperations = [] + this.legacyScopeAliases = new Map() + this.requestSave = _.debounce(() => this.save(), 1) + } + + /* + Section: Config Subscription + */ + + // Essential: Add a listener for changes to a given key path. This is different + // than {::onDidChange} in that it will immediately call your callback with the + // current value of the config entry. + // + // ### Examples + // + // You might want to be notified when the themes change. We'll watch + // `core.themes` for changes + // + // ```coffee + // atom.config.observe 'core.themes', (value) -> + // # do stuff with value + // ``` + // + // * `keyPath` {String} name of the key to observe + // * `options` (optional) {Object} + // * `scope` (optional) {ScopeDescriptor} describing a path from + // the root of the syntax tree to a token. Get one by calling + // {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples. + // See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) + // for more information. + // * `callback` {Function} to call when the value of the key changes. + // * `value` the new value of the key + // + // Returns a {Disposable} with the following keys on which you can call + // `.dispose()` to unsubscribe. + observe (...args) { + let callback, keyPath, options, scopeDescriptor + if (args.length === 2) { + [keyPath, callback] = args + } else if ((args.length === 3) && (_.isString(args[0]) && _.isObject(args[1]))) { + [keyPath, options, callback] = args + scopeDescriptor = options.scope + } else { + console.error('An unsupported form of Config::observe is being used. See https://atom.io/docs/api/latest/Config for details') + return + } + + if (scopeDescriptor != null) { + return this.observeScopedKeyPath(scopeDescriptor, keyPath, callback) + } else { + return this.observeKeyPath(keyPath, options != null ? options : {}, callback) + } + } + + // Essential: Add a listener for changes to a given key path. If `keyPath` is + // not specified, your callback will be called on changes to any key. + // + // * `keyPath` (optional) {String} name of the key to observe. Must be + // specified if `scopeDescriptor` is specified. + // * `options` (optional) {Object} + // * `scope` (optional) {ScopeDescriptor} describing a path from + // the root of the syntax tree to a token. Get one by calling + // {editor.getLastCursor().getScopeDescriptor()}. See {::get} for examples. + // See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) + // for more information. + // * `callback` {Function} to call when the value of the key changes. + // * `event` {Object} + // * `newValue` the new value of the key + // * `oldValue` the prior value of the key. + // + // Returns a {Disposable} with the following keys on which you can call + // `.dispose()` to unsubscribe. + onDidChange (...args) { + let callback, keyPath, scopeDescriptor + if (args.length === 1) { + [callback] = args + } else if (args.length === 2) { + [keyPath, callback] = args + } else { + let options; + [keyPath, options, callback] = args + scopeDescriptor = options.scope + } + + if (scopeDescriptor != null) { + return this.onDidChangeScopedKeyPath(scopeDescriptor, keyPath, callback) + } else { + return this.onDidChangeKeyPath(keyPath, callback) + } + } + + /* + Section: Managing Settings + */ + + // Essential: Retrieves the setting for the given key. + // + // ### Examples + // + // You might want to know what themes are enabled, so check `core.themes` + // + // ```coffee + // atom.config.get('core.themes') + // ``` + // + // With scope descriptors you can get settings within a specific editor + // scope. For example, you might want to know `editor.tabLength` for ruby + // files. + // + // ```coffee + // atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 2 + // ``` + // + // This setting in ruby files might be different than the global tabLength setting + // + // ```coffee + // atom.config.get('editor.tabLength') # => 4 + // atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 2 + // ``` + // + // You can get the language scope descriptor via + // {TextEditor::getRootScopeDescriptor}. This will get the setting specifically + // for the editor's language. + // + // ```coffee + // atom.config.get('editor.tabLength', scope: @editor.getRootScopeDescriptor()) # => 2 + // ``` + // + // Additionally, you can get the setting at the specific cursor position. + // + // ```coffee + // scopeDescriptor = @editor.getLastCursor().getScopeDescriptor() + // atom.config.get('editor.tabLength', scope: scopeDescriptor) # => 2 + // ``` + // + // * `keyPath` The {String} name of the key to retrieve. + // * `options` (optional) {Object} + // * `sources` (optional) {Array} of {String} source names. If provided, only + // values that were associated with these sources during {::set} will be used. + // * `excludeSources` (optional) {Array} of {String} source names. If provided, + // values that were associated with these sources during {::set} will not + // be used. + // * `scope` (optional) {ScopeDescriptor} describing a path from + // the root of the syntax tree to a token. Get one by calling + // {editor.getLastCursor().getScopeDescriptor()} + // See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) + // for more information. + // + // Returns the value from Atom's default settings, the user's configuration + // file in the type specified by the configuration schema. + get (...args) { + let keyPath, options, scope + if (args.length > 1) { + if ((typeof args[0] === 'string') || (args[0] == null)) { + [keyPath, options] = args; + ({scope} = options) + } + } else { + [keyPath] = args + } + + if (scope != null) { + const value = this.getRawScopedValue(scope, keyPath, options) + return value != null ? value : this.getRawValue(keyPath, options) + } else { + return this.getRawValue(keyPath, options) + } + } + + // Extended: Get all of the values for the given key-path, along with their + // associated scope selector. + // + // * `keyPath` The {String} name of the key to retrieve + // * `options` (optional) {Object} see the `options` argument to {::get} + // + // Returns an {Array} of {Object}s with the following keys: + // * `scopeDescriptor` The {ScopeDescriptor} with which the value is associated + // * `value` The value for the key-path + getAll (keyPath, options) { + let globalValue, result, scope + if (options != null) { ({scope} = options) } + + if (scope != null) { + let legacyScopeDescriptor + const scopeDescriptor = ScopeDescriptor.fromObject(scope) + result = this.scopedSettingsStore.getAll( + scopeDescriptor.getScopeChain(), + keyPath, + options + ) + legacyScopeDescriptor = this.getLegacyScopeDescriptorForNewScopeDescriptor(scopeDescriptor) + if (legacyScopeDescriptor) { + result.push(...Array.from(this.scopedSettingsStore.getAll( + legacyScopeDescriptor.getScopeChain(), + keyPath, + options + ) || [])) + } + } else { + result = [] + } + + globalValue = this.getRawValue(keyPath, options) + if (globalValue) { + result.push({scopeSelector: '*', value: globalValue}) + } + + return result + } + + // Essential: Sets the value for a configuration setting. + // + // This value is stored in Atom's internal configuration file. + // + // ### Examples + // + // You might want to change the themes programmatically: + // + // ```coffee + // atom.config.set('core.themes', ['atom-light-ui', 'atom-light-syntax']) + // ``` + // + // You can also set scoped settings. For example, you might want change the + // `editor.tabLength` only for ruby files. + // + // ```coffee + // atom.config.get('editor.tabLength') # => 4 + // atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 4 + // atom.config.get('editor.tabLength', scope: ['source.js']) # => 4 + // + // # Set ruby to 2 + // atom.config.set('editor.tabLength', 2, scopeSelector: '.source.ruby') # => true + // + // # Notice it's only set to 2 in the case of ruby + // atom.config.get('editor.tabLength') # => 4 + // atom.config.get('editor.tabLength', scope: ['source.ruby']) # => 2 + // atom.config.get('editor.tabLength', scope: ['source.js']) # => 4 + // ``` + // + // * `keyPath` The {String} name of the key. + // * `value` The value of the setting. Passing `undefined` will revert the + // setting to the default value. + // * `options` (optional) {Object} + // * `scopeSelector` (optional) {String}. eg. '.source.ruby' + // See [the scopes docs](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) + // for more information. + // * `source` (optional) {String} The name of a file with which the setting + // is associated. Defaults to the user's config file. + // + // Returns a {Boolean} + // * `true` if the value was set. + // * `false` if the value was not able to be coerced to the type specified in the setting's schema. + set (...args) { + let [keyPath, value, options = {}] = args + + if (!this.settingsLoaded) { + this.pendingOperations.push(() => this.set(keyPath, value, options)) + } + + const scopeSelector = options.scopeSelector + let source = options.source + const shouldSave = options.save != null ? options.save : true + + if (source && !scopeSelector && source !== this.projectFile) { + throw new Error("::set with a 'source' and no 'sourceSelector' is not yet implemented!") + } + + if (!source) source = this.mainSource + + if (value !== undefined) { + try { + value = this.makeValueConformToSchema(keyPath, value) + } catch (e) { + return false + } + } + + if (scopeSelector != null) { + this.setRawScopedValue(keyPath, value, source, scopeSelector) + } else { + this.setRawValue(keyPath, value, {source}) + } + + if (source === this.mainSource && shouldSave && this.settingsLoaded) { + this.requestSave() + } + return true + } + + // Essential: Restore the setting at `keyPath` to its default value. + // + // * `keyPath` The {String} name of the key. + // * `options` (optional) {Object} + // * `scopeSelector` (optional) {String}. See {::set} + // * `source` (optional) {String}. See {::set} + unset (keyPath, options) { + if (!this.settingsLoaded) { + this.pendingOperations.push(() => this.unset(keyPath, options)) + } + + let {scopeSelector, source} = options != null ? options : {} + if (source == null) { source = this.mainSource } + + if (scopeSelector != null) { + if (keyPath != null) { + let settings = this.scopedSettingsStore.propertiesForSourceAndSelector(source, scopeSelector) + if (getValueAtKeyPath(settings, keyPath) != null) { + this.scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector) + setValueAtKeyPath(settings, keyPath, undefined) + settings = withoutEmptyObjects(settings) + if (settings != null) { + this.set(null, settings, {scopeSelector, source, priority: this.priorityForSource(source)}) + } + + const configIsReady = (source === this.mainSource) && this.settingsLoaded + if (configIsReady) { + return this.requestSave() + } + } + } else { + this.scopedSettingsStore.removePropertiesForSourceAndSelector(source, scopeSelector) + return this.emitChangeEvent() + } + } else { + for (scopeSelector in this.scopedSettingsStore.propertiesForSource(source)) { + this.unset(keyPath, {scopeSelector, source}) + } + if ((keyPath != null) && (source === this.mainSource)) { + return this.set(keyPath, getValueAtKeyPath(this.defaultSettings, keyPath)) + } + } + } + + // Extended: Get an {Array} of all of the `source` {String}s with which + // settings have been added via {::set}. + getSources () { + return _.uniq(_.pluck(this.scopedSettingsStore.propertySets, 'source')).sort() + } + + // Extended: Retrieve the schema for a specific key path. The schema will tell + // you what type the keyPath expects, and other metadata about the config + // option. + // + // * `keyPath` The {String} name of the key. + // + // Returns an {Object} eg. `{type: 'integer', default: 23, minimum: 1}`. + // Returns `null` when the keyPath has no schema specified, but is accessible + // from the root schema. + getSchema (keyPath) { + const keys = splitKeyPath(keyPath) + let { schema } = this + for (let key of keys) { + let childSchema + if (schema.type === 'object') { + childSchema = schema.properties != null ? schema.properties[key] : undefined + if (childSchema == null) { + if (isPlainObject(schema.additionalProperties)) { + childSchema = schema.additionalProperties + } else if (schema.additionalProperties === false) { + return null + } else { + return {type: 'any'} + } + } + } else { + return null + } + schema = childSchema + } + return schema + } + + getUserConfigPath () { + return this.mainSource + } + + // Extended: Suppress calls to handler functions registered with {::onDidChange} + // and {::observe} for the duration of `callback`. After `callback` executes, + // handlers will be called once if the value for their key-path has changed. + // + // * `callback` {Function} to execute while suppressing calls to handlers. + transact (callback) { + this.beginTransaction() + try { + return callback() + } finally { + this.endTransaction() + } + } + + getLegacyScopeDescriptorForNewScopeDescriptor (scopeDescriptor) { + return null + } + + /* + Section: Internal methods used by core + */ + + // Private: Suppress calls to handler functions registered with {::onDidChange} + // and {::observe} for the duration of the {Promise} returned by `callback`. + // After the {Promise} is either resolved or rejected, handlers will be called + // once if the value for their key-path has changed. + // + // * `callback` {Function} that returns a {Promise}, which will be executed + // while suppressing calls to handlers. + // + // Returns a {Promise} that is either resolved or rejected according to the + // `{Promise}` returned by `callback`. If `callback` throws an error, a + // rejected {Promise} will be returned instead. + transactAsync (callback) { + let endTransaction + this.beginTransaction() + try { + endTransaction = fn => (...args) => { + this.endTransaction() + return fn(...args) + } + const result = callback() + return new Promise((resolve, reject) => { + return result.then(endTransaction(resolve)).catch(endTransaction(reject)) + }) + } catch (error) { + this.endTransaction() + return Promise.reject(error) + } + } + + beginTransaction () { + this.transactDepth++ + } + + endTransaction () { + this.transactDepth-- + this.emitChangeEvent() + } + + pushAtKeyPath (keyPath, value) { + const left = this.get(keyPath) + const arrayValue = (left == null ? [] : left) + const result = arrayValue.push(value) + this.set(keyPath, arrayValue) + return result + } + + unshiftAtKeyPath (keyPath, value) { + const left = this.get(keyPath) + const arrayValue = (left == null ? [] : left) + const result = arrayValue.unshift(value) + this.set(keyPath, arrayValue) + return result + } + + removeAtKeyPath (keyPath, value) { + const left = this.get(keyPath) + const arrayValue = (left == null ? [] : left) + const result = _.remove(arrayValue, value) + this.set(keyPath, arrayValue) + return result + } + + setSchema (keyPath, schema) { + if (!isPlainObject(schema)) { + throw new Error(`Error loading schema for ${keyPath}: schemas can only be objects!`) + } + + if (schema.type == null) { + throw new Error(`Error loading schema for ${keyPath}: schema objects must have a type attribute`) + } + + let rootSchema = this.schema + if (keyPath) { + for (let key of splitKeyPath(keyPath)) { + rootSchema.type = 'object' + if (rootSchema.properties == null) { rootSchema.properties = {} } + const { properties } = rootSchema + if (properties[key] == null) { properties[key] = {} } + rootSchema = properties[key] + } + } + + Object.assign(rootSchema, schema) + this.transact(() => { + this.setDefaults(keyPath, this.extractDefaultsFromSchema(schema)) + this.setScopedDefaultsFromSchema(keyPath, schema) + this.resetSettingsForSchemaChange() + }) + } + + save () { + if (this.saveCallback) { + let allSettings = {'*': this.settings} + allSettings = Object.assign(allSettings, this.scopedSettingsStore.propertiesForSource(this.mainSource)) + allSettings = sortObject(allSettings) + this.saveCallback(allSettings) + } + } + + /* + Section: Private methods managing global settings + */ + + resetUserSettings (newSettings, options = {}) { + this._resetSettings(newSettings, options) + } + + _resetSettings (newSettings, options = {}) { + const source = options.source + newSettings = Object.assign({}, newSettings) + if (newSettings.global != null) { + newSettings['*'] = newSettings.global + delete newSettings.global + } + + if (newSettings['*'] != null) { + const scopedSettings = newSettings + newSettings = newSettings['*'] + delete scopedSettings['*'] + this.resetScopedSettings(scopedSettings, {source}) + } + + return this.transact(() => { + this._clearUnscopedSettingsForSource(source) + this.settingsLoaded = true + for (let key in newSettings) { + const value = newSettings[key] + this.set(key, value, {save: false, source}) + } + if (this.pendingOperations.length) { + for (let op of this.pendingOperations) { op() } + this.pendingOperations = [] + } + }) + } + + _clearUnscopedSettingsForSource (source) { + if (source === this.projectFile) { + this.projectSettings = {} + } else { + this.settings = {} + } + } + + resetProjectSettings (newSettings, projectFile) { + // Sets the scope and source of all project settings to `path`. + newSettings = Object.assign({}, newSettings) + const oldProjectFile = this.projectFile + this.projectFile = projectFile + if (this.projectFile != null) { + this._resetSettings(newSettings, {source: this.projectFile}) + } else { + this.scopedSettingsStore.removePropertiesForSource(oldProjectFile) + this.projectSettings = {} + } + } + + clearProjectSettings () { + this.resetProjectSettings({}, null) + } + + getRawValue (keyPath, options = {}) { + let value + if (!options.excludeSources || !options.excludeSources.includes(this.mainSource)) { + value = getValueAtKeyPath(this.settings, keyPath) + if (this.projectFile != null) { + const projectValue = getValueAtKeyPath(this.projectSettings, keyPath) + value = (projectValue === undefined) ? value : projectValue + } + } + + let defaultValue + if (!options.sources || options.sources.length === 0) { + defaultValue = getValueAtKeyPath(this.defaultSettings, keyPath) + } + + if (value != null) { + value = this.deepClone(value) + if (isPlainObject(value) && isPlainObject(defaultValue)) { + this.deepDefaults(value, defaultValue) + } + return value + } else { + return this.deepClone(defaultValue) + } + } + + setRawValue (keyPath, value, options = {}) { + const source = options.source ? options.source : undefined + const settingsToChange = source === this.projectFile ? 'projectSettings' : 'settings' + const defaultValue = getValueAtKeyPath(this.defaultSettings, keyPath) + + if (_.isEqual(defaultValue, value)) { + if (keyPath != null) { + deleteValueAtKeyPath(this[settingsToChange], keyPath) + } else { + this[settingsToChange] = null + } + } else { + if (keyPath != null) { + setValueAtKeyPath(this[settingsToChange], keyPath, value) + } else { + this[settingsToChange] = value + } + } + return this.emitChangeEvent() + } + + observeKeyPath (keyPath, options, callback) { + callback(this.get(keyPath)) + return this.onDidChangeKeyPath(keyPath, event => callback(event.newValue)) + } + + onDidChangeKeyPath (keyPath, callback) { + let oldValue = this.get(keyPath) + return this.emitter.on('did-change', () => { + const newValue = this.get(keyPath) + if (!_.isEqual(oldValue, newValue)) { + const event = {oldValue, newValue} + oldValue = newValue + return callback(event) + } + }) + } + + isSubKeyPath (keyPath, subKeyPath) { + if ((keyPath == null) || (subKeyPath == null)) { return false } + const pathSubTokens = splitKeyPath(subKeyPath) + const pathTokens = splitKeyPath(keyPath).slice(0, pathSubTokens.length) + return _.isEqual(pathTokens, pathSubTokens) + } + + setRawDefault (keyPath, value) { + setValueAtKeyPath(this.defaultSettings, keyPath, value) + return this.emitChangeEvent() + } + + setDefaults (keyPath, defaults) { + if ((defaults != null) && isPlainObject(defaults)) { + const keys = splitKeyPath(keyPath) + this.transact(() => { + const result = [] + for (let key in defaults) { + const childValue = defaults[key] + if (!defaults.hasOwnProperty(key)) { continue } + result.push(this.setDefaults(keys.concat([key]).join('.'), childValue)) + } + return result + }) + } else { + try { + defaults = this.makeValueConformToSchema(keyPath, defaults) + this.setRawDefault(keyPath, defaults) + } catch (e) { + console.warn(`'${keyPath}' could not set the default. Attempted default: ${JSON.stringify(defaults)}; Schema: ${JSON.stringify(this.getSchema(keyPath))}`) + } + } + } + + deepClone (object) { + if (object instanceof Color) { + return object.clone() + } else if (Array.isArray(object)) { + return object.map(value => this.deepClone(value)) + } else if (isPlainObject(object)) { + return _.mapObject(object, (key, value) => [key, this.deepClone(value)]) + } else { + return object + } + } + + deepDefaults (target) { + let result = target + let i = 0 + while (++i < arguments.length) { + const object = arguments[i] + if (isPlainObject(result) && isPlainObject(object)) { + for (let key of Object.keys(object)) { + result[key] = this.deepDefaults(result[key], object[key]) + } + } else { + if ((result == null)) { + result = this.deepClone(object) + } + } + } + return result + } + + // `schema` will look something like this + // + // ```coffee + // type: 'string' + // default: 'ok' + // scopes: + // '.source.js': + // default: 'omg' + // ``` + setScopedDefaultsFromSchema (keyPath, schema) { + if ((schema.scopes != null) && isPlainObject(schema.scopes)) { + const scopedDefaults = {} + for (let scope in schema.scopes) { + const scopeSchema = schema.scopes[scope] + if (!scopeSchema.hasOwnProperty('default')) { continue } + scopedDefaults[scope] = {} + setValueAtKeyPath(scopedDefaults[scope], keyPath, scopeSchema.default) + } + this.scopedSettingsStore.addProperties('schema-default', scopedDefaults) + } + + if ((schema.type === 'object') && (schema.properties != null) && isPlainObject(schema.properties)) { + const keys = splitKeyPath(keyPath) + for (let key in schema.properties) { + const childValue = schema.properties[key] + if (!schema.properties.hasOwnProperty(key)) { continue } + this.setScopedDefaultsFromSchema(keys.concat([key]).join('.'), childValue) + } + } + } + + extractDefaultsFromSchema (schema) { + if (schema.default != null) { + return schema.default + } else if ((schema.type === 'object') && (schema.properties != null) && isPlainObject(schema.properties)) { + const defaults = {} + const properties = schema.properties || {} + for (let key in properties) { const value = properties[key]; defaults[key] = this.extractDefaultsFromSchema(value) } + return defaults + } + } + + makeValueConformToSchema (keyPath, value, options) { + if (options != null ? options.suppressException : undefined) { + try { + return this.makeValueConformToSchema(keyPath, value) + } catch (e) { + return undefined + } + } else { + let schema + if ((schema = this.getSchema(keyPath)) == null) { + if (schema === false) { throw new Error(`Illegal key path ${keyPath}`) } + } + return this.constructor.executeSchemaEnforcers(keyPath, value, schema) + } + } + + // When the schema is changed / added, there may be values set in the config + // that do not conform to the schema. This will reset make them conform. + resetSettingsForSchemaChange (source) { + if (source == null) { source = this.mainSource } + return this.transact(() => { + this.settings = this.makeValueConformToSchema(null, this.settings, {suppressException: true}) + const selectorsAndSettings = this.scopedSettingsStore.propertiesForSource(source) + this.scopedSettingsStore.removePropertiesForSource(source) + for (let scopeSelector in selectorsAndSettings) { + let settings = selectorsAndSettings[scopeSelector] + settings = this.makeValueConformToSchema(null, settings, {suppressException: true}) + this.setRawScopedValue(null, settings, source, scopeSelector) + } + }) + } + + /* + Section: Private Scoped Settings + */ + + priorityForSource (source) { + switch (source) { + case this.mainSource: + return 1000 + case this.projectFile: + return 2000 + default: + return 0 + } + } + + emitChangeEvent () { + if (this.transactDepth <= 0) { return this.emitter.emit('did-change') } + } + + resetScopedSettings (newScopedSettings, options = {}) { + const source = options.source == null ? this.mainSource : options.source + const priority = this.priorityForSource(source) + this.scopedSettingsStore.removePropertiesForSource(source) + + for (let scopeSelector in newScopedSettings) { + let settings = newScopedSettings[scopeSelector] + settings = this.makeValueConformToSchema(null, settings, {suppressException: true}) + const validatedSettings = {} + validatedSettings[scopeSelector] = withoutEmptyObjects(settings) + if (validatedSettings[scopeSelector] != null) { this.scopedSettingsStore.addProperties(source, validatedSettings, {priority}) } + } + + return this.emitChangeEvent() + } + + setRawScopedValue (keyPath, value, source, selector, options) { + if (keyPath != null) { + const newValue = {} + setValueAtKeyPath(newValue, keyPath, value) + value = newValue + } + + const settingsBySelector = {} + settingsBySelector[selector] = value + this.scopedSettingsStore.addProperties(source, settingsBySelector, {priority: this.priorityForSource(source)}) + return this.emitChangeEvent() + } + + getRawScopedValue (scopeDescriptor, keyPath, options) { + scopeDescriptor = ScopeDescriptor.fromObject(scopeDescriptor) + const result = this.scopedSettingsStore.getPropertyValue( + scopeDescriptor.getScopeChain(), + keyPath, + options + ) + + const legacyScopeDescriptor = this.getLegacyScopeDescriptorForNewScopeDescriptor(scopeDescriptor) + if (result != null) { + return result + } else if (legacyScopeDescriptor) { + return this.scopedSettingsStore.getPropertyValue( + legacyScopeDescriptor.getScopeChain(), + keyPath, + options + ) + } + } + + observeScopedKeyPath (scope, keyPath, callback) { + callback(this.get(keyPath, {scope})) + return this.onDidChangeScopedKeyPath(scope, keyPath, event => callback(event.newValue)) + } + + onDidChangeScopedKeyPath (scope, keyPath, callback) { + let oldValue = this.get(keyPath, {scope}) + return this.emitter.on('did-change', () => { + const newValue = this.get(keyPath, {scope}) + if (!_.isEqual(oldValue, newValue)) { + const event = {oldValue, newValue} + oldValue = newValue + callback(event) + } + }) + } +}; + +// Base schema enforcers. These will coerce raw input into the specified type, +// and will throw an error when the value cannot be coerced. Throwing the error +// will indicate that the value should not be set. +// +// Enforcers are run from most specific to least. For a schema with type +// `integer`, all the enforcers for the `integer` type will be run first, in +// order of specification. Then the `*` enforcers will be run, in order of +// specification. +Config.addSchemaEnforcers({ + 'any': { + coerce (keyPath, value, schema) { + return value + } + }, + + 'integer': { + coerce (keyPath, value, schema) { + value = parseInt(value) + if (isNaN(value) || !isFinite(value)) { throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} cannot be coerced into an int`) } + return value + } + }, + + 'number': { + coerce (keyPath, value, schema) { + value = parseFloat(value) + if (isNaN(value) || !isFinite(value)) { throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} cannot be coerced into a number`) } + return value + } + }, + + 'boolean': { + coerce (keyPath, value, schema) { + switch (typeof value) { + case 'string': + if (value.toLowerCase() === 'true') { + return true + } else if (value.toLowerCase() === 'false') { + return false + } else { + throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} must be a boolean or the string 'true' or 'false'`) + } + case 'boolean': + return value + default: + throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} must be a boolean or the string 'true' or 'false'`) + } + } + }, + + 'string': { + validate (keyPath, value, schema) { + if (typeof value !== 'string') { + throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} must be a string`) + } + return value + }, + + validateMaximumLength (keyPath, value, schema) { + if ((typeof schema.maximumLength === 'number') && (value.length > schema.maximumLength)) { + return value.slice(0, schema.maximumLength) + } else { + return value + } + } + }, + + 'null': { + // null sort of isnt supported. It will just unset in this case + coerce (keyPath, value, schema) { + if (![undefined, null].includes(value)) { throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} must be null`) } + return value + } + }, + + 'object': { + coerce (keyPath, value, schema) { + if (!isPlainObject(value)) { throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} must be an object`) } + if (schema.properties == null) { return value } + + let defaultChildSchema = null + let allowsAdditionalProperties = true + if (isPlainObject(schema.additionalProperties)) { + defaultChildSchema = schema.additionalProperties + } + if (schema.additionalProperties === false) { + allowsAdditionalProperties = false + } + + const newValue = {} + for (let prop in value) { + const propValue = value[prop] + const childSchema = schema.properties[prop] != null ? schema.properties[prop] : defaultChildSchema + if (childSchema != null) { + try { + newValue[prop] = this.executeSchemaEnforcers(pushKeyPath(keyPath, prop), propValue, childSchema) + } catch (error) { + console.warn(`Error setting item in object: ${error.message}`) + } + } else if (allowsAdditionalProperties) { + // Just pass through un-schema'd values + newValue[prop] = propValue + } else { + console.warn(`Illegal object key: ${keyPath}.${prop}`) + } + } + + return newValue + } + }, + + 'array': { + coerce (keyPath, value, schema) { + if (!Array.isArray(value)) { throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} must be an array`) } + const itemSchema = schema.items + if (itemSchema != null) { + const newValue = [] + for (let item of value) { + try { + newValue.push(this.executeSchemaEnforcers(keyPath, item, itemSchema)) + } catch (error) { + console.warn(`Error setting item in array: ${error.message}`) + } + } + return newValue + } else { + return value + } + } + }, + + 'color': { + coerce (keyPath, value, schema) { + const color = Color.parse(value) + if (color == null) { + throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} cannot be coerced into a color`) + } + return color + } + }, + + '*': { + coerceMinimumAndMaximum (keyPath, value, schema) { + if (typeof value !== 'number') { return value } + if ((schema.minimum != null) && (typeof schema.minimum === 'number')) { + value = Math.max(value, schema.minimum) + } + if ((schema.maximum != null) && (typeof schema.maximum === 'number')) { + value = Math.min(value, schema.maximum) + } + return value + }, + + validateEnum (keyPath, value, schema) { + let possibleValues = schema.enum + + if (Array.isArray(possibleValues)) { + possibleValues = possibleValues.map(value => { + if (value.hasOwnProperty('value')) { return value.value } else { return value } + }) + } + + if ((possibleValues == null) || !Array.isArray(possibleValues) || !possibleValues.length) { return value } + + for (let possibleValue of possibleValues) { + // Using `isEqual` for possibility of placing enums on array and object schemas + if (_.isEqual(possibleValue, value)) { return value } + } + + throw new Error(`Validation failed at ${keyPath}, ${JSON.stringify(value)} is not one of ${JSON.stringify(possibleValues)}`) + } + } +}) + +let isPlainObject = value => _.isObject(value) && !Array.isArray(value) && !_.isFunction(value) && !_.isString(value) && !(value instanceof Color) + +let sortObject = value => { + if (!isPlainObject(value)) { return value } + const result = {} + for (let key of Object.keys(value).sort()) { + result[key] = sortObject(value[key]) + } + return result +} + +const withoutEmptyObjects = (object) => { + let resultObject + if (isPlainObject(object)) { + for (let key in object) { + const value = object[key] + const newValue = withoutEmptyObjects(value) + if (newValue != null) { + if (resultObject == null) { resultObject = {} } + resultObject[key] = newValue + } + } + } else { + resultObject = object + } + return resultObject +} + +module.exports = Config diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 10a7a3bdb..9cff5497b 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -5,6 +5,7 @@ fs = require 'fs-plus' {Disposable} = require 'event-kit' {remote} = require 'electron' MenuHelpers = require './menu-helpers' +{sortMenuItems} = require './menu-sort-helpers' platformContextMenu = require('../package.json')?._atomMenu?['context-menu'] @@ -149,7 +150,7 @@ class ContextMenuManager @pruneRedundantSeparators(template) @addAccelerators(template) - template + return @sortTemplate(template) # Adds an `accelerator` property to items that have key bindings. Electron # uses this property to surface the relevant keymaps in the context menu. @@ -175,6 +176,13 @@ class ContextMenuManager keepNextItemIfSeparator = true index++ + sortTemplate: (template) -> + template = sortMenuItems(template) + for id, item of template + if Array.isArray(item.submenu) + item.submenu = @sortTemplate(item.submenu) + return template + # Returns an object compatible with `::add()` or `null`. cloneItemForEvent: (item, event) -> return null if item.devMode and not @devMode diff --git a/src/cursor.js b/src/cursor.js index 41e47bb75..f75f94709 100644 --- a/src/cursor.js +++ b/src/cursor.js @@ -326,7 +326,9 @@ class Cursor extends Model { // Public: Moves the cursor to the bottom of the buffer. moveToBottom () { + const column = this.goalColumn this.setBufferPosition(this.editor.getEofBufferPosition()) + this.goalColumn = column } // Public: Moves the cursor to the beginning of the line. @@ -524,7 +526,7 @@ class Cursor extends Model { : new Range(new Point(position.row, 0), position) const ranges = this.editor.buffer.findAllInRangeSync( - options.wordRegex || this.wordRegExp(), + options.wordRegex || this.wordRegExp(options), scanRange ) @@ -556,7 +558,7 @@ class Cursor extends Model { : new Range(position, new Point(position.row, Infinity)) const ranges = this.editor.buffer.findAllInRangeSync( - options.wordRegex || this.wordRegExp(), + options.wordRegex || this.wordRegExp(options), scanRange ) @@ -664,7 +666,7 @@ class Cursor extends Model { // Returns a {RegExp}. wordRegExp (options) { const nonWordCharacters = _.escapeRegExp(this.getNonWordCharacters()) - let source = `^[\t\r ]*$|[^\\s${nonWordCharacters}]+` + let source = `^[\t ]*$|[^\\s${nonWordCharacters}]+` if (!options || options.includeNonWordCharacters !== false) { source += `|${`[${nonWordCharacters}]+`}` } @@ -711,6 +713,7 @@ class Cursor extends Model { changePosition (options, fn) { this.clearSelection({autoscroll: false}) fn() + this.goalColumn = null const autoscroll = (options && options.autoscroll != null) ? options.autoscroll : this.isLastCursor() diff --git a/src/decoration.js b/src/decoration.js index 731935506..69bbcaa19 100644 --- a/src/decoration.js +++ b/src/decoration.js @@ -1,4 +1,3 @@ -const _ = require('underscore-plus') const {Emitter} = require('event-kit') let idCounter = 0 @@ -49,7 +48,7 @@ class Decoration { // 'line-number' is a 'gutter', but a 'gutter' is not a 'line-number'. static isType (decorationProperties, type) { // 'line-number' is a special case of 'gutter'. - if (_.isArray(decorationProperties.type)) { + if (Array.isArray(decorationProperties.type)) { if (decorationProperties.type.includes(type)) { return true } @@ -158,7 +157,7 @@ class Decoration { // ## Examples // // ```coffee - // decoration.update({type: 'line-number', class: 'my-new-class'}) + // decoration.setProperties({type: 'line-number', class: 'my-new-class'}) // ``` // // * `newProperties` {Object} eg. `{type: 'line-number', class: 'my-new-class'}` diff --git a/src/deserializer-manager.js b/src/deserializer-manager.js index f5f2e6429..72ed9485d 100644 --- a/src/deserializer-manager.js +++ b/src/deserializer-manager.js @@ -1,6 +1,4 @@ -/** @babel */ - -import {Disposable} from 'event-kit' +const {Disposable} = require('event-kit') // Extended: Manages the deserializers used for serialized state // @@ -21,7 +19,8 @@ import {Disposable} from 'event-kit' // serialize: -> // @state // ``` -export default class DeserializerManager { +module.exports = +class DeserializerManager { constructor (atomEnvironment) { this.atomEnvironment = atomEnvironment this.deserializers = {} @@ -34,7 +33,7 @@ export default class DeserializerManager { // common approach is to register a *constructor* as the deserializer for its // instances by adding a `.deserialize()` class method. When your method is // called, it will be passed serialized state as the first argument and the - // {Atom} environment object as the second argument, which is useful if you + // {AtomEnvironment} object as the second argument, which is useful if you // wish to avoid referencing the `atom` global. add (...deserializers) { for (let i = 0; i < deserializers.length; i++) { diff --git a/src/dock.js b/src/dock.js index 7f2856800..dc77365fb 100644 --- a/src/dock.js +++ b/src/dock.js @@ -1,11 +1,11 @@ -'use strict' - +const etch = require('etch') const _ = require('underscore-plus') const {CompositeDisposable, Emitter} = require('event-kit') const PaneContainer = require('./pane-container') const TextEditor = require('./text-editor') const Grim = require('grim') +const $ = etch.dom const MINIMUM_SIZE = 100 const DEFAULT_INITIAL_SIZE = 300 const SHOULD_ANIMATE_CLASS = 'atom-dock-should-animate' @@ -26,6 +26,8 @@ module.exports = class Dock { this.handleMouseUp = this.handleMouseUp.bind(this) this.handleDrag = _.throttle(this.handleDrag.bind(this), 30) this.handleDragEnd = this.handleDragEnd.bind(this) + this.handleToggleButtonDragEnter = this.handleToggleButtonDragEnter.bind(this) + this.toggle = this.toggle.bind(this) this.location = params.location this.widthOrHeight = getWidthOrHeight(this.location) @@ -72,11 +74,16 @@ module.exports = class Dock { // This method is called explicitly by the object which adds the Dock to the document. elementAttached () { // Re-render when the dock is attached to make sure we remeasure sizes defined in CSS. - this.render(this.state) + etch.updateSync(this) } getElement () { - if (!this.element) this.render(this.state) + // Because this code is included in the snapshot, we have to make sure we don't touch the DOM + // during initialization. Therefore, we defer initialization of the component (which creates a + // DOM element) until somebody asks for the element. + if (this.element == null) { + etch.initialize(this) + } return this.element } @@ -151,88 +158,94 @@ module.exports = class Dock { } this.state = nextState - this.render(this.state) - const {visible} = this.state + const {hovered, visible} = this.state + + // Render immediately if the dock becomes visible or the size changes in case people are + // measuring after opening, for example. + if (this.element != null) { + if ((visible && !prevState.visible) || (this.state.size !== prevState.size)) etch.updateSync(this) + else etch.update(this) + } + + if (hovered !== prevState.hovered) { + this.emitter.emit('did-change-hovered', hovered) + } if (visible !== prevState.visible) { this.emitter.emit('did-change-visible', visible) } } - render (state) { - if (this.element == null) { - this.element = document.createElement('atom-dock') - this.element.classList.add(this.location) - this.innerElement = document.createElement('div') - this.innerElement.classList.add('atom-dock-inner', this.location) - this.maskElement = document.createElement('div') - this.maskElement.classList.add('atom-dock-mask') - this.wrapperElement = document.createElement('div') - this.wrapperElement.classList.add('atom-dock-content-wrapper', this.location) - this.resizeHandle = new DockResizeHandle({ - location: this.location, - onResizeStart: this.handleResizeHandleDragStart, - onResizeToFit: this.handleResizeToFit - }) - this.toggleButton = new DockToggleButton({ - onDragEnter: this.handleToggleButtonDragEnter.bind(this), - location: this.location, - toggle: this.toggle.bind(this) - }) - this.cursorOverlayElement = document.createElement('div') - this.cursorOverlayElement.classList.add('atom-dock-cursor-overlay', this.location) + render () { + const innerElementClassList = ['atom-dock-inner', this.location] + if (this.state.visible) innerElementClassList.push(VISIBLE_CLASS) - // Add the children to the DOM tree - this.element.appendChild(this.innerElement) - this.innerElement.appendChild(this.maskElement) - this.maskElement.appendChild(this.wrapperElement) - this.wrapperElement.appendChild(this.resizeHandle.getElement()) - this.wrapperElement.appendChild(this.paneContainer.getElement()) - this.wrapperElement.appendChild(this.cursorOverlayElement) - // The toggle button must be rendered outside the mask because (1) it shouldn't be masked and - // (2) if we made the mask larger to avoid masking it, the mask would block mouse events. - this.innerElement.appendChild(this.toggleButton.getElement()) - } + const maskElementClassList = ['atom-dock-mask'] + if (this.state.shouldAnimate) maskElementClassList.push(SHOULD_ANIMATE_CLASS) - if (state.visible) { - this.innerElement.classList.add(VISIBLE_CLASS) - } else { - this.innerElement.classList.remove(VISIBLE_CLASS) - } + const cursorOverlayElementClassList = ['atom-dock-cursor-overlay', this.location] + if (this.state.resizing) cursorOverlayElementClassList.push(CURSOR_OVERLAY_VISIBLE_CLASS) - if (state.shouldAnimate) { - this.maskElement.classList.add(SHOULD_ANIMATE_CLASS) - } else { - this.maskElement.classList.remove(SHOULD_ANIMATE_CLASS) - } - - if (state.resizing) { - this.cursorOverlayElement.classList.add(CURSOR_OVERLAY_VISIBLE_CLASS) - } else { - this.cursorOverlayElement.classList.remove(CURSOR_OVERLAY_VISIBLE_CLASS) - } - - const shouldBeVisible = state.visible || state.showDropTarget + const shouldBeVisible = this.state.visible || this.state.showDropTarget const size = Math.max(MINIMUM_SIZE, - state.size || - (state.draggingItem && getPreferredSize(state.draggingItem, this.location)) || + this.state.size || + (this.state.draggingItem && getPreferredSize(this.state.draggingItem, this.location)) || DEFAULT_INITIAL_SIZE ) // We need to change the size of the mask... - this.maskElement.style[this.widthOrHeight] = `${shouldBeVisible ? size : 0}px` + const maskStyle = {[this.widthOrHeight]: `${shouldBeVisible ? size : 0}px`} // ...but the content needs to maintain a constant size. - this.wrapperElement.style[this.widthOrHeight] = `${size}px` + const wrapperStyle = {[this.widthOrHeight]: `${size}px`} - this.resizeHandle.update({dockIsVisible: this.state.visible}) - this.toggleButton.update({ - dockIsVisible: shouldBeVisible, - visible: - // Don't show the toggle button if the dock is closed and empty... - (state.hovered && (this.state.visible || this.getPaneItems().length > 0)) || - // ...or if the item can't be dropped in that dock. - (!shouldBeVisible && state.draggingItem && isItemAllowed(state.draggingItem, this.location)) - }) + return $( + 'atom-dock', + {className: this.location}, + $.div( + {ref: 'innerElement', className: innerElementClassList.join(' ')}, + $.div( + { + className: maskElementClassList.join(' '), + style: maskStyle + }, + $.div( + { + ref: 'wrapperElement', + className: `atom-dock-content-wrapper ${this.location}`, + style: wrapperStyle + }, + $(DockResizeHandle, { + location: this.location, + onResizeStart: this.handleResizeHandleDragStart, + onResizeToFit: this.handleResizeToFit, + dockIsVisible: this.state.visible + }), + $(ElementComponent, {element: this.paneContainer.getElement()}), + $.div({className: cursorOverlayElementClassList.join(' ')}) + ) + ), + $(DockToggleButton, { + ref: 'toggleButton', + onDragEnter: this.state.draggingItem ? this.handleToggleButtonDragEnter : null, + location: this.location, + toggle: this.toggle, + dockIsVisible: shouldBeVisible, + visible: + // Don't show the toggle button if the dock is closed and empty... + (this.state.hovered && + (this.state.visible || this.getPaneItems().length > 0)) || + // ...or if the item can't be dropped in that dock. + (!shouldBeVisible && + this.state.draggingItem && + isItemAllowed(this.state.draggingItem, this.location)) + }) + ) + ) + } + + update (props) { + // Since we're interopping with non-etch stuff, this method's actually never called. + return etch.update(this) } handleDidAddPaneItem () { @@ -296,7 +309,7 @@ module.exports = class Dock { } handleDrag (event) { - if (!this.pointWithinHoverArea({x: event.pageX, y: event.pageY}, false)) { + if (!this.pointWithinHoverArea({x: event.pageX, y: event.pageY}, true)) { this.draggedOut() } } @@ -313,9 +326,13 @@ module.exports = class Dock { // Determine whether the cursor is within the dock hover area. This isn't as simple as just using // mouseenter/leave because we want to be a little more forgiving. For example, if the cursor is - // over the footer, we want to show the bottom dock's toggle button. - pointWithinHoverArea (point, includeButtonWidth = this.state.hovered) { - const dockBounds = this.innerElement.getBoundingClientRect() + // over the footer, we want to show the bottom dock's toggle button. Also note that our criteria + // for detecting entry are different than detecting exit but, in order for us to avoid jitter, the + // area considered when detecting exit MUST fully encompass the area considered when detecting + // entry. + pointWithinHoverArea (point, detectingExit) { + const dockBounds = this.refs.innerElement.getBoundingClientRect() + // Copy the bounds object since we can't mutate it. const bounds = { top: dockBounds.top, @@ -324,7 +341,20 @@ module.exports = class Dock { left: dockBounds.left } - // Include all panels that are closer to the edge than the dock in our calculations. + // To provide a minimum target, expand the area toward the center a bit. + switch (this.location) { + case 'right': + bounds.left = Math.min(bounds.left, bounds.right - 2) + break + case 'bottom': + bounds.top = Math.min(bounds.top, bounds.bottom - 1) + break + case 'left': + bounds.right = Math.max(bounds.right, bounds.left + 2) + break + } + + // Further expand the area to include all panels that are closer to the edge than the dock. switch (this.location) { case 'right': bounds.right = Number.POSITIVE_INFINITY @@ -337,23 +367,41 @@ module.exports = class Dock { break } - // The area used when detecting "leave" events is actually larger than when detecting entrances. - if (includeButtonWidth) { + // If we're in this area, we know we're within the hover area without having to take further + // measurements. + if (rectContainsPoint(bounds, point)) return true + + // If we're within the toggle button, we're definitely in the hover area. Unfortunately, we + // can't do this measurement conditionally (e.g. only if the toggle button is visible) because + // our knowledge of the toggle's button is incomplete due to CSS animations. (We may think the + // toggle button isn't visible when in actuality it is, but is animating to its hidden state.) + // + // Since `point` is always the current mouse position, one possible optimization would be to + // remove it as an argument and determine whether we're inside the toggle button using + // mouseenter/leave events on it. This class would still need to keep track of the mouse + // position (via a mousemove listener) for the other measurements, though. + const toggleButtonBounds = this.refs.toggleButton.getBounds() + if (rectContainsPoint(toggleButtonBounds, point)) return true + + // The area used when detecting exit is actually larger than when detecting entrances. Expand + // our bounds and recheck them. + if (detectingExit) { const hoverMargin = 20 - const {width, height} = this.toggleButton.getBounds() switch (this.location) { case 'right': - bounds.left -= width + hoverMargin + bounds.left = Math.min(bounds.left, toggleButtonBounds.left) - hoverMargin break case 'bottom': - bounds.top -= height + hoverMargin + bounds.top = Math.min(bounds.top, toggleButtonBounds.top) - hoverMargin break case 'left': - bounds.right += width + hoverMargin + bounds.right = Math.max(bounds.right, toggleButtonBounds.right) + hoverMargin break } + if (rectContainsPoint(bounds, point)) return true } - return rectContainsPoint(bounds, point) + + return false } getInitialSize () { @@ -574,6 +622,16 @@ module.exports = class Dock { return this.paneContainer.onDidDestroyPaneItem(callback) } + // Extended: Invoke the given callback when the hovered state of the dock changes. + // + // * `callback` {Function} to be called when the hovered state changes. + // * `hovered` {Boolean} Is the dock now hovered? + // + // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeHovered (callback) { + return this.emitter.on('did-change-hovered', callback) + } + /* Section: Pane Items */ @@ -659,13 +717,18 @@ module.exports = class Dock { class DockResizeHandle { constructor (props) { - this.handleMouseDown = this.handleMouseDown.bind(this) - - this.element = document.createElement('div') - this.element.classList.add('atom-dock-resize-handle', props.location) - this.element.addEventListener('mousedown', this.handleMouseDown) this.props = props - this.update(props) + etch.initialize(this) + } + + render () { + const classList = ['atom-dock-resize-handle', this.props.location] + if (this.props.dockIsVisible) classList.push(RESIZE_HANDLE_RESIZABLE_CLASS) + + return $.div({ + className: classList.join(' '), + on: {mousedown: this.handleMouseDown} + }) } getElement () { @@ -681,12 +744,7 @@ class DockResizeHandle { update (newProps) { this.props = Object.assign({}, this.props, newProps) - - if (this.props.dockIsVisible) { - this.element.classList.add(RESIZE_HANDLE_RESIZABLE_CLASS) - } else { - this.element.classList.remove(RESIZE_HANDLE_RESIZABLE_CLASS) - } + return etch.update(this) } handleMouseDown (event) { @@ -700,22 +758,34 @@ class DockResizeHandle { class DockToggleButton { constructor (props) { - this.handleClick = this.handleClick.bind(this) - this.handleDragEnter = this.handleDragEnter.bind(this) - - this.element = document.createElement('div') - this.element.classList.add('atom-dock-toggle-button', props.location) - this.element.classList.add(props.location) - this.innerElement = document.createElement('div') - this.innerElement.classList.add('atom-dock-toggle-button-inner', props.location) - this.innerElement.addEventListener('click', this.handleClick) - this.innerElement.addEventListener('dragenter', this.handleDragEnter) - this.iconElement = document.createElement('span') - this.innerElement.appendChild(this.iconElement) - this.element.appendChild(this.innerElement) - this.props = props - this.update(props) + etch.initialize(this) + } + + render () { + const classList = ['atom-dock-toggle-button', this.props.location] + if (this.props.visible) classList.push(TOGGLE_BUTTON_VISIBLE_CLASS) + + return $.div( + {className: classList.join(' ')}, + $.div( + { + ref: 'innerElement', + className: `atom-dock-toggle-button-inner ${this.props.location}`, + on: { + click: this.handleClick, + dragenter: this.props.onDragEnter + } + }, + $.span({ + ref: 'iconElement', + className: `icon ${getIconName( + this.props.location, + this.props.dockIsVisible + )}` + }) + ) + ) } getElement () { @@ -723,30 +793,28 @@ class DockToggleButton { } getBounds () { - if (this.bounds == null) { - this.bounds = this.element.getBoundingClientRect() - } - return this.bounds + return this.refs.innerElement.getBoundingClientRect() } update (newProps) { this.props = Object.assign({}, this.props, newProps) - - if (this.props.visible) { - this.element.classList.add(TOGGLE_BUTTON_VISIBLE_CLASS) - } else { - this.element.classList.remove(TOGGLE_BUTTON_VISIBLE_CLASS) - } - - this.iconElement.className = 'icon ' + getIconName(this.props.location, this.props.dockIsVisible) + return etch.update(this) } handleClick () { this.props.toggle() } +} - handleDragEnter () { - this.props.onDragEnter() +// An etch component that doesn't use etch, this component provides a gateway from JSX back into +// the mutable DOM world. +class ElementComponent { + constructor (props) { + this.element = props.element + } + + update (props) { + this.element = props.element } } diff --git a/src/git-repository.js b/src/git-repository.js index 55d70c12c..80f76e40a 100644 --- a/src/git-repository.js +++ b/src/git-repository.js @@ -163,7 +163,7 @@ class GitRepository { // Public: Invoke the given callback when a multiple files' statuses have // changed. For example, on window focus, the status of all the paths in the // repo is checked. If any of them have changed, this will be fired. Call - // {::getPathStatus(path)} to get the status for your path of choice. + // {::getPathStatus} to get the status for your path of choice. // // * `callback` {Function} // diff --git a/src/grammar-registry.js b/src/grammar-registry.js index b316bdbb0..0c79d5b1e 100644 --- a/src/grammar-registry.js +++ b/src/grammar-registry.js @@ -10,7 +10,6 @@ const Token = require('./token') const fs = require('fs-plus') const {Point, Range} = require('text-buffer') -const GRAMMAR_TYPE_BONUS = 1000 const PATH_SPLIT_REGEX = new RegExp('[/.]') // Extended: This class holds the grammars used for tokenizing. @@ -38,6 +37,14 @@ class GrammarRegistry { const grammarAddedOrUpdated = this.grammarAddedOrUpdated.bind(this) this.textmateRegistry.onDidAddGrammar(grammarAddedOrUpdated) this.textmateRegistry.onDidUpdateGrammar(grammarAddedOrUpdated) + + this.subscriptions.add(this.config.onDidChange('core.useTreeSitterParsers', () => { + this.grammarScoresByBuffer.forEach((score, buffer) => { + if (!this.languageOverridesByBufferId.has(buffer.id)) { + this.autoAssignLanguageMode(buffer) + } + }) + })) } serialize () { @@ -115,7 +122,6 @@ class GrammarRegistry { // found. assignLanguageMode (buffer, languageId) { if (buffer.getBuffer) buffer = buffer.getBuffer() - languageId = this.normalizeLanguageId(languageId) let grammar = null if (languageId != null) { @@ -128,13 +134,21 @@ class GrammarRegistry { } this.grammarScoresByBuffer.set(buffer, null) - if (grammar.scopeName !== buffer.getLanguageMode().getLanguageId()) { + if (grammar !== buffer.getLanguageMode().grammar) { buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer)) } return true } + // Extended: Get the `languageId` that has been explicitly assigned to + // to the given buffer, if any. + // + // Returns a {String} id of the language + getAssignedLanguageId (buffer) { + return this.languageOverridesByBufferId.get(buffer.id) + } + // Extended: Remove any language mode override that has been set for the // given {TextBuffer}. This will assign to the buffer the best language // mode available. @@ -147,14 +161,14 @@ class GrammarRegistry { ) this.languageOverridesByBufferId.delete(buffer.id) this.grammarScoresByBuffer.set(buffer, result.score) - if (result.grammar.scopeName !== buffer.getLanguageMode().getLanguageId()) { + if (result.grammar !== buffer.getLanguageMode().grammar) { buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(result.grammar, buffer)) } } languageModeForGrammarAndBuffer (grammar, buffer) { if (grammar instanceof TreeSitterGrammar) { - return new TreeSitterLanguageMode({grammar, buffer, config: this.config}) + return new TreeSitterLanguageMode({grammar, buffer, config: this.config, grammars: this}) } else { return new TextMateLanguageMode({grammar, buffer, config: this.config}) } @@ -193,16 +207,34 @@ class GrammarRegistry { contents = fs.readFileSync(filePath, 'utf8') } + // Initially identify matching grammars based on the filename and the first + // line of the file. let score = this.getGrammarPathScore(grammar, filePath) - if (score > 0 && !grammar.bundledPackage) { - score += 0.125 - } - if (this.grammarMatchesContents(grammar, contents)) { - score += 0.25 - } + if (this.grammarMatchesPrefix(grammar, contents)) score += 0.5 - if (score > 0 && this.isGrammarPreferredType(grammar)) { - score += GRAMMAR_TYPE_BONUS + // If multiple grammars match by one of the above criteria, break ties. + if (score > 0) { + // Prefer either TextMate or Tree-sitter grammars based on the user's settings. + if (grammar instanceof TreeSitterGrammar) { + if (this.config.get('core.useTreeSitterParsers')) { + score += 0.1 + } else { + return -Infinity + } + } + + // Prefer grammars with matching content regexes. Prefer a grammar with no content regex + // over one with a non-matching content regex. + if (grammar.contentRegex) { + if (grammar.contentRegex.test(contents)) { + score += 0.05 + } else { + score -= 0.05 + } + } + + // Prefer grammars that the user has manually installed over bundled grammars. + if (!grammar.bundledPackage) score += 0.01 } return score @@ -240,12 +272,8 @@ class GrammarRegistry { return pathScore } - grammarMatchesContents (grammar, contents) { - if (contents == null) return false - - if (grammar.contentRegExp) { // TreeSitter grammars - return grammar.contentRegExp.test(contents) - } else if (grammar.firstLineRegex) { // FirstMate grammars + grammarMatchesPrefix (grammar, contents) { + if (contents && grammar.firstLineRegex) { let escaped = false let numberOfNewlinesInRegex = 0 for (let character of grammar.firstLineRegex.source) { @@ -262,8 +290,12 @@ class GrammarRegistry { } } - const lines = contents.split('\n') - return grammar.firstLineRegex.testSync(lines.slice(0, numberOfNewlinesInRegex + 1).join('\n')) + const prefix = contents.split('\n').slice(0, numberOfNewlinesInRegex + 1).join('\n') + if (grammar.firstLineRegex.testSync) { + return grammar.firstLineRegex.testSync(prefix) + } else { + return grammar.firstLineRegex.test(prefix) + } } else { return false } @@ -271,18 +303,25 @@ class GrammarRegistry { forEachGrammar (callback) { this.textmateRegistry.grammars.forEach(callback) - for (let grammarId in this.treeSitterGrammarsById) { - callback(this.treeSitterGrammarsById[grammarId]) + for (const grammarId in this.treeSitterGrammarsById) { + const grammar = this.treeSitterGrammarsById[grammarId] + if (grammar.scopeName) callback(grammar) } } grammarForId (languageId) { - languageId = this.normalizeLanguageId(languageId) - - return ( - this.textmateRegistry.grammarForScopeName(languageId) || - this.treeSitterGrammarsById[languageId] - ) + if (!languageId) return null + if (this.config.get('core.useTreeSitterParsers')) { + return ( + this.treeSitterGrammarsById[languageId] || + this.textmateRegistry.grammarForScopeName(languageId) + ) + } else { + return ( + this.textmateRegistry.grammarForScopeName(languageId) || + this.treeSitterGrammarsById[languageId] + ) + } } // Deprecated: Get the grammar override for the given file path. @@ -293,7 +332,7 @@ class GrammarRegistry { grammarOverrideForPath (filePath) { Grim.deprecate('Use buffer.getLanguageMode().getLanguageId() instead') const buffer = atom.project.findBufferForPath(filePath) - if (buffer) return this.languageOverridesByBufferId.get(buffer.id) + if (buffer) return this.getAssignedLanguageId(buffer) } // Deprecated: Set the grammar override for the given file path. @@ -327,26 +366,23 @@ class GrammarRegistry { this.grammarScoresByBuffer.forEach((score, buffer) => { const languageMode = buffer.getLanguageMode() - if (grammar.injectionSelector) { - if (languageMode.hasTokenForSelector(grammar.injectionSelector)) { - languageMode.retokenizeLines() - } - return - } - const languageOverride = this.languageOverridesByBufferId.get(buffer.id) - if ((grammar.id === buffer.getLanguageMode().getLanguageId() || - grammar.id === languageOverride)) { + if (grammar === buffer.getLanguageMode().grammar || + grammar === this.grammarForId(languageOverride)) { buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer)) + return } else if (!languageOverride) { const score = this.getGrammarScore(grammar, buffer.getPath(), getGrammarSelectionContent(buffer)) const currentScore = this.grammarScoresByBuffer.get(buffer) if (currentScore == null || score > currentScore) { buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer)) this.grammarScoresByBuffer.set(buffer, score) + return } } + + languageMode.updateForInjection(grammar) }) } @@ -371,6 +407,32 @@ class GrammarRegistry { return this.textmateRegistry.onDidUpdateGrammar(callback) } + // Experimental: Specify a type of syntax node that may embed other languages. + // + // * `grammarId` The {String} id of the parent language + // * `injectionPoint` An {Object} with the following keys: + // * `type` The {String} type of syntax node that may embed other languages + // * `language` A {Function} that is called with syntax nodes of the specified `type` and + // returns a {String} that will be tested against other grammars' `injectionRegex` in + // order to determine what language should be embedded. + // * `content` A {Function} that is called with syntax nodes of the specified `type` and + // returns another syntax node or array of syntax nodes that contain the embedded source code. + addInjectionPoint (grammarId, injectionPoint) { + const grammar = this.treeSitterGrammarsById[grammarId] + if (grammar) { + grammar.injectionPoints.push(injectionPoint) + } else { + this.treeSitterGrammarsById[grammarId] = { + injectionPoints: [injectionPoint] + } + } + return new Disposable(() => { + const grammar = this.treeSitterGrammarsById[grammarId] + const index = grammar.injectionPoints.indexOf(injectionPoint) + if (index !== -1) grammar.injectionPoints.splice(index, 1) + }) + } + get nullGrammar () { return this.textmateRegistry.nullGrammar } @@ -389,12 +451,9 @@ class GrammarRegistry { addGrammar (grammar) { if (grammar instanceof TreeSitterGrammar) { - this.treeSitterGrammarsById[grammar.id] = grammar - if (grammar.legacyScopeName) { - this.config.addLegacyScopeAlias(grammar.id, grammar.legacyScopeName) - this.textMateScopeNamesByTreeSitterLanguageId.set(grammar.id, grammar.legacyScopeName) - this.treeSitterLanguageIdsByTextMateScopeName.set(grammar.legacyScopeName, grammar.id) - } + const existingParams = this.treeSitterGrammarsById[grammar.scopeName] || {} + if (grammar.scopeName) this.treeSitterGrammarsById[grammar.scopeName] = grammar + if (existingParams.injectionPoints) grammar.injectionPoints.push(...existingParams.injectionPoints) this.grammarAddedOrUpdated(grammar) return new Disposable(() => this.removeGrammar(grammar)) } else { @@ -404,12 +463,7 @@ class GrammarRegistry { removeGrammar (grammar) { if (grammar instanceof TreeSitterGrammar) { - delete this.treeSitterGrammarsById[grammar.id] - if (grammar.legacyScopeName) { - this.config.removeLegacyScopeAlias(grammar.id) - this.textMateScopeNamesByTreeSitterLanguageId.delete(grammar.id) - this.treeSitterLanguageIdsByTextMateScopeName.delete(grammar.legacyScopeName) - } + delete this.treeSitterGrammarsById[grammar.scopeName] } else { return this.textmateRegistry.removeGrammar(grammar) } @@ -429,7 +483,7 @@ class GrammarRegistry { this.readGrammar(grammarPath, (error, grammar) => { if (error) return callback(error) this.addGrammar(grammar) - callback(grammar) + callback(null, grammar) }) } @@ -495,10 +549,23 @@ class GrammarRegistry { return this.textmateRegistry.scopeForId(id) } - isGrammarPreferredType (grammar) { - return this.config.get('core.useTreeSitterParsers') - ? grammar instanceof TreeSitterGrammar - : grammar instanceof FirstMate.Grammar + treeSitterGrammarForLanguageString (languageString) { + let longestMatchLength = 0 + let grammarWithLongestMatch = null + for (const id in this.treeSitterGrammarsById) { + const grammar = this.treeSitterGrammarsById[id] + if (grammar.injectionRegex) { + const match = languageString.match(grammar.injectionRegex) + if (match) { + const {length} = match[0] + if (length > longestMatchLength) { + grammarWithLongestMatch = grammar + longestMatchLength = length + } + } + } + } + return grammarWithLongestMatch } normalizeLanguageId (languageId) { diff --git a/src/gutter-container.js b/src/gutter-container.js index 3faece073..cd0c796b2 100644 --- a/src/gutter-container.js +++ b/src/gutter-container.js @@ -97,7 +97,7 @@ module.exports = class GutterContainer { // The public interface is Gutter::decorateMarker or TextEditor::decorateMarker. addGutterDecoration (gutter, marker, options) { - if (gutter.name === 'line-number') { + if (gutter.type === 'line-number') { options.type = 'line-number' } else { options.type = 'gutter' diff --git a/src/gutter.js b/src/gutter.js index 3bf7a72ea..bd5955b78 100644 --- a/src/gutter.js +++ b/src/gutter.js @@ -11,6 +11,12 @@ module.exports = class Gutter { this.name = options && options.name this.priority = (options && options.priority != null) ? options.priority : DefaultPriority this.visible = (options && options.visible != null) ? options.visible : true + this.type = (options && options.type != null) ? options.type : 'decorated' + this.labelFn = options && options.labelFn + this.className = options && options.class + + this.onMouseDown = options && options.onMouseDown + this.onMouseMove = options && options.onMouseMove this.emitter = new Emitter() } diff --git a/src/history-manager.js b/src/history-manager.js index 306c11812..e4651d9d9 100644 --- a/src/history-manager.js +++ b/src/history-manager.js @@ -1,13 +1,11 @@ -/** @babel */ - -import {Emitter, CompositeDisposable} from 'event-kit' +const {Emitter, CompositeDisposable} = require('event-kit') // Extended: History manager for remembering which projects have been opened. // // An instance of this class is always available as the `atom.history` global. // // The project history is used to enable the 'Reopen Project' menu. -export class HistoryManager { +class HistoryManager { constructor ({project, commands, stateStore}) { this.stateStore = stateStore this.emitter = new Emitter() @@ -116,7 +114,7 @@ function arrayEquivalent (a, b) { return true } -export class HistoryProject { +class HistoryProject { constructor (paths, lastOpened) { this.paths = paths this.lastOpened = lastOpened || new Date() @@ -128,3 +126,5 @@ export class HistoryProject { set lastOpened (lastOpened) { this._lastOpened = lastOpened } get lastOpened () { return this._lastOpened } } + +module.exports = {HistoryManager, HistoryProject} diff --git a/src/initialize-application-window.coffee b/src/initialize-application-window.coffee index f8f670cf5..913c83977 100644 --- a/src/initialize-application-window.coffee +++ b/src/initialize-application-window.coffee @@ -36,6 +36,9 @@ if global.isGeneratingSnapshot require('image-view') require('incompatible-packages') require('keybinding-resolver') + require('language-html') + require('language-javascript') + require('language-ruby') require('line-ending-selector') require('link') require('markdown-preview') diff --git a/src/initialize-benchmark-window.js b/src/initialize-benchmark-window.js index 7ba99c468..131785454 100644 --- a/src/initialize-benchmark-window.js +++ b/src/initialize-benchmark-window.js @@ -1,11 +1,9 @@ -/** @babel */ +const {remote} = require('electron') +const path = require('path') +const ipcHelpers = require('./ipc-helpers') +const util = require('util') -import {remote} from 'electron' -import path from 'path' -import ipcHelpers from './ipc-helpers' -import util from 'util' - -export default async function () { +module.exports = async function () { const getWindowLoadSettings = require('./get-window-load-settings') const {test, headless, resourcePath, benchmarkPaths} = getWindowLoadSettings() try { diff --git a/src/initialize-test-window.coffee b/src/initialize-test-window.coffee index c6aaada0e..4cbd02bfd 100644 --- a/src/initialize-test-window.coffee +++ b/src/initialize-test-window.coffee @@ -24,9 +24,13 @@ module.exports = ({blobStore}) -> ApplicationDelegate = require '../src/application-delegate' Clipboard = require '../src/clipboard' TextEditor = require '../src/text-editor' + {updateProcessEnv} = require('./update-process-env') require './electron-shims' - {testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths} = getWindowLoadSettings() + ipcRenderer.on 'environment', (event, env) -> + updateProcessEnv(env) + + {testRunnerPath, legacyTestRunnerPath, headless, logFile, testPaths, env} = getWindowLoadSettings() unless headless # Show window synchronously so a focusout doesn't fire on input elements @@ -59,6 +63,8 @@ module.exports = ({blobStore}) -> require('module').globalPaths.push(exportsPath) process.env.NODE_PATH = exportsPath # Set NODE_PATH env variable since tasks may need it. + updateProcessEnv(env) + # Set up optional transpilation for packages under test if any FindParentDir = require 'find-parent-dir' if packageRoot = FindParentDir.sync(testPaths[0], 'package.json') diff --git a/src/ipc-helpers.js b/src/ipc-helpers.js index 4be9f9613..b68877f99 100644 --- a/src/ipc-helpers.js +++ b/src/ipc-helpers.js @@ -1,15 +1,13 @@ -'use strict' - const Disposable = require('event-kit').Disposable let ipcRenderer = null let ipcMain = null let BrowserWindow = null +let nextResponseChannelId = 0 + exports.on = function (emitter, eventName, callback) { emitter.on(eventName, callback) - return new Disposable(function () { - emitter.removeListener(eventName, callback) - }) + return new Disposable(() => emitter.removeListener(eventName, callback)) } exports.call = function (channel, ...args) { @@ -18,34 +16,28 @@ exports.call = function (channel, ...args) { ipcRenderer.setMaxListeners(20) } - var responseChannel = getResponseChannel(channel) + const responseChannel = `ipc-helpers-response-${nextResponseChannelId++}` - return new Promise(function (resolve) { - ipcRenderer.on(responseChannel, function (event, result) { + return new Promise(resolve => { + ipcRenderer.on(responseChannel, (event, result) => { ipcRenderer.removeAllListeners(responseChannel) resolve(result) }) - ipcRenderer.send(channel, ...args) + ipcRenderer.send(channel, responseChannel, ...args) }) } exports.respondTo = function (channel, callback) { if (!ipcMain) { - var electron = require('electron') + const electron = require('electron') ipcMain = electron.ipcMain BrowserWindow = electron.BrowserWindow } - var responseChannel = getResponseChannel(channel) - - return exports.on(ipcMain, channel, function (event, ...args) { - var browserWindow = BrowserWindow.fromWebContents(event.sender) - var result = callback(browserWindow, ...args) + return exports.on(ipcMain, channel, async (event, responseChannel, ...args) => { + const browserWindow = BrowserWindow.fromWebContents(event.sender) + const result = await callback(browserWindow, ...args) event.sender.send(responseChannel, result) }) } - -function getResponseChannel (channel) { - return 'ipc-helpers-' + channel + '-response' -} diff --git a/src/item-registry.coffee b/src/item-registry.coffee deleted file mode 100644 index 43af4cd11..000000000 --- a/src/item-registry.coffee +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = -class ItemRegistry - constructor: -> - @items = new WeakSet - - addItem: (item) -> - if @hasItem(item) - throw new Error("The workspace can only contain one instance of item #{item}") - @items.add(item) - - removeItem: (item) -> - @items.delete(item) - - hasItem: (item) -> - @items.has(item) diff --git a/src/item-registry.js b/src/item-registry.js new file mode 100644 index 000000000..80c83c701 --- /dev/null +++ b/src/item-registry.js @@ -0,0 +1,21 @@ +module.exports = +class ItemRegistry { + constructor () { + this.items = new WeakSet() + } + + addItem (item) { + if (this.hasItem(item)) { + throw new Error(`The workspace can only contain one instance of item ${item}`) + } + return this.items.add(item) + } + + removeItem (item) { + return this.items.delete(item) + } + + hasItem (item) { + return this.items.has(item) + } +} diff --git a/src/main-process/application-menu.js b/src/main-process/application-menu.js index 26dcd1941..2a46f06f4 100644 --- a/src/main-process/application-menu.js +++ b/src/main-process/application-menu.js @@ -201,7 +201,7 @@ class ApplicationMenu { if (item.command) { item.accelerator = this.acceleratorForCommand(item.command, keystrokesByCommand) item.click = () => global.atomApplication.sendCommand(item.command, item.commandDetail) - if (!/^application:/.test(item.command, item.commandDetail)) { + if (!/^application:/.test(item.command)) { item.metadata.windowSpecific = true } } diff --git a/src/main-process/atom-application.js b/src/main-process/atom-application.js index 3696a3b40..8a6d03733 100644 --- a/src/main-process/atom-application.js +++ b/src/main-process/atom-application.js @@ -4,6 +4,7 @@ const AtomProtocolHandler = require('./atom-protocol-handler') const AutoUpdateManager = require('./auto-update-manager') const StorageFolder = require('../storage-folder') const Config = require('../config') +const ConfigFile = require('../config-file') const FileRecoveryService = require('./file-recovery-service') const ipcHelpers = require('../ipc-helpers') const {BrowserWindow, Menu, app, clipboard, dialog, ipcMain, shell, screen} = require('electron') @@ -32,7 +33,7 @@ class AtomApplication extends EventEmitter { // Public: The entry point into the Atom application. static open (options) { if (!options.socketPath) { - const username = process.platform === 'win32' ? process.env.USERNAME : process.env.USER + const {username} = os.userInfo() // Lowercasing the ATOM_HOME to make sure that we don't get multiple sockets // on case-insensitive filesystems due to arbitrary case differences in paths. @@ -43,7 +44,7 @@ class AtomApplication extends EventEmitter { .update('|') .update(process.arch) .update('|') - .update(username) + .update(username || '') .update('|') .update(atomHomeUnique) @@ -92,7 +93,6 @@ class AtomApplication extends EventEmitter { this.quitting = false this.getAllWindows = this.getAllWindows.bind(this) this.getLastFocusedWindow = this.getLastFocusedWindow.bind(this) - this.resourcePath = options.resourcePath this.devResourcePath = options.devResourcePath this.version = options.version @@ -107,20 +107,21 @@ class AtomApplication extends EventEmitter { this.waitSessionsByWindow = new Map() this.windowStack = new WindowStack() - this.config = new Config({enablePersistence: true}) - this.config.setSchema(null, {type: 'object', properties: _.clone(ConfigSchema)}) - ConfigSchema.projectHome = { - type: 'string', - default: path.join(fs.getHomeDirectory(), 'github'), - description: - 'The directory where projects are assumed to be located. Packages created using the Package Generator will be stored here by default.' - } - this.config.initialize({ - configDirPath: process.env.ATOM_HOME, - resourcePath: this.resourcePath, - projectHomeSchema: ConfigSchema.projectHome + this.initializeAtomHome(process.env.ATOM_HOME) + + const configFilePath = fs.existsSync(path.join(process.env.ATOM_HOME, 'config.json')) + ? path.join(process.env.ATOM_HOME, 'config.json') + : path.join(process.env.ATOM_HOME, 'config.cson') + + this.configFile = ConfigFile.at(configFilePath) + this.config = new Config({ + saveCallback: settings => { + if (!this.quitting) { + return this.configFile.update(settings) + } + } }) - this.config.load() + this.config.setSchema(null, {type: 'object', properties: _.clone(ConfigSchema)}) this.fileRecoveryService = new FileRecoveryService(path.join(process.env.ATOM_HOME, 'recovery')) this.storageFolder = new StorageFolder(process.env.ATOM_HOME) @@ -138,7 +139,7 @@ class AtomApplication extends EventEmitter { // for testing purposes without booting up the world. As you add tests, feel free to move instantiation // of these various sub-objects into the constructor, but you'll need to remove the side-effects they // perform during their construction, adding an initialize method that you call here. - initialize (options) { + async initialize (options) { global.atomApplication = this // DEPRECATED: This can be removed at some point (added in 1.13) @@ -148,16 +149,15 @@ class AtomApplication extends EventEmitter { this.config.set('core.titleBar', 'custom') } - this.config.onDidChange('core.titleBar', this.promptForRestart.bind(this)) - - process.nextTick(() => this.autoUpdateManager.initialize()) this.applicationMenu = new ApplicationMenu(this.version, this.autoUpdateManager) this.atomProtocolHandler = new AtomProtocolHandler(this.resourcePath, this.safeMode) this.listenForArgumentsFromNewProcess() this.setupDockMenu() - return this.launch(options) + const result = await this.launch(options) + this.autoUpdateManager.initialize() + return result } async destroy () { @@ -169,18 +169,39 @@ class AtomApplication extends EventEmitter { this.disposable.dispose() } - launch (options) { + async launch (options) { + if (!this.configFilePromise) { + this.configFilePromise = this.configFile.watch() + this.disposable.add(await this.configFilePromise) + this.config.onDidChange('core.titleBar', () => this.promptForRestart()) + this.config.onDidChange('core.colorProfile', () => this.promptForRestart()) + } + + const optionsForWindowsToOpen = [] + + let shouldReopenPreviousWindows = false + if (options.test || options.benchmark || options.benchmarkTest) { - return this.openWithOptions(options) + optionsForWindowsToOpen.push(options) } else if ((options.pathsToOpen && options.pathsToOpen.length > 0) || (options.urlsToOpen && options.urlsToOpen.length > 0)) { - if (this.config.get('core.restorePreviousWindowsOnStart') === 'always') { - this.loadState(_.deepClone(options)) - } - return this.openWithOptions(options) + optionsForWindowsToOpen.push(options) + shouldReopenPreviousWindows = this.config.get('core.restorePreviousWindowsOnStart') === 'always' } else { - return this.loadState(options) || this.openPath(options) + shouldReopenPreviousWindows = this.config.get('core.restorePreviousWindowsOnStart') !== 'no' } + + if (shouldReopenPreviousWindows) { + for (const previousOptions of await this.loadPreviousWindowOptions()) { + optionsForWindowsToOpen.push(Object.assign({}, options, previousOptions)) + } + } + + if (optionsForWindowsToOpen.length === 0) { + optionsForWindowsToOpen.push(options) + } + + return optionsForWindowsToOpen.map(options => this.openWithOptions(options)) } openWithOptions (options) { @@ -271,7 +292,7 @@ class AtomApplication extends EventEmitter { return } } - if (!window.isSpec) this.saveState(true) + if (!window.isSpec) this.saveCurrentWindowOptions(true) } // Public: Adds the {AtomWindow} to the global window list. @@ -285,7 +306,7 @@ class AtomApplication extends EventEmitter { if (!window.isSpec) { const focusHandler = () => this.windowStack.touch(window) - const blurHandler = () => this.saveState(false) + const blurHandler = () => this.saveCurrentWindowOptions(false) window.browserWindow.on('focus', focusHandler) window.browserWindow.on('blur', blurHandler) window.browserWindow.once('closed', () => { @@ -397,6 +418,18 @@ class AtomApplication extends EventEmitter { this.openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet') this.openPathOnEvent('application:open-license', path.join(process.resourcesPath, 'LICENSE.md')) + this.configFile.onDidChange(settings => { + for (let window of this.getAllWindows()) { + window.didChangeUserSettings(settings) + } + this.config.resetUserSettings(settings) + }) + + this.configFile.onDidError(message => { + const window = this.focusedWindow() || this.getLastFocusedWindow() + if (window) window.didFailToReadUserSettings(message) + }) + this.disposable.add(ipcHelpers.on(app, 'before-quit', async event => { let resolveBeforeQuitPromise this.lastBeforeQuitPromise = new Promise(resolve => { resolveBeforeQuitPromise = resolve }) @@ -406,7 +439,11 @@ class AtomApplication extends EventEmitter { event.preventDefault() const windowUnloadPromises = this.getAllWindows().map(window => window.prepareToUnload()) const windowUnloadedResults = await Promise.all(windowUnloadPromises) - if (windowUnloadedResults.every(Boolean)) app.quit() + if (windowUnloadedResults.every(Boolean)) { + app.quit() + } else { + this.quitting = false + } } resolveBeforeQuitPromise() @@ -474,12 +511,12 @@ class AtomApplication extends EventEmitter { if (this.applicationMenu) this.applicationMenu.update(window, template, menu) })) - this.disposable.add(ipcHelpers.on(ipcMain, 'run-package-specs', (event, packageSpecPath) => { - this.runTests({ + this.disposable.add(ipcHelpers.on(ipcMain, 'run-package-specs', (event, packageSpecPath, options = {}) => { + this.runTests(Object.assign({ resourcePath: this.devResourcePath, pathsToOpen: [packageSpecPath], headless: false - }) + }, options)) })) this.disposable.add(ipcHelpers.on(ipcMain, 'run-benchmarks', (event, benchmarksPath) => { @@ -530,6 +567,12 @@ class AtomApplication extends EventEmitter { window.setPosition(x, y) })) + this.disposable.add(ipcHelpers.respondTo('set-user-settings', (window, settings, filePath) => { + if (!this.quitting) { + ConfigFile.at(filePath || this.configFilePath).update(JSON.parse(settings)) + } + })) + this.disposable.add(ipcHelpers.respondTo('center-window', window => window.center())) this.disposable.add(ipcHelpers.respondTo('focus-window', window => window.focus())) this.disposable.add(ipcHelpers.respondTo('show-window', window => window.show())) @@ -568,18 +611,16 @@ class AtomApplication extends EventEmitter { event.returnValue = this.autoUpdateManager.getErrorMessage() })) - this.disposable.add(ipcHelpers.on(ipcMain, 'will-save-path', (event, path) => { - this.fileRecoveryService.willSavePath(this.atomWindowForEvent(event), path) - event.returnValue = true - })) + this.disposable.add(ipcHelpers.respondTo('will-save-path', (window, path) => + this.fileRecoveryService.willSavePath(window, path) + )) - this.disposable.add(ipcHelpers.on(ipcMain, 'did-save-path', (event, path) => { - this.fileRecoveryService.didSavePath(this.atomWindowForEvent(event), path) - event.returnValue = true - })) + this.disposable.add(ipcHelpers.respondTo('did-save-path', (window, path) => + this.fileRecoveryService.didSavePath(window, path) + )) this.disposable.add(ipcHelpers.on(ipcMain, 'did-change-paths', () => - this.saveState(false) + this.saveCurrentWindowOptions(false) )) this.disposable.add(this.disableZoomOnDisplayChange()) @@ -593,6 +634,13 @@ class AtomApplication extends EventEmitter { } } + initializeAtomHome (configDirPath) { + if (!fs.existsSync(configDirPath)) { + const templateConfigDirPath = fs.resolve(this.resourcePath, 'dot-atom') + fs.copySync(templateConfigDirPath, configDirPath) + } + } + // Public: Executes the given command. // // If it isn't handled globally, delegate to the currently focused window. @@ -800,13 +848,12 @@ class AtomApplication extends EventEmitter { let existingWindow if (!newWindow) { existingWindow = this.windowForPaths(pathsToOpen, devMode) - const stats = pathsToOpen.map(pathToOpen => fs.statSyncNoException(pathToOpen)) if (!existingWindow) { let lastWindow = window || this.getLastFocusedWindow() if (lastWindow && lastWindow.devMode === devMode) { if (addToLastWindow || ( - stats.every(s => s.isFile && s.isFile()) || - (stats.some(s => s.isDirectory && s.isDirectory()) && !lastWindow.hasProjectPath()))) { + locationsToOpen.every(({stat}) => stat && stat.isFile()) || + (locationsToOpen.some(({stat}) => stat && stat.isDirectory()) && !lastWindow.hasProjectPath()))) { existingWindow = lastWindow } } @@ -839,6 +886,7 @@ class AtomApplication extends EventEmitter { } if (!resourcePath) resourcePath = this.resourcePath if (!windowDimensions) windowDimensions = this.getDimensionsForNewWindow() + openedWindow = new AtomWindow(this, this.fileRecoveryService, { initialPaths, locationsToOpen, @@ -910,7 +958,7 @@ class AtomApplication extends EventEmitter { } } - saveState (allowEmpty = false) { + async saveCurrentWindowOptions (allowEmpty = false) { if (this.quitting) return const states = [] @@ -920,28 +968,23 @@ class AtomApplication extends EventEmitter { states.reverse() if (states.length > 0 || allowEmpty) { - this.storageFolder.storeSync('application.json', states) + await this.storageFolder.store('application.json', states) this.emit('application:did-save-state') } } - loadState (options) { - const states = this.storageFolder.load('application.json') - if ( - ['yes', 'always'].includes(this.config.get('core.restorePreviousWindowsOnStart')) && - states && states.length > 0 - ) { - return states.map(state => - this.openWithOptions(Object.assign(options, { - initialPaths: state.initialPaths, - pathsToOpen: state.initialPaths.filter(p => fs.isDirectorySync(p)), - urlsToOpen: [], - devMode: this.devMode, - safeMode: this.safeMode - })) - ) + async loadPreviousWindowOptions () { + const states = await this.storageFolder.load('application.json') + if (states) { + return states.map(state => ({ + initialPaths: state.initialPaths, + pathsToOpen: state.initialPaths.filter(p => fs.isDirectorySync(p)), + urlsToOpen: [], + devMode: this.devMode, + safeMode: this.safeMode + })) } else { - return null + return [] } } @@ -1124,6 +1167,7 @@ class AtomApplication extends EventEmitter { env }) this.addWindow(window) + if (env) window.replaceEnvironment(env) return window } @@ -1234,11 +1278,11 @@ class AtomApplication extends EventEmitter { initialLine = initialColumn = null } - if (url.parse(pathToOpen).protocol == null) { - pathToOpen = path.resolve(executedFrom, fs.normalize(pathToOpen)) - } + const normalizedPath = path.normalize(path.resolve(executedFrom, fs.normalize(pathToOpen))) + const stat = fs.statSyncNoException(normalizedPath) + if (stat || !url.parse(pathToOpen).protocol) pathToOpen = normalizedPath - return {pathToOpen, initialLine, initialColumn} + return {pathToOpen, stat, initialLine, initialColumn} } // Opens a native dialog to prompt the user for a path. @@ -1292,17 +1336,16 @@ class AtomApplication extends EventEmitter { // File dialog defaults to project directory of currently active editor if (path) openOptions.defaultPath = path - return dialog.showOpenDialog(parentWindow, openOptions, callback) + dialog.showOpenDialog(parentWindow, openOptions, callback) } promptForRestart () { - const chosen = dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { + dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { type: 'warning', title: 'Restart required', message: 'You will need to restart Atom for this change to take effect.', buttons: ['Restart Atom', 'Cancel'] - }) - if (chosen === 0) return this.restart() + }, response => { if (response === 0) this.restart() }) } restart () { diff --git a/src/main-process/atom-protocol-handler.js b/src/main-process/atom-protocol-handler.js index 1affba02a..47c3da14f 100644 --- a/src/main-process/atom-protocol-handler.js +++ b/src/main-process/atom-protocol-handler.js @@ -20,6 +20,7 @@ class AtomProtocolHandler { if (!safeMode) { this.loadPaths.push(path.join(process.env.ATOM_HOME, 'dev', 'packages')) + this.loadPaths.push(path.join(resourcePath, 'packages')) } this.loadPaths.push(path.join(process.env.ATOM_HOME, 'packages')) diff --git a/src/main-process/atom-window.js b/src/main-process/atom-window.js index 0268cc1cf..a56679143 100644 --- a/src/main-process/atom-window.js +++ b/src/main-process/atom-window.js @@ -51,10 +51,18 @@ class AtomWindow extends EventEmitter { // taskbar's icon. See https://github.com/atom/atom/issues/4811 for more. if (process.platform === 'linux') options.icon = ICON_PATH if (this.shouldAddCustomTitleBar()) options.titleBarStyle = 'hidden' - if (this.shouldAddCustomInsetTitleBar()) options.titleBarStyle = 'hidden-inset' + if (this.shouldAddCustomInsetTitleBar()) options.titleBarStyle = 'hiddenInset' if (this.shouldHideTitleBar()) options.frame = false this.browserWindow = new BrowserWindow(options) + Object.defineProperty(this.browserWindow, 'loadSettingsJSON', { + get: () => JSON.stringify(Object.assign({ + userSettings: !this.isSpec + ? this.atomApplication.configFile.get() + : null + }, this.loadSettings)) + }) + this.handleEvents() this.loadSettings = Object.assign({}, settings) @@ -67,14 +75,13 @@ class AtomWindow extends EventEmitter { if (!this.loadSettings.initialPaths) { this.loadSettings.initialPaths = [] - for (const {pathToOpen} of locationsToOpen) { + for (const {pathToOpen, stat} of locationsToOpen) { if (!pathToOpen) continue - const stat = fs.statSyncNoException(pathToOpen) || null if (stat && stat.isDirectory()) { this.loadSettings.initialPaths.push(pathToOpen) } else { const parentDirectory = path.dirname(pathToOpen) - if ((stat && stat.isFile()) || fs.existsSync(parentDirectory)) { + if (stat && stat.isFile() || fs.existsSync(parentDirectory)) { this.loadSettings.initialPaths.push(parentDirectory) } else { this.loadSettings.initialPaths.push(pathToOpen) @@ -96,8 +103,6 @@ class AtomWindow extends EventEmitter { this.representedDirectoryPaths = this.loadSettings.initialPaths if (!this.loadSettings.env) this.env = this.loadSettings.env - this.browserWindow.loadSettingsJSON = JSON.stringify(this.loadSettings) - this.browserWindow.on('window:loaded', () => { this.disableZoom() this.emit('window:loaded') @@ -150,12 +155,13 @@ class AtomWindow extends EventEmitter { containsPath (pathToCheck) { if (!pathToCheck) return false - const stat = fs.statSyncNoException(pathToCheck) - if (stat && stat.isDirectory()) return false - - return this.representedDirectoryPaths.some(projectPath => - pathToCheck === projectPath || pathToCheck.startsWith(path.join(projectPath, path.sep)) - ) + let stat + return this.representedDirectoryPaths.some(projectPath => { + if (pathToCheck === projectPath) return true + if (!pathToCheck.startsWith(path.join(projectPath, path.sep))) return false + if (stat === undefined) stat = fs.statSyncNoException(pathToCheck) + return !stat || !stat.isDirectory() + }) } handleEvents () { @@ -163,7 +169,7 @@ class AtomWindow extends EventEmitter { if (!this.atomApplication.quitting && !this.unloading) { event.preventDefault() this.unloading = true - this.atomApplication.saveState(false) + this.atomApplication.saveCurrentWindowOptions(false) if (await this.prepareToUnload()) this.close() } }) @@ -176,34 +182,36 @@ class AtomWindow extends EventEmitter { this.browserWindow.on('unresponsive', () => { if (this.isSpec) return - const chosen = dialog.showMessageBox(this.browserWindow, { + dialog.showMessageBox(this.browserWindow, { type: 'warning', buttons: ['Force Close', 'Keep Waiting'], + cancelId: 1, // Canceling should be the least destructive action message: 'Editor is not responding', detail: 'The editor is not responding. Would you like to force close it or just keep waiting?' - }) - if (chosen === 0) this.browserWindow.destroy() + }, response => { if (response === 0) this.browserWindow.destroy() }) }) - this.browserWindow.webContents.on('crashed', () => { + this.browserWindow.webContents.on('crashed', async () => { if (this.headless) { console.log('Renderer process crashed, exiting') this.atomApplication.exit(100) return } - this.fileRecoveryService.didCrashWindow(this) - const chosen = dialog.showMessageBox(this.browserWindow, { + await this.fileRecoveryService.didCrashWindow(this) + dialog.showMessageBox(this.browserWindow, { type: 'warning', buttons: ['Close Window', 'Reload', 'Keep It Open'], + cancelId: 2, // Canceling should be the least destructive action message: 'The editor has crashed', detail: 'Please report this issue to https://github.com/atom/atom' + }, response => { + switch (response) { + case 0: return this.browserWindow.destroy() + case 1: return this.browserWindow.reload() + } }) - switch (chosen) { - case 0: return this.browserWindow.destroy() - case 1: return this.browserWindow.reload() - } }) this.browserWindow.webContents.on('will-navigate', (event, url) => { @@ -246,6 +254,14 @@ class AtomWindow extends EventEmitter { this.sendMessage('open-locations', locationsToOpen) } + didChangeUserSettings (settings) { + this.sendMessage('did-change-user-settings', settings) + } + + didFailToReadUserSettings (message) { + this.sendMessage('did-fail-to-read-user-settings', message) + } + replaceEnvironment (env) { this.browserWindow.webContents.send('environment', env) } @@ -414,8 +430,7 @@ class AtomWindow extends EventEmitter { this.representedDirectoryPaths = representedDirectoryPaths this.representedDirectoryPaths.sort() this.loadSettings.initialPaths = this.representedDirectoryPaths - this.browserWindow.loadSettingsJSON = JSON.stringify(this.loadSettings) - return this.atomApplication.saveState() + return this.atomApplication.saveCurrentWindowOptions() } didClosePathWithWaitSession (path) { diff --git a/src/main-process/auto-update-manager.coffee b/src/main-process/auto-update-manager.coffee index 0e4144c1a..74bf2f886 100644 --- a/src/main-process/auto-update-manager.coffee +++ b/src/main-process/auto-update-manager.coffee @@ -94,7 +94,7 @@ class AutoUpdateManager scheduleUpdateCheck: -> # Only schedule update check periodically if running in release version and # and there is no existing scheduled update check. - unless /\w{7}/.test(@version) or @checkForUpdatesIntervalID + unless /-dev/.test(@version) or @checkForUpdatesIntervalID checkForUpdates = => @check(hidePopups: true) fourHours = 1000 * 60 * 60 * 4 @checkForUpdatesIntervalID = setInterval(checkForUpdates, fourHours) @@ -118,24 +118,26 @@ class AutoUpdateManager onUpdateNotAvailable: => autoUpdater.removeListener 'error', @onUpdateError {dialog} = require 'electron' - dialog.showMessageBox + dialog.showMessageBox { type: 'info' buttons: ['OK'] icon: @iconPath message: 'No update available.' title: 'No Update Available' detail: "Version #{@version} is the latest version." + }, -> # noop callback to get async behavior onUpdateError: (event, message) => autoUpdater.removeListener 'update-not-available', @onUpdateNotAvailable {dialog} = require 'electron' - dialog.showMessageBox + dialog.showMessageBox { type: 'warning' buttons: ['OK'] icon: @iconPath message: 'There was an error checking for updates.' title: 'Update Error' detail: message + }, -> # noop callback to get async behavior getWindows: -> global.atomApplication.getAllWindows() diff --git a/src/main-process/file-recovery-service.js b/src/main-process/file-recovery-service.js index f55e3f956..abe2df84e 100644 --- a/src/main-process/file-recovery-service.js +++ b/src/main-process/file-recovery-service.js @@ -1,11 +1,11 @@ -'use babel' +const {dialog} = require('electron') +const crypto = require('crypto') +const Path = require('path') +const fs = require('fs-plus') +const mkdirp = require('mkdirp') -import {dialog} from 'electron' -import crypto from 'crypto' -import Path from 'path' -import fs from 'fs-plus' - -export default class FileRecoveryService { +module.exports = +class FileRecoveryService { constructor (recoveryDirectory) { this.recoveryDirectory = recoveryDirectory this.recoveryFilesByFilePath = new Map() @@ -13,15 +13,16 @@ export default class FileRecoveryService { this.windowsByRecoveryFile = new Map() } - willSavePath (window, path) { - if (!fs.existsSync(path)) return + async willSavePath (window, path) { + const stats = await tryStatFile(path) + if (!stats) return const recoveryPath = Path.join(this.recoveryDirectory, RecoveryFile.fileNameForPath(path)) const recoveryFile = - this.recoveryFilesByFilePath.get(path) || new RecoveryFile(path, recoveryPath) + this.recoveryFilesByFilePath.get(path) || new RecoveryFile(path, stats.mode, recoveryPath) try { - recoveryFile.retain() + await recoveryFile.retain() } catch (err) { console.log(`Couldn't retain ${recoveryFile.recoveryPath}. Code: ${err.code}. Message: ${err.message}`) return @@ -39,11 +40,11 @@ export default class FileRecoveryService { this.recoveryFilesByFilePath.set(path, recoveryFile) } - didSavePath (window, path) { + async didSavePath (window, path) { const recoveryFile = this.recoveryFilesByFilePath.get(path) if (recoveryFile != null) { try { - recoveryFile.release() + await recoveryFile.release() } catch (err) { console.log(`Couldn't release ${recoveryFile.recoveryPath}. Code: ${err.code}. Message: ${err.message}`) } @@ -53,27 +54,31 @@ export default class FileRecoveryService { } } - didCrashWindow (window) { + async didCrashWindow (window) { if (!this.recoveryFilesByWindow.has(window)) return + const promises = [] for (const recoveryFile of this.recoveryFilesByWindow.get(window)) { - try { - recoveryFile.recoverSync() - } catch (error) { - const message = 'A file that Atom was saving could be corrupted' - const detail = - `Error ${error.code}. There was a crash while saving "${recoveryFile.originalPath}", so this file might be blank or corrupted.\n` + - `Atom couldn't recover it automatically, but a recovery file has been saved at: "${recoveryFile.recoveryPath}".` - console.log(detail) - dialog.showMessageBox(window.browserWindow, {type: 'info', buttons: ['OK'], message, detail}) - } finally { - for (let window of this.windowsByRecoveryFile.get(recoveryFile)) { - this.recoveryFilesByWindow.get(window).delete(recoveryFile) - } - this.windowsByRecoveryFile.delete(recoveryFile) - this.recoveryFilesByFilePath.delete(recoveryFile.originalPath) - } + promises.push(recoveryFile.recover() + .catch(error => { + const message = 'A file that Atom was saving could be corrupted' + const detail = + `Error ${error.code}. There was a crash while saving "${recoveryFile.originalPath}", so this file might be blank or corrupted.\n` + + `Atom couldn't recover it automatically, but a recovery file has been saved at: "${recoveryFile.recoveryPath}".` + console.log(detail) + dialog.showMessageBox(window, {type: 'info', buttons: ['OK'], message, detail}, () => { /* noop callback to get async behavior */ }) + }) + .then(() => { + for (let window of this.windowsByRecoveryFile.get(recoveryFile)) { + this.recoveryFilesByWindow.get(window).delete(recoveryFile) + } + this.windowsByRecoveryFile.delete(recoveryFile) + this.recoveryFilesByFilePath.delete(recoveryFile.originalPath) + }) + ) } + + await Promise.all(promises) } didCloseWindow (window) { @@ -94,36 +99,67 @@ class RecoveryFile { return `${basename}-${randomSuffix}${extension}` } - constructor (originalPath, recoveryPath) { + constructor (originalPath, fileMode, recoveryPath) { this.originalPath = originalPath + this.fileMode = fileMode this.recoveryPath = recoveryPath this.refCount = 0 } - storeSync () { - fs.copyFileSync(this.originalPath, this.recoveryPath) + async store () { + await copyFile(this.originalPath, this.recoveryPath, this.fileMode) } - recoverSync () { - fs.copyFileSync(this.recoveryPath, this.originalPath) - this.removeSync() + async recover () { + await copyFile(this.recoveryPath, this.originalPath, this.fileMode) + await this.remove() } - removeSync () { - fs.unlinkSync(this.recoveryPath) + async remove () { + return new Promise((resolve, reject) => + fs.unlink(this.recoveryPath, error => + error && error.code !== 'ENOENT' ? reject(error) : resolve() + ) + ) } - retain () { - if (this.isReleased()) this.storeSync() + async retain () { + if (this.isReleased()) await this.store() this.refCount++ } - release () { + async release () { this.refCount-- - if (this.isReleased()) this.removeSync() + if (this.isReleased()) await this.remove() } isReleased () { return this.refCount === 0 } } + +async function tryStatFile (path) { + return new Promise((resolve, reject) => + fs.stat(path, (error, result) => + resolve(error == null && result) + ) + ) +} + +async function copyFile (source, destination, mode) { + return new Promise((resolve, reject) => { + mkdirp(Path.dirname(destination), (error) => { + if (error) return reject(error) + const readStream = fs.createReadStream(source) + readStream + .on('error', reject) + .once('open', () => { + const writeStream = fs.createWriteStream(destination, {mode}) + writeStream + .on('error', reject) + .on('open', () => readStream.pipe(writeStream)) + .once('close', () => resolve()) + }) + }) + }) +} diff --git a/src/main-process/main.js b/src/main-process/main.js index ee7b96232..11703bf3f 100644 --- a/src/main-process/main.js +++ b/src/main-process/main.js @@ -4,37 +4,55 @@ if (typeof snapshotResult !== 'undefined') { const startTime = Date.now() -const electron = require('electron') -const fs = require('fs') const path = require('path') +const fs = require('fs-plus') +const CSON = require('season') const yargs = require('yargs') +const electron = require('electron') const args = yargs(process.argv) .alias('d', 'dev') .alias('t', 'test') + .alias('r', 'resource-path') .argv +function isAtomRepoPath (repoPath) { + let packageJsonPath = path.join(repoPath, 'package.json') + if (fs.statSyncNoException(packageJsonPath)) { + let packageJson = CSON.readFileSync(packageJsonPath) + return packageJson.name === 'atom' + } + + return false +} + let resourcePath +let devResourcePath if (args.resourcePath) { resourcePath = args.resourcePath + devResourcePath = resourcePath } else { const stableResourcePath = path.dirname(path.dirname(__dirname)) const defaultRepositoryPath = path.join(electron.app.getPath('home'), 'github', 'atom') + if (process.env.ATOM_DEV_RESOURCE_PATH) { + devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH + } else if (isAtomRepoPath(process.cwd())) { + devResourcePath = process.cwd() + } else if (fs.statSyncNoException(defaultRepositoryPath)) { + devResourcePath = defaultRepositoryPath + } else { + devResourcePath = stableResourcePath + } + if (args.dev || args.test || args.benchmark || args.benchmarkTest) { - if (process.env.ATOM_DEV_RESOURCE_PATH) { - resourcePath = process.env.ATOM_DEV_RESOURCE_PATH - } else if (fs.statSyncNoException(defaultRepositoryPath)) { - resourcePath = defaultRepositoryPath - } else { - resourcePath = stableResourcePath - } + resourcePath = devResourcePath } else { resourcePath = stableResourcePath } } const start = require(path.join(resourcePath, 'src', 'main-process', 'start')) -start(resourcePath, startTime) +start(resourcePath, devResourcePath, startTime) diff --git a/src/main-process/parse-command-line.js b/src/main-process/parse-command-line.js index 3b0654962..5d7849eac 100644 --- a/src/main-process/parse-command-line.js +++ b/src/main-process/parse-command-line.js @@ -3,8 +3,6 @@ const dedent = require('dedent') const yargs = require('yargs') const {app} = require('electron') -const path = require('path') -const fs = require('fs-plus') module.exports = function parseCommandLine (processArgs) { const options = yargs(processArgs).wrap(yargs.terminalWidth()) @@ -12,13 +10,18 @@ module.exports = function parseCommandLine (processArgs) { options.usage( dedent`Atom Editor v${version} - Usage: atom [options] [path ...] + Usage: + atom [options] [path ...] + atom file[:line[:column]] One or more paths to files or folders may be specified. If there is an existing Atom window that contains all of the given folders, the paths will be opened in that window. Otherwise, they will be opened in a new window. + A file may be opened at the desired line (and optionally column) by + appending the numbers right after the file name, e.g. \`atom file:5:8\`. + Paths that start with \`atom://\` will be interpreted as URLs. Environment Variables: @@ -44,7 +47,7 @@ module.exports = function parseCommandLine (processArgs) { 'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.' ) options.boolean('benchmark').describe('benchmark', 'Open a new window that runs the specified benchmarks.') - options.boolean('benchmark-test').describe('benchmark--test', 'Run a faster version of the benchmarks in headless mode.') + options.boolean('benchmark-test').describe('benchmark-test', 'Run a faster version of the benchmarks in headless mode.') options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.') options.alias('m', 'main-process').boolean('m').describe('m', 'Run the specified specs in the main process.') options.string('timeout').describe( @@ -114,8 +117,6 @@ module.exports = function parseCommandLine (processArgs) { let pathsToOpen = [] let urlsToOpen = [] let devMode = args['dev'] - let devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH || path.join(app.getPath('home'), 'github', 'atom') - let resourcePath = null for (const path of args._) { if (path.startsWith('atom://')) { @@ -125,21 +126,8 @@ module.exports = function parseCommandLine (processArgs) { } } - if (args['resource-path']) { + if (args.resourcePath || test) { devMode = true - devResourcePath = args['resource-path'] - } - - if (test) { - devMode = true - } - - if (devMode) { - resourcePath = devResourcePath - } - - if (!fs.statSyncNoException(resourcePath)) { - resourcePath = path.dirname(path.dirname(__dirname)) } if (args['path-environment']) { @@ -148,12 +136,7 @@ module.exports = function parseCommandLine (processArgs) { process.env.PATH = args['path-environment'] } - resourcePath = normalizeDriveLetterName(resourcePath) - devResourcePath = normalizeDriveLetterName(devResourcePath) - return { - resourcePath, - devResourcePath, pathsToOpen, urlsToOpen, executedFrom, @@ -176,11 +159,3 @@ module.exports = function parseCommandLine (processArgs) { env: process.env } } - -function normalizeDriveLetterName (filePath) { - if (process.platform === 'win32') { - return filePath.replace(/^([a-z]):/, ([driveLetter]) => driveLetter.toUpperCase() + ':') - } else { - return filePath - } -} diff --git a/src/main-process/start.js b/src/main-process/start.js index 9670e67b6..10713fa4b 100644 --- a/src/main-process/start.js +++ b/src/main-process/start.js @@ -5,8 +5,11 @@ const temp = require('temp').track() const parseCommandLine = require('./parse-command-line') const startCrashReporter = require('../crash-reporter-start') const atomPaths = require('../atom-paths') +const fs = require('fs') +const CSON = require('season') +const Config = require('../config') -module.exports = function start (resourcePath, startTime) { +module.exports = function start (resourcePath, devResourcePath, startTime) { global.shellStartTime = startTime process.on('uncaughtException', function (error = {}) { @@ -19,16 +22,35 @@ module.exports = function start (resourcePath, startTime) { } }) + process.on('unhandledRejection', function (error = {}) { + if (error.message != null) { + console.log(error.message) + } + + if (error.stack != null) { + console.log(error.stack) + } + }) + const previousConsoleLog = console.log console.log = nslog app.commandLine.appendSwitch('enable-experimental-web-platform-features') const args = parseCommandLine(process.argv.slice(1)) + args.resourcePath = normalizeDriveLetterName(resourcePath) + args.devResourcePath = normalizeDriveLetterName(devResourcePath) + atomPaths.setAtomHome(app.getPath('home')) atomPaths.setUserData(app) setupCompileCache() + const config = getConfig() + const colorProfile = config.get('core.colorProfile') + if (colorProfile && colorProfile !== 'default') { + app.commandLine.appendSwitch('force-color-profile', colorProfile) + } + if (handleStartupEventWithSquirrel()) { return } else if (args.test && args.mainProcess) { @@ -87,3 +109,29 @@ function setupCompileCache () { CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME) CompileCache.install(process.resourcesPath, require) } + +function getConfig () { + const config = new Config() + + let configFilePath + if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.json'))) { + configFilePath = path.join(process.env.ATOM_HOME, 'config.json') + } else if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.cson'))) { + configFilePath = path.join(process.env.ATOM_HOME, 'config.cson') + } + + if (configFilePath) { + const configFileData = CSON.readFileSync(configFilePath) + config.resetUserSettings(configFileData) + } + + return config +} + +function normalizeDriveLetterName (filePath) { + if (process.platform === 'win32' && filePath) { + return filePath.replace(/^([a-z]):/, ([driveLetter]) => driveLetter.toUpperCase() + ':') + } else { + return filePath + } +} diff --git a/src/main-process/win-shell.js b/src/main-process/win-shell.js index 9670936c7..dd694b9dd 100644 --- a/src/main-process/win-shell.js +++ b/src/main-process/win-shell.js @@ -1,7 +1,5 @@ -'use babel' - -import Registry from 'winreg' -import Path from 'path' +const Registry = require('winreg') +const Path = require('path') let exeName = Path.basename(process.execPath) let appPath = `\"${process.execPath}\"` diff --git a/src/menu-helpers.coffee b/src/menu-helpers.coffee deleted file mode 100644 index d648d81ed..000000000 --- a/src/menu-helpers.coffee +++ /dev/null @@ -1,74 +0,0 @@ -_ = require 'underscore-plus' - -ItemSpecificities = new WeakMap - -merge = (menu, item, itemSpecificity=Infinity) -> - item = cloneMenuItem(item) - ItemSpecificities.set(item, itemSpecificity) if itemSpecificity - matchingItemIndex = findMatchingItemIndex(menu, item) - matchingItem = menu[matchingItemIndex] unless matchingItemIndex is - 1 - - if matchingItem? - if item.submenu? - merge(matchingItem.submenu, submenuItem, itemSpecificity) for submenuItem in item.submenu - else if itemSpecificity - unless itemSpecificity < ItemSpecificities.get(matchingItem) - menu[matchingItemIndex] = item - else unless item.type is 'separator' and _.last(menu)?.type is 'separator' - menu.push(item) - - return - -unmerge = (menu, item) -> - matchingItemIndex = findMatchingItemIndex(menu, item) - matchingItem = menu[matchingItemIndex] unless matchingItemIndex is - 1 - - if matchingItem? - if item.submenu? - unmerge(matchingItem.submenu, submenuItem) for submenuItem in item.submenu - - unless matchingItem.submenu?.length > 0 - menu.splice(matchingItemIndex, 1) - -findMatchingItemIndex = (menu, {type, label, submenu}) -> - return -1 if type is 'separator' - for item, index in menu - if normalizeLabel(item.label) is normalizeLabel(label) and item.submenu? is submenu? - return index - -1 - -normalizeLabel = (label) -> - return undefined unless label? - - if process.platform is 'darwin' - label - else - label.replace(/\&/g, '') - -cloneMenuItem = (item) -> - item = _.pick(item, 'type', 'label', 'enabled', 'visible', 'command', 'submenu', 'commandDetail', 'role', 'accelerator') - if item.submenu? - item.submenu = item.submenu.map (submenuItem) -> cloneMenuItem(submenuItem) - item - -# Determine the Electron accelerator for a given Atom keystroke. -# -# keystroke - The keystroke. -# -# Returns a String containing the keystroke in a format that can be interpreted -# by Electron to provide nice icons where available. -acceleratorForKeystroke = (keystroke) -> - return null unless keystroke - modifiers = keystroke.split(/-(?=.)/) - key = modifiers.pop().toUpperCase().replace('+', 'Plus') - - modifiers = modifiers.map (modifier) -> - modifier.replace(/shift/ig, "Shift") - .replace(/cmd/ig, "Command") - .replace(/ctrl/ig, "Ctrl") - .replace(/alt/ig, "Alt") - - keys = modifiers.concat([key]) - keys.join("+") - -module.exports = {merge, unmerge, normalizeLabel, cloneMenuItem, acceleratorForKeystroke} diff --git a/src/menu-helpers.js b/src/menu-helpers.js new file mode 100644 index 000000000..12598764e --- /dev/null +++ b/src/menu-helpers.js @@ -0,0 +1,132 @@ +const _ = require('underscore-plus') + +const ItemSpecificities = new WeakMap() + +// Add an item to a menu, ensuring separators are not duplicated. +function addItemToMenu (item, menu) { + const lastMenuItem = _.last(menu) + const lastMenuItemIsSpearator = lastMenuItem && lastMenuItem.type === 'separator' + if (!(item.type === 'separator' && lastMenuItemIsSpearator)) { + menu.push(item) + } +} + +function merge (menu, item, itemSpecificity = Infinity) { + item = cloneMenuItem(item) + ItemSpecificities.set(item, itemSpecificity) + const matchingItemIndex = findMatchingItemIndex(menu, item) + + if (matchingItemIndex === -1) { + addItemToMenu(item, menu) + return + } + + const matchingItem = menu[matchingItemIndex] + if (item.submenu != null) { + for (let submenuItem of item.submenu) { + merge(matchingItem.submenu, submenuItem, itemSpecificity) + } + } else if (itemSpecificity && itemSpecificity >= ItemSpecificities.get(matchingItem)) { + menu[matchingItemIndex] = item + } +} + +function unmerge (menu, item) { + const matchingItemIndex = findMatchingItemIndex(menu, item) + if (matchingItemIndex === -1) { + return + } + + const matchingItem = menu[matchingItemIndex] + if (item.submenu != null) { + for (let submenuItem of item.submenu) { + unmerge(matchingItem.submenu, submenuItem) + } + } + + if (matchingItem.submenu == null || matchingItem.submenu.length === 0) { + menu.splice(matchingItemIndex, 1) + } +} + +function findMatchingItemIndex (menu, { type, label, submenu }) { + if (type === 'separator') { + return -1 + } + for (let index = 0; index < menu.length; index++) { + const item = menu[index] + if ( + normalizeLabel(item.label) === normalizeLabel(label) && + (item.submenu != null) === (submenu != null) + ) { + return index + } + } + return -1 +} + +function normalizeLabel (label) { + if (label == null) { + return + } + return process.platform === 'darwin' ? label : label.replace(/&/g, '') +} + +function cloneMenuItem (item) { + item = _.pick( + item, + 'type', + 'label', + 'enabled', + 'visible', + 'command', + 'submenu', + 'commandDetail', + 'role', + 'accelerator', + 'before', + 'after', + 'beforeGroupContaining', + 'afterGroupContaining' + ) + if (item.submenu != null) { + item.submenu = item.submenu.map(submenuItem => cloneMenuItem(submenuItem)) + } + return item +} + +// Determine the Electron accelerator for a given Atom keystroke. +// +// keystroke - The keystroke. +// +// Returns a String containing the keystroke in a format that can be interpreted +// by Electron to provide nice icons where available. +function acceleratorForKeystroke (keystroke) { + if (!keystroke) { + return null + } + let modifiers = keystroke.split(/-(?=.)/) + const key = modifiers + .pop() + .toUpperCase() + .replace('+', 'Plus') + + modifiers = modifiers.map(modifier => + modifier + .replace(/shift/gi, 'Shift') + .replace(/cmd/gi, 'Command') + .replace(/ctrl/gi, 'Ctrl') + .replace(/alt/gi, 'Alt') + ) + + const keys = [...modifiers, key] + return keys.join('+') +} + +module.exports = { + merge, + unmerge, + normalizeLabel, + cloneMenuItem, + acceleratorForKeystroke +} diff --git a/src/menu-manager.coffee b/src/menu-manager.coffee index dbdcb2f0d..a3d35a1de 100644 --- a/src/menu-manager.coffee +++ b/src/menu-manager.coffee @@ -149,9 +149,9 @@ class MenuManager update: -> return unless @initialized - clearImmediate(@pendingUpdateOperation) if @pendingUpdateOperation? + clearTimeout(@pendingUpdateOperation) if @pendingUpdateOperation? - @pendingUpdateOperation = setImmediate => + @pendingUpdateOperation = setTimeout(=> unsetKeystrokes = new Set for binding in @keymapManager.getKeyBindings() if binding.command is 'unset!' @@ -168,6 +168,7 @@ class MenuManager keystrokesByCommand[binding.command].unshift binding.keystrokes @sendToBrowserProcess(@template, keystrokesByCommand) + , 1) loadPlatformItems: -> if platformMenu? diff --git a/src/menu-sort-helpers.js b/src/menu-sort-helpers.js new file mode 100644 index 000000000..259f8321e --- /dev/null +++ b/src/menu-sort-helpers.js @@ -0,0 +1,186 @@ +// UTILS + +function splitArray (arr, predicate) { + let lastArr = [] + const multiArr = [lastArr] + arr.forEach(item => { + if (predicate(item)) { + if (lastArr.length > 0) { + lastArr = [] + multiArr.push(lastArr) + } + } else { + lastArr.push(item) + } + }) + return multiArr +} + +function joinArrays (arrays, joiner) { + const joinedArr = [] + arrays.forEach((arr, i) => { + if (i > 0 && arr.length > 0) { + joinedArr.push(joiner) + } + joinedArr.push(...arr) + }) + return joinedArr +} + +const pushOntoMultiMap = (map, key, value) => { + if (!map.has(key)) { + map.set(key, []) + } + map.get(key).push(value) +} + +function indexOfGroupContainingCommand (groups, command, ignoreGroup) { + return groups.findIndex( + candiateGroup => + candiateGroup !== ignoreGroup && + candiateGroup.some( + candidateItem => candidateItem.command === command + ) + ) +} + +// Sort nodes topologically using a depth-first approach. Encountered cycles +// are broken. +function sortTopologically (originalOrder, edgesById) { + const sorted = [] + const marked = new Set() + + function visit (id) { + if (marked.has(id)) { + // Either this node has already been placed, or we have encountered a + // cycle and need to exit. + return + } + marked.add(id) + const edges = edgesById.get(id) + if (edges != null) { + edges.forEach(visit) + } + sorted.push(id) + } + + originalOrder.forEach(visit) + return sorted +} + +function attemptToMergeAGroup (groups) { + for (let i = 0; i < groups.length; i++) { + const group = groups[i] + for (const item of group) { + const toCommands = [...(item.before || []), ...(item.after || [])] + for (const command of toCommands) { + const index = indexOfGroupContainingCommand(groups, command, group) + if (index === -1) { + // No valid edge for this command + continue + } + const mergeTarget = groups[index] + // Merge with group containing `command` + mergeTarget.push(...group) + groups.splice(i, 1) + return true + } + } + } + return false +} + +// Merge groups based on before/after positions +// Mutates both the array of groups, and the individual group arrays. +function mergeGroups (groups) { + let mergedAGroup = true + while (mergedAGroup) { + mergedAGroup = attemptToMergeAGroup(groups) + } + return groups +} + +function sortItemsInGroup (group) { + const originalOrder = group.map((node, i) => i) + const edges = new Map() + const commandToIndex = new Map(group.map((item, i) => [item.command, i])) + + group.forEach((item, i) => { + if (item.before) { + item.before.forEach(toCommand => { + const to = commandToIndex.get(toCommand) + if (to != null) { + pushOntoMultiMap(edges, to, i) + } + }) + } + if (item.after) { + item.after.forEach(toCommand => { + const to = commandToIndex.get(toCommand) + if (to != null) { + pushOntoMultiMap(edges, i, to) + } + }) + } + }) + + const sortedNodes = sortTopologically(originalOrder, edges) + + return sortedNodes.map(i => group[i]) +} + +function findEdgesInGroup (groups, i, edges) { + const group = groups[i] + for (const item of group) { + if (item.beforeGroupContaining) { + for (const command of item.beforeGroupContaining) { + const to = indexOfGroupContainingCommand(groups, command, group) + if (to !== -1) { + pushOntoMultiMap(edges, to, i) + return + } + } + } + if (item.afterGroupContaining) { + for (const command of item.afterGroupContaining) { + const to = indexOfGroupContainingCommand(groups, command, group) + if (to !== -1) { + pushOntoMultiMap(edges, i, to) + return + } + } + } + } +} + +function sortGroups (groups) { + const originalOrder = groups.map((item, i) => i) + const edges = new Map() + + for (let i = 0; i < groups.length; i++) { + findEdgesInGroup(groups, i, edges) + } + + const sortedGroupIndexes = sortTopologically(originalOrder, edges) + return sortedGroupIndexes.map(i => groups[i]) +} + +function isSeparator (item) { + return item.type === 'separator' +} + +function sortMenuItems (menuItems) { + // Split the items into their implicit groups based upon separators. + const groups = splitArray(menuItems, isSeparator) + // Merge groups that contain before/after references to eachother. + const mergedGroups = mergeGroups(groups) + // Sort each individual group internally. + const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup) + // Sort the groups based upon their beforeGroupContaining/afterGroupContaining + // references. + const sortedGroups = sortGroups(mergedGroupsWithSortedItems) + // Join the groups back + return joinArrays(sortedGroups, { type: 'separator' }) +} + +module.exports = {sortMenuItems} diff --git a/src/module-cache.coffee b/src/module-cache.coffee index 5bc162ab1..358ed3393 100644 --- a/src/module-cache.coffee +++ b/src/module-cache.coffee @@ -189,7 +189,7 @@ resolveModulePath = (relativePath, parentModule) -> return unless candidates? for version, resolvedPath of candidates - if Module._cache.hasOwnProperty(resolvedPath) or isCorePath(resolvedPath) + if Module._cache[resolvedPath] or isCorePath(resolvedPath) return resolvedPath if satisfies(version, range) return diff --git a/src/native-watcher-registry.js b/src/native-watcher-registry.js index e63ac6cda..97f33e3fb 100644 --- a/src/native-watcher-registry.js +++ b/src/native-watcher-registry.js @@ -1,5 +1,3 @@ -/** @babel */ - const path = require('path') // Private: re-join the segments split from an absolute path to form another absolute path. diff --git a/src/notification.js b/src/notification.js index 320866d6b..96fad59e0 100644 --- a/src/notification.js +++ b/src/notification.js @@ -21,7 +21,7 @@ class Notification { throw new Error(`Notification must be created with string message: ${this.message}`) } - if (!_.isObject(this.options) || _.isArray(this.options)) { + if (!_.isObject(this.options) || Array.isArray(this.options)) { throw new Error(`Notification must be created with an options object: ${this.options}`) } } diff --git a/src/null-grammar.js b/src/null-grammar.js index fe9c3889e..12cfbbe53 100644 --- a/src/null-grammar.js +++ b/src/null-grammar.js @@ -1,8 +1,6 @@ -/** @babel */ +const {Disposable} = require('event-kit') -import {Disposable} from 'event-kit' - -export default { +module.exports = { name: 'Null Grammar', scopeName: 'text.plain.null-grammar', scopeForId (id) { diff --git a/src/package-manager.js b/src/package-manager.js index 17a5f2214..0c7943bca 100644 --- a/src/package-manager.js +++ b/src/package-manager.js @@ -61,6 +61,7 @@ module.exports = class PackageManager { if (params.configDirPath != null && !params.safeMode) { if (this.devMode) { this.packageDirPaths.push(path.join(params.configDirPath, 'dev', 'packages')) + this.packageDirPaths.push(path.join(this.resourcePath, 'packages')) } this.packageDirPaths.push(path.join(params.configDirPath, 'packages')) } diff --git a/src/package.js b/src/package.js index 8d5cbc3ca..bbcb0061f 100644 --- a/src/package.js +++ b/src/package.js @@ -43,8 +43,8 @@ class Package { ? params.bundledPackage : this.packageManager.isBundledPackagePath(this.path) this.name = - params.name || (this.metadata && this.metadata.name) || + params.name || path.basename(this.path) this.reset() } diff --git a/src/pane-container-element.coffee b/src/pane-container-element.coffee deleted file mode 100644 index 78e2fbad3..000000000 --- a/src/pane-container-element.coffee +++ /dev/null @@ -1,28 +0,0 @@ -{CompositeDisposable} = require 'event-kit' -_ = require 'underscore-plus' - -module.exports = -class PaneContainerElement extends HTMLElement - createdCallback: -> - @subscriptions = new CompositeDisposable - @classList.add 'panes' - - initialize: (@model, {@views}) -> - throw new Error("Must pass a views parameter when initializing PaneContainerElements") unless @views? - - @subscriptions.add @model.observeRoot(@rootChanged.bind(this)) - this - - rootChanged: (root) -> - focusedElement = document.activeElement if @hasFocus() - @firstChild?.remove() - if root? - view = @views.getView(root) - @appendChild(view) - focusedElement?.focus() - - hasFocus: -> - this is document.activeElement or @contains(document.activeElement) - - -module.exports = PaneContainerElement = document.registerElement 'atom-pane-container', prototype: PaneContainerElement.prototype diff --git a/src/pane-container-element.js b/src/pane-container-element.js new file mode 100644 index 000000000..7a2a88463 --- /dev/null +++ b/src/pane-container-element.js @@ -0,0 +1,40 @@ +const {CompositeDisposable} = require('event-kit') + +class PaneContainerElement extends HTMLElement { + createdCallback () { + this.subscriptions = new CompositeDisposable() + this.classList.add('panes') + } + + initialize (model, {views}) { + this.model = model + this.views = views + if (this.views == null) { + throw new Error('Must pass a views parameter when initializing PaneContainerElements') + } + this.subscriptions.add(this.model.observeRoot(this.rootChanged.bind(this))) + return this + } + + rootChanged (root) { + const focusedElement = this.hasFocus() ? document.activeElement : null + if (this.firstChild != null) { + this.firstChild.remove() + } + if (root != null) { + const view = this.views.getView(root) + this.appendChild(view) + if (focusedElement != null) { + focusedElement.focus() + } + } + } + + hasFocus () { + return this === document.activeElement || this.contains(document.activeElement) + } +} + +module.exports = document.registerElement('atom-pane-container', { + prototype: PaneContainerElement.prototype +}) diff --git a/src/pane-container.js b/src/pane-container.js index 25e57acc8..b14d6ea57 100644 --- a/src/pane-container.js +++ b/src/pane-container.js @@ -51,6 +51,7 @@ class PaneContainer { deserialize (state, deserializerManager) { if (state.version !== SERIALIZATION_VERSION) return + this.itemRegistry = new ItemRegistry() this.setRoot(deserializerManager.deserialize(state.root)) this.activePane = find(this.getRoot().getPanes(), pane => pane.id === state.activePaneId) || this.getPanes()[0] if (this.config.get('core.destroyEmptyPanes')) this.destroyEmptyPanes() diff --git a/src/pane-element.coffee b/src/pane-element.coffee deleted file mode 100644 index d68b3b834..000000000 --- a/src/pane-element.coffee +++ /dev/null @@ -1,139 +0,0 @@ -path = require 'path' -{CompositeDisposable} = require 'event-kit' - -class PaneElement extends HTMLElement - attached: false - - createdCallback: -> - @attached = false - @subscriptions = new CompositeDisposable - @inlineDisplayStyles = new WeakMap - - @initializeContent() - @subscribeToDOMEvents() - - attachedCallback: -> - @attached = true - @focus() if @model.isFocused() - - detachedCallback: -> - @attached = false - - initializeContent: -> - @setAttribute 'class', 'pane' - @setAttribute 'tabindex', -1 - @appendChild @itemViews = document.createElement('div') - @itemViews.setAttribute 'class', 'item-views' - - subscribeToDOMEvents: -> - handleFocus = (event) => - @model.focus() unless @isActivating or @model.isDestroyed() or @contains(event.relatedTarget) - if event.target is this and view = @getActiveView() - view.focus() - event.stopPropagation() - - handleBlur = (event) => - @model.blur() unless @contains(event.relatedTarget) - - handleDragOver = (event) -> - event.preventDefault() - event.stopPropagation() - - handleDrop = (event) => - event.preventDefault() - event.stopPropagation() - @getModel().activate() - pathsToOpen = Array::map.call event.dataTransfer.files, (file) -> file.path - @applicationDelegate.open({pathsToOpen}) if pathsToOpen.length > 0 - - @addEventListener 'focus', handleFocus, true - @addEventListener 'blur', handleBlur, true - @addEventListener 'dragover', handleDragOver - @addEventListener 'drop', handleDrop - - initialize: (@model, {@views, @applicationDelegate}) -> - throw new Error("Must pass a views parameter when initializing PaneElements") unless @views? - throw new Error("Must pass an applicationDelegate parameter when initializing PaneElements") unless @applicationDelegate? - - @subscriptions.add @model.onDidActivate(@activated.bind(this)) - @subscriptions.add @model.observeActive(@activeStatusChanged.bind(this)) - @subscriptions.add @model.observeActiveItem(@activeItemChanged.bind(this)) - @subscriptions.add @model.onDidRemoveItem(@itemRemoved.bind(this)) - @subscriptions.add @model.onDidDestroy(@paneDestroyed.bind(this)) - @subscriptions.add @model.observeFlexScale(@flexScaleChanged.bind(this)) - this - - getModel: -> @model - - activated: -> - @isActivating = true - @focus() unless @hasFocus() # Don't steal focus from children. - @isActivating = false - - activeStatusChanged: (active) -> - if active - @classList.add('active') - else - @classList.remove('active') - - activeItemChanged: (item) -> - delete @dataset.activeItemName - delete @dataset.activeItemPath - @changePathDisposable?.dispose() - - return unless item? - - hasFocus = @hasFocus() - itemView = @views.getView(item) - - if itemPath = item.getPath?() - @dataset.activeItemName = path.basename(itemPath) - @dataset.activeItemPath = itemPath - - if item.onDidChangePath? - @changePathDisposable = item.onDidChangePath => - itemPath = item.getPath() - @dataset.activeItemName = path.basename(itemPath) - @dataset.activeItemPath = itemPath - - unless @itemViews.contains(itemView) - @itemViews.appendChild(itemView) - - for child in @itemViews.children - if child is itemView - @showItemView(child) if @attached - else - @hideItemView(child) - - itemView.focus() if hasFocus - - showItemView: (itemView) -> - inlineDisplayStyle = @inlineDisplayStyles.get(itemView) - if inlineDisplayStyle? - itemView.style.display = inlineDisplayStyle - else - itemView.style.display = '' - - hideItemView: (itemView) -> - inlineDisplayStyle = itemView.style.display - unless inlineDisplayStyle is 'none' - @inlineDisplayStyles.set(itemView, inlineDisplayStyle) if inlineDisplayStyle? - itemView.style.display = 'none' - - itemRemoved: ({item, index, destroyed}) -> - if viewToRemove = @views.getView(item) - viewToRemove.remove() - - paneDestroyed: -> - @subscriptions.dispose() - @changePathDisposable?.dispose() - - flexScaleChanged: (flexScale) -> - @style.flexGrow = flexScale - - getActiveView: -> @views.getView(@model.getActiveItem()) - - hasFocus: -> - this is document.activeElement or @contains(document.activeElement) - -module.exports = PaneElement = document.registerElement 'atom-pane', prototype: PaneElement.prototype diff --git a/src/pane-element.js b/src/pane-element.js new file mode 100644 index 000000000..2c9f4eeef --- /dev/null +++ b/src/pane-element.js @@ -0,0 +1,218 @@ +const path = require('path') +const {CompositeDisposable} = require('event-kit') + +class PaneElement extends HTMLElement { + createdCallback () { + this.attached = false + this.subscriptions = new CompositeDisposable() + this.inlineDisplayStyles = new WeakMap() + this.initializeContent() + this.subscribeToDOMEvents() + } + + attachedCallback () { + this.attached = true + if (this.model.isFocused()) { + this.focus() + } + } + + detachedCallback () { + this.attached = false + } + + initializeContent () { + this.setAttribute('class', 'pane') + this.setAttribute('tabindex', -1) + this.itemViews = document.createElement('div') + this.appendChild(this.itemViews) + this.itemViews.setAttribute('class', 'item-views') + } + + subscribeToDOMEvents () { + const handleFocus = event => { + if ( + !( + this.isActivating || + this.model.isDestroyed() || + this.contains(event.relatedTarget) + ) + ) { + this.model.focus() + } + if (event.target !== this) return + const view = this.getActiveView() + if (view) { + view.focus() + event.stopPropagation() + } + } + const handleBlur = event => { + if (!this.contains(event.relatedTarget)) { + this.model.blur() + } + } + const handleDragOver = event => { + event.preventDefault() + event.stopPropagation() + } + const handleDrop = event => { + event.preventDefault() + event.stopPropagation() + this.getModel().activate() + const pathsToOpen = [...event.dataTransfer.files].map(file => file.path) + if (pathsToOpen.length > 0) { + this.applicationDelegate.open({pathsToOpen}) + } + } + this.addEventListener('focus', handleFocus, true) + this.addEventListener('blur', handleBlur, true) + this.addEventListener('dragover', handleDragOver) + this.addEventListener('drop', handleDrop) + } + + initialize (model, {views, applicationDelegate}) { + this.model = model + this.views = views + this.applicationDelegate = applicationDelegate + if (this.views == null) { + throw new Error( + 'Must pass a views parameter when initializing PaneElements' + ) + } + if (this.applicationDelegate == null) { + throw new Error( + 'Must pass an applicationDelegate parameter when initializing PaneElements' + ) + } + this.subscriptions.add(this.model.onDidActivate(this.activated.bind(this))) + this.subscriptions.add( + this.model.observeActive(this.activeStatusChanged.bind(this)) + ) + this.subscriptions.add( + this.model.observeActiveItem(this.activeItemChanged.bind(this)) + ) + this.subscriptions.add( + this.model.onDidRemoveItem(this.itemRemoved.bind(this)) + ) + this.subscriptions.add( + this.model.onDidDestroy(this.paneDestroyed.bind(this)) + ) + this.subscriptions.add( + this.model.observeFlexScale(this.flexScaleChanged.bind(this)) + ) + return this + } + + getModel () { + return this.model + } + + activated () { + this.isActivating = true + if (!this.hasFocus()) { + // Don't steal focus from children. + this.focus() + } + this.isActivating = false + } + + activeStatusChanged (active) { + if (active) { + this.classList.add('active') + } else { + this.classList.remove('active') + } + } + + activeItemChanged (item) { + delete this.dataset.activeItemName + delete this.dataset.activeItemPath + if (this.changePathDisposable != null) { + this.changePathDisposable.dispose() + } + if (item == null) { + return + } + const hasFocus = this.hasFocus() + const itemView = this.views.getView(item) + const itemPath = typeof item.getPath === 'function' ? item.getPath() : null + if (itemPath) { + this.dataset.activeItemName = path.basename(itemPath) + this.dataset.activeItemPath = itemPath + if (item.onDidChangePath != null) { + this.changePathDisposable = item.onDidChangePath(() => { + const itemPath = item.getPath() + this.dataset.activeItemName = path.basename(itemPath) + this.dataset.activeItemPath = itemPath + }) + } + } + if (!this.itemViews.contains(itemView)) { + this.itemViews.appendChild(itemView) + } + for (const child of this.itemViews.children) { + if (child === itemView) { + if (this.attached) { + this.showItemView(child) + } + } else { + this.hideItemView(child) + } + } + if (hasFocus) { + itemView.focus() + } + } + + showItemView (itemView) { + const inlineDisplayStyle = this.inlineDisplayStyles.get(itemView) + if (inlineDisplayStyle != null) { + itemView.style.display = inlineDisplayStyle + } else { + itemView.style.display = '' + } + } + + hideItemView (itemView) { + const inlineDisplayStyle = itemView.style.display + if (inlineDisplayStyle !== 'none') { + if (inlineDisplayStyle != null) { + this.inlineDisplayStyles.set(itemView, inlineDisplayStyle) + } + itemView.style.display = 'none' + } + } + + itemRemoved ({item, index, destroyed}) { + const viewToRemove = this.views.getView(item) + if (viewToRemove) { + viewToRemove.remove() + } + } + + paneDestroyed () { + this.subscriptions.dispose() + if (this.changePathDisposable != null) { + this.changePathDisposable.dispose() + } + } + + flexScaleChanged (flexScale) { + this.style.flexGrow = flexScale + } + + getActiveView () { + return this.views.getView(this.model.getActiveItem()) + } + + hasFocus () { + return ( + this === document.activeElement || this.contains(document.activeElement) + ) + } +} + +module.exports = document.registerElement('atom-pane', { + prototype: PaneElement.prototype +}) diff --git a/src/pane.js b/src/pane.js index af93f8e1e..fca1260c4 100644 --- a/src/pane.js +++ b/src/pane.js @@ -155,9 +155,17 @@ class Pane { getFlexScale () { return this.flexScale } - increaseSize () { this.setFlexScale(this.getFlexScale() * 1.1) } + increaseSize () { + if (this.getContainer().getPanes().length > 1) { + this.setFlexScale(this.getFlexScale() * 1.1) + } + } - decreaseSize () { this.setFlexScale(this.getFlexScale() / 1.1) } + decreaseSize () { + if (this.getContainer().getPanes().length > 1) { + this.setFlexScale(this.getFlexScale() / 1.1) + } + } /* Section: Event Subscription @@ -606,15 +614,15 @@ class Pane { if (this.items.includes(item)) return + const itemSubscriptions = new CompositeDisposable() + this.subscriptionsPerItem.set(item, itemSubscriptions) if (typeof item.onDidDestroy === 'function') { - const itemSubscriptions = new CompositeDisposable() itemSubscriptions.add(item.onDidDestroy(() => this.removeItem(item, false))) - if (typeof item.onDidTerminatePendingState === 'function') { - itemSubscriptions.add(item.onDidTerminatePendingState(() => { - if (this.getPendingItem() === item) this.clearPendingItem() - })) - } - this.subscriptionsPerItem.set(item, itemSubscriptions) + } + if (typeof item.onDidTerminatePendingState === 'function') { + itemSubscriptions.add(item.onDidTerminatePendingState(() => { + if (this.getPendingItem() === item) this.clearPendingItem() + })) } this.items.splice(index, 0, item) diff --git a/src/path-watcher.js b/src/path-watcher.js index d0ff90dd1..6693489ef 100644 --- a/src/path-watcher.js +++ b/src/path-watcher.js @@ -1,10 +1,9 @@ -/** @babel */ - const fs = require('fs') const path = require('path') const {Emitter, Disposable, CompositeDisposable} = require('event-kit') const nsfw = require('@atom/nsfw') +const watcher = require('@atom/watcher') const {NativeWatcherRegistry} = require('./native-watcher-registry') // Private: Associate native watcher action flags with descriptive String equivalents. @@ -23,145 +22,7 @@ const WATCHER_STATE = { STOPPING: Symbol('stopping') } -// Private: Emulate a "filesystem watcher" by subscribing to Atom events like buffers being saved. This will miss -// any changes made to files outside of Atom, but it also has no overhead. -class AtomBackend { - async start (rootPath, eventCallback, errorCallback) { - const getRealPath = givenPath => { - return new Promise(resolve => { - fs.realpath(givenPath, (err, resolvedPath) => { - err ? resolve(null) : resolve(resolvedPath) - }) - }) - } - - this.subs = new CompositeDisposable() - - this.subs.add(atom.workspace.observeTextEditors(async editor => { - let realPath = await getRealPath(editor.getPath()) - if (!realPath || !realPath.startsWith(rootPath)) { - return - } - - const announce = (action, oldPath) => { - const payload = {action, path: realPath} - if (oldPath) payload.oldPath = oldPath - eventCallback([payload]) - } - - const buffer = editor.getBuffer() - - this.subs.add(buffer.onDidConflict(() => announce('modified'))) - this.subs.add(buffer.onDidReload(() => announce('modified'))) - this.subs.add(buffer.onDidSave(event => { - if (event.path === realPath) { - announce('modified') - } else { - const oldPath = realPath - realPath = event.path - announce('renamed', oldPath) - } - })) - - this.subs.add(buffer.onDidDelete(() => announce('deleted'))) - - this.subs.add(buffer.onDidChangePath(newPath => { - if (newPath !== realPath) { - const oldPath = realPath - realPath = newPath - announce('renamed', oldPath) - } - })) - })) - - // Giant-ass brittle hack to hook files (and eventually directories) created from the TreeView. - const treeViewPackage = await atom.packages.getLoadedPackage('tree-view') - if (!treeViewPackage) return - await treeViewPackage.activationPromise - const treeViewModule = treeViewPackage.mainModule - if (!treeViewModule) return - const treeView = treeViewModule.getTreeViewInstance() - - const isOpenInEditor = async eventPath => { - const openPaths = await Promise.all( - atom.workspace.getTextEditors().map(editor => getRealPath(editor.getPath())) - ) - return openPaths.includes(eventPath) - } - - this.subs.add(treeView.onFileCreated(async event => { - const realPath = await getRealPath(event.path) - if (!realPath) return - - eventCallback([{action: 'added', path: realPath}]) - })) - - this.subs.add(treeView.onEntryDeleted(async event => { - const realPath = await getRealPath(event.path) - if (!realPath || isOpenInEditor(realPath)) return - - eventCallback([{action: 'deleted', path: realPath}]) - })) - - this.subs.add(treeView.onEntryMoved(async event => { - const [realNewPath, realOldPath] = await Promise.all([ - getRealPath(event.newPath), - getRealPath(event.initialPath) - ]) - if (!realNewPath || !realOldPath || isOpenInEditor(realNewPath) || isOpenInEditor(realOldPath)) return - - eventCallback([{action: 'renamed', path: realNewPath, oldPath: realOldPath}]) - })) - } - - async stop () { - this.subs && this.subs.dispose() - } -} - -// Private: Implement a native watcher by translating events from an NSFW watcher. -class NSFWBackend { - async start (rootPath, eventCallback, errorCallback) { - const handler = events => { - eventCallback(events.map(event => { - const action = ACTION_MAP.get(event.action) || `unexpected (${event.action})` - const payload = {action} - - if (event.file) { - payload.path = path.join(event.directory, event.file) - } else { - payload.oldPath = path.join(event.directory, event.oldFile) - payload.path = path.join(event.directory, event.newFile) - } - - return payload - })) - } - - this.watcher = await nsfw( - rootPath, - handler, - {debounceMS: 100, errorCallback} - ) - - await this.watcher.start() - } - - stop () { - return this.watcher.stop() - } -} - -// Private: Map configuration settings from the feature flag to backend implementations. -const BACKENDS = { - atom: AtomBackend, - native: NSFWBackend -} - -// Private: the backend implementation to fall back to if the config setting is invalid. -const DEFAULT_BACKEND = BACKENDS.nsfw - -// Private: Interface with and normalize events from a native OS filesystem watcher. +// Private: Interface with and normalize events from a filesystem watcher implementation. class NativeWatcher { // Private: Initialize a native watcher on a path. @@ -172,37 +33,10 @@ class NativeWatcher { this.emitter = new Emitter() this.subs = new CompositeDisposable() - this.backend = null this.state = WATCHER_STATE.STOPPED this.onEvents = this.onEvents.bind(this) this.onError = this.onError.bind(this) - - this.subs.add(atom.config.onDidChange('core.fileSystemWatcher', async () => { - if (this.state === WATCHER_STATE.STARTING) { - // Wait for this watcher to finish starting. - await new Promise(resolve => { - const sub = this.onDidStart(() => { - sub.dispose() - resolve() - }) - }) - } - - // Re-read the config setting in case it's changed again while we were waiting for the watcher - // to start. - const Backend = this.getCurrentBackend() - if (this.state === WATCHER_STATE.RUNNING && !(this.backend instanceof Backend)) { - await this.stop() - await this.start() - } - })) - } - - // Private: Read the `core.fileSystemWatcher` setting to determine the filesystem backend to use. - getCurrentBackend () { - const setting = atom.config.get('core.fileSystemWatcher') - return BACKENDS[setting] || DEFAULT_BACKEND } // Private: Begin watching for filesystem events. @@ -214,15 +48,16 @@ class NativeWatcher { } this.state = WATCHER_STATE.STARTING - const Backend = this.getCurrentBackend() - - this.backend = new Backend() - await this.backend.start(this.normalizedPath, this.onEvents, this.onError) + await this.doStart() this.state = WATCHER_STATE.RUNNING this.emitter.emit('did-start') } + doStart () { + return Promise.reject('doStart() not overridden') + } + // Private: Return true if the underlying watcher is actively listening for filesystem events. isRunning () { return this.state === WATCHER_STATE.RUNNING @@ -285,8 +120,8 @@ class NativeWatcher { // // * `replacement` the new {NativeWatcher} instance that a live {Watcher} instance should reattach to instead. // * `watchedPath` absolute path watched by the new {NativeWatcher}. - reattachTo (replacement, watchedPath) { - this.emitter.emit('should-detach', {replacement, watchedPath}) + reattachTo (replacement, watchedPath, options) { + this.emitter.emit('should-detach', {replacement, watchedPath, options}) } // Private: Stop the native watcher and release any operating system resources associated with it. @@ -299,12 +134,17 @@ class NativeWatcher { this.state = WATCHER_STATE.STOPPING this.emitter.emit('will-stop') - await this.backend.stop() + await this.doStop() + this.state = WATCHER_STATE.STOPPED this.emitter.emit('did-stop') } + doStop () { + return Promise.resolve() + } + // Private: Detach any event subscribers. dispose () { this.emitter.dispose() @@ -326,6 +166,133 @@ class NativeWatcher { } } +// Private: Emulate a "filesystem watcher" by subscribing to Atom events like buffers being saved. This will miss +// any changes made to files outside of Atom, but it also has no overhead. +class AtomNativeWatcher extends NativeWatcher { + async doStart () { + const getRealPath = givenPath => { + if (!givenPath) { + return Promise.resolve(null) + } + + return new Promise(resolve => { + fs.realpath(givenPath, (err, resolvedPath) => { + err ? resolve(null) : resolve(resolvedPath) + }) + }) + } + + this.subs.add(atom.workspace.observeTextEditors(async editor => { + let realPath = await getRealPath(editor.getPath()) + if (!realPath || !realPath.startsWith(this.normalizedPath)) { + return + } + + const announce = (action, oldPath) => { + const payload = {action, path: realPath} + if (oldPath) payload.oldPath = oldPath + this.onEvents([payload]) + } + + const buffer = editor.getBuffer() + + this.subs.add(buffer.onDidConflict(() => announce('modified'))) + this.subs.add(buffer.onDidReload(() => announce('modified'))) + this.subs.add(buffer.onDidSave(event => { + if (event.path === realPath) { + announce('modified') + } else { + const oldPath = realPath + realPath = event.path + announce('renamed', oldPath) + } + })) + + this.subs.add(buffer.onDidDelete(() => announce('deleted'))) + + this.subs.add(buffer.onDidChangePath(newPath => { + if (newPath !== this.normalizedPath) { + const oldPath = this.normalizedPath + this.normalizedPath = newPath + announce('renamed', oldPath) + } + })) + })) + + // Giant-ass brittle hack to hook files (and eventually directories) created from the TreeView. + const treeViewPackage = await atom.packages.getLoadedPackage('tree-view') + if (!treeViewPackage) return + await treeViewPackage.activationPromise + const treeViewModule = treeViewPackage.mainModule + if (!treeViewModule) return + const treeView = treeViewModule.getTreeViewInstance() + + const isOpenInEditor = async eventPath => { + const openPaths = await Promise.all( + atom.workspace.getTextEditors().map(editor => getRealPath(editor.getPath())) + ) + return openPaths.includes(eventPath) + } + + this.subs.add(treeView.onFileCreated(async event => { + const realPath = await getRealPath(event.path) + if (!realPath) return + + this.onEvents([{action: 'added', path: realPath}]) + })) + + this.subs.add(treeView.onEntryDeleted(async event => { + const realPath = await getRealPath(event.path) + if (!realPath || await isOpenInEditor(realPath)) return + + this.onEvents([{action: 'deleted', path: realPath}]) + })) + + this.subs.add(treeView.onEntryMoved(async event => { + const [realNewPath, realOldPath] = await Promise.all([ + getRealPath(event.newPath), + getRealPath(event.initialPath) + ]) + if (!realNewPath || !realOldPath || await isOpenInEditor(realNewPath) || await isOpenInEditor(realOldPath)) return + + this.onEvents([{action: 'renamed', path: realNewPath, oldPath: realOldPath}]) + })) + } +} + +// Private: Implement a native watcher by translating events from an NSFW watcher. +class NSFWNativeWatcher extends NativeWatcher { + async doStart (rootPath, eventCallback, errorCallback) { + const handler = events => { + this.onEvents(events.map(event => { + const action = ACTION_MAP.get(event.action) || `unexpected (${event.action})` + const payload = {action} + + if (event.file) { + payload.path = path.join(event.directory, event.file) + } else { + payload.oldPath = path.join(event.directory, event.oldFile) + payload.path = path.join(event.directory, event.newFile) + } + + return payload + })) + } + + this.watcher = await nsfw( + this.normalizedPath, + handler, + {debounceMS: 100, errorCallback: this.onError} + ) + + await this.watcher.start() + } + + doStop () { + return this.watcher.stop() + } +} + // Extended: Manage a subscription to filesystem events that occur beneath a root directory. Construct these by // calling `watchPath`. To watch for events within active project directories, use {Project::onDidChangeFiles} // instead. @@ -386,6 +353,15 @@ class PathWatcher { this.native = null this.changeCallbacks = new Map() + this.attachedPromise = new Promise(resolve => { + this.resolveAttachedPromise = resolve + }) + + this.startPromise = new Promise((resolve, reject) => { + this.resolveStartPromise = resolve + this.rejectStartPromise = reject + }) + this.normalizedPathPromise = new Promise((resolve, reject) => { fs.realpath(watchedPath, (err, real) => { if (err) { @@ -397,13 +373,7 @@ class PathWatcher { resolve(real) }) }) - - this.attachedPromise = new Promise(resolve => { - this.resolveAttachedPromise = resolve - }) - this.startPromise = new Promise(resolve => { - this.resolveStartPromise = resolve - }) + this.normalizedPathPromise.catch(err => this.rejectStartPromise(err)) this.emitter = new Emitter() this.subs = new CompositeDisposable() @@ -526,7 +496,29 @@ class PathWatcher { // events may include events for paths above this watcher's root path, so filter them to only include the relevant // ones, then re-broadcast them to our subscribers. onNativeEvents (events, callback) { - const filtered = events.filter(event => event.path.startsWith(this.normalizedPath)) + const isWatchedPath = eventPath => eventPath.startsWith(this.normalizedPath) + + const filtered = [] + for (let i = 0; i < events.length; i++) { + const event = events[i] + + if (event.action === 'renamed') { + const srcWatched = isWatchedPath(event.oldPath) + const destWatched = isWatchedPath(event.path) + + if (srcWatched && destWatched) { + filtered.push(event) + } else if (srcWatched && !destWatched) { + filtered.push({action: 'deleted', kind: event.kind, path: event.oldPath}) + } else if (!srcWatched && destWatched) { + filtered.push({action: 'created', kind: event.kind, path: event.path}) + } + } else { + if (isWatchedPath(event.path)) { + filtered.push(event) + } + } + } if (filtered.length > 0) { callback(filtered) @@ -545,46 +537,139 @@ class PathWatcher { } } -// Private: Globally tracked state used to de-duplicate related [PathWatchers]{PathWatcher}. +// Private: Globally tracked state used to de-duplicate related [PathWatchers]{PathWatcher} backed by emulated Atom +// events or NSFW. class PathWatcherManager { - // Private: Access or lazily initialize the singleton manager instance. - // - // Returns the one and only {PathWatcherManager}. - static instance () { - if (!PathWatcherManager.theManager) { - PathWatcherManager.theManager = new PathWatcherManager() + // Private: Access the currently active manager instance, creating one if necessary. + static active () { + if (!this.activeManager) { + this.activeManager = new PathWatcherManager(atom.config.get('core.fileSystemWatcher')) + this.sub = atom.config.onDidChange('core.fileSystemWatcher', ({newValue}) => { this.transitionTo(newValue) }) } - return PathWatcherManager.theManager + return this.activeManager + } + + // Private: Replace the active {PathWatcherManager} with a new one that creates [NativeWatchers]{NativeWatcher} + // based on the value of `setting`. + static async transitionTo (setting) { + const current = this.active() + + if (this.transitionPromise) { + await this.transitionPromise + } + + if (current.setting === setting) { + return + } + current.isShuttingDown = true + + let resolveTransitionPromise = () => {} + this.transitionPromise = new Promise(resolve => { + resolveTransitionPromise = resolve + }) + + const replacement = new PathWatcherManager(setting) + this.activeManager = replacement + + await Promise.all( + Array.from(current.live, async ([root, native]) => { + const w = await replacement.createWatcher(root, {}, () => {}) + native.reattachTo(w.native, root, w.native.options || {}) + }) + ) + + current.stopAllWatchers() + + resolveTransitionPromise() + this.transitionPromise = null } // Private: Initialize global {PathWatcher} state. - constructor () { - this.live = new Set() - this.nativeRegistry = new NativeWatcherRegistry( - normalizedPath => { - const nativeWatcher = new NativeWatcher(normalizedPath) + constructor (setting) { + this.setting = setting + this.live = new Map() - this.live.add(nativeWatcher) - const sub = nativeWatcher.onWillStop(() => { - this.live.delete(nativeWatcher) - sub.dispose() - }) + const initLocal = NativeConstructor => { + this.nativeRegistry = new NativeWatcherRegistry( + normalizedPath => { + const nativeWatcher = new NativeConstructor(normalizedPath) - return nativeWatcher - } - ) + this.live.set(normalizedPath, nativeWatcher) + const sub = nativeWatcher.onWillStop(() => { + this.live.delete(normalizedPath) + sub.dispose() + }) + + return nativeWatcher + } + ) + } + + if (setting === 'atom') { + initLocal(AtomNativeWatcher) + } else if (setting === 'experimental') { + // + } else if (setting === 'poll') { + // + } else { + initLocal(NSFWNativeWatcher) + } + + this.isShuttingDown = false + } + + useExperimentalWatcher () { + return this.setting === 'experimental' || this.setting === 'poll' } // Private: Create a {PathWatcher} tied to this global state. See {watchPath} for detailed arguments. - createWatcher (rootPath, options, eventCallback) { - const watcher = new PathWatcher(this.nativeRegistry, rootPath, options) - watcher.onDidChange(eventCallback) - return watcher + async createWatcher (rootPath, options, eventCallback) { + if (this.isShuttingDown) { + await this.constructor.transitionPromise + return PathWatcherManager.active().createWatcher(rootPath, options, eventCallback) + } + + if (this.useExperimentalWatcher()) { + if (this.setting === 'poll') { + options.poll = true + } + + const w = await watcher.watchPath(rootPath, options, eventCallback) + this.live.set(rootPath, w.native) + return w + } + + const w = new PathWatcher(this.nativeRegistry, rootPath, options) + w.onDidChange(eventCallback) + await w.getStartPromise() + return w + } + + // Private: Directly access the {NativeWatcherRegistry}. + getRegistry () { + if (this.useExperimentalWatcher()) { + return watcher.getRegistry() + } + + return this.nativeRegistry + } + + // Private: Sample watcher usage statistics. Only available for experimental watchers. + status () { + if (this.useExperimentalWatcher()) { + return watcher.status() + } + + return {} } // Private: Return a {String} depicting the currently active native watchers. print () { + if (this.useExperimentalWatcher()) { + return watcher.printWatchers() + } + return this.nativeRegistry.print() } @@ -592,8 +677,12 @@ class PathWatcherManager { // // Returns a {Promise} that resolves when all native watcher resources are disposed. stopAllWatchers () { + if (this.useExperimentalWatcher()) { + return watcher.stopAllWatchers() + } + return Promise.all( - Array.from(this.live, watcher => watcher.stop()) + Array.from(this.live, ([, w]) => w.stop()) ) } } @@ -638,19 +727,33 @@ class PathWatcherManager { // ``` // function watchPath (rootPath, options, eventCallback) { - const watcher = PathWatcherManager.instance().createWatcher(rootPath, options, eventCallback) - return watcher.getStartPromise().then(() => watcher) + return PathWatcherManager.active().createWatcher(rootPath, options, eventCallback) } // Private: Return a Promise that resolves when all {NativeWatcher} instances associated with a FileSystemManager // have stopped listening. This is useful for `afterEach()` blocks in unit tests. function stopAllWatchers () { - return PathWatcherManager.instance().stopAllWatchers() + return PathWatcherManager.active().stopAllWatchers() } -// Private: Show the currently active native watchers. -function printWatchers () { - return PathWatcherManager.instance().print() +// Private: Show the currently active native watchers in a formatted {String}. +watchPath.printWatchers = function () { + return PathWatcherManager.active().print() } -module.exports = {watchPath, stopAllWatchers, printWatchers} +// Private: Access the active {NativeWatcherRegistry}. +watchPath.getRegistry = function () { + return PathWatcherManager.active().getRegistry() +} + +// Private: Sample usage statistics for the active watcher. +watchPath.status = function () { + return PathWatcherManager.active().status() +} + +// Private: Configure @atom/watcher ("experimental") directly. +watchPath.configure = function (...args) { + return watcher.configure(...args) +} + +module.exports = {watchPath, stopAllWatchers} diff --git a/src/project.js b/src/project.js index 8de92b97e..8ccf60c0b 100644 --- a/src/project.js +++ b/src/project.js @@ -77,6 +77,31 @@ class Project extends Model { } } + // Layers the contents of a project's file's config + // on top of the current global config. + replace (projectSpecification) { + if (projectSpecification == null) { + atom.config.clearProjectSettings() + this.setPaths([]) + } else { + if (projectSpecification.originPath == null) { + return + } + + // If no path is specified, set to directory of originPath. + if (!Array.isArray(projectSpecification.paths)) { + projectSpecification.paths = [path.dirname(projectSpecification.originPath)] + } + atom.config.resetProjectSettings(projectSpecification.config, projectSpecification.originPath) + this.setPaths(projectSpecification.paths) + } + this.emitter.emit('did-replace', projectSpecification) + } + + onDidReplace (callback) { + return this.emitter.on('did-replace', callback) + } + /* Section: Serialization */ @@ -174,7 +199,7 @@ class Project extends Model { // const disposable = atom.project.onDidChangeFiles(events => { // for (const event of events) { // // "created", "modified", "deleted", or "renamed" - // console.log(`Event action: ${event.type}`) + // console.log(`Event action: ${event.action}`) // // // absolute path to the filesystem entry that was touched // console.log(`Event path: ${event.path}`) @@ -191,7 +216,7 @@ class Project extends Model { // To watch paths outside of open projects, use the `watchPaths` function instead; see {PathWatcher}. // // When writing tests against functionality that uses this method, be sure to wait for the - // {Promise} returned by {getWatcherPromise()} before manipulating the filesystem to ensure that + // {Promise} returned by {::getWatcherPromise} before manipulating the filesystem to ensure that // the watcher is receiving events. // // * `callback` {Function} to be called with batches of filesystem events reported by @@ -209,6 +234,38 @@ class Project extends Model { return this.emitter.on('did-change-files', callback) } + // Public: Invoke the given callback with all current and future + // repositories in the project. + // + // * `callback` {Function} to be called with current and future + // repositories. + // * `repository` A {GitRepository} that is present at the time of + // subscription or that is added at some later time. + // + // Returns a {Disposable} on which `.dispose()` can be called to + // unsubscribe. + observeRepositories (callback) { + for (const repo of this.repositories) { + if (repo != null) { + callback(repo) + } + } + + return this.onDidAddRepository(callback) + } + + // Public: Invoke the given callback when a repository is added to the + // project. + // + // * `callback` {Function} to be called when a repository is added. + // * `repository` A {GitRepository}. + // + // Returns a {Disposable} on which `.dispose()` can be called to + // unsubscribe. + onDidAddRepository (callback) { + return this.emitter.on('did-add-repository', callback) + } + /* Section: Accessing the git repository */ @@ -218,7 +275,7 @@ class Project extends Model { // // This method will be removed in 2.0 because it does synchronous I/O. // Prefer the following, which evaluates to a {Promise} that resolves to an - // {Array} of {Repository} objects: + // {Array} of {GitRepository} objects: // ``` // Promise.all(atom.project.getDirectories().map( // atom.project.repositoryForDirectory.bind(atom.project))) @@ -229,10 +286,10 @@ class Project extends Model { // Public: Get the repository for a given directory asynchronously. // - // * `directory` {Directory} for which to get a {Repository}. + // * `directory` {Directory} for which to get a {GitRepository}. // // Returns a {Promise} that resolves with either: - // * {Repository} if a repository can be created for the given directory + // * {GitRepository} if a repository can be created for the given directory // * `null` if no repository can be created for the given directory. repositoryForDirectory (directory) { const pathForDirectory = directory.getRealPathSync() @@ -323,7 +380,6 @@ class Project extends Model { // a file or does not exist, its parent directory will be added instead. addPath (projectPath, options = {}) { const directory = this.getDirectoryForProjectPath(projectPath) - let ok = true if (options.exact === true) { ok = (directory.getPath() === projectPath) @@ -353,6 +409,7 @@ class Project extends Model { this.emitter.emit('did-change-files', events) } } + // We'll use the directory's custom onDidChangeFiles callback, if available. // CustomDirectory::onDidChangeFiles should match the signature of // Project::onDidChangeFiles below (although it may resolve asynchronously) @@ -375,6 +432,9 @@ class Project extends Model { if (repo) { break } } this.repositories.push(repo != null ? repo : null) + if (repo != null) { + this.emitter.emit('did-add-repository', repo) + } if (options.emitEvent !== false) { this.emitter.emit('did-change-paths', this.getPaths()) @@ -637,27 +697,32 @@ class Project extends Model { // * `text` The {String} text to use as a buffer. // // Returns a {Promise} that resolves to the {TextBuffer}. - buildBuffer (absoluteFilePath) { + async buildBuffer (absoluteFilePath) { const params = {shouldDestroyOnFileDelete: this.shouldDestroyBufferOnFileDelete} - let promise + let buffer if (absoluteFilePath != null) { if (this.loadPromisesByPath[absoluteFilePath] == null) { this.loadPromisesByPath[absoluteFilePath] = - TextBuffer.load(absoluteFilePath, params).catch(error => { - delete this.loadPromisesByPath[absoluteFilePath] - throw error - }) + TextBuffer.load(absoluteFilePath, params) + .then(result => { + delete this.loadPromisesByPath[absoluteFilePath] + return result + }) + .catch(error => { + delete this.loadPromisesByPath[absoluteFilePath] + throw error + }) } - promise = this.loadPromisesByPath[absoluteFilePath] + buffer = await this.loadPromisesByPath[absoluteFilePath] } else { - promise = Promise.resolve(new TextBuffer(params)) + buffer = new TextBuffer(params) } - return promise.then(buffer => { - delete this.loadPromisesByPath[absoluteFilePath] - this.addBuffer(buffer) - return buffer - }) + + this.grammarRegistry.autoAssignLanguageMode(buffer) + + this.addBuffer(buffer) + return buffer } addBuffer (buffer, options = {}) { @@ -695,7 +760,7 @@ class Project extends Model { } subscribeToBuffer (buffer) { - buffer.onWillSave(({path}) => this.applicationDelegate.emitWillSavePath(path)) + buffer.onWillSave(async ({path}) => this.applicationDelegate.emitWillSavePath(path)) buffer.onDidSave(({path}) => this.applicationDelegate.emitDidSavePath(path)) buffer.onDidDestroy(() => this.removeBuffer(buffer)) buffer.onDidChangePath(() => { diff --git a/src/register-default-commands.coffee b/src/register-default-commands.coffee index a367e6188..badae227c 100644 --- a/src/register-default-commands.coffee +++ b/src/register-default-commands.coffee @@ -122,8 +122,6 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage commandRegistry.add( 'atom-text-editor', stopEventPropagation({ - 'core:undo': -> @undo() - 'core:redo': -> @redo() 'core:move-left': -> @moveLeft() 'core:move-right': -> @moveRight() 'core:select-left': -> @selectLeft() @@ -166,15 +164,35 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage false ) + commandRegistry.add( + 'atom-text-editor:not([readonly])', + stopEventPropagation({ + 'core:undo': -> @undo() + 'core:redo': -> @redo() + }), + false + ) + commandRegistry.add( 'atom-text-editor', + stopEventPropagationAndGroupUndo( + config, + { + 'core:copy': -> @copySelectedText() + 'editor:copy-selection': -> @copyOnlySelectedText() + } + ), + false + ) + + commandRegistry.add( + 'atom-text-editor:not([readonly])', stopEventPropagationAndGroupUndo( config, { 'core:backspace': -> @backspace() 'core:delete': -> @delete() 'core:cut': -> @cutSelectedText() - 'core:copy': -> @copySelectedText() 'core:paste': -> @pasteText() 'editor:paste-without-reformatting': -> @pasteText({ normalizeLineEndings: false, @@ -195,7 +213,6 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage 'editor:transpose': -> @transpose() 'editor:upper-case': -> @upperCase() 'editor:lower-case': -> @lowerCase() - 'editor:copy-selection': -> @copyOnlySelectedText() } ), false @@ -266,7 +283,7 @@ module.exports = ({commandRegistry, commandInstaller, config, notificationManage ) commandRegistry.add( - 'atom-text-editor:not([mini])', + 'atom-text-editor:not([mini]):not([readonly])', stopEventPropagationAndGroupUndo( config, { diff --git a/src/reopen-project-list-view.js b/src/reopen-project-list-view.js index f08ee725a..d59577684 100644 --- a/src/reopen-project-list-view.js +++ b/src/reopen-project-list-view.js @@ -1,8 +1,7 @@ -/** @babel */ +const SelectListView = require('atom-select-list') -import SelectListView from 'atom-select-list' - -export default class ReopenProjectListView { +module.exports = +class ReopenProjectListView { constructor (callback) { this.callback = callback this.selectListView = new SelectListView({ diff --git a/src/reopen-project-menu-manager.js b/src/reopen-project-menu-manager.js index 3f88e41f0..35564f705 100644 --- a/src/reopen-project-menu-manager.js +++ b/src/reopen-project-menu-manager.js @@ -1,9 +1,8 @@ -/** @babel */ +const {CompositeDisposable} = require('event-kit') +const path = require('path') -import {CompositeDisposable} from 'event-kit' -import path from 'path' - -export default class ReopenProjectMenuManager { +module.exports = +class ReopenProjectMenuManager { constructor ({menu, commands, history, config, open}) { this.menuManager = menu this.historyManager = history diff --git a/src/scope-descriptor.coffee b/src/scope-descriptor.coffee deleted file mode 100644 index 2085bd6b2..000000000 --- a/src/scope-descriptor.coffee +++ /dev/null @@ -1,63 +0,0 @@ -# Extended: Wraps an {Array} of `String`s. The Array describes a path from the -# root of the syntax tree to a token including _all_ scope names for the entire -# path. -# -# Methods that take a `ScopeDescriptor` will also accept an {Array} of {Strings} -# scope names e.g. `['.source.js']`. -# -# You can use `ScopeDescriptor`s to get language-specific config settings via -# {Config::get}. -# -# You should not need to create a `ScopeDescriptor` directly. -# -# * {TextEditor::getRootScopeDescriptor} to get the language's descriptor. -# * {TextEditor::scopeDescriptorForBufferPosition} to get the descriptor at a -# specific position in the buffer. -# * {Cursor::getScopeDescriptor} to get a cursor's descriptor based on position. -# -# See the [scopes and scope descriptor guide](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) -# for more information. -module.exports = -class ScopeDescriptor - @fromObject: (scopes) -> - if scopes instanceof ScopeDescriptor - scopes - else - new ScopeDescriptor({scopes}) - - ### - Section: Construction and Destruction - ### - - # Public: Create a {ScopeDescriptor} object. - # - # * `object` {Object} - # * `scopes` {Array} of {String}s - constructor: ({@scopes}) -> - - # Public: Returns an {Array} of {String}s - getScopesArray: -> @scopes - - getScopeChain: -> - # For backward compatibility, prefix TextMate-style scope names with - # leading dots (e.g. 'source.js' -> '.source.js'). - if @scopes[0].includes('.') - result = '' - for scope, i in @scopes - result += ' ' if i > 0 - result += '.' if scope[0] isnt '.' - result += scope - result - else - @scopes.join(' ') - - toString: -> - @getScopeChain() - - isEqual: (other) -> - if @scopes.length isnt other.scopes.length - return false - for scope, i in @scopes - if scope isnt other.scopes[i] - return false - true diff --git a/src/scope-descriptor.js b/src/scope-descriptor.js new file mode 100644 index 000000000..63075e8a1 --- /dev/null +++ b/src/scope-descriptor.js @@ -0,0 +1,80 @@ +// Extended: Wraps an {Array} of `String`s. The Array describes a path from the +// root of the syntax tree to a token including _all_ scope names for the entire +// path. +// +// Methods that take a `ScopeDescriptor` will also accept an {Array} of {String} +// scope names e.g. `['.source.js']`. +// +// You can use `ScopeDescriptor`s to get language-specific config settings via +// {Config::get}. +// +// You should not need to create a `ScopeDescriptor` directly. +// +// * {TextEditor::getRootScopeDescriptor} to get the language's descriptor. +// * {TextEditor::scopeDescriptorForBufferPosition} to get the descriptor at a +// specific position in the buffer. +// * {Cursor::getScopeDescriptor} to get a cursor's descriptor based on position. +// +// See the [scopes and scope descriptor guide](http://flight-manual.atom.io/behind-atom/sections/scoped-settings-scopes-and-scope-descriptors/) +// for more information. +module.exports = +class ScopeDescriptor { + static fromObject (scopes) { + if (scopes instanceof ScopeDescriptor) { + return scopes + } else { + return new ScopeDescriptor({scopes}) + } + } + + /* + Section: Construction and Destruction + */ + + // Public: Create a {ScopeDescriptor} object. + // + // * `object` {Object} + // * `scopes` {Array} of {String}s + constructor ({scopes}) { + this.scopes = scopes + } + + // Public: Returns an {Array} of {String}s + getScopesArray () { + return this.scopes + } + + getScopeChain () { + // For backward compatibility, prefix TextMate-style scope names with + // leading dots (e.g. 'source.js' -> '.source.js'). + if (this.scopes[0] != null && this.scopes[0].includes('.')) { + let result = '' + for (let i = 0; i < this.scopes.length; i++) { + const scope = this.scopes[i] + if (i > 0) { result += ' ' } + if (scope[0] !== '.') { result += '.' } + result += scope + } + return result + } else { + return this.scopes.join(' ') + } + } + + toString () { + return this.getScopeChain() + } + + isEqual (other) { + if (this.scopes.length !== other.scopes.length) { + return false + } + for (let i = 0; i < this.scopes.length; i++) { + const scope = this.scopes[i] + if (scope !== other.scopes[i]) { + return false + } + } + return true + } +} diff --git a/src/selection.js b/src/selection.js index 2c64fa126..209036be3 100644 --- a/src/selection.js +++ b/src/selection.js @@ -407,6 +407,25 @@ class Selection { if (autoscroll) this.cursor.autoscroll() } + // Private: Ensure that the {TextEditor} is not marked read-only before allowing a buffer modification to occur. if + // the editor is read-only, require an explicit opt-in option to proceed (`bypassReadOnly`) or throw an Error. + ensureWritable (methodName, opts) { + if (!opts.bypassReadOnly && this.editor.isReadOnly()) { + if (atom.inDevMode() || atom.inSpecMode()) { + const e = new Error('Attempt to mutate a read-only TextEditor through a Selection') + e.detail = + `Your package is attempting to call ${methodName} on a selection within an editor that has been marked ` + + ' read-only. Pass {bypassReadOnly: true} to modify it anyway, or test editors with .isReadOnly() before ' + + ' attempting modifications.' + throw e + } + + return false + } + + return true + } + /* Section: Modifying the selected text */ @@ -427,8 +446,11 @@ class Selection { // behavior is suppressed. // level between the first lines and the trailing lines. // * `normalizeLineEndings` (optional) {Boolean} (default: true) - // * `undo` If `skip`, skips the undo stack for this operation. + // * `undo` *Deprecated* If `skip`, skips the undo stack for this operation. This property is deprecated. Call groupLastChanges() on the {TextBuffer} afterward instead. + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) insertText (text, options = {}) { + if (!this.ensureWritable('insertText', options)) return + let desiredIndentLevel, indentAdjustment const oldBufferRange = this.getBufferRange() const wasReversed = this.isReversed() @@ -492,90 +514,134 @@ class Selection { // Public: Removes the first character before the selection if the selection // is empty otherwise it deletes the selection. - backspace () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + backspace (options = {}) { + if (!this.ensureWritable('backspace', options)) return if (this.isEmpty()) this.selectLeft() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes the selection or, if nothing is selected, then all // characters from the start of the selection back to the previous word // boundary. - deleteToPreviousWordBoundary () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToPreviousWordBoundary (options = {}) { + if (!this.ensureWritable('deleteToPreviousWordBoundary', options)) return if (this.isEmpty()) this.selectToPreviousWordBoundary() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes the selection or, if nothing is selected, then all // characters from the start of the selection up to the next word // boundary. - deleteToNextWordBoundary () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToNextWordBoundary (options = {}) { + if (!this.ensureWritable('deleteToNextWordBoundary', options)) return if (this.isEmpty()) this.selectToNextWordBoundary() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes from the start of the selection to the beginning of the // current word if the selection is empty otherwise it deletes the selection. - deleteToBeginningOfWord () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToBeginningOfWord (options = {}) { + if (!this.ensureWritable('deleteToBeginningOfWord', options)) return if (this.isEmpty()) this.selectToBeginningOfWord() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes from the beginning of the line which the selection begins on // all the way through to the end of the selection. - deleteToBeginningOfLine () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToBeginningOfLine (options = {}) { + if (!this.ensureWritable('deleteToBeginningOfLine', options)) return if (this.isEmpty() && this.cursor.isAtBeginningOfLine()) { this.selectLeft() } else { this.selectToBeginningOfLine() } - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes the selection or the next character after the start of the // selection if the selection is empty. - delete () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + delete (options = {}) { + if (!this.ensureWritable('delete', options)) return if (this.isEmpty()) this.selectRight() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: If the selection is empty, removes all text from the cursor to the // end of the line. If the cursor is already at the end of the line, it // removes the following newline. If the selection isn't empty, only deletes // the contents of the selection. - deleteToEndOfLine () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToEndOfLine (options = {}) { + if (!this.ensureWritable('deleteToEndOfLine', options)) return if (this.isEmpty()) { if (this.cursor.isAtEndOfLine()) { - this.delete() + this.delete(options) return } this.selectToEndOfLine() } - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes the selection or all characters from the start of the // selection to the end of the current word if nothing is selected. - deleteToEndOfWord () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToEndOfWord (options = {}) { + if (!this.ensureWritable('deleteToEndOfWord', options)) return if (this.isEmpty()) this.selectToEndOfWord() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes the selection or all characters from the start of the // selection to the end of the current word if nothing is selected. - deleteToBeginningOfSubword () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToBeginningOfSubword (options = {}) { + if (!this.ensureWritable('deleteToBeginningOfSubword', options)) return if (this.isEmpty()) this.selectToPreviousSubwordBoundary() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes the selection or all characters from the start of the // selection to the end of the current word if nothing is selected. - deleteToEndOfSubword () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteToEndOfSubword (options = {}) { + if (!this.ensureWritable('deleteToEndOfSubword', options)) return if (this.isEmpty()) this.selectToNextSubwordBoundary() - this.deleteSelectedText() + this.deleteSelectedText(options) } // Public: Removes only the selected text. - deleteSelectedText () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteSelectedText (options = {}) { + if (!this.ensureWritable('deleteSelectedText', options)) return const bufferRange = this.getBufferRange() if (!bufferRange.isEmpty()) this.editor.buffer.delete(bufferRange) if (this.cursor) this.cursor.setBufferPosition(bufferRange.start) @@ -584,7 +650,11 @@ class Selection { // Public: Removes the line at the beginning of the selection if the selection // is empty unless the selection spans multiple lines in which case all lines // are removed. - deleteLine () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + deleteLine (options = {}) { + if (!this.ensureWritable('deleteLine', options)) return const range = this.getBufferRange() if (range.isEmpty()) { const start = this.cursor.getScreenRow() @@ -607,7 +677,11 @@ class Selection { // be separated by a single space. // // If there selection spans more than one line, all the lines are joined together. - joinLines () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + joinLines (options = {}) { + if (!this.ensureWritable('joinLines', options)) return let joinMarker const selectedRange = this.getBufferRange() if (selectedRange.isEmpty()) { @@ -629,7 +703,7 @@ class Selection { }) if (trailingWhitespaceRange) { this.setBufferRange(trailingWhitespaceRange) - this.deleteSelectedText() + this.deleteSelectedText(options) } const currentRow = selectedRange.start.row @@ -638,7 +712,7 @@ class Selection { (nextRow <= this.editor.buffer.getLastRow()) && (this.editor.buffer.lineLengthForRow(nextRow) > 0) && (this.editor.buffer.lineLengthForRow(currentRow) > 0) - if (insertSpace) this.insertText(' ') + if (insertSpace) this.insertText(' ', options) this.cursor.moveToEndOfLine() @@ -647,7 +721,7 @@ class Selection { this.cursor.moveRight() this.cursor.moveToFirstCharacterOfLine() }) - this.deleteSelectedText() + this.deleteSelectedText(options) if (insertSpace) this.cursor.moveLeft() } @@ -660,7 +734,11 @@ class Selection { } // Public: Removes one level of indent from the currently selected rows. - outdentSelectedRows () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + outdentSelectedRows (options = {}) { + if (!this.ensureWritable('outdentSelectedRows', options)) return const [start, end] = this.getBufferRowRange() const {buffer} = this.editor const leadingTabRegex = new RegExp(`^( {1,${this.editor.getTabLength()}}|\t)`) @@ -674,7 +752,11 @@ class Selection { // Public: Sets the indentation level of all selected rows to values suggested // by the relevant grammars. - autoIndentSelectedRows () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + autoIndentSelectedRows (options = {}) { + if (!this.ensureWritable('autoIndentSelectedRows', options)) return const [start, end] = this.getBufferRowRange() return this.editor.autoIndentBufferRows(start, end) } @@ -683,29 +765,45 @@ class Selection { // of a comment. // // Removes the comment if they are currently wrapped in a comment. - toggleLineComments () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + toggleLineComments (options = {}) { + if (!this.ensureWritable('toggleLineComments', options)) return this.editor.toggleLineCommentsForBufferRows(...(this.getBufferRowRange() || [])) } // Public: Cuts the selection until the end of the screen line. - cutToEndOfLine (maintainClipboard) { + // + // * `maintainClipboard` {Boolean} + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + cutToEndOfLine (maintainClipboard, options = {}) { + if (!this.ensureWritable('cutToEndOfLine', options)) return if (this.isEmpty()) this.selectToEndOfLine() - return this.cut(maintainClipboard) + return this.cut(maintainClipboard, false, options.bypassReadOnly) } // Public: Cuts the selection until the end of the buffer line. - cutToEndOfBufferLine (maintainClipboard) { + // + // * `maintainClipboard` {Boolean} + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + cutToEndOfBufferLine (maintainClipboard, options = {}) { + if (!this.ensureWritable('cutToEndOfBufferLine', options)) return if (this.isEmpty()) this.selectToEndOfBufferLine() - this.cut(maintainClipboard) + this.cut(maintainClipboard, false, options.bypassReadOnly) } // Public: Copies the selection to the clipboard and then deletes it. // // * `maintainClipboard` {Boolean} (default: false) See {::copy} // * `fullLine` {Boolean} (default: false) See {::copy} - cut (maintainClipboard = false, fullLine = false) { + // * `bypassReadOnly` {Boolean} (default: false) Must be `true` to modify text within a read-only editor. + cut (maintainClipboard = false, fullLine = false, bypassReadOnly = false) { + if (!this.ensureWritable('cut', {bypassReadOnly})) return this.copy(maintainClipboard, fullLine) - this.delete() + this.delete({bypassReadOnly}) } // Public: Copies the current selection to the clipboard. @@ -783,7 +881,9 @@ class Selection { // * `options` (optional) {Object} with the keys: // * `autoIndent` If `true`, the line is indented to an automatically-inferred // level. Otherwise, {TextEditor::getTabText} is inserted. - indent ({autoIndent} = {}) { + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + indent ({autoIndent, bypassReadOnly} = {}) { + if (!this.ensureWritable('indent', {bypassReadOnly})) return const {row} = this.cursor.getBufferPosition() if (this.isEmpty()) { @@ -793,17 +893,21 @@ class Selection { if (autoIndent && delta > 0) { if (!this.editor.getSoftTabs()) delta = Math.max(delta, 1) - this.insertText(this.editor.buildIndentString(delta)) + this.insertText(this.editor.buildIndentString(delta), {bypassReadOnly}) } else { - this.insertText(this.editor.buildIndentString(1, this.cursor.getBufferColumn())) + this.insertText(this.editor.buildIndentString(1, this.cursor.getBufferColumn()), {bypassReadOnly}) } } else { - this.indentSelectedRows() + this.indentSelectedRows({bypassReadOnly}) } } // Public: If the selection spans multiple rows, indent all of them. - indentSelectedRows () { + // + // * `options` (optional) {Object} with the keys: + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify text within a read-only editor. (default: false) + indentSelectedRows (options = {}) { + if (!this.ensureWritable('indentSelectedRows', options)) return const [start, end] = this.getBufferRowRange() for (let row = start; row <= end; row++) { if (this.editor.buffer.lineLengthForRow(row) !== 0) { diff --git a/src/selectors.js b/src/selectors.js new file mode 100644 index 000000000..ce03b80b4 --- /dev/null +++ b/src/selectors.js @@ -0,0 +1,38 @@ +module.exports = {selectorMatchesAnyScope, matcherForSelector} + +const {isSubset} = require('underscore-plus') + +// Private: Parse a selector into parts. +// If already parsed, returns the selector unmodified. +// +// * `selector` a {String|Array} specifying what to match +// Returns selector parts, an {Array}. +function parse (selector) { + return typeof selector === 'string' + ? selector.replace(/^\./, '').split('.') + : selector +} + +const always = scope => true + +// Essential: Return a matcher function for a selector. +// +// * selector, a {String} selector +// Returns {(scope: String) -> Boolean}, a matcher function returning +// true iff the scope matches the selector. +function matcherForSelector (selector) { + const parts = parse(selector) + if (typeof parts === 'function') return parts + return selector + ? scope => isSubset(parts, parse(scope)) + : always +} + +// Essential: Return true iff the selector matches any provided scope. +// +// * {String} selector +// * {Array} scopes +// Returns {Boolean} true if any scope matches the selector. +function selectorMatchesAnyScope (selector, scopes) { + return !selector || scopes.some(matcherForSelector(selector)) +} diff --git a/src/storage-folder.coffee b/src/storage-folder.coffee deleted file mode 100644 index 280eb8b5c..000000000 --- a/src/storage-folder.coffee +++ /dev/null @@ -1,39 +0,0 @@ -path = require "path" -fs = require "fs-plus" - -module.exports = -class StorageFolder - constructor: (containingPath) -> - @path = path.join(containingPath, "storage") if containingPath? - - clear: -> - return unless @path? - - try - fs.removeSync(@path) - catch error - console.warn "Error deleting #{@path}", error.stack, error - - storeSync: (name, object) -> - return unless @path? - - fs.writeFileSync(@pathForKey(name), JSON.stringify(object), 'utf8') - - load: (name) -> - return unless @path? - - statePath = @pathForKey(name) - try - stateString = fs.readFileSync(statePath, 'utf8') - catch error - unless error.code is 'ENOENT' - console.warn "Error reading state file: #{statePath}", error.stack, error - return undefined - - try - JSON.parse(stateString) - catch error - console.warn "Error parsing state file: #{statePath}", error.stack, error - - pathForKey: (name) -> path.join(@getPath(), name) - getPath: -> @path diff --git a/src/storage-folder.js b/src/storage-folder.js new file mode 100644 index 000000000..4931dab11 --- /dev/null +++ b/src/storage-folder.js @@ -0,0 +1,49 @@ +const path = require('path') +const fs = require('fs-plus') + +module.exports = +class StorageFolder { + constructor (containingPath) { + if (containingPath) { + this.path = path.join(containingPath, 'storage') + } + } + + store (name, object) { + return new Promise((resolve, reject) => { + if (!this.path) return resolve() + fs.writeFile(this.pathForKey(name), JSON.stringify(object), 'utf8', error => + error ? reject(error) : resolve() + ) + }) + } + + load (name) { + return new Promise(resolve => { + if (!this.path) return resolve(null) + const statePath = this.pathForKey(name) + fs.readFile(statePath, 'utf8', (error, stateString) => { + if (error && error.code !== 'ENOENT') { + console.warn(`Error reading state file: ${statePath}`, error.stack, error) + } + + if (!stateString) return resolve(null) + + try { + resolve(JSON.parse(stateString)) + } catch (error) { + console.warn(`Error parsing state file: ${statePath}`, error.stack, error) + resolve(null) + } + }) + }) + } + + pathForKey (name) { + return path.join(this.getPath(), name) + } + + getPath () { + return this.path + } +} diff --git a/src/test.ejs b/src/test.ejs new file mode 100644 index 000000000..7b93c31b3 --- /dev/null +++ b/src/test.ejs @@ -0,0 +1,9 @@ + + +<% if something() { %> +
+ <%= html `ok how about this` %> +
+<% } %> + + diff --git a/src/text-editor-component.js b/src/text-editor-component.js index 48cb919d0..ffb55c454 100644 --- a/src/text-editor-component.js +++ b/src/text-editor-component.js @@ -148,12 +148,13 @@ class TextEditorComponent { this.lineNumbersToRender = { maxDigits: 2, bufferRows: [], + screenRows: [], keys: [], softWrappedFlags: [], foldableFlags: [] } this.decorationsToRender = { - lineNumbers: null, + lineNumbers: new Map(), lines: null, highlights: [], cursors: [], @@ -266,14 +267,22 @@ class TextEditorComponent { if (useScheduler === true) { const scheduler = etch.getScheduler() scheduler.readDocument(() => { - this.measureContentDuringUpdateSync() + const restartFrame = this.measureContentDuringUpdateSync() scheduler.updateDocument(() => { - this.updateSyncAfterMeasuringContent() + if (restartFrame) { + this.updateSync(true) + } else { + this.updateSyncAfterMeasuringContent() + } }) }) } else { - this.measureContentDuringUpdateSync() - this.updateSyncAfterMeasuringContent() + const restartFrame = this.measureContentDuringUpdateSync() + if (restartFrame) { + this.updateSync(false) + } else { + this.updateSyncAfterMeasuringContent() + } } this.updateScheduled = false @@ -391,15 +400,16 @@ class TextEditorComponent { this.measureHorizontalPositions() this.updateAbsolutePositionedDecorations() + const isHorizontalScrollbarVisible = ( + this.canScrollHorizontally() && + this.getHorizontalScrollbarHeight() > 0 + ) + if (this.pendingAutoscroll) { this.derivedDimensionsCache = {} const {screenRange, options} = this.pendingAutoscroll this.autoscrollHorizontally(screenRange, options) - const isHorizontalScrollbarVisible = ( - this.canScrollHorizontally() && - this.getHorizontalScrollbarHeight() > 0 - ) if (!wasHorizontalScrollbarVisible && isHorizontalScrollbarVisible) { this.autoscrollVertically(screenRange, options) } @@ -408,6 +418,8 @@ class TextEditorComponent { this.linesToMeasure.clear() this.measuredContent = true + + return wasHorizontalScrollbarVisible !== isHorizontalScrollbarVisible } updateSyncAfterMeasuringContent () { @@ -447,15 +459,18 @@ class TextEditorComponent { let clientContainerWidth = '100%' if (this.hasInitialMeasurements) { if (model.getAutoHeight()) { - clientContainerHeight = this.getContentHeight() - if (this.canScrollHorizontally()) clientContainerHeight += this.getHorizontalScrollbarHeight() - clientContainerHeight += 'px' + clientContainerHeight = + this.getContentHeight() + + this.getHorizontalScrollbarHeight() + + 'px' } if (model.getAutoWidth()) { style.width = 'min-content' - clientContainerWidth = this.getGutterContainerWidth() + this.getContentWidth() - if (this.canScrollVertically()) clientContainerWidth += this.getVerticalScrollbarWidth() - clientContainerWidth += 'px' + clientContainerWidth = + this.getGutterContainerWidth() + + this.getContentWidth() + + this.getVerticalScrollbarWidth() + + 'px' } else { style.width = this.element.style.width } @@ -466,7 +481,7 @@ class TextEditorComponent { attributes.mini = '' } - if (!this.isInputEnabled()) { + if (model.isReadOnly()) { attributes.readonly = '' } @@ -740,20 +755,14 @@ class TextEditorComponent { scrollLeft = this.getScrollLeft() canScrollHorizontally = this.canScrollHorizontally() canScrollVertically = this.canScrollVertically() - horizontalScrollbarHeight = - canScrollHorizontally - ? this.getHorizontalScrollbarHeight() - : 0 - verticalScrollbarWidth = - canScrollVertically - ? this.getVerticalScrollbarWidth() - : 0 + horizontalScrollbarHeight = this.getHorizontalScrollbarHeight() + verticalScrollbarWidth = this.getVerticalScrollbarWidth() forceScrollbarVisible = this.remeasureScrollbars } else { forceScrollbarVisible = true } - const dummyScrollbarVnodes = [ + return [ $(DummyScrollbarComponent, { ref: 'verticalScrollbar', orientation: 'vertical', @@ -775,13 +784,10 @@ class TextEditorComponent { scrollLeft, verticalScrollbarWidth, forceScrollbarVisible - }) - ] + }), - // If both scrollbars are visible, push a dummy element to force a "corner" - // to render where the two scrollbars meet at the lower right - if (verticalScrollbarWidth > 0 && horizontalScrollbarHeight > 0) { - dummyScrollbarVnodes.push($.div( + // Force a "corner" to render where the two scrollbars meet at the lower right + $.div( { ref: 'scrollbarCorner', className: 'scrollbar-corner', @@ -794,10 +800,8 @@ class TextEditorComponent { overflow: 'scroll' } } - )) - } - - return dummyScrollbarVnodes + ) + ] } else { return null } @@ -845,10 +849,7 @@ class TextEditorComponent { } for (let i = 0; i < newClassList.length; i++) { - const className = newClassList[i] - if (!oldClassList || !oldClassList.includes(className)) { - this.element.classList.add(className) - } + this.element.classList.add(newClassList[i]) } this.classList = newClassList @@ -886,7 +887,7 @@ class TextEditorComponent { queryLineNumbersToRender () { const {model} = this.props - if (!model.isLineNumberGutterVisible()) return + if (!model.anyLineNumberGutterVisible()) return if (this.showLineNumbers !== model.doesShowLineNumbers()) { this.remeasureGutterDimensions = true this.showLineNumbers = model.doesShowLineNumbers() @@ -942,7 +943,7 @@ class TextEditorComponent { queryMaxLineNumberDigits () { const {model} = this.props - if (model.isLineNumberGutterVisible()) { + if (model.anyLineNumberGutterVisible()) { const maxDigits = Math.max(2, model.getLineCount().toString().length) if (maxDigits !== this.lineNumbersToRender.maxDigits) { this.remeasureGutterDimensions = true @@ -977,7 +978,7 @@ class TextEditorComponent { } queryDecorationsToRender () { - this.decorationsToRender.lineNumbers = [] + this.decorationsToRender.lineNumbers.clear() this.decorationsToRender.lines = [] this.decorationsToRender.overlays.length = 0 this.decorationsToRender.customGutter.clear() @@ -1040,7 +1041,17 @@ class TextEditorComponent { } addLineDecorationToRender (type, decoration, screenRange, reversed) { - const decorationsToRender = (type === 'line') ? this.decorationsToRender.lines : this.decorationsToRender.lineNumbers + let decorationsToRender + if (type === 'line') { + decorationsToRender = this.decorationsToRender.lines + } else { + const gutterName = decoration.gutterName || 'line-number' + decorationsToRender = this.decorationsToRender.lineNumbers.get(gutterName) + if (!decorationsToRender) { + decorationsToRender = [] + this.decorationsToRender.lineNumbers.set(gutterName, decorationsToRender) + } + } let omitLastRow = false if (screenRange.isEmpty()) { @@ -1520,15 +1531,11 @@ class TextEditorComponent { let {wheelDeltaX, wheelDeltaY} = event if (Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY)) { - wheelDeltaX = (Math.sign(wheelDeltaX) === 1) - ? Math.max(1, wheelDeltaX * scrollSensitivity) - : Math.min(-1, wheelDeltaX * scrollSensitivity) + wheelDeltaX = wheelDeltaX * scrollSensitivity wheelDeltaY = 0 } else { wheelDeltaX = 0 - wheelDeltaY = (Math.sign(wheelDeltaY) === 1) - ? Math.max(1, wheelDeltaY * scrollSensitivity) - : Math.min(-1, wheelDeltaY * scrollSensitivity) + wheelDeltaY = wheelDeltaY * scrollSensitivity } if (this.getPlatform() !== 'darwin' && event.shiftKey) { @@ -1752,28 +1759,28 @@ class TextEditorComponent { const screenPosition = this.screenPositionForMouseEvent(event) - if (button !== 0 || (platform === 'darwin' && ctrlKey)) { - // Always set cursor position on middle-click - // Only set cursor position on right-click if there is one cursor with no selection - const ranges = model.getSelectedBufferRanges() - if (button === 1 || (ranges.length === 1 && ranges[0].isEmpty())) { - model.setCursorScreenPosition(screenPosition, {autoscroll: false}) - } + if (button === 1) { + model.setCursorScreenPosition(screenPosition, {autoscroll: false}) // On Linux, pasting happens on middle click. A textInput event with the // contents of the selection clipboard will be dispatched by the browser // automatically on mouseup. - if (platform === 'linux' && button === 1) model.insertText(clipboard.readText('selection')) + if (platform === 'linux' && this.isInputEnabled()) model.insertText(clipboard.readText('selection')) return } + if (button !== 0) return + + // Ctrl-click brings up the context menu on macOS + if (platform === 'darwin' && ctrlKey) return + if (target && target.matches('.fold-marker')) { const bufferPosition = model.bufferPositionForScreenPosition(screenPosition) model.destroyFoldsContainingBufferPositions([bufferPosition], false) return } - const addOrRemoveSelection = metaKey || ctrlKey + const addOrRemoveSelection = metaKey || (ctrlKey && platform !== 'darwin') switch (detail) { case 1: @@ -2619,37 +2626,25 @@ class TextEditorComponent { getScrollContainerHeight () { if (this.props.model.getAutoHeight()) { - return this.getScrollHeight() + return this.getScrollHeight() + this.getHorizontalScrollbarHeight() } else { return this.getClientContainerHeight() } } getScrollContainerClientWidth () { - if (this.canScrollVertically()) { - return this.getScrollContainerWidth() - this.getVerticalScrollbarWidth() - } else { - return this.getScrollContainerWidth() - } + return this.getScrollContainerWidth() - this.getVerticalScrollbarWidth() } getScrollContainerClientHeight () { - if (this.canScrollHorizontally()) { - return this.getScrollContainerHeight() - this.getHorizontalScrollbarHeight() - } else { - return this.getScrollContainerHeight() - } + return this.getScrollContainerHeight() - this.getHorizontalScrollbarHeight() } canScrollVertically () { const {model} = this.props if (model.isMini()) return false if (model.getAutoHeight()) return false - if (this.getContentHeight() > this.getScrollContainerHeight()) return true - return ( - this.getContentWidth() > this.getScrollContainerWidth() && - this.getContentHeight() > (this.getScrollContainerHeight() - this.getHorizontalScrollbarHeight()) - ) + return this.getContentHeight() > this.getScrollContainerClientHeight() } canScrollHorizontally () { @@ -2657,11 +2652,7 @@ class TextEditorComponent { if (model.isMini()) return false if (model.getAutoWidth()) return false if (model.isSoftWrapped()) return false - if (this.getContentWidth() > this.getScrollContainerWidth()) return true - return ( - this.getContentHeight() > this.getScrollContainerHeight() && - this.getContentWidth() > (this.getScrollContainerWidth() - this.getVerticalScrollbarWidth()) - ) + return this.getContentWidth() > this.getScrollContainerClientWidth() } getScrollHeight () { @@ -2694,7 +2685,7 @@ class TextEditorComponent { } getContentWidth () { - return Math.round(this.getLongestLineWidth() + this.getBaseCharacterWidth()) + return Math.ceil(this.getLongestLineWidth() + this.getBaseCharacterWidth()) } getScrollContainerClientWidthInBaseCharacters () { @@ -2804,7 +2795,7 @@ class TextEditorComponent { setScrollTop (scrollTop) { if (Number.isNaN(scrollTop) || scrollTop == null) return false - scrollTop = Math.round(Math.max(0, Math.min(this.getMaxScrollTop(), scrollTop))) + scrollTop = roundToPhysicalPixelBoundary(Math.max(0, Math.min(this.getMaxScrollTop(), scrollTop))) if (scrollTop !== this.scrollTop) { this.derivedDimensionsCache = {} this.scrollTopPending = true @@ -2835,7 +2826,7 @@ class TextEditorComponent { setScrollLeft (scrollLeft) { if (Number.isNaN(scrollLeft) || scrollLeft == null) return false - scrollLeft = Math.round(Math.max(0, Math.min(this.getMaxScrollLeft(), scrollLeft))) + scrollLeft = roundToPhysicalPixelBoundary(Math.max(0, Math.min(this.getMaxScrollLeft(), scrollLeft))) if (scrollLeft !== this.scrollLeft) { this.scrollLeftPending = true this.scrollLeft = scrollLeft @@ -2958,11 +2949,11 @@ class TextEditorComponent { } setInputEnabled (inputEnabled) { - this.props.model.update({readOnly: !inputEnabled}) + this.props.model.update({keyboardInputEnabled: inputEnabled}) } - isInputEnabled (inputEnabled) { - return !this.props.model.isReadOnly() + isInputEnabled () { + return !this.props.model.isReadOnly() && this.props.model.isKeyboardInputEnabled() } getHiddenInput () { @@ -3119,7 +3110,7 @@ class GutterContainerComponent { }, $.div({style: innerStyle}, guttersToRender.map((gutter) => { - if (gutter.name === 'line-number') { + if (gutter.type === 'line-number') { return this.renderLineNumberGutter(gutter) } else { return $(CustomGutterComponent, { @@ -3138,18 +3129,29 @@ class GutterContainerComponent { renderLineNumberGutter (gutter) { const { - rootComponent, isLineNumberGutterVisible, showLineNumbers, hasInitialMeasurements, lineNumbersToRender, + rootComponent, showLineNumbers, hasInitialMeasurements, lineNumbersToRender, renderedStartRow, renderedEndRow, rowsPerTile, decorationsToRender, didMeasureVisibleBlockDecoration, scrollHeight, lineNumberGutterWidth, lineHeight } = this.props - if (!isLineNumberGutterVisible) return null + if (!gutter.isVisible()) { + return null + } + + const oneTrueLineNumberGutter = gutter.name === 'line-number' + const ref = oneTrueLineNumberGutter ? 'lineNumberGutter' : undefined + const width = oneTrueLineNumberGutter ? lineNumberGutterWidth : undefined if (hasInitialMeasurements) { const {maxDigits, keys, bufferRows, screenRows, softWrappedFlags, foldableFlags} = lineNumbersToRender return $(LineNumberGutterComponent, { - ref: 'lineNumberGutter', + ref, element: gutter.getElement(), + name: gutter.name, + className: gutter.className, + labelFn: gutter.labelFn, + onMouseDown: gutter.onMouseDown, + onMouseMove: gutter.onMouseMove, rootComponent: rootComponent, startRow: renderedStartRow, endRow: renderedEndRow, @@ -3160,18 +3162,22 @@ class GutterContainerComponent { screenRows: screenRows, softWrappedFlags: softWrappedFlags, foldableFlags: foldableFlags, - decorations: decorationsToRender.lineNumbers, + decorations: decorationsToRender.lineNumbers.get(gutter.name) || [], blockDecorations: decorationsToRender.blocks, didMeasureVisibleBlockDecoration: didMeasureVisibleBlockDecoration, height: scrollHeight, - width: lineNumberGutterWidth, + width, lineHeight: lineHeight, showLineNumbers }) } else { return $(LineNumberGutterComponent, { - ref: 'lineNumberGutter', + ref, element: gutter.getElement(), + name: gutter.name, + className: gutter.className, + onMouseDown: gutter.onMouseDown, + onMouseMove: gutter.onMouseMove, maxDigits: lineNumbersToRender.maxDigits, showLineNumbers }) @@ -3199,7 +3205,8 @@ class LineNumberGutterComponent { render () { const { rootComponent, showLineNumbers, height, width, startRow, endRow, rowsPerTile, - maxDigits, keys, bufferRows, screenRows, softWrappedFlags, foldableFlags, decorations + maxDigits, keys, bufferRows, screenRows, softWrappedFlags, foldableFlags, decorations, + className } = this.props let children = null @@ -3227,8 +3234,12 @@ class LineNumberGutterComponent { let number = null if (showLineNumbers) { - number = softWrapped ? '•' : bufferRow + 1 - number = NBSP_CHARACTER.repeat(maxDigits - number.length) + number + if (this.props.labelFn == null) { + number = softWrapped ? '•' : bufferRow + 1 + number = NBSP_CHARACTER.repeat(maxDigits - number.length) + number + } else { + number = this.props.labelFn({bufferRow, screenRow, foldable, softWrapped, maxDigits}) + } } // We need to adjust the line number position to account for block @@ -3255,6 +3266,7 @@ class LineNumberGutterComponent { const tileTop = rootComponent.pixelPositionBeforeBlocksForRow(tileStartRow) const tileBottom = rootComponent.pixelPositionBeforeBlocksForRow(tileEndRow) const tileHeight = tileBottom - tileTop + const tileWidth = width != null && width > 0 ? width + 'px' : '' children[i] = $.div({ key: rootComponent.idsByTileStartRow.get(tileStartRow), @@ -3263,20 +3275,26 @@ class LineNumberGutterComponent { position: 'absolute', top: 0, height: tileHeight + 'px', - width: width + 'px', + width: tileWidth, transform: `translateY(${tileTop}px)` } }, ...tileChildren) } } + let rootClassName = 'gutter line-numbers' + if (className) { + rootClassName += ' ' + className + } + return $.div( { - className: 'gutter line-numbers', - attributes: {'gutter-name': 'line-number'}, + className: rootClassName, + attributes: {'gutter-name': this.props.name}, style: {position: 'relative', height: ceilToPhysicalPixelBoundary(height) + 'px'}, on: { - mousedown: this.didMouseDown + mousedown: this.didMouseDown, + mousemove: this.didMouseMove } }, $.div({key: 'placeholder', className: 'line-number dummy', style: {visibility: 'hidden'}}, @@ -3298,6 +3316,8 @@ class LineNumberGutterComponent { if (oldProps.endRow !== newProps.endRow) return true if (oldProps.rowsPerTile !== newProps.rowsPerTile) return true if (oldProps.maxDigits !== newProps.maxDigits) return true + if (oldProps.labelFn !== newProps.labelFn) return true + if (oldProps.className !== newProps.className) return true if (newProps.didMeasureVisibleBlockDecoration) return true if (!arraysEqual(oldProps.keys, newProps.keys)) return true if (!arraysEqual(oldProps.bufferRows, newProps.bufferRows)) return true @@ -3344,7 +3364,27 @@ class LineNumberGutterComponent { } didMouseDown (event) { - this.props.rootComponent.didMouseDownOnLineNumberGutter(event) + if (this.props.onMouseDown == null) { + this.props.rootComponent.didMouseDownOnLineNumberGutter(event) + } else { + const {bufferRow, screenRow} = event.target.dataset + this.props.onMouseDown({ + bufferRow: parseInt(bufferRow, 10), + screenRow: parseInt(screenRow, 10), + domEvent: event + }) + } + } + + didMouseMove (event) { + if (this.props.onMouseMove != null) { + const {bufferRow, screenRow} = event.target.dataset + this.props.onMouseMove({ + bufferRow: parseInt(bufferRow, 10), + screenRow: parseInt(screenRow, 10), + domEvent: event + }) + } } } @@ -3352,7 +3392,8 @@ class LineNumberComponent { constructor (props) { const {className, width, marginTop, bufferRow, screenRow, number, nodePool} = props this.props = props - const style = {width: width + 'px'} + const style = {} + if (width != null && width > 0) style.width = width + 'px' if (marginTop != null && marginTop > 0) style.marginTop = marginTop + 'px' this.element = nodePool.getElement('DIV', className, style) this.element.dataset.bufferRow = bufferRow @@ -3372,22 +3413,31 @@ class LineNumberComponent { if (this.props.bufferRow !== bufferRow) this.element.dataset.bufferRow = bufferRow if (this.props.screenRow !== screenRow) this.element.dataset.screenRow = screenRow if (this.props.className !== className) this.element.className = className - if (this.props.width !== width) this.element.style.width = width + 'px' + if (this.props.width !== width) { + if (width != null && width > 0) { + this.element.style.width = width + 'px' + } else { + this.element.style.width = '' + } + } if (this.props.marginTop !== marginTop) { - if (marginTop != null) { + if (marginTop != null && marginTop > 0) { this.element.style.marginTop = marginTop + 'px' } else { this.element.style.marginTop = '' } } + if (this.props.number !== number) { - if (number) { - this.element.insertBefore(nodePool.getTextNode(number), this.element.firstChild) - } else { + if (this.props.number != null) { const numberNode = this.element.firstChild numberNode.remove() nodePool.release(numberNode) } + + if (number != null) { + this.element.insertBefore(nodePool.getTextNode(number), this.element.firstChild) + } } this.props = props @@ -3413,9 +3463,13 @@ class CustomGutterComponent { } render () { + let className = 'gutter' + if (this.props.className) { + className += ' ' + this.props.className + } return $.div( { - className: 'gutter', + className, attributes: {'gutter-name': this.props.name}, style: { display: this.props.visible ? '' : 'none' @@ -3515,7 +3569,7 @@ class CursorsAndInputComponent { const cursorStyle = { height: cursorHeight, - width: pixelWidth + 'px', + width: Math.min(pixelWidth, scrollWidth - pixelLeft) + 'px', transform: `translate(${pixelLeft}px, ${pixelTop}px)` } if (extraCursorStyle) Object.assign(cursorStyle, extraCursorStyle) diff --git a/src/text-editor-registry.js b/src/text-editor-registry.js index 650d945fb..e9dbf2f5c 100644 --- a/src/text-editor-registry.js +++ b/src/text-editor-registry.js @@ -1,3 +1,4 @@ +const _ = require('underscore-plus') const {Emitter, Disposable, CompositeDisposable} = require('event-kit') const TextEditor = require('./text-editor') const ScopeDescriptor = require('./scope-descriptor') @@ -147,11 +148,11 @@ class TextEditorRegistry { } this.editorsWithMaintainedConfig.add(editor) - this.subscribeToSettingsForEditorScope(editor) - const grammarChangeSubscription = editor.onDidChangeGrammar(() => { - this.subscribeToSettingsForEditorScope(editor) + this.updateAndMonitorEditorSettings(editor) + const languageChangeSubscription = editor.buffer.onDidChangeLanguageMode((newLanguageMode, oldLanguageMode) => { + this.updateAndMonitorEditorSettings(editor, oldLanguageMode) }) - this.subscriptions.add(grammarChangeSubscription) + this.subscriptions.add(languageChangeSubscription) const updateTabTypes = () => { const configOptions = {scope: editor.getRootScopeDescriptor()} @@ -169,8 +170,8 @@ class TextEditorRegistry { return new Disposable(() => { this.editorsWithMaintainedConfig.delete(editor) tokenizeSubscription.dispose() - grammarChangeSubscription.dispose() - this.subscriptions.remove(grammarChangeSubscription) + languageChangeSubscription.dispose() + this.subscriptions.remove(languageChangeSubscription) this.subscriptions.remove(tokenizeSubscription) }) } @@ -204,7 +205,7 @@ class TextEditorRegistry { // Returns a {String} scope name, or `null` if no override has been set // for the given editor. getGrammarOverride (editor) { - return editor.getBuffer().getLanguageMode().grammar.scopeName + return atom.grammars.getAssignedLanguageId(editor.getBuffer()) } // Deprecated: Remove any grammar override that has been set for the given {TextEditor}. @@ -214,14 +215,43 @@ class TextEditorRegistry { atom.grammars.autoAssignLanguageMode(editor.getBuffer()) } - async subscribeToSettingsForEditorScope (editor) { + async updateAndMonitorEditorSettings (editor, oldLanguageMode) { await this.initialPackageActivationPromise + this.updateEditorSettingsForLanguageMode(editor, oldLanguageMode) + this.subscribeToSettingsForEditorScope(editor) + } + + updateEditorSettingsForLanguageMode (editor, oldLanguageMode) { + const newLanguageMode = editor.buffer.getLanguageMode() + + if (oldLanguageMode) { + const newSettings = this.textEditorParamsForScope(newLanguageMode.rootScopeDescriptor) + const oldSettings = this.textEditorParamsForScope(oldLanguageMode.rootScopeDescriptor) + + const updatedSettings = {} + for (const [, paramName] of EDITOR_PARAMS_BY_SETTING_KEY) { + // Update the setting only if it has changed between the two language + // modes. This prevents user-modified settings in an editor (like + // 'softWrapped') from being reset when the language mode changes. + if (!_.isEqual(newSettings[paramName], oldSettings[paramName])) { + updatedSettings[paramName] = newSettings[paramName] + } + } + + if (_.size(updatedSettings) > 0) { + editor.update(updatedSettings) + } + } else { + editor.update(this.textEditorParamsForScope(newLanguageMode.rootScopeDescriptor)) + } + } + + subscribeToSettingsForEditorScope (editor) { + if (!this.editorsWithMaintainedConfig) return const scopeDescriptor = editor.getRootScopeDescriptor() const scopeChain = scopeDescriptor.getScopeChain() - editor.update(this.textEditorParamsForScope(scopeDescriptor)) - if (!this.scopesWithConfigSubscriptions.has(scopeChain)) { this.scopesWithConfigSubscriptions.add(scopeChain) const configOptions = {scope: scopeDescriptor} diff --git a/src/text-editor.js b/src/text-editor.js index 47fb9f485..3616db28c 100644 --- a/src/text-editor.js +++ b/src/text-editor.js @@ -11,6 +11,7 @@ const Cursor = require('./cursor') const Selection = require('./selection') const NullGrammar = require('./null-grammar') const TextMateLanguageMode = require('./text-mate-language-mode') +const ScopeDescriptor = require('./scope-descriptor') const TextMateScopeSelector = require('first-mate').ScopeSelector const GutterContainer = require('./gutter-container') @@ -41,9 +42,10 @@ const DEFAULT_NON_WORD_CHARACTERS = "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…" // then be called with all current editor instances and also when any editor is // created in the future. // -// ```coffee -// atom.workspace.observeTextEditors (editor) -> +// ```js +// atom.workspace.observeTextEditors(editor => { // editor.insertText('Hello World') +// }) // ``` // // ## Buffer vs. Screen Coordinates @@ -105,6 +107,13 @@ class TextEditor { } state.assert = atomEnvironment.assert.bind(atomEnvironment) + + // Semantics of the readOnly flag have changed since its introduction. + // Only respect readOnly2, which has been set with the current readOnly semantics. + delete state.readOnly + state.readOnly = state.readOnly2 + delete state.readOnly2 + const editor = new TextEditor(state) if (state.registered) { const disposable = atomEnvironment.textEditors.add(editor) @@ -128,6 +137,7 @@ class TextEditor { this.decorationManager = params.decorationManager this.selectionsMarkerLayer = params.selectionsMarkerLayer this.mini = (params.mini != null) ? params.mini : false + this.keyboardInputEnabled = (params.keyboardInputEnabled != null) ? params.keyboardInputEnabled : true this.readOnly = (params.readOnly != null) ? params.readOnly : false this.placeholderText = params.placeholderText this.showLineNumbers = params.showLineNumbers @@ -223,7 +233,7 @@ class TextEditor { this.defaultMarkerLayer = this.displayLayer.addMarkerLayer() if (!this.selectionsMarkerLayer) { - this.selectionsMarkerLayer = this.addMarkerLayer({maintainHistory: true, persistent: true}) + this.selectionsMarkerLayer = this.addMarkerLayer({maintainHistory: true, persistent: true, role: 'selections'}) } this.decorationManager = new DecorationManager(this) @@ -248,6 +258,7 @@ class TextEditor { this.gutterContainer = new GutterContainer(this) this.lineNumberGutter = this.gutterContainer.addGutter({ name: 'line-number', + type: 'line-number', priority: 0, visible: params.lineNumberGutterVisible }) @@ -414,6 +425,15 @@ class TextEditor { } break + case 'keyboardInputEnabled': + if (value !== this.keyboardInputEnabled) { + this.keyboardInputEnabled = value + if (this.component != null) { + this.component.scheduleUpdate() + } + } + break + case 'placeholderText': if (value !== this.placeholderText) { this.placeholderText = value @@ -544,7 +564,8 @@ class TextEditor { softWrapAtPreferredLineLength: this.softWrapAtPreferredLineLength, preferredLineLength: this.preferredLineLength, mini: this.mini, - readOnly: this.readOnly, + readOnly2: this.readOnly, // readOnly encompassed both readOnly and keyboardInputEnabled + keyboardInputEnabled: this.keyboardInputEnabled, editorWidthInChars: this.editorWidthInChars, width: this.width, maxScreenLineLength: this.maxScreenLineLength, @@ -986,6 +1007,12 @@ class TextEditor { isReadOnly () { return this.readOnly } + enableKeyboardInput (enabled) { + this.update({keyboardInputEnabled: enabled}) + } + + isKeyboardInputEnabled () { return this.keyboardInputEnabled } + onDidChangeMini (callback) { return this.emitter.on('did-change-mini', callback) } @@ -994,6 +1021,10 @@ class TextEditor { isLineNumberGutterVisible () { return this.lineNumberGutter.isVisible() } + anyLineNumberGutterVisible () { + return this.getGutters().some(gutter => gutter.type === 'line-number' && gutter.visible) + } + onDidChangeLineNumberGutterVisible (callback) { return this.emitter.on('did-change-line-number-gutter-visible', callback) } @@ -1305,7 +1336,12 @@ class TextEditor { // Essential: Replaces the entire contents of the buffer with the given {String}. // // * `text` A {String} to replace with - setText (text) { return this.buffer.setText(text) } + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + setText (text, options = {}) { + if (!this.ensureWritable('setText', options)) return + return this.buffer.setText(text) + } // Essential: Set the text in the given {Range} in buffer coordinates. // @@ -1313,10 +1349,12 @@ class TextEditor { // * `text` A {String} // * `options` (optional) {Object} // * `normalizeLineEndings` (optional) {Boolean} (default: true) - // * `undo` (optional) {String} 'skip' will skip the undo system + // * `undo` (optional) *Deprecated* {String} 'skip' will skip the undo system. This property is deprecated. Call groupLastChanges() on the {TextBuffer} afterward instead. + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) // // Returns the {Range} of the newly-inserted text. - setTextInBufferRange (range, text, options) { + setTextInBufferRange (range, text, options = {}) { + if (!this.ensureWritable('setTextInBufferRange', options)) return return this.getBuffer().setTextInRange(range, text, options) } @@ -1325,9 +1363,9 @@ class TextEditor { // * `text` A {String} representing the text to insert. // * `options` (optional) See {Selection::insertText}. // - // Returns a {Range} when the text has been inserted - // Returns a {Boolean} false when the text has not been inserted + // Returns a {Range} when the text has been inserted. Returns a {Boolean} `false` when the text has not been inserted. insertText (text, options = {}) { + if (!this.ensureWritable('insertText', options)) return if (!this.emitWillInsertTextEvent(text)) return false let groupLastChanges = false @@ -1351,20 +1389,31 @@ class TextEditor { } // Essential: For each selection, replace the selected text with a newline. - insertNewline (options) { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + insertNewline (options = {}) { return this.insertText('\n', options) } // Essential: For each selection, if the selection is empty, delete the character // following the cursor. Otherwise delete the selected text. - delete () { - return this.mutateSelectedText(selection => selection.delete()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + delete (options = {}) { + if (!this.ensureWritable('delete', options)) return + return this.mutateSelectedText(selection => selection.delete(options)) } // Essential: For each selection, if the selection is empty, delete the character // preceding the cursor. Otherwise delete the selected text. - backspace () { - return this.mutateSelectedText(selection => selection.backspace()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + backspace (options = {}) { + if (!this.ensureWritable('backspace', options)) return + return this.mutateSelectedText(selection => selection.backspace(options)) } // Extended: Mutate the text of all the selections in a single transaction. @@ -1385,7 +1434,12 @@ class TextEditor { // Move lines intersecting the most recent selection or multiple selections // up by one row in screen coordinates. - moveLineUp () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + moveLineUp (options = {}) { + if (!this.ensureWritable('moveLineUp', options)) return + const selections = this.getSelectedBufferRanges().sort((a, b) => a.compare(b)) if (selections[0].start.row === 0) return @@ -1453,7 +1507,12 @@ class TextEditor { // Move lines intersecting the most recent selection or multiple selections // down by one row in screen coordinates. - moveLineDown () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + moveLineDown (options = {}) { + if (!this.ensureWritable('moveLineDown', options)) return + const selections = this.getSelectedBufferRanges() selections.sort((a, b) => b.compare(a)) @@ -1525,7 +1584,11 @@ class TextEditor { } // Move any active selections one column to the left. - moveSelectionLeft () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + moveSelectionLeft (options = {}) { + if (!this.ensureWritable('moveSelectionLeft', options)) return const selections = this.getSelectedBufferRanges() const noSelectionAtStartOfLine = selections.every(selection => selection.start.column !== 0) @@ -1549,7 +1612,11 @@ class TextEditor { } // Move any active selections one column to the right. - moveSelectionRight () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + moveSelectionRight (options = {}) { + if (!this.ensureWritable('moveSelectionRight', options)) return const selections = this.getSelectedBufferRanges() const noSelectionAtEndOfLine = selections.every(selection => { return selection.end.column !== this.buffer.lineLengthForRow(selection.end.row) @@ -1574,7 +1641,12 @@ class TextEditor { } } - duplicateLines () { + // Duplicate all lines containing active selections. + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + duplicateLines (options = {}) { + if (!this.ensureWritable('duplicateLines', options)) return this.transact(() => { const selections = this.getSelectionsOrderedByBufferPosition() const previousSelectionRanges = [] @@ -1661,7 +1733,11 @@ class TextEditor { // // If the selection is empty, the characters preceding and following the cursor // are swapped. Otherwise, the selected characters are reversed. - transpose () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + transpose (options = {}) { + if (!this.ensureWritable('transpose', options)) return this.mutateSelectedText(selection => { if (selection.isEmpty()) { selection.selectRight() @@ -1679,23 +1755,35 @@ class TextEditor { // // For each selection, if the selection is empty, converts the containing word // to upper case. Otherwise convert the selected text to upper case. - upperCase () { - this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toUpperCase()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + upperCase (options = {}) { + if (!this.ensureWritable('upperCase', options)) return + this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toUpperCase(options)) } // Extended: Convert the selected text to lower case. // // For each selection, if the selection is empty, converts the containing word // to upper case. Otherwise convert the selected text to upper case. - lowerCase () { - this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toLowerCase()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + lowerCase (options = {}) { + if (!this.ensureWritable('lowerCase', options)) return + this.replaceSelectedText({selectWordIfEmpty: true}, text => text.toLowerCase(options)) } // Extended: Toggle line comments for rows intersecting selections. // // If the current grammar doesn't support comments, does nothing. - toggleLineCommentsInSelection () { - this.mutateSelectedText(selection => selection.toggleLineComments()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + toggleLineCommentsInSelection (options = {}) { + if (!this.ensureWritable('toggleLineCommentsInSelection', options)) return + this.mutateSelectedText(selection => selection.toggleLineComments(options)) } // Convert multiple lines to a single line. @@ -1706,20 +1794,32 @@ class TextEditor { // // Joining a line means that multiple lines are converted to a single line with // the contents of each of the original non-empty lines separated by a space. - joinLines () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + joinLines (options = {}) { + if (!this.ensureWritable('joinLines', options)) return this.mutateSelectedText(selection => selection.joinLines()) } // Extended: For each cursor, insert a newline at beginning the following line. - insertNewlineBelow () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + insertNewlineBelow (options = {}) { + if (!this.ensureWritable('insertNewlineBelow', options)) return this.transact(() => { this.moveToEndOfLine() - this.insertNewline() + this.insertNewline(options) }) } // Extended: For each cursor, insert a newline at the end of the preceding line. - insertNewlineAbove () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + insertNewlineAbove (options = {}) { + if (!this.ensureWritable('insertNewlineAbove', options)) return this.transact(() => { const bufferRow = this.getCursorBufferPosition().row const indentLevel = this.indentationForBufferRow(bufferRow) @@ -1727,7 +1827,7 @@ class TextEditor { this.moveToBeginningOfLine() this.moveLeft() - this.insertNewline() + this.insertNewline(options) if (this.shouldAutoIndent() && (this.indentationForBufferRow(bufferRow) < indentLevel)) { this.setIndentationForBufferRow(bufferRow, indentLevel) @@ -1743,62 +1843,117 @@ class TextEditor { // Extended: For each selection, if the selection is empty, delete all characters // of the containing word that precede the cursor. Otherwise delete the // selected text. - deleteToBeginningOfWord () { - this.mutateSelectedText(selection => selection.deleteToBeginningOfWord()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToBeginningOfWord (options = {}) { + if (!this.ensureWritable('deleteToBeginningOfWord', options)) return + this.mutateSelectedText(selection => selection.deleteToBeginningOfWord(options)) } // Extended: Similar to {::deleteToBeginningOfWord}, but deletes only back to the // previous word boundary. - deleteToPreviousWordBoundary () { - this.mutateSelectedText(selection => selection.deleteToPreviousWordBoundary()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToPreviousWordBoundary (options = {}) { + if (!this.ensureWritable('deleteToPreviousWordBoundary', options)) return + this.mutateSelectedText(selection => selection.deleteToPreviousWordBoundary(options)) } // Extended: Similar to {::deleteToEndOfWord}, but deletes only up to the // next word boundary. - deleteToNextWordBoundary () { - this.mutateSelectedText(selection => selection.deleteToNextWordBoundary()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToNextWordBoundary (options = {}) { + if (!this.ensureWritable('deleteToNextWordBoundary', options)) return + this.mutateSelectedText(selection => selection.deleteToNextWordBoundary(options)) } // Extended: For each selection, if the selection is empty, delete all characters // of the containing subword following the cursor. Otherwise delete the selected // text. - deleteToBeginningOfSubword () { - this.mutateSelectedText(selection => selection.deleteToBeginningOfSubword()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToBeginningOfSubword (options = {}) { + if (!this.ensureWritable('deleteToBeginningOfSubword', options)) return + this.mutateSelectedText(selection => selection.deleteToBeginningOfSubword(options)) } // Extended: For each selection, if the selection is empty, delete all characters // of the containing subword following the cursor. Otherwise delete the selected // text. - deleteToEndOfSubword () { - this.mutateSelectedText(selection => selection.deleteToEndOfSubword()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToEndOfSubword (options = {}) { + if (!this.ensureWritable('deleteToEndOfSubword', options)) return + this.mutateSelectedText(selection => selection.deleteToEndOfSubword(options)) } // Extended: For each selection, if the selection is empty, delete all characters // of the containing line that precede the cursor. Otherwise delete the // selected text. - deleteToBeginningOfLine () { - this.mutateSelectedText(selection => selection.deleteToBeginningOfLine()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToBeginningOfLine (options = {}) { + if (!this.ensureWritable('deleteToBeginningOfLine', options)) return + this.mutateSelectedText(selection => selection.deleteToBeginningOfLine(options)) } // Extended: For each selection, if the selection is not empty, deletes the // selection; otherwise, deletes all characters of the containing line // following the cursor. If the cursor is already at the end of the line, // deletes the following newline. - deleteToEndOfLine () { - this.mutateSelectedText(selection => selection.deleteToEndOfLine()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToEndOfLine (options = {}) { + if (!this.ensureWritable('deleteToEndOfLine', options)) return + this.mutateSelectedText(selection => selection.deleteToEndOfLine(options)) } // Extended: For each selection, if the selection is empty, delete all characters // of the containing word following the cursor. Otherwise delete the selected // text. - deleteToEndOfWord () { - this.mutateSelectedText(selection => selection.deleteToEndOfWord()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteToEndOfWord (options = {}) { + if (!this.ensureWritable('deleteToEndOfWord', options)) return + this.mutateSelectedText(selection => selection.deleteToEndOfWord(options)) } // Extended: Delete all lines intersecting selections. - deleteLine () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + deleteLine (options = {}) { + if (!this.ensureWritable('deleteLine', options)) return this.mergeSelectionsOnSameRows() - this.mutateSelectedText(selection => selection.deleteLine()) + this.mutateSelectedText(selection => selection.deleteLine(options)) + } + + // Private: Ensure that this editor is not marked read-only before allowing a buffer modification to occur. If + // the editor is read-only, require an explicit opt-in option to proceed (`bypassReadOnly`) or throw an Error. + ensureWritable (methodName, opts) { + if (!opts.bypassReadOnly && this.isReadOnly()) { + if (atom.inDevMode() || atom.inSpecMode()) { + const e = new Error('Attempt to mutate a read-only TextEditor') + e.detail = + `Your package is attempting to call ${methodName} on an editor that has been marked read-only. ` + + 'Pass {bypassReadOnly: true} to modify it anyway, or test editors with .isReadOnly() before attempting ' + + 'modifications.' + throw e + } + + return false + } + + return true } /* @@ -1806,14 +1961,22 @@ class TextEditor { */ // Essential: Undo the last change. - undo () { - this.avoidMergingSelections(() => this.buffer.undo()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + undo (options = {}) { + if (!this.ensureWritable('undo', options)) return + this.avoidMergingSelections(() => this.buffer.undo({selectionsMarkerLayer: this.selectionsMarkerLayer})) this.getLastSelection().autoscroll() } // Essential: Redo the last change. - redo () { - this.avoidMergingSelections(() => this.buffer.redo()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. (default: false) + redo (options = {}) { + if (!this.ensureWritable('redo', options)) return + this.avoidMergingSelections(() => this.buffer.redo({selectionsMarkerLayer: this.selectionsMarkerLayer})) this.getLastSelection().autoscroll() } @@ -1830,7 +1993,13 @@ class TextEditor { // still 'groupable', the two transactions are merged with respect to undo and redo. // * `fn` A {Function} to call inside the transaction. transact (groupingInterval, fn) { - return this.buffer.transact(groupingInterval, fn) + const options = {selectionsMarkerLayer: this.selectionsMarkerLayer} + if (typeof groupingInterval === 'function') { + fn = groupingInterval + } else { + options.groupingInterval = groupingInterval + } + return this.buffer.transact(options, fn) } // Extended: Abort an open transaction, undoing any operations performed so far @@ -1841,7 +2010,9 @@ class TextEditor { // with {::revertToCheckpoint} and {::groupChangesSinceCheckpoint}. // // Returns a checkpoint value. - createCheckpoint () { return this.buffer.createCheckpoint() } + createCheckpoint () { + return this.buffer.createCheckpoint({selectionsMarkerLayer: this.selectionsMarkerLayer}) + } // Extended: Revert the buffer to the state it was in when the given // checkpoint was created. @@ -1865,7 +2036,9 @@ class TextEditor { // * `checkpoint` The checkpoint from which to group changes. // // Returns a {Boolean} indicating whether the operation succeeded. - groupChangesSinceCheckpoint (checkpoint) { return this.buffer.groupChangesSinceCheckpoint(checkpoint) } + groupChangesSinceCheckpoint (checkpoint) { + return this.buffer.groupChangesSinceCheckpoint(checkpoint, {selectionsMarkerLayer: this.selectionsMarkerLayer}) + } /* Section: TextEditor Coordinates @@ -1956,11 +2129,11 @@ class TextEditor { // // ## Examples // - // ```coffee - // editor.clipBufferPosition([-1, -1]) # -> `[0, 0]` + // ```js + // editor.clipBufferPosition([-1, -1]) // -> `[0, 0]` // - // # When the line at buffer row 2 is 10 characters long - // editor.clipBufferPosition([2, Infinity]) # -> `[2, 10]` + // // When the line at buffer row 2 is 10 characters long + // editor.clipBufferPosition([2, Infinity]) // -> `[2, 10]` // ``` // // * `bufferPosition` The {Point} representing the position to clip. @@ -1985,11 +2158,11 @@ class TextEditor { // // ## Examples // - // ```coffee - // editor.clipScreenPosition([-1, -1]) # -> `[0, 0]` + // ```js + // editor.clipScreenPosition([-1, -1]) // -> `[0, 0]` // - // # When the line at screen row 2 is 10 characters long - // editor.clipScreenPosition([2, Infinity]) # -> `[2, 10]` + // // When the line at screen row 2 is 10 characters long + // editor.clipScreenPosition([2, Infinity]) // -> `[2, 10]` // ``` // // * `screenPosition` The {Point} representing the position to clip. @@ -2673,7 +2846,7 @@ class TextEditor { return this.cursors.slice() } - // Extended: Get all {Cursors}s, ordered by their position in the buffer + // Extended: Get all {Cursor}s, ordered by their position in the buffer // instead of the order in which they were added. // // Returns an {Array} of {Selection}s. @@ -3547,13 +3720,21 @@ class TextEditor { } // Extended: Indent rows intersecting selections by one level. - indentSelectedRows () { - return this.mutateSelectedText(selection => selection.indentSelectedRows()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + indentSelectedRows (options = {}) { + if (!this.ensureWritable('indentSelectedRows', options)) return + return this.mutateSelectedText(selection => selection.indentSelectedRows(options)) } // Extended: Outdent rows intersecting selections by one level. - outdentSelectedRows () { - return this.mutateSelectedText(selection => selection.outdentSelectedRows()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + outdentSelectedRows (options = {}) { + if (!this.ensureWritable('outdentSelectedRows', options)) return + return this.mutateSelectedText(selection => selection.outdentSelectedRows(options)) } // Extended: Get the indentation level of the given line of text. @@ -3584,13 +3765,21 @@ class TextEditor { // Extended: Indent rows intersecting selections based on the grammar's suggested // indent level. - autoIndentSelectedRows () { - return this.mutateSelectedText(selection => selection.autoIndentSelectedRows()) + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + autoIndentSelectedRows (options = {}) { + if (!this.ensureWritable('autoIndentSelectedRows', options)) return + return this.mutateSelectedText(selection => selection.autoIndentSelectedRows(options)) } // Indent all lines intersecting selections. See {Selection::indent} for more // information. + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. indent (options = {}) { + if (!this.ensureWritable('indent', options)) return if (options.autoIndent == null) options.autoIndent = this.shouldAutoIndent() this.mutateSelectedText(selection => selection.indent(options)) } @@ -3655,7 +3844,10 @@ class TextEditor { // // Returns a {ScopeDescriptor}. scopeDescriptorForBufferPosition (bufferPosition) { - return this.buffer.getLanguageMode().scopeDescriptorForPosition(bufferPosition) + const languageMode = this.buffer.getLanguageMode() + return languageMode.scopeDescriptorForPosition + ? languageMode.scopeDescriptorForPosition(bufferPosition) + : new ScopeDescriptor({scopes: ['text']}) } // Extended: Get the range in buffer coordinates of all tokens surrounding the @@ -3725,14 +3917,18 @@ class TextEditor { } // Essential: For each selection, cut the selected text. - cutSelectedText () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + cutSelectedText (options = {}) { + if (!this.ensureWritable('cutSelectedText', options)) return let maintainClipboard = false this.mutateSelectedText(selection => { if (selection.isEmpty()) { selection.selectLine() - selection.cut(maintainClipboard, true) + selection.cut(maintainClipboard, true, options.bypassReadOnly) } else { - selection.cut(maintainClipboard, false) + selection.cut(maintainClipboard, false, options.bypassReadOnly) } maintainClipboard = true }) @@ -3746,7 +3942,8 @@ class TextEditor { // corresponding clipboard selection text. // // * `options` (optional) See {Selection::insertText}. - pasteText (options) { + pasteText (options = {}) { + if (!this.ensureWritable('parseText', options)) return options = Object.assign({}, options) let {text: clipboardText, metadata} = this.constructor.clipboard.readWithMetadata() if (!this.emitWillInsertTextEvent(clipboardText)) return false @@ -3787,10 +3984,14 @@ class TextEditor { // Essential: For each selection, if the selection is empty, cut all characters // of the containing screen line following the cursor. Otherwise cut the selected // text. - cutToEndOfLine () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + cutToEndOfLine (options = {}) { + if (!this.ensureWritable('cutToEndOfLine', options)) return let maintainClipboard = false this.mutateSelectedText(selection => { - selection.cutToEndOfLine(maintainClipboard) + selection.cutToEndOfLine(maintainClipboard, options) maintainClipboard = true }) } @@ -3798,10 +3999,14 @@ class TextEditor { // Essential: For each selection, if the selection is empty, cut all characters // of the containing buffer line following the cursor. Otherwise cut the // selected text. - cutToEndOfBufferLine () { + // + // * `options` (optional) {Object} + // * `bypassReadOnly` (optional) {Boolean} Must be `true` to modify a read-only editor. + cutToEndOfBufferLine (options = {}) { + if (!this.ensureWritable('cutToEndOfBufferLine', options)) return let maintainClipboard = false this.mutateSelectedText(selection => { - selection.cutToEndOfBufferLine(maintainClipboard) + selection.cutToEndOfBufferLine(maintainClipboard, options) maintainClipboard = true }) } @@ -3893,7 +4098,7 @@ class TextEditor { // Extended: Unfold all existing folds. unfoldAll () { const result = this.displayLayer.destroyAllFolds() - this.scrollToCursorPosition() + if (result.length > 0) this.scrollToCursorPosition() return result } @@ -4011,6 +4216,29 @@ class TextEditor { // window. (default: -100) // * `visible` (optional) {Boolean} specifying whether the gutter is visible // initially after being created. (default: true) + // * `type` (optional) {String} specifying the type of gutter to create. `'decorated'` + // gutters are useful as a destination for decorations created with {Gutter::decorateMarker}. + // `'line-number'` gutters. + // * `class` (optional) {String} added to the CSS classnames of the gutter's root DOM element. + // * `labelFn` (optional) {Function} called by a `'line-number'` gutter to generate the label for each line number + // element. Should return a {String} that will be used to label the corresponding line. + // * `lineData` an {Object} containing information about each line to label. + // * `bufferRow` {Number} indicating the zero-indexed buffer index of this line. + // * `screenRow` {Number} indicating the zero-indexed screen index. + // * `foldable` {Boolean} that is `true` if a fold may be created here. + // * `softWrapped` {Boolean} if this screen row is the soft-wrapped continuation of the same buffer row. + // * `maxDigits` {Number} the maximum number of digits necessary to represent any known screen row. + // * `onMouseDown` (optional) {Function} to be called when a mousedown event is received by a line-number + // element within this `type: 'line-number'` {Gutter}. If unspecified, the default behavior is to select the + // clicked buffer row. + // * `lineData` an {Object} containing information about the line that's being clicked. + // * `bufferRow` {Number} of the originating line element + // * `screenRow` {Number} + // * `onMouseMove` (optional) {Function} to be called when a mousemove event occurs on a line-number element within + // within this `type: 'line-number'` {Gutter}. + // * `lineData` an {Object} containing information about the line that's being clicked. + // * `bufferRow` {Number} of the originating line element + // * `screenRow` {Number} // // Returns the newly-created {Gutter}. addGutter (options) { @@ -4615,7 +4843,7 @@ class TextEditor { let endRow = bufferRow const rowCount = this.getLineCount() - while (endRow < rowCount) { + while (endRow + 1 < rowCount) { if (!NON_WHITESPACE_REGEXP.test(this.lineTextForBufferRow(endRow + 1))) break if (languageMode.isRowCommented(endRow + 1) !== isCommented) break endRow++ diff --git a/src/text-mate-language-mode.js b/src/text-mate-language-mode.js index 1a7cb6d2e..471af9af2 100644 --- a/src/text-mate-language-mode.js +++ b/src/text-mate-language-mode.js @@ -7,6 +7,7 @@ const ScopeDescriptor = require('./scope-descriptor') const NullGrammar = require('./null-grammar') const {OnigRegExp} = require('oniguruma') const {toFirstMateScopeId, fromFirstMateScopeId} = require('./first-mate-helpers') +const {selectorMatchesAnyScope} = require('./selectors') const NON_WHITESPACE_REGEX = /\S/ @@ -235,15 +236,18 @@ class TextMateLanguageMode { return this.buffer.getTextInRange([[0, 0], [10, 0]]) } - hasTokenForSelector (selector) { + updateForInjection (grammar) { + if (!grammar.injectionSelector) return for (const tokenizedLine of this.tokenizedLines) { if (tokenizedLine) { for (let token of tokenizedLine.tokens) { - if (selector.matches(token.scopes)) return true + if (grammar.injectionSelector.matches(token.scopes)) { + this.retokenizeLines() + return + } } } } - return false } retokenizeLines () { @@ -605,7 +609,7 @@ class TextMateLanguageMode { for (let row = point.row - 1; row >= 0; row--) { const endRow = this.endRowForFoldAtRow(row, tabLength) - if (endRow != null && endRow > point.row) { + if (endRow != null && endRow >= point.row) { return Range(Point(row, Infinity), Point(endRow, Infinity)) } } @@ -723,14 +727,6 @@ class TextMateLanguageMode { TextMateLanguageMode.prototype.chunkSize = 50 -function selectorMatchesAnyScope (selector, scopes) { - const targetClasses = selector.replace(/^\./, '').split('.') - return scopes.some((scope) => { - const scopeClasses = scope.split('.') - return _.isSubset(targetClasses, scopeClasses) - }) -} - class TextMateHighlightIterator { constructor (languageMode) { this.languageMode = languageMode diff --git a/src/theme-manager.js b/src/theme-manager.js index 68a5eb45a..389dd1bc3 100644 --- a/src/theme-manager.js +++ b/src/theme-manager.js @@ -103,7 +103,7 @@ class ThemeManager { warnForNonExistentThemes () { let themeNames = this.config.get('core.themes') || [] - if (!_.isArray(themeNames)) { themeNames = [themeNames] } + if (!Array.isArray(themeNames)) { themeNames = [themeNames] } for (let themeName of themeNames) { if (!themeName || (typeof themeName !== 'string') || !this.packageManager.resolvePackagePath(themeName)) { console.warn(`Enabled theme '${themeName}' is not installed.`) @@ -116,7 +116,7 @@ class ThemeManager { // Returns an array of theme names in the order that they should be activated. getEnabledThemeNames () { let themeNames = this.config.get('core.themes') || [] - if (!_.isArray(themeNames)) { themeNames = [themeNames] } + if (!Array.isArray(themeNames)) { themeNames = [themeNames] } themeNames = themeNames.filter((themeName) => (typeof themeName === 'string') && this.packageManager.resolvePackagePath(themeName) ) @@ -138,7 +138,7 @@ class ThemeManager { if (themeNames.length === 0) { themeNames = ['one-dark-syntax', 'one-dark-ui'] } else if (themeNames.length === 1) { - if (_.endsWith(themeNames[0], '-ui')) { + if (themeNames[0].endsWith('-ui')) { themeNames.unshift('one-dark-syntax') } else { themeNames.push('one-dark-ui') diff --git a/src/tooltip-manager.js b/src/tooltip-manager.js index 34f96775b..07b4f196a 100644 --- a/src/tooltip-manager.js +++ b/src/tooltip-manager.js @@ -153,9 +153,11 @@ class TooltipManager { } window.addEventListener('resize', hideTooltip) + window.addEventListener('keydown', hideTooltip) const disposable = new Disposable(() => { window.removeEventListener('resize', hideTooltip) + window.removeEventListener('keydown', hideTooltip) hideTooltip() tooltip.destroy() diff --git a/src/tree-sitter-grammar.js b/src/tree-sitter-grammar.js index d00344fb1..fc572221a 100644 --- a/src/tree-sitter-grammar.js +++ b/src/tree-sitter-grammar.js @@ -6,12 +6,17 @@ module.exports = class TreeSitterGrammar { constructor (registry, filePath, params) { this.registry = registry - this.id = params.id this.name = params.name - this.legacyScopeName = params.legacyScopeName - if (params.contentRegExp) this.contentRegExp = new RegExp(params.contentRegExp) + this.scopeName = params.scopeName + + // TODO - Remove the `RegExp` spelling and only support `Regex`, once all of the existing + // Tree-sitter grammars are updated to spell it `Regex`. + this.contentRegex = buildRegex(params.contentRegex || params.contentRegExp) + this.injectionRegex = buildRegex(params.injectionRegex || params.injectionRegExp) + this.firstLineRegex = buildRegex(params.firstLineRegex) this.folds = params.folds || [] + this.folds.forEach(normalizeFoldSpecification) this.commentStrings = { commentStartString: params.comments && params.comments.start, @@ -20,14 +25,22 @@ class TreeSitterGrammar { const scopeSelectors = {} for (const key in params.scopes || {}) { - scopeSelectors[key] = params.scopes[key] - .split('.') - .map(s => `syntax--${s}`) - .join(' ') + const classes = toSyntaxClasses(params.scopes[key]) + const selectors = key.split(/,\s+/) + for (let selector of selectors) { + selector = selector.trim() + if (!selector) continue + if (scopeSelectors[selector]) { + scopeSelectors[selector] = [].concat(scopeSelectors[selector], classes) + } else { + scopeSelectors[selector] = classes + } + } } this.scopeMap = new SyntaxScopeMap(scopeSelectors) - this.fileTypes = params.fileTypes + this.fileTypes = params.fileTypes || [] + this.injectionPoints = params.injectionPoints || [] // TODO - When we upgrade to a new enough version of node, use `require.resolve` // with the new `paths` option instead of this private API. @@ -39,11 +52,16 @@ class TreeSitterGrammar { this.languageModule = require(languageModulePath) this.scopesById = new Map() + this.conciseScopesById = new Map() this.idsByScope = {} this.nextScopeId = 256 + 1 this.registration = null } + inspect () { + return `TreeSitterGrammar {scopeName: ${this.scopeName}}` + } + idForScope (scope) { let id = this.idsByScope[scope] if (!id) { @@ -58,8 +76,15 @@ class TreeSitterGrammar { return this.scopesById.get(id) } - get scopeName () { - return this.id + scopeNameForScopeId (id) { + let result = this.conciseScopesById.get(id) + if (!result) { + result = this.scopesById.get(id) + .slice('syntax--'.length) + .replace(/ syntax--/g, '.') + this.conciseScopesById.set(id, result) + } + return result } activate () { @@ -70,3 +95,56 @@ class TreeSitterGrammar { if (this.registration) this.registration.dispose() } } + +const toSyntaxClasses = scopes => + typeof scopes === 'string' + ? scopes + .split('.') + .map(s => `syntax--${s}`) + .join(' ') + : Array.isArray(scopes) + ? scopes.map(toSyntaxClasses) + : scopes.match + ? {match: new RegExp(scopes.match), scopes: toSyntaxClasses(scopes.scopes)} + : Object.assign({}, scopes, {scopes: toSyntaxClasses(scopes.scopes)}) + +const NODE_NAME_REGEX = /[\w_]+/ + +function matcherForSpec (spec) { + if (typeof spec === 'string') { + if (spec[0] === '"' && spec[spec.length - 1] === '"') { + return { + type: spec.substr(1, spec.length - 2), + named: false + } + } + + if (!NODE_NAME_REGEX.test(spec)) { + return {type: spec, named: false} + } + + return {type: spec, named: true} + } + return spec +} + +function normalizeFoldSpecification (spec) { + if (spec.type) { + if (Array.isArray(spec.type)) { + spec.matchers = spec.type.map(matcherForSpec) + } else { + spec.matchers = [matcherForSpec(spec.type)] + } + } + + if (spec.start) normalizeFoldSpecification(spec.start) + if (spec.end) normalizeFoldSpecification(spec.end) +} + +function buildRegex (value) { + // Allow multiple alternatives to be specified via an array, for + // readability of the grammar file + if (Array.isArray(value)) value = value.map(_ => `(${_})`).join('|') + if (typeof value === 'string') return new RegExp(value) + return null +} diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 41c87ba00..103b8816e 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -1,48 +1,117 @@ -const {Document} = require('tree-sitter') -const {Point, Range, Emitter} = require('atom') +const Parser = require('tree-sitter') +const {Point, Range, spliceArray} = require('text-buffer') +const {Patch} = require('superstring') +const {Emitter} = require('event-kit') const ScopeDescriptor = require('./scope-descriptor') const TokenizedLine = require('./tokenized-line') const TextMateLanguageMode = require('./text-mate-language-mode') +const {matcherForSelector} = require('./selectors') let nextId = 0 +const MAX_RANGE = new Range(Point.ZERO, Point.INFINITY).freeze() +const PARSER_POOL = [] +const WORD_REGEX = /\w/ -module.exports = class TreeSitterLanguageMode { - constructor ({buffer, grammar, config}) { + static _patchSyntaxNode () { + if (!Parser.SyntaxNode.prototype.hasOwnProperty('range')) { + Object.defineProperty(Parser.SyntaxNode.prototype, 'range', { + get () { + return rangeForNode(this) + } + }) + } + } + + constructor ({buffer, grammar, config, grammars, syncOperationLimit}) { + TreeSitterLanguageMode._patchSyntaxNode() this.id = nextId++ this.buffer = buffer this.grammar = grammar this.config = config - this.document = new Document() - this.document.setInput(new TreeSitterTextBufferInput(buffer)) - this.document.setLanguage(grammar.languageModule) - this.document.parse() - this.rootScopeDescriptor = new ScopeDescriptor({scopes: [this.grammar.id]}) + this.grammarRegistry = grammars + this.parser = new Parser() + this.rootLanguageLayer = new LanguageLayer(this, grammar) + this.injectionsMarkerLayer = buffer.addMarkerLayer() + + if (syncOperationLimit != null) { + this.syncOperationLimit = syncOperationLimit + } + + this.rootScopeDescriptor = new ScopeDescriptor({scopes: [this.grammar.scopeName]}) this.emitter = new Emitter() this.isFoldableCache = [] + this.hasQueuedParse = false + + this.grammarForLanguageString = this.grammarForLanguageString.bind(this) + this.emitRangeUpdate = this.emitRangeUpdate.bind(this) + + this.subscription = this.buffer.onDidChangeText(({changes}) => { + for (let i = 0, {length} = changes; i < length; i++) { + const {oldRange, newRange} = changes[i] + spliceArray( + this.isFoldableCache, + newRange.start.row, + oldRange.end.row - oldRange.start.row, + {length: newRange.end.row - newRange.start.row} + ) + } + + this.rootLanguageLayer.update(null) + }) + + this.rootLanguageLayer.update(null).then(() => + this.emitter.emit('did-tokenize') + ) // TODO: Remove this once TreeSitterLanguageMode implements its own auto-indentation system. This // is temporarily needed in order to delegate to the TextMateLanguageMode's auto-indent system. this.regexesByPattern = {} } - getLanguageId () { - return this.grammar.id + destroy () { + this.injectionsMarkerLayer.destroy() + this.subscription.dispose() + this.rootLanguageLayer = null + this.parser = null } - bufferDidChange ({oldRange, newRange, oldText, newText}) { - const startRow = oldRange.start.row - const oldEndRow = oldRange.end.row - const newEndRow = newRange.end.row - this.isFoldableCache.splice(startRow, oldEndRow - startRow, ...new Array(newEndRow - startRow)) - this.document.edit({ - startIndex: this.buffer.characterIndexForPosition(oldRange.start), - lengthRemoved: oldText.length, - lengthAdded: newText.length, - startPosition: oldRange.start, - extentRemoved: oldRange.getExtent(), - extentAdded: newRange.getExtent() + getLanguageId () { + return this.grammar.scopeName + } + + bufferDidChange (change) { + this.rootLanguageLayer.handleTextChange(change) + for (const marker of this.injectionsMarkerLayer.getMarkers()) { + marker.languageLayer.handleTextChange(change) + } + } + + parse (language, oldTree, ranges) { + const parser = PARSER_POOL.pop() || new Parser() + parser.setLanguage(language) + const result = parser.parseTextBuffer(this.buffer.buffer, oldTree, { + syncOperationLimit: this.syncOperationLimit, + includedRanges: ranges }) + + if (result.then) { + return result.then(tree => { + PARSER_POOL.push(parser) + return tree + }) + } else { + PARSER_POOL.push(parser) + return result + } + } + + get tree () { + return this.rootLanguageLayer.tree + } + + updateForInjection (grammar) { + this.rootLanguageLayer.updateInjections(grammar) } /* @@ -50,21 +119,20 @@ class TreeSitterLanguageMode { */ buildHighlightIterator () { - const invalidatedRanges = this.document.parse() - for (let i = 0, n = invalidatedRanges.length; i < n; i++) { - const range = invalidatedRanges[i] - const startRow = range.start.row - const endRow = range.end.row - for (let row = startRow; row < endRow; row++) { - this.isFoldableCache[row] = undefined - } - this.emitter.emit('did-change-highlighting', range) - } - return new TreeSitterHighlightIterator(this) + if (!this.rootLanguageLayer) return new NullHighlightIterator() + const layerIterators = [ + this.rootLanguageLayer.buildHighlightIterator(), + ...this.injectionsMarkerLayer.getMarkers().map(m => m.languageLayer.buildHighlightIterator()) + ] + return new HighlightIterator(this, layerIterators) + } + + onDidTokenize (callback) { + return this.emitter.on('did-tokenize', callback) } onDidChangeHighlighting (callback) { - return this.emitter.on('did-change-hightlighting', callback) + return this.emitter.on('did-change-highlighting', callback) } classNameForScopeId (scopeId) { @@ -79,7 +147,15 @@ class TreeSitterLanguageMode { return this.grammar.commentStrings } - isRowCommented () { + isRowCommented (row) { + const firstNonWhitespaceRange = this.buffer.findInRangeSync( + /\S/, + new Range(new Point(row, 0), new Point(row, Infinity)) + ) + if (firstNonWhitespaceRange) { + const firstNode = this.getSyntaxNodeContainingRange(firstNonWhitespaceRange) + if (firstNode) return firstNode.type.includes('comment') + } return false } @@ -136,13 +212,17 @@ class TreeSitterLanguageMode { return this.getFoldableRangesAtIndentLevel(null) } + /** + * TODO: Make this method generate folds for nested languages (currently, + * folds are only generated for the root language layer). + */ getFoldableRangesAtIndentLevel (goalLevel) { let result = [] - let stack = [{node: this.document.rootNode, level: 0}] + let stack = [{node: this.tree.rootNode, level: 0}] while (stack.length > 0) { const {node, level} = stack.pop() - const range = this.getFoldableRangeForNode(node) + const range = this.getFoldableRangeForNode(node, this.grammar) if (range) { if (goalLevel == null || level === goalLevel) { let updatedExistingRange = false @@ -182,54 +262,71 @@ class TreeSitterLanguageMode { } getFoldableRangeContainingPoint (point, tabLength, existenceOnly = false) { - let node = this.document.rootNode.descendantForPosition(this.buffer.clipPosition(point)) - while (node) { - if (existenceOnly && node.startPosition.row < point.row) break - if (node.endPosition.row > point.row) { - const range = this.getFoldableRangeForNode(node, existenceOnly) - if (range) return range + if (!this.tree) return null + + let smallestRange + this._forEachTreeWithRange(new Range(point, point), (tree, grammar) => { + let node = tree.rootNode.descendantForPosition(this.buffer.clipPosition(point)) + while (node) { + if (existenceOnly && node.startPosition.row < point.row) return + if (node.endPosition.row > point.row) { + const range = this.getFoldableRangeForNode(node, grammar) + if (range && rangeIsSmaller(range, smallestRange)) { + smallestRange = range + return + } + } + node = node.parent } - node = node.parent + }) + + return existenceOnly + ? smallestRange && smallestRange.start.row === point.row + : smallestRange + } + + _forEachTreeWithRange (range, callback) { + if (this.rootLanguageLayer.tree) { + callback(this.rootLanguageLayer.tree, this.rootLanguageLayer.grammar) + } + + const injectionMarkers = this.injectionsMarkerLayer.findMarkers({ + intersectsRange: range + }) + + for (const injectionMarker of injectionMarkers) { + const {tree, grammar} = injectionMarker.languageLayer + if (tree) callback(tree, grammar) } } - getFoldableRangeForNode (node, existenceOnly) { - const {children, type: nodeType} = node + getFoldableRangeForNode (node, grammar, existenceOnly) { + const {children} = node const childCount = children.length - let childTypes - for (var i = 0, {length} = this.grammar.folds; i < length; i++) { - const foldEntry = this.grammar.folds[i] + for (var i = 0, {length} = grammar.folds; i < length; i++) { + const foldSpec = grammar.folds[i] - if (foldEntry.type) { - if (typeof foldEntry.type === 'string') { - if (foldEntry.type !== nodeType) continue - } else { - if (!foldEntry.type.includes(nodeType)) continue - } - } + if (foldSpec.matchers && !hasMatchingFoldSpec(foldSpec.matchers, node)) continue let foldStart - const startEntry = foldEntry.start + const startEntry = foldSpec.start if (startEntry) { + let foldStartNode if (startEntry.index != null) { - const child = children[startEntry.index] - if (!child || (startEntry.type && startEntry.type !== child.type)) continue - foldStart = child.endPosition + foldStartNode = children[startEntry.index] + if (!foldStartNode || startEntry.matchers && !hasMatchingFoldSpec(startEntry.matchers, foldStartNode)) continue } else { - if (!childTypes) childTypes = children.map(child => child.type) - const index = typeof startEntry.type === 'string' - ? childTypes.indexOf(startEntry.type) - : childTypes.findIndex(type => startEntry.type.includes(type)) - if (index === -1) continue - foldStart = children[index].endPosition + foldStartNode = children.find(child => hasMatchingFoldSpec(startEntry.matchers, child)) + if (!foldStartNode) continue } + foldStart = new Point(foldStartNode.endPosition.row, Infinity) } else { foldStart = new Point(node.startPosition.row, Infinity) } let foldEnd - const endEntry = foldEntry.end + const endEntry = foldSpec.end if (endEntry) { let foldEndNode if (endEntry.index != null) { @@ -237,18 +334,17 @@ class TreeSitterLanguageMode { foldEndNode = children[index] if (!foldEndNode || (endEntry.type && endEntry.type !== foldEndNode.type)) continue } else { - if (!childTypes) childTypes = children.map(foldEndNode => foldEndNode.type) - const index = typeof endEntry.type === 'string' - ? childTypes.indexOf(endEntry.type) - : childTypes.findIndex(type => endEntry.type.includes(type)) - if (index === -1) continue - foldEndNode = children[index] + foldEndNode = children.find(child => hasMatchingFoldSpec(endEntry.matchers, child)) + if (!foldEndNode) continue } - if (foldEndNode.endIndex - foldEndNode.startIndex > 1 && foldEndNode.startPosition.row > foldStart.row) { - foldEnd = new Point(foldEndNode.startPosition.row - 1, Infinity) - } else { - foldEnd = foldEndNode.startPosition + if (foldEndNode.startPosition.row <= foldStart.row) continue + + foldEnd = foldEndNode.startPosition + if (this.buffer.findInRangeSync( + WORD_REGEX, new Range(foldEnd, new Point(foldEnd.row, Infinity)) + )) { + foldEnd = new Point(foldEnd.row - 1, Infinity) } } else { const {endPosition} = node @@ -266,17 +362,46 @@ class TreeSitterLanguageMode { } /* - Syntax Tree APIs + Section - Syntax Tree APIs */ - getRangeForSyntaxNodeContainingRange (range) { + getSyntaxNodeContainingRange (range, where = _ => true) { const startIndex = this.buffer.characterIndexForPosition(range.start) const endIndex = this.buffer.characterIndexForPosition(range.end) - let node = this.document.rootNode.descendantForIndex(startIndex, endIndex - 1) - while (node && node.startIndex === startIndex && node.endIndex === endIndex) { - node = node.parent + const searchEndIndex = Math.max(0, endIndex - 1) + + let smallestNode + this._forEachTreeWithRange(range, tree => { + let node = tree.rootNode.descendantForIndex(startIndex, searchEndIndex) + while (node) { + if (nodeContainsIndices(node, startIndex, endIndex) && where(node)) { + if (nodeIsSmaller(node, smallestNode)) smallestNode = node + break + } + node = node.parent + } + }) + + return smallestNode + } + + getRangeForSyntaxNodeContainingRange (range, where) { + const node = this.getSyntaxNodeContainingRange(range, where) + return node && node.range + } + + getSyntaxNodeAtPosition (position, where) { + return this.getSyntaxNodeContainingRange(new Range(position, position), where) + } + + bufferRangeForScopeAtPosition (selector, position) { + if (typeof selector === 'string') { + const match = matcherForSelector(selector) + selector = ({type}) => match(type) } - if (node) return new Range(node.startPosition, node.endPosition) + if (selector === null) selector = undefined + const node = this.getSyntaxNodeAtPosition(position, selector) + return node && node.range } /* @@ -296,51 +421,331 @@ class TreeSitterLanguageMode { } scopeDescriptorForPosition (point) { - const result = [] - let node = this.document.rootNode.descendantForPosition(point) - - // Don't include anonymous token types like '(' because they prevent scope chains - // from being parsed as CSS selectors by the `slick` parser. Other css selector - // parsers like `postcss-selector-parser` do allow arbitrary quoted strings in - // selectors. - if (!node.isNamed) node = node.parent - - while (node) { - result.push(node.type) - node = node.parent + const iterator = this.buildHighlightIterator() + const scopes = [] + for (const scope of iterator.seek(point)) { + scopes.push(this.grammar.scopeNameForScopeId(scope, false)) } - result.push(this.grammar.id) - return new ScopeDescriptor({scopes: result.reverse()}) - } - - hasTokenForSelector (scopeSelector) { - return false + for (const scope of iterator.getOpenScopeIds()) { + scopes.push(this.grammar.scopeNameForScopeId(scope, false)) + } + return new ScopeDescriptor({scopes}) } getGrammar () { return this.grammar } + + /* + Section - Private + */ + + grammarForLanguageString (languageString) { + return this.grammarRegistry.treeSitterGrammarForLanguageString(languageString) + } + + emitRangeUpdate (range) { + const startRow = range.start.row + const endRow = range.end.row + for (let row = startRow; row < endRow; row++) { + this.isFoldableCache[row] = undefined + } + this.emitter.emit('did-change-highlighting', range) + } } -class TreeSitterHighlightIterator { - constructor (layer, document) { - this.layer = layer +class LanguageLayer { + constructor (languageMode, grammar, contentChildTypes) { + this.languageMode = languageMode + this.grammar = grammar + this.tree = null + this.currentParsePromise = null + this.patchSinceCurrentParseStarted = null + this.contentChildTypes = contentChildTypes + } - // Conceptually, the iterator represents a single position in the text. It stores this - // position both as a character index and as a `Point`. This position corresponds to a - // leaf node of the syntax tree, which either contains or follows the iterator's - // textual position. The `currentNode` property represents that leaf node, and - // `currentChildIndex` represents the child index of that leaf node within its parent. - this.currentIndex = null - this.currentPosition = null - this.currentNode = null - this.currentChildIndex = null + buildHighlightIterator () { + if (this.tree) { + return new LayerHighlightIterator(this, this.tree.walk()) + } else { + return new NullHighlightIterator() + } + } + + handleTextChange ({oldRange, newRange, oldText, newText}) { + if (this.tree) { + this.tree.edit(this._treeEditForBufferChange( + oldRange.start, oldRange.end, newRange.end, oldText, newText + )) + + if (this.editedRange) { + if (newRange.start.isLessThan(this.editedRange.start)) { + this.editedRange.start = newRange.start + } + if (oldRange.end.isLessThan(this.editedRange.end)) { + this.editedRange.end = newRange.end.traverse(this.editedRange.end.traversalFrom(oldRange.end)) + } else { + this.editedRange.end = newRange.end + } + } else { + this.editedRange = newRange.copy() + } + } + + if (this.patchSinceCurrentParseStarted) { + this.patchSinceCurrentParseStarted.splice( + oldRange.start, + oldRange.end, + newRange.end, + oldText, + newText + ) + } + } + + destroy () { + for (const marker of this.languageMode.injectionsMarkerLayer.getMarkers()) { + if (marker.parentLanguageLayer === this) { + marker.languageLayer.destroy() + marker.destroy() + } + } + } + + async update (nodeRangeSet) { + if (!this.currentParsePromise) { + do { + this.currentParsePromise = this._performUpdate(nodeRangeSet) + await this.currentParsePromise + } while (this.tree && this.tree.rootNode.hasChanges()) + this.currentParsePromise = null + } + } + + updateInjections (grammar) { + if (grammar.injectionRegex) { + if (!this.currentParsePromise) this.currentParsePromise = Promise.resolve() + this.currentParsePromise = this.currentParsePromise.then(async () => { + await this._populateInjections(MAX_RANGE, null) + this.currentParsePromise = null + }) + } + } + + async _performUpdate (nodeRangeSet) { + let includedRanges = null + if (nodeRangeSet) { + includedRanges = nodeRangeSet.getRanges() + if (includedRanges.length === 0) { + this.tree = null + return + } + } + + let affectedRange = this.editedRange + this.editedRange = null + + this.patchSinceCurrentParseStarted = new Patch() + let tree = this.languageMode.parse( + this.grammar.languageModule, + this.tree, + includedRanges + ) + if (tree.then) tree = await tree + tree.buffer = this.languageMode.buffer + + const changes = this.patchSinceCurrentParseStarted.getChanges() + this.patchSinceCurrentParseStarted = null + for (let i = changes.length - 1; i >= 0; i--) { + const {oldStart, oldEnd, newEnd, oldText, newText} = changes[i] + tree.edit(this._treeEditForBufferChange( + oldStart, oldEnd, newEnd, oldText, newText + )) + } + + if (this.tree) { + const rangesWithSyntaxChanges = this.tree.getChangedRanges(tree) + this.tree = tree + + if (!affectedRange) return + if (rangesWithSyntaxChanges.length > 0) { + for (const range of rangesWithSyntaxChanges) { + this.languageMode.emitRangeUpdate(rangeForNode(range)) + } + + affectedRange = affectedRange.union(new Range( + rangesWithSyntaxChanges[0].startPosition, + last(rangesWithSyntaxChanges).endPosition + )) + } else { + this.languageMode.emitRangeUpdate(affectedRange) + } + } else { + this.tree = tree + this.languageMode.emitRangeUpdate(rangeForNode(tree.rootNode)) + if (includedRanges) { + affectedRange = new Range(includedRanges[0].startPosition, last(includedRanges).endPosition) + } else { + affectedRange = MAX_RANGE + } + } + + await this._populateInjections(affectedRange, nodeRangeSet) + } + + _populateInjections (range, nodeRangeSet) { + const {injectionsMarkerLayer, grammarForLanguageString} = this.languageMode + + const existingInjectionMarkers = injectionsMarkerLayer + .findMarkers({intersectsRange: range}) + .filter(marker => marker.parentLanguageLayer === this) + + if (existingInjectionMarkers.length > 0) { + range = range.union(new Range( + existingInjectionMarkers[0].getRange().start, + last(existingInjectionMarkers).getRange().end + )) + } + + const markersToUpdate = new Map() + for (const injectionPoint of this.grammar.injectionPoints) { + const nodes = this.tree.rootNode.descendantsOfType( + injectionPoint.type, + range.start, + range.end + ) + + for (const node of nodes) { + const languageName = injectionPoint.language(node) + if (!languageName) continue + + const grammar = grammarForLanguageString(languageName) + if (!grammar) continue + + const contentNodes = injectionPoint.content(node) + if (!contentNodes) continue + + const injectionNodes = [].concat(contentNodes) + if (!injectionNodes.length) continue + + const injectionRange = rangeForNode(node) + let marker = existingInjectionMarkers.find(m => + m.getRange().isEqual(injectionRange) && + m.languageLayer.grammar === grammar + ) + if (!marker) { + marker = injectionsMarkerLayer.markRange(injectionRange) + marker.languageLayer = new LanguageLayer(this.languageMode, grammar, injectionPoint.contentChildTypes) + marker.parentLanguageLayer = this + } + + markersToUpdate.set(marker, new NodeRangeSet(nodeRangeSet, injectionNodes)) + } + } + + for (const marker of existingInjectionMarkers) { + if (!markersToUpdate.has(marker)) { + marker.languageLayer.destroy() + this.languageMode.emitRangeUpdate(marker.getRange()) + marker.destroy() + } + } + + const promises = [] + for (const [marker, nodeRangeSet] of markersToUpdate) { + promises.push(marker.languageLayer.update(nodeRangeSet)) + } + return Promise.all(promises) + } + + _treeEditForBufferChange (start, oldEnd, newEnd, oldText, newText) { + const startIndex = this.languageMode.buffer.characterIndexForPosition(start) + return { + startIndex, + oldEndIndex: startIndex + oldText.length, + newEndIndex: startIndex + newText.length, + startPosition: start, + oldEndPosition: oldEnd, + newEndPosition: newEnd + } + } +} + +class HighlightIterator { + constructor (languageMode, iterators) { + this.languageMode = languageMode + this.iterators = iterators.sort((a, b) => b.getIndex() - a.getIndex()) + } + + seek (targetPosition) { + const containingTags = [] + const containingTagStartIndices = [] + const targetIndex = this.languageMode.buffer.characterIndexForPosition(targetPosition) + for (let i = this.iterators.length - 1; i >= 0; i--) { + this.iterators[i].seek(targetIndex, containingTags, containingTagStartIndices) + } + this.iterators.sort((a, b) => b.getIndex() - a.getIndex()) + return containingTags + } + + moveToSuccessor () { + const lastIndex = this.iterators.length - 1 + const leader = this.iterators[lastIndex] + leader.moveToSuccessor() + const leaderCharIndex = leader.getIndex() + let i = lastIndex + while (i > 0 && this.iterators[i - 1].getIndex() < leaderCharIndex) i-- + if (i < lastIndex) this.iterators.splice(i, 0, this.iterators.pop()) + } + + getPosition () { + return last(this.iterators).getPosition() + } + + getCloseScopeIds () { + return last(this.iterators).getCloseScopeIds() + } + + getOpenScopeIds () { + return last(this.iterators).getOpenScopeIds() + } + + logState () { + const iterator = last(this.iterators) + if (iterator.treeCursor) { + console.log( + iterator.getPosition(), + iterator.treeCursor.nodeType, + new Range( + iterator.languageLayer.tree.rootNode.startPosition, + iterator.languageLayer.tree.rootNode.endPosition + ).toString() + ) + console.log('close', iterator.closeTags.map(id => this.shortClassNameForScopeId(id))) + console.log('open', iterator.openTags.map(id => this.shortClassNameForScopeId(id))) + } + } + + shortClassNameForScopeId (id) { + return this.languageMode.classNameForScopeId(id).replace(/syntax--/g, '') + } +} + +class LayerHighlightIterator { + constructor (languageLayer, treeCursor) { + this.languageLayer = languageLayer + + // The iterator is always positioned at either the start or the end of some node + // in the syntax tree. + this.atEnd = false + this.treeCursor = treeCursor // In order to determine which selectors match its current node, the iterator maintains // a list of the current node's ancestors. Because the selectors can use the `:nth-child` // pseudo-class, each node's child index is also stored. this.containingNodeTypes = [] this.containingNodeChildIndices = [] + this.containingNodeEndIndices = [] // At any given position, the iterator exposes the list of class names that should be // *ended* at its current position and the list of class names that should be *started* @@ -349,49 +754,59 @@ class TreeSitterHighlightIterator { this.openTags = [] } - seek (targetPosition) { - const containingTags = [] + seek (targetIndex, containingTags, containingTagStartIndices) { + while (this.treeCursor.gotoParent()) {} + this.done = false + this.atEnd = true this.closeTags.length = 0 this.openTags.length = 0 this.containingNodeTypes.length = 0 this.containingNodeChildIndices.length = 0 - this.currentPosition = targetPosition - this.currentIndex = this.layer.buffer.characterIndexForPosition(targetPosition) + this.containingNodeEndIndices.length = 0 - var node = this.layer.document.rootNode - var childIndex = -1 - var done = false - var nodeContainsTarget = true - do { - this.currentNode = node - this.currentChildIndex = childIndex - if (!nodeContainsTarget) break - this.containingNodeTypes.push(node.type) + const containingTagEndIndices = [] + + if (targetIndex >= this.treeCursor.endIndex) { + this.done = true + return + } + + let childIndex = -1 + for (;;) { + this.containingNodeTypes.push(this.treeCursor.nodeType) this.containingNodeChildIndices.push(childIndex) + this.containingNodeEndIndices.push(this.treeCursor.endIndex) - const scopeName = this.currentScopeName() - if (scopeName) { - const id = this.layer.grammar.idForScope(scopeName) - if (this.currentIndex === node.startIndex) { - this.openTags.push(id) + const scopeId = this._currentScopeId() + if (scopeId) { + if (this.treeCursor.startIndex < targetIndex) { + insertContainingTag( + scopeId, this.treeCursor.startIndex, + containingTags, containingTagStartIndices + ) + containingTagEndIndices.push(this.treeCursor.endIndex) } else { - containingTags.push(id) - } - } - - done = true - for (var i = 0, {children} = node, childCount = children.length; i < childCount; i++) { - const child = children[i] - if (child.endIndex > this.currentIndex) { - node = child - childIndex = i - done = false - if (child.startIndex > this.currentIndex) nodeContainsTarget = false + this.atEnd = false + this.openTags.push(scopeId) + this._moveDown() break } } - } while (!done) + + childIndex = this.treeCursor.gotoFirstChildForIndex(targetIndex) + if (childIndex === null) break + if (this.treeCursor.startIndex >= targetIndex) this.atEnd = false + } + + if (this.atEnd) { + const currentIndex = this.treeCursor.endIndex + for (let i = 0, {length} = containingTags; i < length; i++) { + if (containingTagEndIndices[i] === currentIndex) { + this.closeTags.push(containingTags[i]) + } + } + } return containingTags } @@ -400,59 +815,45 @@ class TreeSitterHighlightIterator { this.closeTags.length = 0 this.openTags.length = 0 - if (!this.currentNode) { - this.currentPosition = {row: Infinity, column: Infinity} - return false - } - - do { - if (this.currentIndex < this.currentNode.startIndex) { - this.currentIndex = this.currentNode.startIndex - this.currentPosition = this.currentNode.startPosition - this.pushOpenTag() - this.descendLeft() - } else if (this.currentIndex < this.currentNode.endIndex) { - while (true) { - this.currentIndex = this.currentNode.endIndex - this.currentPosition = this.currentNode.endPosition - this.pushCloseTag() - - const {nextSibling} = this.currentNode - if (nextSibling) { - this.currentNode = nextSibling - this.currentChildIndex++ - if (this.currentIndex === nextSibling.startIndex) { - this.pushOpenTag() - this.descendLeft() - } - break - } else { - this.currentNode = this.currentNode.parent - this.currentChildIndex = last(this.containingNodeChildIndices) - if (!this.currentNode) break - } + while (!this.done && !this.closeTags.length && !this.openTags.length) { + if (this.atEnd) { + if (this._moveRight()) { + const scopeId = this._currentScopeId() + if (scopeId) this.openTags.push(scopeId) + this.atEnd = false + this._moveDown() + } else if (this._moveUp(true)) { + this.atEnd = true + } else { + this.done = true } - } else if (this.currentNode.startIndex < this.currentNode.endIndex) { - this.currentNode = this.currentNode.nextSibling - if (this.currentNode) { - this.currentChildIndex++ - this.currentPosition = this.currentNode.startPosition - this.currentIndex = this.currentNode.startIndex - this.pushOpenTag() - this.descendLeft() - } - } else { - this.pushCloseTag() - this.currentNode = this.currentNode.parent - this.currentChildIndex = last(this.containingNodeChildIndices) + } else if (!this._moveDown()) { + const scopeId = this._currentScopeId() + if (scopeId) this.closeTags.push(scopeId) + this.atEnd = true + this._moveUp(false) } - } while (this.closeTags.length === 0 && this.openTags.length === 0 && this.currentNode) - - return true + } } getPosition () { - return this.currentPosition + if (this.done) { + return Point.INFINITY + } else if (this.atEnd) { + return this.treeCursor.endPosition + } else { + return this.treeCursor.startPosition + } + } + + getIndex () { + if (this.done) { + return Infinity + } else if (this.atEnd) { + return this.treeCursor.endIndex + } else { + return this.treeCursor.startIndex + } } getCloseScopeIds () { @@ -464,61 +865,219 @@ class TreeSitterHighlightIterator { } // Private methods + _moveUp (atLastChild) { + let result = false + const {endIndex} = this.treeCursor + let depth = this.containingNodeEndIndices.length - descendLeft () { - let child - while ((child = this.currentNode.firstChild) && this.currentIndex === child.startIndex) { - this.currentNode = child - this.currentChildIndex = 0 - this.pushOpenTag() + // The iterator should not move up until it has visited all of the children of this node. + while (depth > 1 && (atLastChild || this.containingNodeEndIndices[depth - 2] === endIndex)) { + atLastChild = false + result = true + this.treeCursor.gotoParent() + this.containingNodeTypes.pop() + this.containingNodeChildIndices.pop() + this.containingNodeEndIndices.pop() + --depth + const scopeId = this._currentScopeId() + if (scopeId) this.closeTags.push(scopeId) + } + return result + } + + _moveDown () { + let result = false + const {startIndex} = this.treeCursor + + // Once the iterator has found a scope boundary, it needs to stay at the same + // position, so it should not move down if the first child node starts later than the + // current node. + while (this.treeCursor.gotoFirstChild()) { + if ((this.closeTags.length || this.openTags.length) && + this.treeCursor.startIndex > startIndex) { + this.treeCursor.gotoParent() + break + } + + result = true + this.containingNodeTypes.push(this.treeCursor.nodeType) + this.containingNodeChildIndices.push(0) + this.containingNodeEndIndices.push(this.treeCursor.endIndex) + + const scopeId = this._currentScopeId() + if (scopeId) this.openTags.push(scopeId) + } + + return result + } + + _moveRight () { + if (this.treeCursor.gotoNextSibling()) { + const depth = this.containingNodeTypes.length + this.containingNodeTypes[depth - 1] = this.treeCursor.nodeType + this.containingNodeChildIndices[depth - 1]++ + this.containingNodeEndIndices[depth - 1] = this.treeCursor.endIndex + return true } } - currentScopeName () { - return this.layer.grammar.scopeMap.get( + _currentScopeId () { + const rules = this.languageLayer.grammar.scopeMap.get( this.containingNodeTypes, this.containingNodeChildIndices, - this.currentNode.isNamed + this.treeCursor.nodeIsNamed ) - } - - pushCloseTag () { - const scopeName = this.currentScopeName() - if (scopeName) this.closeTags.push(this.layer.grammar.idForScope(scopeName)) - this.containingNodeTypes.pop() - this.containingNodeChildIndices.pop() - } - - pushOpenTag () { - this.containingNodeTypes.push(this.currentNode.type) - this.containingNodeChildIndices.push(this.currentChildIndex) - const scopeName = this.currentScopeName() - if (scopeName) this.openTags.push(this.layer.grammar.idForScope(scopeName)) + const scopes = applyLeafRules(rules, this.treeCursor) + if (scopes) { + return this.languageLayer.languageMode.grammar.idForScope(scopes) + } } } -class TreeSitterTextBufferInput { - constructor (buffer) { - this.buffer = buffer - this.seek(0) +const applyLeafRules = (rules, cursor) => { + if (!rules || typeof rules === 'string') return rules + if (Array.isArray(rules)) { + for (let i = 0, {length} = rules; i !== length; ++i) { + const result = applyLeafRules(rules[i], cursor) + if (result) return result + } + return undefined + } + if (typeof rules === 'object') { + if (rules.exact) { + return cursor.nodeText === rules.exact + ? applyLeafRules(rules.scopes, cursor) + : undefined + } + if (rules.match) { + return rules.match.test(cursor.nodeText) + ? applyLeafRules(rules.scopes, cursor) + : undefined + } + } +} + +class NullHighlightIterator { + seek () { return [] } + moveToSuccessor () {} + getIndex () { return Infinity } + getPosition () { return Point.INFINITY } + getOpenScopeIds () { return [] } + getCloseScopeIds () { return [] } +} + +class NodeRangeSet { + constructor (previous, nodes) { + this.previous = previous + this.nodes = nodes } - seek (characterIndex) { - this.position = this.buffer.positionForCharacterIndex(characterIndex) + getRanges () { + const previousRanges = this.previous && this.previous.getRanges() + const result = [] + + for (const node of this.nodes) { + let position = node.startPosition + let index = node.startIndex + + for (const child of node.children) { + const nextPosition = child.startPosition + const nextIndex = child.startIndex + if (nextIndex > index) { + this._pushRange(previousRanges, result, { + startIndex: index, + endIndex: nextIndex, + startPosition: position, + endPosition: nextPosition + }) + } + position = child.endPosition + index = child.endIndex + } + + if (node.endIndex > index) { + this._pushRange(previousRanges, result, { + startIndex: index, + endIndex: node.endIndex, + startPosition: position, + endPosition: node.endPosition + }) + } + } + + return result } - read () { - const endPosition = this.buffer.clipPosition(this.position.traverse({row: 1000, column: 0})) - const text = this.buffer.getTextInRange([this.position, endPosition]) - this.position = endPosition - return text + _pushRange (previousRanges, newRanges, newRange) { + if (!previousRanges) { + newRanges.push(newRange) + return + } + + for (const previousRange of previousRanges) { + if (previousRange.endIndex <= newRange.startIndex) continue + if (previousRange.startIndex >= newRange.endIndex) break + newRanges.push({ + startIndex: Math.max(previousRange.startIndex, newRange.startIndex), + endIndex: Math.min(previousRange.endIndex, newRange.endIndex), + startPosition: Point.max(previousRange.startPosition, newRange.startPosition), + endPosition: Point.min(previousRange.endPosition, newRange.endPosition) + }) + } } } +function insertContainingTag (tag, index, tags, indices) { + const i = indices.findIndex(existingIndex => existingIndex > index) + if (i === -1) { + tags.push(tag) + indices.push(index) + } else { + tags.splice(i, 0, tag) + indices.splice(i, 0, index) + } +} + +// Return true iff `mouse` is smaller than `house`. Only correct if +// mouse and house overlap. +// +// * `mouse` {Range} +// * `house` {Range} +function rangeIsSmaller (mouse, house) { + if (!house) return true + const mvec = vecFromRange(mouse) + const hvec = vecFromRange(house) + return Point.min(mvec, hvec) === mvec +} + +function vecFromRange ({start, end}) { + return end.translate(start.negate()) +} + +function rangeForNode (node) { + return new Range(node.startPosition, node.endPosition) +} + +function nodeContainsIndices (node, start, end) { + if (node.startIndex < start) return node.endIndex >= end + if (node.startIndex === start) return node.endIndex > end + return false +} + +function nodeIsSmaller (left, right) { + if (!left) return false + if (!right) return true + return left.endIndex - left.startIndex < right.endIndex - right.startIndex +} + function last (array) { return array[array.length - 1] } +function hasMatchingFoldSpec (specs, node) { + return specs.some(({type, named}) => type === node.type && named === node.isNamed) +} + // TODO: Remove this once TreeSitterLanguageMode implements its own auto-indent system. [ '_suggestedIndentForLineWithScopeAtBufferRow', @@ -526,7 +1085,13 @@ function last (array) { 'increaseIndentRegexForScopeDescriptor', 'decreaseIndentRegexForScopeDescriptor', 'decreaseNextIndentRegexForScopeDescriptor', - 'regexForPattern' + 'regexForPattern', + 'getNonWordCharacters' ].forEach(methodName => { - module.exports.prototype[methodName] = TextMateLanguageMode.prototype[methodName] + TreeSitterLanguageMode.prototype[methodName] = TextMateLanguageMode.prototype[methodName] }) + +TreeSitterLanguageMode.LanguageLayer = LanguageLayer +TreeSitterLanguageMode.prototype.syncOperationLimit = 1000 + +module.exports = TreeSitterLanguageMode diff --git a/src/update-process-env.js b/src/update-process-env.js index 00bb13927..20d937d96 100644 --- a/src/update-process-env.js +++ b/src/update-process-env.js @@ -1,7 +1,5 @@ -/** @babel */ - -import fs from 'fs' -import childProcess from 'child_process' +const fs = require('fs') +const childProcess = require('child_process') const ENVIRONMENT_VARIABLES_TO_PRESERVE = new Set([ 'NODE_ENV', @@ -20,7 +18,7 @@ async function updateProcessEnv (launchEnv) { if (launchEnv) { if (shouldGetEnvFromShell(launchEnv)) { envToAssign = await getEnvFromShell(launchEnv) - } else if (launchEnv.PWD) { + } else if (launchEnv.PWD || launchEnv.PROMPT || launchEnv.PSModulePath) { envToAssign = launchEnv } } @@ -120,4 +118,4 @@ async function getEnvFromShell (env) { return result } -export default { updateProcessEnv, shouldGetEnvFromShell } +module.exports = {updateProcessEnv, shouldGetEnvFromShell} diff --git a/src/workspace-element.js b/src/workspace-element.js index bd0e1b971..f94dbd6e9 100644 --- a/src/workspace-element.js +++ b/src/workspace-element.js @@ -56,10 +56,10 @@ class WorkspaceElement extends HTMLElement { } updateGlobalTextEditorStyleSheet () { - const styleSheetSource = `atom-text-editor { - font-size: ${this.config.get('editor.fontSize')}px; - font-family: ${this.config.get('editor.fontFamily')}; - line-height: ${this.config.get('editor.lineHeight')}; + const styleSheetSource = `atom-workspace { + --editor-font-size: ${this.config.get('editor.fontSize')}px; + --editor-font-family: ${this.config.get('editor.fontFamily')}; + --editor-line-height: ${this.config.get('editor.lineHeight')}; }` this.styleManager.addStyleSheet(styleSheetSource, {sourcePath: 'global-text-editor-styles', priority: -1}) } @@ -92,7 +92,13 @@ class WorkspaceElement extends HTMLElement { window.removeEventListener('dragstart', this.handleDragStart) window.removeEventListener('dragend', this.handleDragEnd, true) window.removeEventListener('drop', this.handleDrop, true) - }) + }), + ...[this.model.getLeftDock(), this.model.getRightDock(), this.model.getBottomDock()] + .map(dock => dock.onDidChangeHovered(hovered => { + if (hovered) this.hoveredDock = dock + else if (dock === this.hoveredDock) this.hoveredDock = null + this.checkCleanupDockHoverEvents() + })) ) this.initializeContent() this.observeScrollbarStyle() @@ -104,6 +110,7 @@ class WorkspaceElement extends HTMLElement { this.addEventListener('mousewheel', this.handleMousewheel.bind(this), true) window.addEventListener('dragstart', this.handleDragStart) + window.addEventListener('mousemove', this.handleEdgesMouseMove) this.panelContainers = { top: this.model.panelContainers.top.getElement(), @@ -132,6 +139,10 @@ class WorkspaceElement extends HTMLElement { return this } + destroy () { + this.subscriptions.dispose() + } + getModel () { return this.model } handleDragStart (event) { @@ -169,7 +180,6 @@ class WorkspaceElement extends HTMLElement { // being hovered. this.cursorInCenter = false this.updateHoveredDock({x: event.pageX, y: event.pageY}) - window.addEventListener('mousemove', this.handleEdgesMouseMove) window.addEventListener('dragend', this.handleDockDragEnd) } @@ -182,24 +192,17 @@ class WorkspaceElement extends HTMLElement { } updateHoveredDock (mousePosition) { - this.hoveredDock = null - for (let location in this.model.paneContainers) { - if (location !== 'center') { - const dock = this.model.paneContainers[location] - if (!this.hoveredDock && dock.pointWithinHoverArea(mousePosition)) { - this.hoveredDock = dock - dock.setHovered(true) - } else { - dock.setHovered(false) - } - } - } - this.checkCleanupDockHoverEvents() + // If we haven't left the currently hovered dock, don't change anything. + if (this.hoveredDock && this.hoveredDock.pointWithinHoverArea(mousePosition, true)) return + + const docks = [this.model.getLeftDock(), this.model.getRightDock(), this.model.getBottomDock()] + const nextHoveredDock = + docks.find(dock => dock !== this.hoveredDock && dock.pointWithinHoverArea(mousePosition)) + docks.forEach(dock => { dock.setHovered(dock === nextHoveredDock) }) } checkCleanupDockHoverEvents () { if (this.cursorInCenter && !this.hoveredDock) { - window.removeEventListener('mousemove', this.handleEdgesMouseMove) window.removeEventListener('dragend', this.handleDockDragEnd) } } @@ -307,7 +310,7 @@ class WorkspaceElement extends HTMLElement { } } - runPackageSpecs () { + runPackageSpecs (options = {}) { const activePaneItem = this.model.getActivePaneItem() const activePath = activePaneItem && typeof activePaneItem.getPath === 'function' ? activePaneItem.getPath() : null let projectPath @@ -323,7 +326,7 @@ class WorkspaceElement extends HTMLElement { specPath = testPath } - ipcRenderer.send('run-package-specs', specPath) + ipcRenderer.send('run-package-specs', specPath, options) } } diff --git a/src/workspace.js b/src/workspace.js index 127168748..a3f85ddeb 100644 --- a/src/workspace.js +++ b/src/workspace.js @@ -1,5 +1,3 @@ -'use babel' - const _ = require('underscore-plus') const url = require('url') const path = require('path') @@ -227,6 +225,8 @@ module.exports = class Workspace extends Model { modal: new PanelContainer({viewRegistry: this.viewRegistry, location: 'modal'}) } + this.incoming = new Map() + this.subscribeToEvents() } @@ -310,7 +310,10 @@ module.exports = class Workspace extends Model { this.originalFontSize = null this.openers = [] this.destroyedItemURIs = [] - this.element = null + if (this.element) { + this.element.destroy() + this.element = null + } this.consumeServices(this.packageManager) } @@ -494,14 +497,22 @@ module.exports = class Workspace extends Model { if (item instanceof TextEditor) { const subscriptions = new CompositeDisposable( this.textEditorRegistry.add(item), - this.textEditorRegistry.maintainConfig(item), - item.observeGrammar(this.handleGrammarUsed.bind(this)) + this.textEditorRegistry.maintainConfig(item) ) if (!this.project.findBufferForId(item.buffer.id)) { this.project.addBuffer(item.buffer) } item.onDidDestroy(() => { subscriptions.dispose() }) this.emitter.emit('did-add-text-editor', {textEditor: item, pane, index}) + // It's important to call handleGrammarUsed after emitting the did-add event: + // if we activate a package between adding the editor to the registry and emitting + // the package may receive the editor twice from `observeTextEditors`. + // (Note that the item can be destroyed by an `observeTextEditors` handler.) + if (!item.isDestroyed()) { + subscriptions.add( + item.observeGrammar(this.handleGrammarUsed.bind(this)) + ) + } } }) } @@ -920,133 +931,150 @@ module.exports = class Workspace extends Model { if (typeof item.getURI === 'function') uri = item.getURI() } - if (!atom.config.get('core.allowPendingPaneItems')) { - options.pending = false - } - - // Avoid adding URLs as recent documents to work-around this Spotlight crash: - // https://github.com/atom/atom/issues/10071 - if (uri && (!url.parse(uri).protocol || process.platform === 'win32')) { - this.applicationDelegate.addRecentDocument(uri) - } - - let pane, itemExistsInWorkspace - - // Try to find an existing item in the workspace. - if (item || uri) { - if (options.pane) { - pane = options.pane - } else if (options.searchAllPanes) { - pane = item ? this.paneForItem(item) : this.paneForURI(uri) + let resolveItem = () => {} + if (uri) { + const incomingItem = this.incoming.get(uri) + if (!incomingItem) { + this.incoming.set(uri, new Promise(resolve => { resolveItem = resolve })) } else { - // If an item with the given URI is already in the workspace, assume - // that item's pane container is the preferred location for that URI. - let container - if (uri) container = this.paneContainerForURI(uri) - if (!container) container = this.getActivePaneContainer() + await incomingItem + } + } - // The `split` option affects where we search for the item. - pane = container.getActivePane() - switch (options.split) { - case 'left': - pane = pane.findLeftmostSibling() - break - case 'right': - pane = pane.findRightmostSibling() - break - case 'up': - pane = pane.findTopmostSibling() - break - case 'down': - pane = pane.findBottommostSibling() - break - } + try { + if (!atom.config.get('core.allowPendingPaneItems')) { + options.pending = false } - if (pane) { - if (item) { - itemExistsInWorkspace = pane.getItems().includes(item) + // Avoid adding URLs as recent documents to work-around this Spotlight crash: + // https://github.com/atom/atom/issues/10071 + if (uri && (!url.parse(uri).protocol || process.platform === 'win32')) { + this.applicationDelegate.addRecentDocument(uri) + } + + let pane, itemExistsInWorkspace + + // Try to find an existing item in the workspace. + if (item || uri) { + if (options.pane) { + pane = options.pane + } else if (options.searchAllPanes) { + pane = item ? this.paneForItem(item) : this.paneForURI(uri) } else { - item = pane.itemForURI(uri) - itemExistsInWorkspace = item != null + // If an item with the given URI is already in the workspace, assume + // that item's pane container is the preferred location for that URI. + let container + if (uri) container = this.paneContainerForURI(uri) + if (!container) container = this.getActivePaneContainer() + + // The `split` option affects where we search for the item. + pane = container.getActivePane() + switch (options.split) { + case 'left': + pane = pane.findLeftmostSibling() + break + case 'right': + pane = pane.findRightmostSibling() + break + case 'up': + pane = pane.findTopmostSibling() + break + case 'down': + pane = pane.findBottommostSibling() + break + } + } + + if (pane) { + if (item) { + itemExistsInWorkspace = pane.getItems().includes(item) + } else { + item = pane.itemForURI(uri) + itemExistsInWorkspace = item != null + } } } - } - // If we already have an item at this stage, we won't need to do an async - // lookup of the URI, so we yield the event loop to ensure this method - // is consistently asynchronous. - if (item) await Promise.resolve() + // If we already have an item at this stage, we won't need to do an async + // lookup of the URI, so we yield the event loop to ensure this method + // is consistently asynchronous. + if (item) await Promise.resolve() - if (!itemExistsInWorkspace) { - item = item || await this.createItemForURI(uri, options) - if (!item) return + if (!itemExistsInWorkspace) { + item = item || await this.createItemForURI(uri, options) + if (!item) return - if (options.pane) { - pane = options.pane + if (options.pane) { + pane = options.pane + } else { + let location = options.location + if (!location && !options.split && uri && this.enablePersistence) { + location = await this.itemLocationStore.load(uri) + } + if (!location && typeof item.getDefaultLocation === 'function') { + location = item.getDefaultLocation() + } + + const allowedLocations = typeof item.getAllowedLocations === 'function' ? item.getAllowedLocations() : ALL_LOCATIONS + location = allowedLocations.includes(location) ? location : allowedLocations[0] + + const container = this.paneContainers[location] || this.getCenter() + pane = container.getActivePane() + switch (options.split) { + case 'left': + pane = pane.findLeftmostSibling() + break + case 'right': + pane = pane.findOrCreateRightmostSibling() + break + case 'up': + pane = pane.findTopmostSibling() + break + case 'down': + pane = pane.findOrCreateBottommostSibling() + break + } + } + } + + if (!options.pending && (pane.getPendingItem() === item)) { + pane.clearPendingItem() + } + + this.itemOpened(item) + + if (options.activateItem === false) { + pane.addItem(item, {pending: options.pending}) } else { - let location = options.location - if (!location && !options.split && uri && this.enablePersistence) { - location = await this.itemLocationStore.load(uri) - } - if (!location && typeof item.getDefaultLocation === 'function') { - location = item.getDefaultLocation() - } + pane.activateItem(item, {pending: options.pending}) + } - const allowedLocations = typeof item.getAllowedLocations === 'function' ? item.getAllowedLocations() : ALL_LOCATIONS - location = allowedLocations.includes(location) ? location : allowedLocations[0] + if (options.activatePane !== false) { + pane.activate() + } - const container = this.paneContainers[location] || this.getCenter() - pane = container.getActivePane() - switch (options.split) { - case 'left': - pane = pane.findLeftmostSibling() - break - case 'right': - pane = pane.findOrCreateRightmostSibling() - break - case 'up': - pane = pane.findTopmostSibling() - break - case 'down': - pane = pane.findOrCreateBottommostSibling() - break + let initialColumn = 0 + let initialLine = 0 + if (!Number.isNaN(options.initialLine)) { + initialLine = options.initialLine + } + if (!Number.isNaN(options.initialColumn)) { + initialColumn = options.initialColumn + } + if (initialLine >= 0 || initialColumn >= 0) { + if (typeof item.setCursorBufferPosition === 'function') { + item.setCursorBufferPosition([initialLine, initialColumn]) } } - } - if (!options.pending && (pane.getPendingItem() === item)) { - pane.clearPendingItem() - } - - this.itemOpened(item) - - if (options.activateItem === false) { - pane.addItem(item, {pending: options.pending}) - } else { - pane.activateItem(item, {pending: options.pending}) - } - - if (options.activatePane !== false) { - pane.activate() - } - - let initialColumn = 0 - let initialLine = 0 - if (!Number.isNaN(options.initialLine)) { - initialLine = options.initialLine - } - if (!Number.isNaN(options.initialColumn)) { - initialColumn = options.initialColumn - } - if (initialLine >= 0 || initialColumn >= 0) { - if (typeof item.setCursorBufferPosition === 'function') { - item.setCursorBufferPosition([initialLine, initialColumn]) + const index = pane.getActiveItemIndex() + this.emitter.emit('did-open', {uri, pane, item, index}) + if (uri) { + this.incoming.delete(uri) } + } finally { + resolveItem() } - - const index = pane.getActiveItemIndex() - this.emitter.emit('did-open', {uri, pane, item, index}) return item } @@ -1216,42 +1244,32 @@ module.exports = class Workspace extends Model { const fileSize = fs.getSizeSync(filePath) - let [resolveConfirmFileOpenPromise, rejectConfirmFileOpenPromise] = [] - const confirmFileOpenPromise = new Promise((resolve, reject) => { - resolveConfirmFileOpenPromise = resolve - rejectConfirmFileOpenPromise = reject - }) - if (fileSize >= (this.config.get('core.warnOnLargeFileLimit') * 1048576)) { // 40MB by default - this.applicationDelegate.confirm({ - message: 'Atom will be unresponsive during the loading of very large files.', - detail: 'Do you still want to load this file?', - buttons: ['Proceed', 'Cancel'] - }, response => { - if (response === 1) { - rejectConfirmFileOpenPromise() - } else { - resolveConfirmFileOpenPromise() - } + await new Promise((resolve, reject) => { + this.applicationDelegate.confirm({ + message: 'Atom will be unresponsive during the loading of very large files.', + detail: 'Do you still want to load this file?', + buttons: ['Proceed', 'Cancel'] + }, response => { + if (response === 1) { + const error = new Error() + error.code = 'CANCELLED' + reject(error) + } else { + resolve() + } + }) }) - } else { - resolveConfirmFileOpenPromise() } - try { - await confirmFileOpenPromise - const buffer = await this.project.bufferForPath(filePath, options) - return this.textEditorRegistry.build(Object.assign({buffer, autoHeight: false}, options)) - } catch (e) { - const error = new Error() - error.code = 'CANCELLED' - throw error - } + const buffer = await this.project.bufferForPath(filePath, options) + return this.textEditorRegistry.build(Object.assign({buffer, autoHeight: false}, options)) } handleGrammarUsed (grammar) { if (grammar == null) { return } - return this.packageManager.triggerActivationHook(`${grammar.packageName}:grammar-used`) + this.packageManager.triggerActivationHook(`${grammar.scopeName}:root-scope-used`) + this.packageManager.triggerActivationHook(`${grammar.packageName}:grammar-used`) } // Public: Returns a {Boolean} that is `true` if `object` is a `TextEditor`. @@ -1570,6 +1588,7 @@ module.exports = class Workspace extends Model { if (this.activeItemSubscriptions != null) { this.activeItemSubscriptions.dispose() } + if (this.element) this.element.destroy() } /* diff --git a/static/docks.less b/static/docks.less index 301d7aee5..ccbb4e903 100644 --- a/static/docks.less +++ b/static/docks.less @@ -16,12 +16,6 @@ atom-dock { .atom-dock-inner { display: flex; - // Keep the area at least 2 pixels wide so that you have something to hover - // over to trigger the toggle button affordance even when fullscreen. - // Needs to be 2 pixels to work on Windows when scaled to 150%. See atom/atom #15728 - &.left, &.right { min-width: 2px; } - &.bottom { min-height: 1px; } - &.bottom { width: 100%; } &.left, &.right { height: 100%; } @@ -119,10 +113,16 @@ atom-dock { // Promote to own layer, fixes rendering issue atom/atom#14915 will-change: transform; - &.right { left: 0; } - &.bottom { top: 0; } - &.left { right: 0; } - } + &.right { + left: 0; + } + &.bottom { + top: 0; + } + &.left { + right: 0; + } + } // Hide the button. &:not(.atom-dock-toggle-button-visible) { diff --git a/static/text-editor.less b/static/text-editor.less index 21cba8482..99f198512 100644 --- a/static/text-editor.less +++ b/static/text-editor.less @@ -2,10 +2,17 @@ @import "octicon-utf-codes"; @import "octicon-mixins"; +:root { + // Fixes specs + --editor-font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace; +} + atom-text-editor { display: flex; - font-family: Menlo, Consolas, 'DejaVu Sans Mono', monospace; cursor: text; + font-family: var(--editor-font-family); + font-size: var(--editor-font-size); + line-height: var(--editor-line-height); .gutter-container { width: min-content; diff --git a/static/variables/ui-variables.less b/static/variables/ui-variables.less index 8ef4d48e7..7bea17e72 100644 --- a/static/variables/ui-variables.less +++ b/static/variables/ui-variables.less @@ -81,5 +81,5 @@ // Other -@font-family: 'BlinkMacSystemFont', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif; +@font-family: system-ui; @use-custom-controls: true; // false uses native controls diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 000000000..136c754fa --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,24 @@ +const path = require('path'); + +module.exports = { + "extends": "stylelint-config-standard", + "ignoreFiles": [path.resolve(__dirname, "static", "atom.less")], + "rules": { + "color-hex-case": null, // TODO: enable? + "max-empty-lines": null, // TODO: enable? + "selector-type-no-unknown": null, + "function-comma-space-after": null, // TODO: enable? + "font-family-no-missing-generic-family-keyword": null, // needed for octicons (no sensible fallback) + "declaration-empty-line-before": null, // TODO: enable? + "declaration-block-trailing-semicolon": null, // TODO: enable + "no-descending-specificity": null, + "number-leading-zero": null, // TODO: enable? + "no-duplicate-selectors": null, + "selector-pseudo-element-colon-notation": null, // TODO: enable? + "selector-list-comma-newline-after": null, // TODO: enable? + "rule-empty-line-before": null, // TODO: enable? + "at-rule-empty-line-before": null, // TODO: enable? + "font-family-no-duplicate-names": null, // TODO: enable? + "unit-no-unknown": [true, {"ignoreUnits": [ "x" ]}], // Needed for -webkit-image-set 1x/2x units + } +} diff --git a/vendor/jasmine.js b/vendor/jasmine.js index 1b80d662f..ac443e1c8 100644 --- a/vendor/jasmine.js +++ b/vendor/jasmine.js @@ -2317,6 +2317,14 @@ jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessag } } + if (optional_timeoutMessage_ == null) { + const objectToCaptureStack = {} + Error.captureStackTrace(objectToCaptureStack, waitsFor) + const stack = objectToCaptureStack.stack + const line = stack.split('\n')[1] + optional_timeoutMessage_ = `condition ${line}` + } + var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); this.addToQueue(waitsForFunc); return this;