diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 0281e21..d23233e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -18,13 +18,10 @@ learn how: http://kcd.im/pull-request Relevant code or config ```javascript - ``` What you did: - - What happened: @@ -38,6 +35,4 @@ minimal amount of code possible. Problem description: - - Suggested solution: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e421002..3af53f1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,21 +15,27 @@ merge of your pull request! --> + **What**: + **Why**: + **How**: + **Checklist**: + + - [ ] Documentation - [ ] Tests -- [ ] Ready to be merged -- [ ] Added myself to contributors table +- [ ] Ready to be merged + diff --git a/CHANGELOG.md b/CHANGELOG.md index 06d221a..2a67529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # CHANGELOG -The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). -You can see it on the [releases page](../../releases). +The changelog is automatically updated using +[semantic-release](https://github.com/semantic-release/semantic-release). You +can see it on the [releases page](../../releases). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2269bf..6a4b598 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,14 @@ Thanks for being willing to contribute! -**Working on your first Pull Request?** You can learn how from this *free* series -[How to Contribute to an Open Source Project on GitHub][egghead] +**Working on your first Pull Request?** You can learn how from this _free_ +series [How to Contribute to an Open Source Project on GitHub][egghead] ## Project setup 1. Fork and clone the repo -2. `$ npm install` to install dependencies -3. `$ npm run validate` to validate you've got it working +2. `npm install` to install dependencies +3. `npm run validate` to validate you've got it working 4. Create a branch for your PR > Tip: Keep your `master` branch pointing at the original repository and make @@ -21,45 +21,11 @@ Thanks for being willing to contribute! > git branch --set-upstream-to=upstream/master master > ``` > -> This will add the original repository as a "remote" called "upstream," -> Then fetch the git information from that remote, then set your local `master` -> branch to use the upstream master branch whenever you run `git pull`. -> Then you can make all of your pull request branches based on this `master` -> branch. Whenever you want to update your version of `master`, do a regular -> `git pull`. - -## Add yourself as a contributor - -This project follows the [all contributors][all-contributors] specification. -To add yourself to the table of contributors on the `README.md`, please use the -automated script as part of your PR: - -```console -npm run add-contributor -``` - -Follow the prompt and commit `.all-contributorsrc` and `README.md` in the PR. -If you've already added yourself to the list and are making -a new type of contribution, you can run it again and select the added -contribution type. - -## Committing and Pushing changes - -Please make sure to run the tests before you commit your changes. You can run -`npm run test:update` which will update any snapshots that need updating. -Make sure to include those changes (if they exist) in your commit. - -### opt into git hooks - -There are git hooks set up with this project that are automatically installed -when you install dependencies. They're really handy, but are turned off by -default (so as to not hinder new contributors). You can opt into these by -creating a file called `.opt-in` at the root of the project and putting this -inside: - -``` -pre-commit -``` +> This will add the original repository as a "remote" called "upstream," Then +> fetch the git information from that remote, then set your local `master` +> branch to use the upstream master branch whenever you run `git pull`. Then you +> can make all of your pull request branches based on this `master` branch. +> Whenever you want to update your version of `master`, do a regular `git pull`. ## Help needed @@ -68,6 +34,7 @@ Please checkout the [the open issues][issues] Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks! -[egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github +[egghead]: + https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github [all-contributors]: https://github.com/kentcdodds/all-contributors [issues]: https://github.com/kentcdodds/cross-env/issues diff --git a/README.md b/README.md index 2025fef..4675b4b 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ + + + +- [cross-env 🔀](#cross-env-) + - [The problem](#the-problem) + - [This solution](#this-solution) + - [Installation](#installation) + - [Usage](#usage) + - [`cross-env` vs `cross-env-shell`](#cross-env-vs-cross-env-shell) + - [Windows Issues](#windows-issues) + - [Inspiration](#inspiration) + - [Other Solutions](#other-solutions) + - [Contributors](#contributors) + - [LICENSE](#license) + + +

