├── .all-contributorsrc
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmrc
├── .prettierrc
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── jest.config.js
├── other
├── CODE_OF_CONDUCT.md
├── EXAMPLES.md
├── ROADMAP.md
└── manual-release.md
├── package.json
├── run-setup.js
├── src
├── __mocks__
│ ├── child_process.js
│ └── semver.js
├── __tests__
│ └── index.js
├── index.js
├── install-deps
│ ├── __tests__
│ │ └── index.js
│ └── index.js
├── run-setup.js
├── setup.js
├── utils
│ ├── __tests__
│ │ └── one-line.js
│ └── one-line.js
└── verify-system
│ ├── __tests__
│ ├── __snapshots__
│ │ ├── index.js.snap
│ │ ├── node.js.snap
│ │ └── npm.js.snap
│ ├── exec-validator.js
│ ├── index.js
│ ├── node.js
│ └── npm.js
│ ├── exec-validator.js
│ ├── index.js
│ ├── node.js
│ ├── npm.js
│ └── yarn.js
├── webpack.config.js
└── yarn.lock
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "workshop-setup",
3 | "projectOwner": "kentcdodds",
4 | "files": [
5 | "README.md"
6 | ],
7 | "imageSize": 100,
8 | "commit": false,
9 | "contributors": [
10 | {
11 | "login": "kentcdodds",
12 | "name": "Kent C. Dodds",
13 | "avatar_url": "https://avatars.githubusercontent.com/u/1500684?v=3",
14 | "profile": "https://kentcdodds.com",
15 | "contributions": [
16 | "code",
17 | "doc",
18 | "infra",
19 | "test"
20 | ]
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js text eol=lf
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
13 |
14 | - `workshop-setup` version:
15 | - `node` version:
16 | - `npm` (or `yarn`) version:
17 |
18 | Relevant code or config
19 |
20 | ```javascript
21 |
22 | ```
23 |
24 | What you did:
25 |
26 |
27 |
28 | What happened:
29 |
30 |
31 |
32 | Reproduction repository:
33 |
34 |
38 |
39 | Problem description:
40 |
41 |
42 |
43 | Suggested solution:
44 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | **What**:
19 |
20 |
21 | **Why**:
22 |
23 |
24 | **How**:
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 | .opt-in
5 | .opt-out
6 | .eslintcache
7 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=http://registry.npmjs.org/
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 2,
4 | "useTabs": false,
5 | "semi": false,
6 | "singleQuote": true,
7 | "trailingComma": "all",
8 | "bracketSpacing": false,
9 | "jsxBracketSameLine": false,
10 | "proseWrap": "always"
11 | }
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | yarn: true
5 | directories:
6 | - ~/.npm
7 | - node_modules
8 | notifications:
9 | email: false
10 | node_js: '8'
11 | script: npm run validate
12 | after_success: kcd-scripts travis-after-success
13 | branches:
14 | only: master
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release).
4 | You can see it on the [releases page](../releases).
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for being willing to contribute!
4 |
5 | **Working on your first Pull Request?** You can learn how from this _free_
6 | series [How to Contribute to an Open Source Project on GitHub][egghead]
7 |
8 | ## Project setup
9 |
10 | 1. Fork and clone the repo
11 | 2. `$ npm install` to install dependencies
12 | 3. `$ npm run validate` to validate you've got it working
13 | 4. Create a branch for your PR
14 |
15 | > Tip: Keep your `master` branch pointing at the original repository and make
16 | > pull requests from branches on your fork. To do this, run:
17 | >
18 | > ```
19 | > git remote add upstream https://github.com/kentcdodds/workshop-setup.git
20 | > git fetch upstream
21 | > git branch --set-upstream-to=upstream/master master
22 | > ```
23 | >
24 | > This will add the original repository as a "remote" called "upstream," Then
25 | > fetch the git information from that remote, then set your local `master`
26 | > branch to use the upstream master branch whenever you run `git pull`. Then you
27 | > can make all of your pull request branches based on this `master` branch.
28 | > Whenever you want to update The installed version of `master`, do a regular
29 | > `git pull`.
30 |
31 | ## Add yourself as a contributor
32 |
33 | This project follows the [all contributors][all-contributors] specification. To
34 | add yourself to the table of contributors on the `README.md`, please use the
35 | automated script as part of your PR:
36 |
37 | ```console
38 | npm run add-contributor
39 | ```
40 |
41 | Follow the prompt and commit `.all-contributorsrc` and `README.md` in the PR. If
42 | you've already added yourself to the list and are making a new type of
43 | contribution, you can run it again and select the added contribution type.
44 |
45 | ## Committing and Pushing changes
46 |
47 | Please make sure to run the tests before you commit your changes. You can run
48 | `npm run test:update` which will update any snapshots that need updating. Make
49 | sure to include those changes (if they exist) in your commit.
50 |
51 | ### opt into git hooks
52 |
53 | There are git hooks set up with this project that are automatically installed
54 | when you install dependencies. They're really handy, but are turned off by
55 | default (so as to not hinder new contributors). You can opt into these by
56 | creating a file called `.opt-in` at the root of the project and putting this
57 | inside:
58 |
59 | ```
60 | pre-commit
61 | ```
62 |
63 | ## Help needed
64 |
65 | Please checkout the [the open issues][issues]
66 |
67 | Also, please watch the repo and respond to questions/bug reports/feature
68 | requests! Thanks!
69 |
70 | [egghead]:
71 | https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
72 | [all-contributors]: https://github.com/kentcdodds/all-contributors
73 | [issues]: https://github.com/kentcdodds/workshop-setup/issues
74 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 Kent C. Dodds
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 | SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # workshop-setup
2 |
3 | Verify and setup a repository for workshop attendees
4 |
5 | [![Build Status][build-badge]][build]
6 | [![Code Coverage][coverage-badge]][coverage]
7 | [![Dependencies][dependencyci-badge]][dependencyci]
8 | [![version][version-badge]][package] [![downloads][downloads-badge]][npm-stat]
9 | [![MIT License][license-badge]][license]
10 |
11 | [](#contributors)
12 | [![PRs Welcome][prs-badge]][prs] [![Donate][donate-badge]][donate]
13 | [![Code of Conduct][coc-badge]][coc] [![Roadmap][roadmap-badge]][roadmap]
14 | [![Examples][examples-badge]][examples]
15 |
16 | [![Watch on GitHub][github-watch-badge]][github-watch]
17 | [![Star on GitHub][github-star-badge]][github-star]
18 | [![Tweet][twitter-badge]][twitter]
19 |
20 | ## The problem
21 |
22 | I make quite a few [workshops][workshops] and one of the biggest challenges I
23 | have is making sure that people have set things up correctly so the workshop has
24 | as few surprises as possible. So I want to have a script validate things on
25 | attendees machines before they start on the workshop and give them helpful info
26 | to fix problems early and on their own.
27 |
28 | The problem is further complicated by the fact that I can't use any modules to
29 | do this because I can pretty much only guarantee that attendees have some
30 | version of node and npm, but not which version. So I need something that exists
31 | when they clone the repository right from the start.
32 |
33 | ## This solution
34 |
35 | This exposes a simple function that takes an array of validators which return
36 | strings of helpful text (or a promise that resolves to a string of helpful text)
37 | if the system is not valid (or `null` if it is valid). To overcome the issue of
38 | not being able to install things, there is a bundled version of this module that
39 | you can download from the registry and commit directly to your project.
40 |
41 | ## Table of Contents
42 |
43 |
44 |
45 |
46 | - [Installation](#installation)
47 | - [Usage](#usage)
48 | - [Alternative usage](#alternative-usage)
49 | - [verifySystem](#verifysystem)
50 | - [installDeps](#installdeps)
51 | - [Inspiration](#inspiration)
52 | - [Other Solutions](#other-solutions)
53 | - [Contributors](#contributors)
54 | - [LICENSE](#license)
55 |
56 |
57 |
58 | ## Installation
59 |
60 | The way I expect people to use this module is by downloading the UMD build and
61 | committing it directly into their project. You can download the UMD build via
62 | npm if you like (then just copy/paste the file from `node_modules`) or download
63 | it from `unpkg.com` here: https://unpkg.com/workshop-setup/dist/index.js
64 |
65 | ```
66 | curl -o scripts/workshop-setup.js -L https://unpkg.com/workshop-setup/dist/index.js
67 | ```
68 |
69 | This module is distributed via [npm][npm] which is bundled with [node][node] and
70 | can be installed as one of your project's `devDependencies`:
71 |
72 | ```
73 | npm install --save-dev workshop-setup
74 | ```
75 |
76 | ## Usage
77 |
78 | Here's what I recommend:
79 |
80 | 1. Download the workshop-setup script into `scripts/workshop-setup.js`
81 | 2. Add `engines` config to your `packge.json` with `node`, `npm`, and `yarn`
82 | listed
83 | 3. Add a `script` to your `package.json` called `setup` with:
84 | `node ./scripts/setup`
85 | 4. Create the `scripts/setup.js` file
86 | 5. And put this in it:
87 |
88 | ```javascript
89 | var path = require('path')
90 | var pkg = require(path.join(process.cwd(), 'package.json'))
91 |
92 | // if you install it then this should be require('workshop-setup')
93 | // but that... doesn't really make sense.
94 | require('./workshop-setup')
95 | .setup(pkg.engines)
96 | .then(
97 | () => {
98 | console.log(`💯 You're all set up! 👏`)
99 | },
100 | error => {
101 | console.error(`🚨 There was a problem:`)
102 | console.error(error)
103 | console.error(
104 | `\nIf you would like to just ignore this error, then feel free to do so and install dependencies as you normally would in "${process.cwd()}". Just know that things may not work properly if you do...`,
105 | )
106 | },
107 | )
108 | ```
109 |
110 | ### Alternative usage
111 |
112 | Whether you install it or download it, usage is basically the same. The
113 | difference is how you require it.
114 |
115 | ```javascript
116 | // if you install it, you'd do
117 | var workshopSetup = require('workshop-setup')
118 | // if you download it, you'd do something like:
119 | var workshopSetup = require('./workshop-setup')
120 | ```
121 |
122 | ### verifySystem
123 |
124 | This allows you to verify the user's system is correct:
125 |
126 | ```javascript
127 | var verifySystem = require('./workshop-setup').verifySystem
128 |
129 | var verifyPromise = verifySystem([
130 | verifySystem.validators.node('^8.4.0'),
131 | verifySystem.validators.npm('^5.4.1'),
132 | ])
133 |
134 | verifyPromise.then(
135 | function() {
136 | // resolves if there are no errors
137 | console.log('🎉 Congrats! Your system is setup properly')
138 | console.log('You should be good to install and run things.')
139 | },
140 | function(error) {
141 | // rejects if there are errors
142 | console.error(error)
143 | console.info(
144 | "\nIf you don't care about these warnings, go " +
145 | 'ahead and install dependencies with `node ./scripts/install`',
146 | )
147 | process.exitCode = 1
148 | },
149 | )
150 | ```
151 |
152 | You can also specify custom validators. There are several utilities exposed by
153 | `workshop-setup` as well which can be quite helpful.
154 |
155 | ```javascript
156 | verifySystem([
157 | function promiseVerify() {
158 | return new Promise(resolve => {
159 | // note the exclusion of reject here. We expect all validator promises to
160 | // resolve with `null` or the error message.
161 | resolve(null) // there were no errors
162 | })
163 | },
164 | function syncVerify() {
165 | if ('cats' > 'dogs') {
166 | return 'dogs are way better than cats'
167 | }
168 | return null
169 | },
170 | // here's a practical example that uses some utilities
171 | function validateYeoman() {
172 | return verifySystem.utils.execValidator('^1.8.5', 'yo --version', function(
173 | actual,
174 | desired,
175 | ) {
176 | return verifySystem.utils.commonTags.oneLine`
177 | You have version ${actual} of yeoman, but
178 | should have a version in the range: ${desired}
179 | `
180 | })
181 | },
182 | ]).then(/* handle success/failure */)
183 | ```
184 |
185 | #### validators
186 |
187 | The built-in validators available on `workshopSetup.verifySystem.validators`
188 | are:
189 |
190 | - `node(desiredVersionRange)`
191 | - `yarn(desiredVersionRange)`
192 | - `npm(desiredNpmVersionRange)`
193 |
194 | #### utils
195 |
196 | Most of the utils are simply exposing other modules which are bundled with
197 | `workshop-setup`. These are available on `workshopSetup.verifySystem.utils`:
198 |
199 | - `execValidator(desiredVersionRange, commandToGetVersion, messageFn)` -
200 | `messageFn` is given `actual, desired`
201 | - `oneLine`: a tag that allows you to have multiple lines for a message and
202 | it'll put it all on one line
203 | - [`semver`][semver] (really useful `satisfies` method on this one)
204 |
205 | ### installDeps
206 |
207 | This will install dependencies in the given directory/directories (defaults to
208 | `process.cwd()`) using `npm`.
209 |
210 | ```javascript
211 | var path = require('path')
212 | var installDeps = require('./workshop-setup').installDeps
213 |
214 | var main = path.resolve(__dirname, '..')
215 | var api = path.resolve(__dirname, '../api')
216 | var client = path.resolve(__dirname, '../client')
217 | installDeps([main, api, client]).then(
218 | () => {
219 | console.log('👍 all dependencies installed')
220 | },
221 | () => {
222 | // ignore, workshop-setup will log for us...
223 | },
224 | )
225 |
226 | // you can also do:
227 | installDeps()
228 | // which is effectively
229 | installDeps(process.cwd())
230 |
231 | // or, to be more specific:
232 | installDeps(path.resolve('..'))
233 | ```
234 |
235 | ## Inspiration
236 |
237 | This project was inspired by all of the people who have ever struggled to set up
238 | one of my workshops before. Hopefully it's easier now!
239 |
240 | ## Other Solutions
241 |
242 | I'm unaware of any other solutions for this problem. Feel free to link them here
243 | if you find any.
244 |
245 | ## Contributors
246 |
247 | Thanks goes to these people ([emoji key][emojis]):
248 |
249 |
250 |
251 | | [
Kent C. Dodds](https://kentcdodds.com)
[💻](https://github.com/kentcdodds/workshop-setup/commits?author=kentcdodds) [📖](https://github.com/kentcdodds/workshop-setup/commits?author=kentcdodds) 🚇 [⚠️](https://github.com/kentcdodds/workshop-setup/commits?author=kentcdodds) |
252 | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
253 |
254 |
255 |
256 |
257 | This project follows the [all-contributors][all-contributors] specification.
258 | Contributions of any kind welcome!
259 |
260 | ## LICENSE
261 |
262 | MIT
263 |
264 | [npm]: https://www.npmjs.com/
265 | [node]: https://nodejs.org
266 | [build-badge]:
267 | https://img.shields.io/travis/kentcdodds/workshop-setup.svg?style=flat-square
268 | [build]: https://travis-ci.org/kentcdodds/workshop-setup
269 | [coverage-badge]:
270 | https://img.shields.io/codecov/c/github/kentcdodds/workshop-setup.svg?style=flat-square
271 | [coverage]: https://codecov.io/github/kentcdodds/workshop-setup
272 | [dependencyci-badge]:
273 | https://dependencyci.com/github/kentcdodds/workshop-setup/badge?style=flat-square
274 | [dependencyci]: https://dependencyci.com/github/kentcdodds/workshop-setup
275 | [version-badge]:
276 | https://img.shields.io/npm/v/workshop-setup.svg?style=flat-square
277 | [package]: https://www.npmjs.com/package/workshop-setup
278 | [downloads-badge]:
279 | https://img.shields.io/npm/dm/workshop-setup.svg?style=flat-square
280 | [npm-stat]:
281 | http://npm-stat.com/charts.html?package=workshop-setup&from=2016-04-01
282 | [license-badge]:
283 | https://img.shields.io/npm/l/workshop-setup.svg?style=flat-square
284 | [license]:
285 | https://github.com/kentcdodds/workshop-setup/blob/master/other/LICENSE
286 | [prs-badge]:
287 | https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
288 | [prs]: http://makeapullrequest.com
289 | [donate-badge]:
290 | https://img.shields.io/badge/$-support-green.svg?style=flat-square
291 | [donate]: http://kcd.im/donate
292 | [coc-badge]:
293 | https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
294 | [coc]:
295 | https://github.com/kentcdodds/workshop-setup/blob/master/other/CODE_OF_CONDUCT.md
296 | [roadmap-badge]:
297 | https://img.shields.io/badge/%F0%9F%93%94-roadmap-CD9523.svg?style=flat-square
298 | [roadmap]:
299 | https://github.com/kentcdodds/workshop-setup/blob/master/other/ROADMAP.md
300 | [examples-badge]:
301 | https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square
302 | [examples]:
303 | https://github.com/kentcdodds/workshop-setup/blob/master/other/EXAMPLES.md
304 | [github-watch-badge]:
305 | https://img.shields.io/github/watchers/kentcdodds/workshop-setup.svg?style=social
306 | [github-watch]: https://github.com/kentcdodds/workshop-setup/watchers
307 | [github-star-badge]:
308 | https://img.shields.io/github/stars/kentcdodds/workshop-setup.svg?style=social
309 | [github-star]: https://github.com/kentcdodds/workshop-setup/stargazers
310 | [twitter]:
311 | https://twitter.com/intent/tweet?text=Check%20out%20workshop-setup!%20https://github.com/kentcdodds/workshop-setup%20%F0%9F%91%8D
312 | [twitter-badge]:
313 | https://img.shields.io/twitter/url/https/github.com/kentcdodds/workshop-setup.svg?style=social
314 | [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key
315 | [all-contributors]: https://github.com/kentcdodds/all-contributors
316 | [workshops]: https://kentcdodds.com/workshops
317 | [semver]: https://www.npmjs.com/package/semver
318 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | const config = require('kcd-scripts/jest')
2 |
3 | config.coverageThreshold.global = {
4 | branches: 50,
5 | statements: 60,
6 | functions: 60,
7 | lines: 50,
8 | }
9 |
--------------------------------------------------------------------------------
/other/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at kent+coc@doddsfamily.us. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/other/EXAMPLES.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | There aren't any examples yet! Want to add one? See `CONTRIBUTING.md`
4 |
--------------------------------------------------------------------------------
/other/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # Project Roadmap
2 |
3 | This is where we'll define a few things about the library's goals.
4 |
5 | ## Want to do
6 |
7 | - Add more validators for common things
8 |
9 | ## Might do
10 |
11 | ## Wont do
12 |
--------------------------------------------------------------------------------
/other/manual-release.md:
--------------------------------------------------------------------------------
1 | # manual release
2 |
3 | This file is just here to be used when there's a mistake in the auto-release.
4 | Just increment the number below and commit your change with the commit type
5 | that should have been released which will trigger a release. Then you can
6 | update the changelog if necessary.
7 |
8 | **Manual Release Count: 1**
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "workshop-setup",
3 | "version": "0.0.0-semantically-released",
4 | "description": "Verify and setup a repository for workshop attendees",
5 | "main": "dist/index.js",
6 | "engines": {
7 | "node": ">=8",
8 | "npm": ">=6",
9 | "yarn": ">=1"
10 | },
11 | "scripts": {
12 | "build": "webpack",
13 | "lint": "kcd-scripts lint",
14 | "test": "kcd-scripts test",
15 | "test:update": "npm test -- --updateSnapshot --coverage",
16 | "validate": "kcd-scripts validate"
17 | },
18 | "husky": {
19 | "hooks": {
20 | "pre-commit": "kcd-scripts pre-commit"
21 | }
22 | },
23 | "files": [
24 | "dist",
25 | "run-setup.js"
26 | ],
27 | "bin": {
28 | "workshop-setup": "run-setup.js"
29 | },
30 | "keywords": [
31 | "workshop",
32 | "tool",
33 | "utility",
34 | "repository"
35 | ],
36 | "author": "Kent C. Dodds (http://kentcdodds.com/)",
37 | "license": "MIT",
38 | "devDependencies": {
39 | "@babel/core": "^7.4.3",
40 | "@babel/preset-env": "^7.4.3",
41 | "babel-loader": "^8.0.5",
42 | "indent-string": "^3.2.0",
43 | "jest-in-case": "^1.0.2",
44 | "kcd-scripts": "^1.2.1",
45 | "semver": "^6.0.0",
46 | "webpack": "^4.30.0",
47 | "webpack-cli": "^3.3.0"
48 | },
49 | "eslintConfig": {
50 | "extends": [
51 | "./node_modules/kcd-scripts/eslint.js"
52 | ],
53 | "rules": {
54 | "no-console": "off"
55 | }
56 | },
57 | "eslintIgnore": [
58 | "node_modules",
59 | "coverage",
60 | "dist"
61 | ],
62 | "babel": {
63 | "presets": [
64 | [
65 | "@babel/preset-env",
66 | {
67 | "targets": {
68 | "node": "8.0.0"
69 | }
70 | }
71 | ]
72 | ]
73 | },
74 | "repository": {
75 | "type": "git",
76 | "url": "https://github.com/kentcdodds/workshop-setup.git"
77 | },
78 | "bugs": {
79 | "url": "https://github.com/kentcdodds/workshop-setup/issues"
80 | },
81 | "homepage": "https://github.com/kentcdodds/workshop-setup#readme"
82 | }
83 |
--------------------------------------------------------------------------------
/run-setup.js:
--------------------------------------------------------------------------------
1 | // lol, I realized after writing this that it doesn't really make sense because
2 | // folks who need to run this wont have the package installed anyway, so this
3 | // file is basically useless, but I'll leave it here as something that can be
4 | // copy/pasted in each individual project :)
5 |
6 | var path = require('path')
7 | var setup = require('./dist').setup
8 |
9 | setup(require(path.join(process.cwd(), 'package.json')).engines).catch(
10 | error => {
11 | console.error(`🚨 There was a problem:`)
12 | console.error(error)
13 | console.error(
14 | `\nIf you would like to just ignore this error, then feel free to do so and install dependencies as you normally would in "${process.cwd()}". Just know that things may not work properly if you do...`,
15 | )
16 | },
17 | )
18 |
19 | /* eslint no-var:0 */
20 |
--------------------------------------------------------------------------------
/src/__mocks__/child_process.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | execSync: jest.fn(),
3 | spawn: jest.fn(),
4 | }
5 |
--------------------------------------------------------------------------------
/src/__mocks__/semver.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | satisfies: jest.fn(),
3 | }
4 |
--------------------------------------------------------------------------------
/src/__tests__/index.js:
--------------------------------------------------------------------------------
1 | /* eslint import/default:0 */
2 | import * as workshopSetup from '../'
3 | import installDeps from '../install-deps'
4 | import verifySystem from '../verify-system'
5 | import setup from '../setup'
6 |
7 | test('exposes the right stuff', () => {
8 | expect(workshopSetup).toEqual({
9 | installDeps,
10 | verifySystem,
11 | setup,
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import verifySystem from './verify-system'
2 | import installDeps from './install-deps'
3 | import setup from './setup'
4 |
5 | export {verifySystem, installDeps, setup}
6 |
--------------------------------------------------------------------------------
/src/install-deps/__tests__/index.js:
--------------------------------------------------------------------------------
1 | import cpMock from 'child_process'
2 | import installDeps from '../' // eslint-disable-line import/default
3 |
4 | jest.mock('child_process')
5 |
6 | beforeEach(() => {
7 | jest.spyOn(console, 'log').mockImplementation(() => {})
8 | jest.spyOn(console, 'warn').mockImplementation(() => {})
9 | jest.spyOn(console, 'error').mockImplementation(() => {})
10 | })
11 |
12 | afterEach(() => {
13 | console.log.mockRestore()
14 | console.warn.mockRestore()
15 | console.error.mockRestore()
16 | })
17 |
18 | test('installs via npm', async () => {
19 | await testInstallDeps()
20 | const installer = 'npm'
21 | const args = ['install', '--no-package-lock']
22 | expect(cpMock.spawn).toHaveBeenCalledWith(installer, args, {
23 | stdio: 'inherit',
24 | shell: true,
25 | cwd: process.cwd(),
26 | })
27 | })
28 |
29 | test('installs in the given directory', async () => {
30 | const mockCwd = './dir'
31 | await testInstallDeps({installDepsArgs: mockCwd})
32 | expect(cpMock.spawn).toHaveBeenCalledWith(
33 | expect.any(String),
34 | expect.any(Array),
35 | {
36 | stdio: 'inherit',
37 | shell: true,
38 | cwd: mockCwd,
39 | },
40 | )
41 | })
42 |
43 | test('installs in all given directories', async () => {
44 | const mockCwds = ['./dir1', './dir2', './dir3']
45 | await testInstallDeps({installDepsArgs: mockCwds})
46 | mockCwds.forEach(cwd => {
47 | expect(cpMock.spawn).toHaveBeenCalledWith(
48 | expect.any(String),
49 | expect.any(Array),
50 | {
51 | stdio: 'inherit',
52 | shell: true,
53 | cwd,
54 | },
55 | )
56 | })
57 | })
58 |
59 | test('rejects if an exit code is non-zero', async () => {
60 | const onMock = jest.fn((event, callback) => {
61 | const exitCode = 1
62 | callback(exitCode)
63 | })
64 | let error
65 | try {
66 | await testInstallDeps({onMock})
67 | } catch (e) {
68 | error = e
69 | }
70 | expect(error).toEqual(process.cwd())
71 | expect(console.error).toHaveBeenCalledTimes(1)
72 | })
73 |
74 | async function testInstallDeps({onMock, installDepsArgs} = {}) {
75 | // not sure why, but default args for onMock messed things up...
76 | const onSpy = jest.fn(onMock || defaultOnMock)
77 | cpMock.spawn.mockClear()
78 | cpMock.spawn.mockImplementation(() => ({on: onSpy}))
79 | await installDeps(installDepsArgs)
80 | expect(cpMock.spawn).toHaveBeenCalledTimes(getSpawnCalls())
81 | expect(onSpy).toHaveBeenCalledWith('exit', expect.any(Function))
82 |
83 | function getSpawnCalls() {
84 | if (!installDepsArgs || !Array.isArray(installDepsArgs)) {
85 | return 1
86 | } else {
87 | return installDepsArgs.length
88 | }
89 | }
90 |
91 | function defaultOnMock(event, callback) {
92 | if (event === 'exit') {
93 | const exitCode = 0
94 | callback(exitCode)
95 | } else {
96 | throw new Error('should not call on with anything but `exit`')
97 | }
98 | }
99 | }
100 |
101 | /* eslint no-console:0 */
102 |
--------------------------------------------------------------------------------
/src/install-deps/index.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import fs from 'fs'
3 | import cp from 'child_process'
4 |
5 | export default installDeps
6 |
7 | function installDeps(directories = [process.cwd()], {yarnOk} = {}) {
8 | if (!Array.isArray(directories)) {
9 | directories = [directories]
10 | }
11 |
12 | let promise = Promise.resolve()
13 | directories.forEach(dir => {
14 | promise = promise.then(() => spawnInstall(dir))
15 | })
16 | return promise
17 |
18 | function spawnInstall(cwd) {
19 | return new Promise((resolve, reject) => {
20 | const hasPkgLock = fs.existsSync(
21 | path.join(process.cwd(), 'package-lock.json'),
22 | )
23 | const hasYarnLock = fs.existsSync(path.join(process.cwd(), 'yarn.lock'))
24 | const useYarn = yarnOk && (hasYarnLock || !hasPkgLock)
25 | let installer = 'npm'
26 | let installerArgs = [
27 | hasPkgLock ? 'ci' : 'install',
28 | hasPkgLock ? null : '--no-package-lock',
29 | ].filter(Boolean)
30 |
31 | if (useYarn) {
32 | installer = 'yarn'
33 | installerArgs = [hasYarnLock ? null : '--no-lockfile'].filter(Boolean)
34 | } else if (!yarnOk && (hasYarnLock && !hasPkgLock)) {
35 | console.warn(
36 | `⚠️ "${cwd}" has a yarn.lock file, but this system does not have the right version of yarn installed.`,
37 | `We'll install using npm instead, but you may experience issues. Install the correct version of yarn to get rid of this warning.`,
38 | )
39 | }
40 |
41 | const command = [installer, ...installerArgs].join(' ')
42 | console.log(`📦 starting \`${command}\` in "${cwd}"`)
43 |
44 | const child = cp.spawn(installer, installerArgs, {
45 | stdio: 'inherit',
46 | shell: true,
47 | cwd,
48 | })
49 | child.on('exit', onExit)
50 |
51 | function onExit(code) {
52 | if (code === 0) {
53 | console.log(`🎉 finished installing dependencies in "${cwd}"`)
54 | resolve()
55 | } else {
56 | console.error(`💀 error installing dependencies in "${cwd}"`)
57 | reject(cwd)
58 | }
59 | }
60 | })
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/run-setup.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import setup from './setup'
3 |
4 | const {engines} = require(path.join(process.cwd(), 'package.json'))
5 |
6 | setup(engines)
7 |
--------------------------------------------------------------------------------
/src/setup.js:
--------------------------------------------------------------------------------
1 | import verifySystem from './verify-system'
2 | import installDeps from './install-deps'
3 |
4 | export default setup
5 |
6 | async function setup({directories, node, yarn, npm}) {
7 | const nodeError = await verifySystem.validators.node(node)()
8 | if (nodeError) {
9 | return Promise.reject(nodeError)
10 | }
11 | const npmError = await verifySystem.validators.npm(npm)()
12 | const yarnError = await verifySystem.validators.yarn(yarn)()
13 | if (yarnError && npmError) {
14 | const errorMessage = [
15 | verifySystem.utils.oneLine`
16 | We tried to validate The installed version of npm and yarn and both failed.
17 | We recommend you fix yarn, but fixing either will resolve this issue.
18 | Here are the validation error messages for each:
19 | `,
20 | '',
21 | yarnError,
22 | '',
23 | npmError,
24 | ].join('\n')
25 | return Promise.reject(errorMessage)
26 | }
27 | return installDeps(directories, {yarnOk: !yarnError})
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/__tests__/one-line.js:
--------------------------------------------------------------------------------
1 | import cases from 'jest-in-case'
2 | import oneLine from '../one-line'
3 |
4 | cases(
5 | 'one-line',
6 | output => {
7 | expect(output).not.toContain('\n')
8 | expect(output).not.toContain(' ')
9 | },
10 | [
11 | oneLine`
12 | foo
13 |
14 | bar
15 | `,
16 | oneLine`foo
17 | baz
18 | buz
19 | `,
20 | ],
21 | )
22 |
--------------------------------------------------------------------------------
/src/utils/one-line.js:
--------------------------------------------------------------------------------
1 | function oneLine(strings, ...interpolations) {
2 | const string = strings
3 | .map(
4 | (s, i) =>
5 | `${s}${interpolations[i] === undefined ? '' : interpolations[i]}`,
6 | )
7 | .join('')
8 | .split('\n')
9 | .join(' ')
10 | .split(' ')
11 | .filter(Boolean)
12 | .join(' ')
13 |
14 | return string
15 | }
16 |
17 | export default oneLine
18 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/__snapshots__/index.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`returns an error message if a validator returns one 1`] = `
4 | "There are some issues with your system.
5 | - error 1
6 | - error 2"
7 | `;
8 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/__snapshots__/node.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`returns error if conditions are not satisfied 1`] = `"The installed version of node (4.7.0) does not satisfy the desired range of 6.9.5. Please install a version within the range. You can use http://git.io/nvm or https://github.com/coreybutler/nvm-windows to make changing The installed version of node easier."`;
4 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/__snapshots__/npm.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`returns npm version mismatch 1`] = `"The installed version of npm (3.11.3) does not satisfy the desired range of ^4.0.3. Please at least have a version within the specified version range. You can install the latest version by running \`npm install --global npm@^4.0.3\`."`;
4 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/exec-validator.js:
--------------------------------------------------------------------------------
1 | import cpMock from 'child_process'
2 | import {satisfies as satisfiesMock} from 'semver'
3 | import execValidator from '../exec-validator'
4 |
5 | jest.mock('child_process')
6 |
7 | test('returns null if the version is satisfied', () => {
8 | const mockActual = '1.2.3'
9 | setup({mockActual})
10 | const desired = '1.2.3'
11 | const command = 'split-guide --version'
12 | expect(execValidator(desired, command, () => 'error')).toBe(null)
13 | expect(cpMock.execSync).toHaveBeenCalledTimes(1)
14 | expect(cpMock.execSync).toHaveBeenCalledWith(command)
15 | expect(satisfiesMock).toHaveBeenCalledTimes(1)
16 | expect(satisfiesMock).toHaveBeenCalledWith(mockActual, desired)
17 | })
18 |
19 | test('returns call to message if the version is not satisfied', () => {
20 | const mockActual = '1.3.4'
21 | setup({mockActual, satisfies: false})
22 | const desired = '1.2.3'
23 | const command = 'split-guide --version'
24 | const errorMessage = 'error'
25 | const messageCallback = jest.fn(() => errorMessage)
26 | expect(execValidator(desired, command, messageCallback)).toBe(errorMessage)
27 | expect(messageCallback).toHaveBeenCalledTimes(1)
28 | expect(messageCallback).toHaveBeenCalledWith(mockActual, desired)
29 | })
30 |
31 | function setup({mockActual = '1.2.3', satisfies = true} = {}) {
32 | satisfiesMock.mockClear()
33 | cpMock.execSync.mockClear()
34 | satisfiesMock.mockImplementation(() => satisfies)
35 | cpMock.execSync.mockImplementation(() => mockActual)
36 | }
37 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/index.js:
--------------------------------------------------------------------------------
1 | import verifySystem from '../'
2 |
3 | test('resolve null with no errors', async () => {
4 | const result = await verifySystem()
5 | expect(result).toBe(null)
6 | })
7 |
8 | test(`filters out falsy values (you're quite welcome)`, async () => {
9 | const result = await verifySystem([undefined, null])
10 | expect(result).toBe(null)
11 | })
12 |
13 | test('calls validators and returns null if no messages returned', async () => {
14 | const v1 = jest.fn()
15 | const v2 = jest.fn(() => null)
16 | const result = await verifySystem([v1, v2])
17 | expect(result).toBe(null)
18 | expect(v1).toHaveBeenCalledTimes(1)
19 | expect(v2).toHaveBeenCalledTimes(1)
20 | })
21 |
22 | test('returns an error message if a validator returns one', async () => {
23 | const result = await verifySystem([() => 'error 1', () => 'error 2']).catch(
24 | error => error,
25 | )
26 | expect(result).toMatchSnapshot()
27 | })
28 |
29 | test('properly pluarlizes a single error message', async () => {
30 | const result = await verifySystem([() => 'error 1']).catch(error => error)
31 | expect(result).toContain('is an issue')
32 | })
33 |
34 | test('properly pluarlizes multiple error messages', async () => {
35 | const result = await verifySystem([() => 'error 1', () => 'error 2']).catch(
36 | error => error,
37 | )
38 | expect(result).toContain('are some issues')
39 | })
40 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/node.js:
--------------------------------------------------------------------------------
1 | import mockProcess from 'process'
2 | import {satisfies as satisfiesMock} from 'semver'
3 | import validateNode from '../node'
4 |
5 | jest.mock('process', () => ({
6 | versions: {
7 | node: '6.9.5',
8 | },
9 | }))
10 |
11 | test('returns null if conditions are satisfied', () => {
12 | setup({version: '6.9.5'})
13 | const desired = '6.9.5'
14 | const result = validateNode(desired)()
15 | expect(result).toBe(null)
16 | })
17 |
18 | test('returns error if conditions are not satisfied', () => {
19 | const mockActual = '4.7.0'
20 | setup({version: mockActual, satisfies: false})
21 | const desired = '6.9.5'
22 | const result = validateNode(desired)()
23 | expect(result).toContain(desired)
24 | expect(result).toContain(mockActual)
25 | expect(result).toMatchSnapshot()
26 | })
27 |
28 | function setup({version = '6.9.5', satisfies = true} = {}) {
29 | mockProcess.versions.node = version
30 | satisfiesMock.mockClear()
31 | satisfiesMock.mockImplementation(() => satisfies)
32 | }
33 |
--------------------------------------------------------------------------------
/src/verify-system/__tests__/npm.js:
--------------------------------------------------------------------------------
1 | import cpMock from 'child_process'
2 | import {satisfies as satisfiesMock} from 'semver'
3 | import oneLine from '../../utils/one-line'
4 | import validateNpm from '../npm'
5 |
6 | jest.mock('child_process')
7 |
8 | test('returns npm version mismatch', () => {
9 | const mockNpmVersion = '3.11.3'
10 | setup({
11 | mockNpmVersion,
12 | satisfiesNpm: false,
13 | mockYarnVersion: () => {
14 | throw new Error('blah')
15 | },
16 | })
17 | const desiredNpm = '^4.0.3'
18 | const result = validateNpm(desiredNpm)()
19 | expect(result).toContain(desiredNpm)
20 | expect(result).toContain(mockNpmVersion)
21 | expect(result).toMatchSnapshot()
22 | })
23 |
24 | function setup({mockNpmVersion = '4.0.3', satisfiesNpm = true} = {}) {
25 | satisfiesMock.mockClear()
26 | cpMock.execSync.mockClear()
27 | satisfiesMock.mockImplementation(satisfiesMockImplementation)
28 | cpMock.execSync.mockImplementation(execSyncMock)
29 |
30 | function satisfiesMockImplementation(actual, desired) {
31 | if (actual === mockNpmVersion) {
32 | return satisfiesNpm
33 | } else {
34 | console.error(
35 | oneLine`
36 | called satisifies with "${desired}, ${actual}".
37 | ${actual} does not match npm@${mockNpmVersion}
38 | `,
39 | )
40 | throw new Error('satisfies mock implementation insufficient')
41 | }
42 | }
43 |
44 | function execSyncMock(command) {
45 | if (command.includes('npm')) {
46 | return callAndReturn(mockNpmVersion)
47 | } else {
48 | console.error(
49 | oneLine`
50 | called execSync with "${command}".
51 | This does not include npm
52 | `,
53 | )
54 | throw new Error('execSync mock implementation insufficient')
55 | }
56 | }
57 |
58 | function callAndReturn(thing) {
59 | if (typeof thing === 'function') {
60 | thing = thing()
61 | }
62 | return thing
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/verify-system/exec-validator.js:
--------------------------------------------------------------------------------
1 | import {execSync} from 'child_process'
2 | import semver from 'semver'
3 | import oneLine from '../utils/one-line'
4 |
5 | export default execValidator
6 |
7 | function execValidator(desired, command, message) {
8 | let actual = '0.0.0'
9 | try {
10 | actual = execSync(command)
11 | .toString()
12 | .trim()
13 | } catch (error) {
14 | return oneLine`
15 | There was an error running the command \`${command}\`:
16 | ${error.message}
17 | `
18 | }
19 | return semver.satisfies(actual, desired) ? null : message(actual, desired)
20 | }
21 |
--------------------------------------------------------------------------------
/src/verify-system/index.js:
--------------------------------------------------------------------------------
1 | import indentString from 'indent-string'
2 | import semver from 'semver'
3 | import oneLine from '../utils/one-line'
4 | import node from './node'
5 | import npm from './npm'
6 | import yarn from './yarn'
7 | import execValidator from './exec-validator'
8 |
9 | export default validateAll
10 |
11 | Object.assign(validateAll, {
12 | utils: {
13 | execValidator,
14 | oneLine,
15 | semver,
16 | },
17 | validators: {
18 | node,
19 | npm,
20 | yarn,
21 | },
22 | })
23 |
24 | function validateAll(validators = []) {
25 | const promises = validators.filter(Boolean).map(v => Promise.resolve(v()))
26 | return Promise.all(promises).then(results => {
27 | const errors = results.filter(Boolean)
28 | const errorCount = errors.length
29 |
30 | if (errorCount) {
31 | const errorMessages = errors.map(error => `- ${error}`).join('\n')
32 | const one = errorCount === 1
33 |
34 | return Promise.reject(
35 | [
36 | oneLine`
37 | There ${one ? 'is an issue' : 'are some issues'} with your system.
38 | `,
39 | indentString(errorMessages, 2),
40 | ].join('\n'),
41 | )
42 | } else {
43 | return null
44 | }
45 | })
46 | }
47 |
--------------------------------------------------------------------------------
/src/verify-system/node.js:
--------------------------------------------------------------------------------
1 | import process from 'process'
2 | import semver from 'semver'
3 | import oneLine from '../utils/one-line'
4 |
5 | export default getNodeValidator
6 |
7 | function getNodeValidator(desired) {
8 | return validateNode
9 |
10 | function validateNode() {
11 | const actual = process.versions.node
12 | if (!semver.satisfies(actual, desired)) {
13 | return oneLine`
14 | The installed version of node (${actual}) does not satisfy
15 | the desired range of ${desired}.
16 | Please install a version within the range. You can use
17 | http://git.io/nvm or https://github.com/coreybutler/nvm-windows
18 | to make changing The installed version of node easier.
19 | `
20 | }
21 | return null
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/verify-system/npm.js:
--------------------------------------------------------------------------------
1 | import oneLine from '../utils/one-line'
2 | import execValidator from './exec-validator'
3 |
4 | export default getNPMValidator
5 |
6 | function getNPMValidator(desired) {
7 | return validateNpm
8 |
9 | function validateNpm() {
10 | return execValidator(desired, 'npm --version', actual => {
11 | return oneLine`
12 | The installed version of npm (${actual}) does not satisfy
13 | the desired range of ${desired}.
14 | Please at least have a version within the specified version range.
15 | You can install the latest version by running
16 | \`npm install --global npm@${desired}\`.
17 | `
18 | })
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/verify-system/yarn.js:
--------------------------------------------------------------------------------
1 | import oneLine from '../utils/one-line'
2 | import execValidator from './exec-validator'
3 |
4 | export default getYarnValidator
5 |
6 | function getYarnValidator(desired) {
7 | return validateYarn
8 |
9 | function validateYarn() {
10 | return execValidator(desired, 'yarn --version', actual => {
11 | return oneLine`
12 | The installed version of yarn (${actual}) does not satisfy
13 | the desired range of ${desired}.
14 | Please at least have a version within the specified version range.
15 | Updating to the latest version of yarn depends on how you installed it
16 | in the first place. Please see more information here:
17 | https://yarnpkg.com/en/docs/install
18 | `
19 | })
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const join = require('path').join
2 | const webpack = require('webpack')
3 |
4 | const include = join(__dirname, 'src')
5 |
6 | module.exports = {
7 | entry: './src/index',
8 | target: 'node',
9 | mode: 'development',
10 | output: {
11 | path: join(__dirname, 'dist'),
12 | libraryTarget: 'commonjs2',
13 | filename: 'index.js',
14 | },
15 | devtool: 'source-map',
16 | module: {
17 | rules: [{test: /\.js$/, use: [{loader: 'babel-loader'}], include}],
18 | },
19 | plugins: [new webpack.optimize.ModuleConcatenationPlugin()],
20 | }
21 |
22 | /* eslint babel/camelcase:0 */
23 |
--------------------------------------------------------------------------------