Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

- **Fixed** Missing environment variables returned by `@voidzero-dev/vite-task-client` are now JavaScript `undefined` instead of `null`, preventing tools such as Vite from assigning `"null"` to `process.env.NODE_ENV` ([vite-plus#2047](https://github.com/voidzero-dev/vite-plus/issues/2047)).
- **Fixed** Windows builds no longer hang on CI when a `node_modules/.bin` `.cmd` shim is routed through PowerShell: the npm/pnpm/yarn `.ps1` wrappers read stdin and block forever on a non-TTY pipe, so the PowerShell rewrite is now skipped when stdin is not an interactive terminal, falling back to the `.cmd` (which never reads stdin) ([#491](https://github.com/voidzero-dev/vite-task/pull/491)).
- **Added** First-party support for caching `vite build` with zero cache config, giving Vite projects correct cache hits out of the box ([vitejs/vite#22453](https://github.com/vitejs/vite/pull/22453)).
- **Added** Support for specifying tasks from dependency packages in `dependsOn`, such as `dependsOn: [{ "task": "build", "from": "dependencies" }]` ([#479](https://github.com/voidzero-dev/vite-task/pull/479)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ steps = [
], comment = "restored from the cache archive" },
]

[[e2e]]
name = "vite_build_defaults_node_env_to_production"
comment = """
When the runner has no `NODE_ENV`, Vite's `getEnv('NODE_ENV')` call must return JavaScript `undefined`. This lets Vite apply its normal production-build default instead of assigning the string `"null"` to `process.env.NODE_ENV`.
"""
ignore = true
steps = [
{ argv = [
"vt",
"run",
"--cache",
"build-production-node-env",
], comment = "Vite config hook observes NODE_ENV=production" },
]

[[e2e]]
name = "vite_build_preserves_explicit_node_env"
comment = """
When the runner has an explicit `NODE_ENV`, Vite's `getEnv('NODE_ENV')` call must return that value instead of applying the production-build default.
"""
ignore = true
steps = [
{ argv = [
"vt",
"run",
"--cache",
"build-test-node-env",
], envs = [
[
"NODE_ENV",
"test",
],
], comment = "Vite config hook observes NODE_ENV=test" },
]

[[e2e]]
name = "vite_prefix_env_change_invalidates_cache"
comment = """
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# vite_build_defaults_node_env_to_production

When the runner has no `NODE_ENV`, Vite's `getEnv('NODE_ENV')` call must return JavaScript `undefined`. This lets Vite apply its normal production-build default instead of assigning the string `"null"` to `process.env.NODE_ENV`.

## `vt run --cache build-production-node-env`

Vite config hook observes NODE_ENV=production

```
$ ASSERT_NODE_ENV=production vite build
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# vite_build_preserves_explicit_node_env

When the runner has an explicit `NODE_ENV`, Vite's `getEnv('NODE_ENV')` call must return that value instead of applying the production-build default.

## `NODE_ENV=test vt run --cache build-test-node-env`

Vite config hook observes NODE_ENV=test

```
$ ASSERT_NODE_ENV=test vite build
```
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
// every `VITE_*` env via `getEnvs`, so the glob + match-set are
// fingerprinted automatically.
"cache": true
},
"build-production-node-env": {
"command": "ASSERT_NODE_ENV=production vite build",
"cache": true
},
"build-test-node-env": {
"command": "ASSERT_NODE_ENV=test vite build",
"cache": true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import { defineConfig } from 'vite';

export default defineConfig({
logLevel: 'silent',
plugins: [
{
name: 'assert-node-env',
config() {
const expectedNodeEnv = process.env.ASSERT_NODE_ENV;
if (expectedNodeEnv !== undefined && process.env.NODE_ENV !== expectedNodeEnv) {
throw new Error(
`Expected NODE_ENV to be ${JSON.stringify(expectedNodeEnv)}, received ${JSON.stringify(process.env.NODE_ENV)}`,
);
}
},
},
],
build: {
rollupOptions: {
output: {
Expand Down
12 changes: 8 additions & 4 deletions crates/vite_task_client_napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
)]
use std::{collections::HashMap, ffi::OsStr};

use napi::{Either, Error, Result};
use napi::{Either, Error, Result, bindgen_prelude::Undefined};
use napi_derive::napi;
use vite_task_client::{Client, GetEnvsQuery};

Expand Down Expand Up @@ -85,14 +85,18 @@ impl RunnerClient {
}

#[napi]
pub fn get_env(&self, name: String, options: Option<GetEnvOptions>) -> Result<Option<String>> {
pub fn get_env(
&self,
name: String,
options: Option<GetEnvOptions>,
) -> Result<Either<String, Undefined>> {
let tracked = options.and_then(|o| o.tracked).unwrap_or(true);
let value = self
.client
.get_env(OsStr::new(&name), tracked)
.map_err(|err| err_string(vite_str::format!("{err}")))?;
value.map_or(Ok(None), |value| {
value.to_str().map(|s| Some(s.to_owned())).ok_or_else(|| {
value.map_or(Ok(Either::B(())), |value| {
value.to_str().map(|s| Either::A(s.to_owned())).ok_or_else(|| {
err_string(vite_str::format!("env value for {name} is not valid UTF-8"))
})
})
Expand Down
Loading