@@ -16,22 +33,11 @@ Run scripts that set and use environment variables across platforms [![Travis Build Status][build-badge]][build] [![AppVeyor Build Status][win-build-badge]][win-build] [![Code Coverage][coverage-badge]][coverage] -[![Dependencies][dependencyci-badge]][dependencyci] -[![version][version-badge]][package] -[![node-version][node-version-badge]][node] -[![downloads][downloads-badge]][npm-stat] +[![version][version-badge]][package] [![MIT License][license-badge]][license] -[![MIT License][license-badge]][LICENSE] [![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors) -[![PRs Welcome][prs-badge]][prs] -[![Donate][donate-badge]][donate] -[![Code of Conduct][coc-badge]][coc] -[![Roadmap][roadmap-badge]][roadmap] -[![Examples][examples-badge]][examples] - -[![Watch on GitHub][github-watch-badge]][github-watch] -[![Star on GitHub][github-star-badge]][github-star] -[![Tweet][twitter-badge]][twitter] +[![PRs Welcome][prs-badge]][prs] [![Code of Conduct][coc-badge]][coc] +[![downloads][downloads-badge]][npmtrends] ## The problem @@ -60,8 +66,8 @@ npm install --save-dev cross-env > WARNING! Make sure that when you're installing packages that you spell things > correctly to avoid [mistakenly installing malware][malware] -> NOTE : Version 6 of cross-env only supports Node.js 8 and higher, to use it on Node.js 7 or lower install version 5 -> ```npm install --save-dev cross-env@5 ``` +> NOTE : Version 6 of cross-env only supports Node.js 8 and higher, to use it on +> Node.js 7 or lower install version 5 `npm install --save-dev cross-env@5` ## Usage @@ -100,12 +106,16 @@ Where `childScript` holds the actual command to execute and `parentScript` sets the environment variables to use. Then instead of run the childScript you run the parent. This is quite useful for launching the same command with different env variables or when the environment variables are too long to have everything -in one line. It also means that you can use `$GREET` env var syntax even on +in one line. It also means that you can use `$GREET` env var syntax even on Windows which would usually require it to be `%GREET%`. -If you precede a dollar sign with an odd number of backslashes the expression statement will not be replaced. Note that this means backslashes after the JSON string escaping took place. `"FOO=\\$BAR"` will not be replaced. `"FOO=\\\\$BAR"` will be replaced though. +If you precede a dollar sign with an odd number of backslashes the expression +statement will not be replaced. Note that this means backslashes after the JSON +string escaping took place. `"FOO=\\$BAR"` will not be replaced. +`"FOO=\\\\$BAR"` will be replaced though. -Lastly, if you want to pass a JSON string (e.g., when using [ts-loader]), you can do as follows: +Lastly, if you want to pass a JSON string (e.g., when using [ts-loader]), you +can do as follows: ```json { @@ -115,21 +125,21 @@ Lastly, if you want to pass a JSON string (e.g., when using [ts-loader]), you ca } ``` -Pay special attention to the **triple backslash** `(\\\)` **before** the **double quotes** `(")` and the **absence** of **single quotes** `(')`. -Both of these conditions have to be met in order to work both on Windows and UNIX. +Pay special attention to the **triple backslash** `(\\\)` **before** the +**double quotes** `(")` and the **absence** of **single quotes** `(')`. Both of +these conditions have to be met in order to work both on Windows and UNIX. ## `cross-env` vs `cross-env-shell` The `cross-env` module exposes two bins: `cross-env` and `cross-env-shell`. The -first one executes commands using [`cross-spawn`][cross-spawn], while the -second one uses the `shell` option from Node's `spawn`. +first one executes commands using [`cross-spawn`][cross-spawn], while the second +one uses the `shell` option from Node's `spawn`. -The main use case for `cross-env-shell` is when you need an environment -variable to be set across an entire inline shell script, rather than just one -command. +The main use case for `cross-env-shell` is when you need an environment variable +to be set across an entire inline shell script, rather than just one command. For example, if you want to have the environment variable apply to several -commands in series then you will need to wrap those in quotes and use +commands in series then you will need to wrap those in quotes and use `cross-env-shell` instead of `cross-env`. ```json @@ -140,29 +150,34 @@ commands in series then you will need to wrap those in quotes and use } ``` -The rule of thumb is: if you want to pass to `cross-env` a command that -contains special shell characters *that you want interpreted*, then use +The rule of thumb is: if you want to pass to `cross-env` a command that contains +special shell characters _that you want interpreted_, then use `cross-env-shell`. Otherwise stick to `cross-env`. -On Windows you need to use `cross-env-shell`, if you want to handle [signal events](https://nodejs.org/api/process.html#process_signal_events) inside of your program. A common case for that is when you want to capture a `SIGINT` event invoked by pressing `Ctrl + C` on the command-line interface. +On Windows you need to use `cross-env-shell`, if you want to handle +[signal events](https://nodejs.org/api/process.html#process_signal_events) +inside of your program. A common case for that is when you want to capture a +`SIGINT` event invoked by pressing `Ctrl + C` on the command-line interface. ## Windows Issues Please note that `npm` uses `cmd` by default and that doesn't support command -substitution, so if you want to leaverage that, then you need to update your +substitution, so if you want to leverage that, then you need to update your `.npmrc` to set the `script-shell` to powershell. [Learn more here](https://github.com/kentcdodds/cross-env/issues/192#issuecomment-513341729). ## Inspiration I originally created this to solve a problem I was having with my npm scripts in -[angular-formly][angular-formly]. This made contributing to the project -much easier for Windows users. +[angular-formly][angular-formly]. This made contributing to the project much +easier for Windows users. ## Other Solutions -- [`env-cmd`](https://github.com/toddbluhm/env-cmd) - Reads environment variables from a file instead -- [`@naholyr/cross-env`](https://www.npmjs.com/package/@naholyr/cross-env) - `cross-env` with support for setting default values +- [`env-cmd`](https://github.com/toddbluhm/env-cmd) - Reads environment + variables from a file instead +- [`@naholyr/cross-env`](https://www.npmjs.com/package/@naholyr/cross-env) - + `cross-env` with support for setting default values ## Contributors @@ -201,11 +216,12 @@ Thanks goes to these people ([emoji key][emojis]): -This project follows the [all-contributors][all-contributors] specification. Contributions of any kind welcome! +This project follows the [all-contributors][all-contributors] specification. +Contributions of any kind welcome! > Note: this was added late into the project. If you've contributed to this -> project in any way, please make a pull request to add yourself to the list -> by following the instructions in the `CONTRIBUTING.md` +> project in any way, please make a pull request to add yourself to the list by +> following the instructions in the `CONTRIBUTING.md` ## LICENSE @@ -213,41 +229,33 @@ MIT [npm]: https://www.npmjs.com/ [node]: https://nodejs.org -[build-badge]: https://img.shields.io/travis/kentcdodds/cross-env.svg?style=flat-square +[build-badge]: + https://img.shields.io/travis/kentcdodds/cross-env.svg?style=flat-square [build]: https://travis-ci.org/kentcdodds/cross-env -[win-build-badge]: https://img.shields.io/appveyor/ci/kentcdodds/cross-env.svg?style=flat-square +[win-build-badge]: + https://img.shields.io/appveyor/ci/kentcdodds/cross-env.svg?style=flat-square [win-build]: https://ci.appveyor.com/project/kentcdodds/cross-env -[coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/cross-env.svg?style=flat-square +[coverage-badge]: + https://img.shields.io/codecov/c/github/kentcdodds/cross-env.svg?style=flat-square [coverage]: https://codecov.io/github/kentcdodds/cross-env -[dependencyci-badge]: https://dependencyci.com/github/kentcdodds/cross-env/badge?style=flat-square -[dependencyci]: https://dependencyci.com/github/kentcdodds/cross-env [version-badge]: https://img.shields.io/npm/v/cross-env.svg?style=flat-square [package]: https://www.npmjs.com/package/cross-env -[node-version-badge]: https://img.shields.io/badge/node-%3E%3D%204.0-orange.svg?style=flat-square [downloads-badge]: https://img.shields.io/npm/dm/cross-env.svg?style=flat-square -[npm-stat]: http://npm-stat.com/charts.html?package=cross-env&from=2016-04-01 +[npmtrends]: https://www.npmtrends.com/cross-env [license-badge]: https://img.shields.io/npm/l/cross-env.svg?style=flat-square [license]: https://github.com/kentcdodds/cross-env/blob/master/other/LICENSE -[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square +[prs-badge]: + https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square [prs]: http://makeapullrequest.com -[donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square -[donate]: http://kcd.im/donate -[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square -[coc]: https://github.com/kentcdodds/cross-env/blob/master/other/CODE_OF_CONDUCT.md -[roadmap-badge]: https://img.shields.io/badge/%F0%9F%93%94-roadmap-CD9523.svg?style=flat-square -[roadmap]: https://github.com/kentcdodds/cross-env/blob/master/other/ROADMAP.md -[examples-badge]: https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square -[examples]: https://github.com/kentcdodds/cross-env/blob/master/other/EXAMPLES.md -[github-watch-badge]: https://img.shields.io/github/watchers/kentcdodds/cross-env.svg?style=social -[github-watch]: https://github.com/kentcdodds/cross-env/watchers -[github-star-badge]: https://img.shields.io/github/stars/kentcdodds/cross-env.svg?style=social -[github-star]: https://github.com/kentcdodds/cross-env/stargazers -[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20cross-env!%20https://github.com/kentcdodds/cross-env%20%F0%9F%91%8D -[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/kentcdodds/cross-env.svg?style=social +[coc-badge]: + https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square +[coc]: + https://github.com/kentcdodds/cross-env/blob/master/other/CODE_OF_CONDUCT.md [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key [all-contributors]: https://github.com/kentcdodds/all-contributors [win-bash]: https://msdn.microsoft.com/en-us/commandline/wsl/about [angular-formly]: https://github.com/formly-js/angular-formly [cross-spawn]: https://www.npmjs.com/package/cross-spawn [ts-loader]: https://www.npmjs.com/package/ts-loader -[malware]: http://blog.npmjs.org/post/163723642530/crossenv-malware-on-the-npm-registry +[malware]: + http://blog.npmjs.org/post/163723642530/crossenv-malware-on-the-npm-registry diff --git a/__mocks__/cross-spawn.js b/__mocks__/cross-spawn.js deleted file mode 100644 index 167027a..0000000 --- a/__mocks__/cross-spawn.js +++ /dev/null @@ -1,17 +0,0 @@ -const __mock = { - reset() { - __mock.spawned = null - Object.assign(module.exports, { - __mock, - spawn: jest.fn(() => { - __mock.spawned = { - on: jest.fn(), - kill: jest.fn(), - } - return __mock.spawned - }), - }) - }, -} - -__mock.reset() diff --git a/jest.config.js b/jest.config.js index bd0a5f5..257c5ce 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,6 @@ const jestConfig = require('kcd-scripts/config').jest jestConfig.coveragePathIgnorePatterns = jestConfig.coveragePathIgnorePatterns.concat( - ['/bin/'] + ['/bin/'], ) module.exports = jestConfig diff --git a/other/CODE_OF_CONDUCT.md b/other/CODE_OF_CONDUCT.md index e724218..070cb5f 100644 --- a/other/CODE_OF_CONDUCT.md +++ b/other/CODE_OF_CONDUCT.md @@ -5,30 +5,30 @@ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual identity +and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities @@ -37,11 +37,11 @@ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. ## Scope @@ -58,8 +58,9 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kent+coc@doddsfamily.us. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other @@ -67,8 +68,8 @@ members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/package.json b/package.json index 8be7cad..3438682 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,7 @@ { "name": "cross-env", "version": "0.0.0-semantically-released", - "description": - "Run scripts that set and use environment variables across platforms", + "description": "Run scripts that set and use environment variables across platforms", "main": "dist/index.js", "bin": { "cross-env": "dist/bin/cross-env.js", @@ -12,28 +11,41 @@ "node": ">=8.0" }, "scripts": { - "add-contributor": "kcd-scripts contributors add", "build": "kcd-scripts build", "lint": "kcd-scripts lint", "test": "kcd-scripts test", - "test:update": "npm test -- --updateSnapshot --coverage", - "validate": "kcd-scripts validate", - "precommit": "kcd-scripts precommit" - }, - "files": ["dist"], - "keywords": ["cross-environment", "environment variable", "windows"], + "validate": "kcd-scripts validate" + }, + "husky": { + "hooks": { + "pre-commit": "kcd-scripts pre-commit" + } + }, + "files": [ + "dist" + ], + "keywords": [ + "cross-environment", + "environment variable", + "windows" + ], "author": "Kent C. Dodds (http://kentcdodds.com/)", "license": "MIT", "dependencies": { + "@babel/runtime": "^7.6.2", "cross-spawn": "^7.0.0" }, "devDependencies": { - "kcd-scripts": "^0.3.4" + "kcd-scripts": "^1.8.0" }, "eslintConfig": { "extends": "./node_modules/kcd-scripts/eslint.js" }, - "eslintIgnore": ["node_modules", "coverage", "dist"], + "eslintIgnore": [ + "node_modules", + "coverage", + "dist" + ], "repository": { "type": "git", "url": "https://github.com/kentcdodds/cross-env.git" diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..4679d9b --- /dev/null +++ b/prettier.config.js @@ -0,0 +1 @@ +module.exports = require('kcd-scripts/prettier') diff --git a/src/__mocks__/is-windows.js b/src/__mocks__/is-windows.js deleted file mode 100644 index e8dacba..0000000 --- a/src/__mocks__/is-windows.js +++ /dev/null @@ -1,12 +0,0 @@ -const __mock = { - reset() { - Object.assign(__mock, {returnValue: false}) - }, -} -__mock.reset() - -module.exports = function isWindowsMock() { - return __mock.returnValue -} - -Object.assign(module.exports, {__mock}) diff --git a/src/__tests__/command.js b/src/__tests__/command.js index f5dbf99..8c6ce12 100644 --- a/src/__tests__/command.js +++ b/src/__tests__/command.js @@ -8,15 +8,16 @@ const env = { test1: 'b', test2: 'c', test3: 'd', - 'empty_var': '' + // eslint-disable-next-line babel/camelcase + empty_var: '', } -beforeEach(() => { - isWindowsMock.__mock.reset() +afterEach(() => { + jest.clearAllMocks() }) test(`converts unix-style env variable usage for windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(commandConvert('$test', env)).toBe('%test%') }) @@ -25,51 +26,55 @@ test(`leaves command unchanged when not a variable`, () => { }) test(`doesn't convert windows-style env variable`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(commandConvert('%test%', env)).toBe('%test%') }) test(`leaves variable unchanged when using correct operating system`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(commandConvert('$test', env)).toBe('$test') }) test(`is stateless`, () => { // this test prevents falling into regexp traps like this: // http://stackoverflow.com/a/1520853/971592 - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(commandConvert('$test', env)).toBe(commandConvert('$test', env)) }) test(`converts embedded unix-style env variables usage for windows`, () => { - isWindowsMock.__mock.returnValue = true - expect(commandConvert('$test1/$test2/$test3', env)).toBe('%test1%/%test2%/%test3%') + isWindowsMock.mockReturnValue(true) + expect(commandConvert('$test1/$test2/$test3', env)).toBe( + '%test1%/%test2%/%test3%', + ) }) // eslint-disable-next-line max-len test(`leaves embedded variables unchanged when using correct operating system`, () => { - isWindowsMock.__mock.returnValue = false - expect(commandConvert('$test1/$test2/$test3', env)).toBe('$test1/$test2/$test3') + isWindowsMock.mockReturnValue(false) + expect(commandConvert('$test1/$test2/$test3', env)).toBe( + '$test1/$test2/$test3', + ) }) test(`converts braced unix-style env variable usage for windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) // eslint-disable-next-line no-template-curly-in-string expect(commandConvert('${test}', env)).toBe('%test%') }) test(`removes non-existent variables from the converted command`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(commandConvert('$test1/$foo/$test2', env)).toBe('%test1%//%test2%') }) test(`removes empty variables from the converted command`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(commandConvert('$foo/$test/$empty_var', env)).toBe('/%test%/') }) test(`normalizes command on windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) // index.js calls `commandConvert` with `normalize` param // as `true` for command only expect(commandConvert('./cmd.bat', env, true)).toBe('cmd.bat') diff --git a/src/__tests__/index.js b/src/__tests__/index.js index 5275528..e1b582b 100644 --- a/src/__tests__/index.js +++ b/src/__tests__/index.js @@ -2,24 +2,33 @@ import crossSpawnMock from 'cross-spawn' import isWindowsMock from '../is-windows' jest.mock('../is-windows') +jest.mock('cross-spawn') const crossEnv = require('../') +const getSpawned = (call = 0) => crossSpawnMock.spawn.mock.results[call].value + process.setMaxListeners(20) beforeEach(() => { - crossSpawnMock.__mock.reset() + jest.spyOn(process, 'exit').mockImplementation(() => {}) + crossSpawnMock.spawn.mockReturnValue({on: jest.fn(), kill: jest.fn()}) +}) + +afterEach(() => { + jest.clearAllMocks() + process.exit.mockRestore() }) -it(`should set environment variables and run the remaining command`, () => { +test(`sets environment variables and run the remaining command`, () => { testEnvSetting({FOO_ENV: 'production'}, 'FOO_ENV=production') }) -it(`should APPDATA be undefined and not string`, () => { +test(`APPDATA is undefined and not string`, () => { testEnvSetting({FOO_ENV: 'production', APPDATA: 2}, 'FOO_ENV=production') }) -it(`should handle multiple env variables`, () => { +test(`handles multiple env variables`, () => { testEnvSetting( { FOO_ENV: 'production', @@ -32,35 +41,35 @@ it(`should handle multiple env variables`, () => { ) }) -it(`should handle special characters`, () => { +test(`handles special characters`, () => { testEnvSetting({FOO_ENV: './!?'}, 'FOO_ENV=./!?') }) -it(`should handle single-quoted strings`, () => { +test(`handles single-quoted strings`, () => { testEnvSetting({FOO_ENV: 'bar env'}, "FOO_ENV='bar env'") }) -it(`should handle double-quoted strings`, () => { +test(`handles double-quoted strings`, () => { testEnvSetting({FOO_ENV: 'bar env'}, 'FOO_ENV="bar env"') }) -it(`should handle equality signs in quoted strings`, () => { +test(`handles equality signs in quoted strings`, () => { testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') }) -it(`should handle empty single-quoted strings`, () => { +test(`handles empty single-quoted strings`, () => { testEnvSetting({FOO_ENV: ''}, "FOO_ENV=''") }) -it(`should handle empty double-quoted strings`, () => { +test(`handles empty double-quoted strings`, () => { testEnvSetting({FOO_ENV: ''}, 'FOO_ENV=""') }) -it(`should handle no value after the equals sign`, () => { +test(`handles no value after the equals sign`, () => { testEnvSetting({FOO_ENV: ''}, 'FOO_ENV=') }) -it(`should handle quoted scripts`, () => { +test(`handles quoted scripts`, () => { crossEnv(['GREETING=Hi', 'NAME=Joe', 'echo $GREETING && echo $NAME'], { shell: true, }) @@ -70,15 +79,12 @@ it(`should handle quoted scripts`, () => { { stdio: 'inherit', shell: true, - env: Object.assign({}, process.env, { - GREETING: 'Hi', - NAME: 'Joe', - }), + env: {...process.env, GREETING: 'Hi', NAME: 'Joe'}, }, ) }) -it(`should handle escaped characters`, () => { +test(`handles escaped characters`, () => { // this escapes \,",' and $ crossEnv( ['GREETING=Hi', 'NAME=Joe', 'echo \\"\\\'\\$GREETING\\\'\\" && echo $NAME'], @@ -92,98 +98,90 @@ it(`should handle escaped characters`, () => { { stdio: 'inherit', shell: true, - env: Object.assign({}, process.env, { - GREETING: 'Hi', - NAME: 'Joe', - }), + env: {...process.env, GREETING: 'Hi', NAME: 'Joe'}, }, ) }) -it(`should do nothing given no command`, () => { +test(`does nothing when given no command`, () => { crossEnv([]) expect(crossSpawnMock.spawn).toHaveBeenCalledTimes(0) }) -it(`should normalize command on windows`, () => { - isWindowsMock.__mock.returnValue = true +test(`normalizes commands on windows`, () => { + isWindowsMock.mockReturnValue(true) crossEnv(['./cmd.bat']) expect(crossSpawnMock.spawn).toHaveBeenCalledWith('cmd.bat', [], { stdio: 'inherit', - env: Object.assign({}, process.env), + env: {...process.env}, }) - isWindowsMock.__mock.reset() }) -it(`should not normalize command arguments on windows`, () => { - isWindowsMock.__mock.returnValue = true +test(`does not normalize command arguments on windows`, () => { + isWindowsMock.mockReturnValue(true) crossEnv(['echo', 'http://example.com']) expect(crossSpawnMock.spawn).toHaveBeenCalledWith( 'echo', ['http://example.com'], { stdio: 'inherit', - env: Object.assign({}, process.env), + env: {...process.env}, }, ) - isWindowsMock.__mock.reset() }) -it(`should propagate kill signals`, () => { +test(`propagates kill signals`, () => { testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') process.emit('SIGTERM') process.emit('SIGINT') process.emit('SIGHUP') process.emit('SIGBREAK') - expect(crossSpawnMock.__mock.spawned.kill).toHaveBeenCalledWith('SIGTERM') - expect(crossSpawnMock.__mock.spawned.kill).toHaveBeenCalledWith('SIGINT') - expect(crossSpawnMock.__mock.spawned.kill).toHaveBeenCalledWith('SIGHUP') - expect(crossSpawnMock.__mock.spawned.kill).toHaveBeenCalledWith('SIGBREAK') + const spawned = getSpawned() + expect(spawned.kill).toHaveBeenCalledWith('SIGTERM') + expect(spawned.kill).toHaveBeenCalledWith('SIGINT') + expect(spawned.kill).toHaveBeenCalledWith('SIGHUP') + expect(spawned.kill).toHaveBeenCalledWith('SIGBREAK') }) -it(`should propagate unhandled exit signal`, () => { - process.exit = jest.fn() - testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') - const spawnExitCallback = crossSpawnMock.__mock.spawned.on.mock.calls[0][1] +test(`keeps backslashes`, () => { + isWindowsMock.mockReturnValue(true) + crossEnv(['echo', '\\\\\\\\someshare\\\\somefolder']) + expect(crossSpawnMock.spawn).toHaveBeenCalledWith( + 'echo', + ['\\\\someshare\\somefolder'], + { + stdio: 'inherit', + env: {...process.env}, + }, + ) +}) + +test(`propagates unhandled exit signal`, () => { + const {spawned} = testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') + const spawnExitCallback = spawned.on.mock.calls[0][1] const spawnExitCode = null spawnExitCallback(spawnExitCode) expect(process.exit).toHaveBeenCalledWith(1) }) -it(`should exit cleanly with SIGINT with a null exit code`, () => { - process.exit = jest.fn() - testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') - const spawnExitCallback = crossSpawnMock.__mock.spawned.on.mock.calls[0][1] +test(`exits cleanly with SIGINT with a null exit code`, () => { + const {spawned} = testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') + const spawnExitCallback = spawned.on.mock.calls[0][1] const spawnExitCode = null const spawnExitSignal = 'SIGINT' spawnExitCallback(spawnExitCode, spawnExitSignal) expect(process.exit).toHaveBeenCalledWith(0) }) -it(`should propagate regular exit code`, () => { - process.exit = jest.fn() - testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') - const spawnExitCallback = crossSpawnMock.__mock.spawned.on.mock.calls[0][1] +test(`propagates regular exit code`, () => { + const {spawned} = testEnvSetting({FOO_ENV: 'foo=bar'}, 'FOO_ENV="foo=bar"') + const spawnExitCallback = spawned.on.mock.calls[0][1] const spawnExitCode = 0 spawnExitCallback(spawnExitCode) expect(process.exit).toHaveBeenCalledWith(0) }) -it(`should keep backslashes`, () => { - isWindowsMock.__mock.returnValue = true - crossEnv(['echo', '\\\\\\\\someshare\\\\somefolder']) - expect(crossSpawnMock.spawn).toHaveBeenCalledWith( - 'echo', - ['\\\\someshare\\somefolder'], - { - stdio: 'inherit', - env: Object.assign({}, process.env), - }, - ) - isWindowsMock.__mock.reset() -}) - function testEnvSetting(expected, ...envSettings) { if (expected.APPDATA === 2) { // kill the APPDATA to test both is undefined @@ -200,17 +198,16 @@ function testEnvSetting(expected, ...envSettings) { env.APPDATA = process.env.APPDATA } Object.assign(env, expected) - expect(ret).toBe(crossSpawnMock.__mock.spawned) + const spawned = getSpawned() + expect(ret).toBe(spawned) expect(crossSpawnMock.spawn).toHaveBeenCalledTimes(1) expect(crossSpawnMock.spawn).toHaveBeenCalledWith('echo', ['hello world'], { stdio: 'inherit', shell: undefined, - env: Object.assign({}, process.env, env), + env: {...process.env, ...env}, }) - expect(crossSpawnMock.__mock.spawned.on).toHaveBeenCalledTimes(1) - expect(crossSpawnMock.__mock.spawned.on).toHaveBeenCalledWith( - 'exit', - expect.any(Function), - ) + expect(spawned.on).toHaveBeenCalledTimes(1) + expect(spawned.on).toHaveBeenCalledWith('exit', expect.any(Function)) + return {spawned} } diff --git a/src/__tests__/is-windows.js b/src/__tests__/is-windows.js index aa1e128..d57d7ee 100644 --- a/src/__tests__/is-windows.js +++ b/src/__tests__/is-windows.js @@ -1,17 +1,32 @@ -import isWindows from "../is-windows" +import isWindows from '../is-windows' -it(`should return true if the current OS is Windows`, () => { +const { + platform, + env: {OSTYPE}, +} = process + +// make the platform property writeable +Object.defineProperty(process, 'platform', { + value: platform, + writable: true, +}) + +afterEach(() => { + process.platform = platform + process.env.OSTYPE = OSTYPE +}) + +test(`returns true if the current OS is Windows`, () => { process.platform = 'win32' expect(isWindows()).toBe(true) - }) -it(`should return false if the current OS is not Windows`, () => { +test(`returns false if the current OS is not Windows`, () => { process.platform = 'linux' expect(isWindows()).toBe(false) }) -it(`should return true if the OSTYPE is cygwin or msys`, () => { +test(`returns true if the OSTYPE is cygwin or msys`, () => { process.platform = 'linux' process.env.OSTYPE = 'cygwin' @@ -22,4 +37,4 @@ it(`should return true if the OSTYPE is cygwin or msys`, () => { process.env.OSTYPE = '' expect(isWindows()).toBe(false) -}) \ No newline at end of file +}) diff --git a/src/__tests__/variable.js b/src/__tests__/variable.js index a432a07..313041e 100644 --- a/src/__tests__/variable.js +++ b/src/__tests__/variable.js @@ -9,7 +9,6 @@ beforeEach(() => { process.env.VAR1 = 'value1' process.env.VAR2 = 'value2' process.env.JSON_VAR = JSON_VALUE - isWindowsMock.__mock.reset() }) afterEach(() => { @@ -17,105 +16,106 @@ afterEach(() => { delete env.VAR1 delete env.VAR2 delete env.JSON_VAR + jest.clearAllMocks() }) test(`doesn't affect simple variable values`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo')).toBe('foo') }) test(`doesn't convert a ; into a : on UNIX`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('foo;bar', 'PATH')).toBe('foo;bar') }) test(`doesn't convert a ; into a : for non-PATH on UNIX`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('foo;bar', 'FOO')).toBe('foo;bar') }) test(`doesn't convert a ; into a : for non-PATH on Windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo;bar', 'FOO')).toBe('foo;bar') }) test(`converts a : into a ; on Windows if PATH`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo:bar', 'PATH')).toBe('foo;bar') }) test(`doesn't convert already valid separators`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('foo:bar')).toBe('foo:bar') }) test(`doesn't convert escaped separators on Windows if PATH`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo\\:bar', 'PATH')).toBe('foo:bar') }) test(`doesn't convert escaped separators on UNIX`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('foo\\:bar', 'PATH')).toBe('foo:bar') }) test(`converts a separator even if preceded by an escaped backslash`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo\\\\:bar', 'PATH')).toBe('foo\\\\;bar') }) test(`converts multiple separators if PATH`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo:bar:baz', 'PATH')).toBe('foo;bar;baz') }) test(`resolves an env variable value`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo-$VAR1')).toBe('foo-value1') }) test(`resolves an env variable value with curly syntax`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) // eslint-disable-next-line no-template-curly-in-string expect(varValueConvert('foo-${VAR1}')).toBe('foo-value1') }) test(`resolves multiple env variable values`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo-$VAR1-$VAR2')).toBe('foo-value1-value2') }) test(`resolves an env variable value for non-existant variable`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('foo-$VAR_POTATO')).toBe('foo-') }) test(`resolves an env variable with a JSON string value on Windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('$JSON_VAR')).toBe(JSON_VALUE) }) test(`resolves an env variable with a JSON string value on UNIX`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('$JSON_VAR')).toBe(JSON_VALUE) }) test(`does not resolve an env variable prefixed with \\ on Windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('\\$VAR1')).toBe('$VAR1') }) test(`does not resolve an env variable prefixed with \\ on UNIX`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('\\$VAR1')).toBe('$VAR1') }) test(`resolves an env variable prefixed with \\\\ on Windows`, () => { - isWindowsMock.__mock.returnValue = true + isWindowsMock.mockReturnValue(true) expect(varValueConvert('\\\\$VAR1')).toBe('\\value1') }) test(`resolves an env variable prefixed with \\\\ on UNIX`, () => { - isWindowsMock.__mock.returnValue = false + isWindowsMock.mockReturnValue(false) expect(varValueConvert('\\\\$VAR1')).toBe('\\value1') }) diff --git a/src/index.js b/src/index.js index 349e9c1..2f13e2f 100644 --- a/src/index.js +++ b/src/index.js @@ -84,7 +84,7 @@ function parseCommand(args) { } function getEnvVars(envSetters) { - const envVars = Object.assign({}, process.env) + const envVars = {...process.env} if (process.env.APPDATA) { envVars.APPDATA = process.env.APPDATA } diff --git a/src/is-windows.js b/src/is-windows.js index b8e10d8..414598f 100644 --- a/src/is-windows.js +++ b/src/is-windows.js @@ -1 +1,2 @@ -export default () => process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE) \ No newline at end of file +export default () => + process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)