mirror of
https://github.com/hanzoai/cross-env.git
synced 2026-09-24 16:58:15 +00:00
fix: handle edge case and update README for clarity
This commit is contained in:
@@ -1,15 +1,32 @@
|
||||
# cross-env 🔀
|
||||
<div align="center">
|
||||
<h1>cross-env 🔀</h1>
|
||||
|
||||
Run scripts that set and use environment variables across platforms.
|
||||
<p>Run scripts that set and use environment variables across platforms</p>
|
||||
</div>
|
||||
|
||||
## The Problem
|
||||
**🎉 NOTICE: cross-env is "done" as in it does what it does and there's no need
|
||||
for new features.
|
||||
[Learn more](https://github.com/kentcdodds/cross-env/issues/257)**
|
||||
|
||||
---
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[![Build Status][build-badge]][build]
|
||||
[![Code Coverage][coverage-badge]][coverage]
|
||||
[![version][version-badge]][package]
|
||||
[![downloads][downloads-badge]][npmtrends]
|
||||
[![MIT License][license-badge]][license]
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## The problem
|
||||
|
||||
Most Windows command prompts will choke when you set environment variables with
|
||||
`NODE_ENV=production` like that. Similarly, there's a difference in how Windows
|
||||
and POSIX commands utilize environment variables. With POSIX, you use:
|
||||
`$ENV_VAR` and on Windows you use `%ENV_VAR%`.
|
||||
`NODE_ENV=production` like that. (The exception is [Bash on Windows][win-bash],
|
||||
which uses native Bash.) Similarly, there's a difference in how windows and
|
||||
POSIX commands utilize environment variables. With POSIX, you use: `$ENV_VAR`
|
||||
and on windows you use `%ENV_VAR%`.
|
||||
|
||||
## The Solution
|
||||
## This solution
|
||||
|
||||
`cross-env` makes it so you can have a single command without worrying about
|
||||
setting or using the environment variable properly for the platform. Just set it
|
||||
@@ -18,10 +35,19 @@ of setting it properly.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
This module is distributed via [npm][npm] which is bundled with [node][node] and
|
||||
should be installed as one of your project's `devDependencies`:
|
||||
|
||||
```
|
||||
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 8 of cross-env only supports Node.js 20 and higher, to use it
|
||||
> on Node.js 18 or lower install version 7 `npm install --save-dev cross-env@7`
|
||||
|
||||
## Usage
|
||||
|
||||
I use this in my npm scripts:
|
||||
@@ -29,7 +55,7 @@ I use this in my npm scripts:
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production node ./start.js"
|
||||
"build": "cross-env NODE_ENV=production node ./start.js --enable-turbo-mode"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -38,12 +64,10 @@ Ultimately, the command that is executed (using [`cross-spawn`][cross-spawn])
|
||||
is:
|
||||
|
||||
```
|
||||
node ./start.js
|
||||
node ./start.js --enable-turbo-mode
|
||||
```
|
||||
|
||||
The `NODE_ENV` environment variable will be set by `cross-env`.
|
||||
|
||||
### Multiple Environment Variables
|
||||
The `NODE_ENV` environment variable will be set by `cross-env`
|
||||
|
||||
You can set multiple environment variables at a time:
|
||||
|
||||
@@ -55,10 +79,8 @@ You can set multiple environment variables at a time:
|
||||
}
|
||||
```
|
||||
|
||||
### Complex Commands
|
||||
|
||||
You can also split a command into several ones, or separate the environment
|
||||
variables declaration from the actual command execution:
|
||||
variables declaration from the actual command execution. You can do it this way:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -69,9 +91,20 @@ variables declaration from the actual command execution:
|
||||
}
|
||||
```
|
||||
|
||||
### JSON Strings
|
||||
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
|
||||
Windows which would usually require it to be `%GREET%`.
|
||||
|
||||
For JSON strings (e.g., when using [ts-loader]):
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -82,17 +115,21 @@ For JSON strings (e.g., when using [ts-loader]):
|
||||
```
|
||||
|
||||
Pay special attention to the **triple backslash** `(\\\)` **before** the
|
||||
**double quotes** `(")` and the **absence** of **single quotes** `(')`.
|
||||
**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 `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`.
|
||||
|
||||
- **`cross-env`**: Executes commands using [`cross-spawn`][cross-spawn]
|
||||
- **`cross-env-shell`**: 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.
|
||||
|
||||
Use `cross-env-shell` 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
|
||||
`cross-env-shell` instead of `cross-env`.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -102,25 +139,56 @@ entire inline shell script, rather than just one command:
|
||||
}
|
||||
```
|
||||
|
||||
**Rule of thumb**: If you want to pass to `cross-env` a command that contains
|
||||
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.
|
||||
|
||||
## Windows Issues
|
||||
|
||||
Please note that `npm` uses `cmd` by default and that doesn't support command
|
||||
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).
|
||||
|
||||
## Requirements
|
||||
## Inspiration
|
||||
|
||||
- Node.js >= 20
|
||||
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.
|
||||
|
||||
## License
|
||||
## 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
|
||||
|
||||
## LICENSE
|
||||
|
||||
MIT
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[npm]: https://npmjs.com
|
||||
[node]: https://nodejs.org
|
||||
[build-badge]: https://img.shields.io/github/actions/workflow/status/kentcdodds/cross-env/validate.yml?branch=main&logo=github&style=flat-square
|
||||
[build]: https://github.com/kentcdodds/cross-env/actions?query=workflow%3Avalidate
|
||||
[coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/cross-env.svg?style=flat-square
|
||||
[coverage]: https://codecov.io/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
|
||||
[downloads-badge]: https://img.shields.io/npm/dm/cross-env.svg?style=flat-square
|
||||
[npmtrends]: http://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/LICENSE
|
||||
|
||||
[angular-formly]: https://github.com/formly-js/angular-formly
|
||||
[cross-spawn]: https://www.npmjs.com/package/cross-spawn
|
||||
[malware]: http://blog.npmjs.org/post/163723642530/crossenv-malware-on-the-npm-registry
|
||||
[ts-loader]: https://www.npmjs.com/package/ts-loader
|
||||
[win-bash]: https://msdn.microsoft.com/en-us/commandline/wsl/about
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
Generated
+7
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.0-semantically-released",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@epic-web/invariant": "^1.0.0",
|
||||
"cross-spawn": "^7.0.6"
|
||||
},
|
||||
"bin": {
|
||||
@@ -659,6 +660,12 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@epic-web/invariant": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
||||
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
||||
|
||||
+5
-4
@@ -41,18 +41,19 @@
|
||||
"author": "Kent C. Dodds <[email protected]> (https://kentcdodds.com)",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@epic-web/invariant": "^1.0.0",
|
||||
"cross-spawn": "^7.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@epic-web/config": "^1.21.1",
|
||||
"eslint": "^9.32.0",
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/cross-spawn": "^6.0.6",
|
||||
"@types/node": "^24.1.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"eslint": "^9.32.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3.2.4",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"zshy": "^0.2.5"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -120,6 +120,11 @@ describe('crossEnv', () => {
|
||||
expect(crossSpawnMock.spawn).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
test('handles empty command after processing', () => {
|
||||
crossEnv(['FOO=bar', ''])
|
||||
expect(crossSpawnMock.spawn).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
test('normalizes commands on windows', () => {
|
||||
isWindowsMock.mockReturnValue(true)
|
||||
crossEnv(['./cmd.bat'])
|
||||
|
||||
+4
-1
@@ -1,4 +1,5 @@
|
||||
import { type SpawnOptions } from 'child_process'
|
||||
import { invariant } from '@epic-web/invariant'
|
||||
import { spawn } from 'cross-spawn'
|
||||
import { commandConvert } from './command.js'
|
||||
import { varValueConvert } from './variable.js'
|
||||
@@ -96,7 +97,9 @@ function parseCommand(
|
||||
return ''
|
||||
})
|
||||
})
|
||||
command = cStart[0] || null
|
||||
const parsedCommand = cStart[0]
|
||||
invariant(parsedCommand, 'Command is required')
|
||||
command = parsedCommand
|
||||
commandArgs = cStart.slice(1).filter(Boolean)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -7,12 +7,17 @@ export default defineConfig({
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
include: ['src/**/*'],
|
||||
exclude: [
|
||||
'node_modules/',
|
||||
'dist/',
|
||||
'**/*.d.ts',
|
||||
'**/*.config.*',
|
||||
'**/coverage/**',
|
||||
'**/*.test.ts',
|
||||
'**/*.spec.ts',
|
||||
'**/src/bin/**', // covered by e2e
|
||||
'**/__tests__/**',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user