@pixelmatters/vite-plugin-validate-env 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +36 -0
- package/package.json +72 -3
- package/skills/validate-vite-env/SKILL.md +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pixelmatters
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @pixelmatters/vite-plugin-validate-env
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@pixelmatters/vite-plugin-validate-env)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
Vite plugin for validating environment variables against a schema
|
|
7
|
+
([Standard Schema](https://standardschema.dev)). A missing or malformed
|
|
8
|
+
variable stops the dev server or the build before it starts, with a line per
|
|
9
|
+
issue.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add -D @pixelmatters/vite-plugin-validate-env
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Vite 6, 7 or 8 and Node `^20.19.0 || >=22.12.0`.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// vite.config.ts
|
|
23
|
+
import { defineConfig } from 'vite'
|
|
24
|
+
// Import your favorite schema library (zod, valibot, arktype, etc.)
|
|
25
|
+
import * as z from 'zod'
|
|
26
|
+
|
|
27
|
+
import { validateEnv } from '@pixelmatters/vite-plugin-validate-env'
|
|
28
|
+
|
|
29
|
+
// Define your environment variables schema (any standard schema library can be used)
|
|
30
|
+
const envSchema = z.object({
|
|
31
|
+
VITE_API_URL: z.url(),
|
|
32
|
+
VITE_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export default defineConfig({
|
|
36
|
+
plugins: [
|
|
37
|
+
// ...
|
|
38
|
+
validateEnv(envSchema),
|
|
39
|
+
],
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The schema must validate synchronously. It runs once Vite has resolved its
|
|
44
|
+
config and receives what Vite's `loadEnv` returns for the current mode: the
|
|
45
|
+
`.env*` files in your `envDir` and the process environment, filtered by
|
|
46
|
+
`envPrefix` (`VITE_` by default). Whatever other plugins set for `root`,
|
|
47
|
+
`envDir` or `envPrefix` is already applied.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
+
/**
|
|
4
|
+
* The schema must validate synchronously. On issues it prints a report and
|
|
5
|
+
* exits the process with code 1.
|
|
6
|
+
*/
|
|
7
|
+
export declare const validateEnv: (schema: StandardSchemaV1) => Plugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const pathLabel = (path) => path?.length ? path.map((s) => String(typeof s === 'object' ? s.key : s)).join('.') : 'root';
|
|
2
|
+
/**
|
|
3
|
+
* The schema must validate synchronously. On issues it prints a report and
|
|
4
|
+
* exits the process with code 1.
|
|
5
|
+
*/
|
|
6
|
+
export const validateEnv = (schema) => {
|
|
7
|
+
return {
|
|
8
|
+
name: 'vite-plugin-validate-env',
|
|
9
|
+
configResolved: async (config) => {
|
|
10
|
+
const { loadEnv } = await import('vite');
|
|
11
|
+
const env = loadEnv(config.mode, config.envDir, config.envPrefix);
|
|
12
|
+
let issues;
|
|
13
|
+
try {
|
|
14
|
+
const result = schema['~standard'].validate(env);
|
|
15
|
+
if (result instanceof Promise) {
|
|
16
|
+
throw new TypeError('Schema validation must be synchronous');
|
|
17
|
+
}
|
|
18
|
+
issues = result.issues;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
22
|
+
throw new Error('Invalid environment variables', { cause: error });
|
|
23
|
+
}
|
|
24
|
+
if (issues) {
|
|
25
|
+
const { styleText } = await import('node:util');
|
|
26
|
+
console.log('========================================');
|
|
27
|
+
console.log('❌ Environment variables issues:');
|
|
28
|
+
issues.forEach((issue) => {
|
|
29
|
+
console.log(` ${styleText('cyan', pathLabel(issue.path))}:`, issue.message);
|
|
30
|
+
});
|
|
31
|
+
console.log('========================================\n');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixelmatters/vite-plugin-validate-env",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Vite plugin for validating environment variables against a schema (Standard Schema)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"environment",
|
|
7
|
+
"pixelmatters",
|
|
8
|
+
"plugin",
|
|
9
|
+
"schema",
|
|
10
|
+
"standard",
|
|
11
|
+
"tanstack-intent",
|
|
12
|
+
"validate",
|
|
13
|
+
"variables",
|
|
14
|
+
"vite",
|
|
15
|
+
"vite-plugin"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/Pixelmatters/devtools/tree/main/packages/vite-plugin-validate-env#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/Pixelmatters/devtools/issues"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Pixelmatters",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/Pixelmatters/devtools.git",
|
|
26
|
+
"directory": "packages/vite-plugin-validate-env"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist/*.js",
|
|
30
|
+
"dist/*.d.ts",
|
|
31
|
+
"skills",
|
|
32
|
+
"!skills/_artifacts",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"type": "module",
|
|
37
|
+
"sideEffects": false,
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"import": "./dist/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"provenance": true
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@standard-schema/spec": "^1.1.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
54
|
+
"@tanstack/intent": "^0.5.5",
|
|
55
|
+
"@types/node": "^26.6.6",
|
|
56
|
+
"tsrs": "0.20.1-ts7.1.0-beta",
|
|
57
|
+
"typescript": "^7.0.2",
|
|
58
|
+
"vite": "^8.3.4",
|
|
59
|
+
"vitest": "^5.0.5"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "rm -rf dist && tsc --project tsconfig.build.json",
|
|
69
|
+
"typecheck": "tsrs --noEmit",
|
|
70
|
+
"test": "vitest run",
|
|
71
|
+
"check:types": "rm -rf .attw && pnpm pack --pack-destination .attw && attw .attw/*.tgz --profile esm-only",
|
|
72
|
+
"check:dist": "pnpm run check:types",
|
|
73
|
+
"validate:skills": "intent validate"
|
|
74
|
+
}
|
|
6
75
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: validate-vite-env
|
|
3
|
+
description: Add `@pixelmatters/vite-plugin-validate-env` to a Vite app so a missing or malformed `import.meta.env` value stops the dev server or build before it starts, checked against a Standard Schema (zod, valibot, ArkType). Use when the host depends on `@pixelmatters/vite-plugin-validate-env`, the user wants Vite env variables validated at startup, or the plugin exits with `Invalid environment variables` or an `Environment variables issues` report.
|
|
4
|
+
metadata:
|
|
5
|
+
purpose: 'Wire the env-validation plugin into vite.config and write a schema that matches what Vite loads.'
|
|
6
|
+
type: core
|
|
7
|
+
library: '@pixelmatters/vite-plugin-validate-env'
|
|
8
|
+
library_version: '1.0.0'
|
|
9
|
+
sources:
|
|
10
|
+
- 'Pixelmatters/devtools:packages/vite-plugin-validate-env/README.md'
|
|
11
|
+
- 'Pixelmatters/devtools:packages/vite-plugin-validate-env/src/index.ts'
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
The plugin runs in Vite's `configResolved` hook, so it covers `vite`, `vite build` and `vite preview`. It validates the env Vite itself loads: `root`, `envDir` and `envPrefix` are the final values after every plugin's `config` hook, and `envDir: false` loads no files. Your schema checks it, and on failure the plugin prints one line per issue and exits with code 1. It only checks: it does not change what `import.meta.env` contains.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add -D @pixelmatters/vite-plugin-validate-env # or: yarn add -D … / npm install -D …
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
ES modules only. Supported: Vite 6, 7 and 8. Node `^20.19.0 || >=22.12.0`. Bring your own schema library; anything that implements Standard Schema works.
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// vite.config.ts
|
|
28
|
+
import { defineConfig } from 'vite'
|
|
29
|
+
import * as z from 'zod'
|
|
30
|
+
|
|
31
|
+
import { validateEnv } from '@pixelmatters/vite-plugin-validate-env'
|
|
32
|
+
|
|
33
|
+
const envSchema = z.object({
|
|
34
|
+
VITE_API_URL: z.url(),
|
|
35
|
+
VITE_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export default defineConfig({ plugins: [validateEnv(envSchema)] })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`validateEnv` is a named export and takes the schema directly, not an options object.
|
|
42
|
+
|
|
43
|
+
## What the schema receives
|
|
44
|
+
|
|
45
|
+
The result of Vite's `loadEnv(mode, envDir, envPrefix)`: values from `.env`, `.env.local`, `.env.[mode]` and `.env.[mode].local` in `envDir` (default: the project root), plus matching variables from the process environment. Only names starting with `envPrefix` (default `VITE_`) are included, as strings.
|
|
46
|
+
|
|
47
|
+
So a schema key without the prefix is always missing, and a number or boolean must be parsed from its string form (`z.coerce.number()`, or `z.enum(['true', 'false'])`).
|
|
48
|
+
|
|
49
|
+
## Failure output
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
========================================
|
|
53
|
+
❌ Environment variables issues:
|
|
54
|
+
VITE_API_URL: Invalid input: expected string, received undefined
|
|
55
|
+
========================================
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The process then exits with code 1. A schema that validates asynchronously throws `Invalid environment variables` instead, after logging `Schema validation must be synchronous`.
|
|
59
|
+
|
|
60
|
+
## Common mistakes
|
|
61
|
+
|
|
62
|
+
- **Validating an unprefixed variable.** `DATABASE_URL` never reaches the schema. Prefix it, or set `envPrefix` in the Vite config to include it, knowing that Vite then exposes it to client code.
|
|
63
|
+
- **Using an async schema.** Validation must be synchronous; use the sync form of your library (zod's `parse`-style schemas are fine, async refinements are not).
|
|
64
|
+
- **Expecting parsed values in `import.meta.env`.** `z.coerce.number()` validates the string but `import.meta.env.VITE_PORT` stays a string. Parse it again where it is used.
|
|
65
|
+
- **Assuming shell-only variables are ignored.** They are validated too, as long as they carry the prefix and are set for the same command that runs Vite.
|
|
66
|
+
- **Looking for it in the browser bundle.** The plugin runs in Node during config; nothing from it ships to the client.
|
|
67
|
+
|
|
68
|
+
## Completion
|
|
69
|
+
|
|
70
|
+
Run `vite build` with every required variable set and confirm it succeeds. Unset one and confirm the build prints that variable's issue and exits with a non-zero code. If a variable you set is reported as missing, check its prefix and which `.env*` file or `envDir` it lives in.
|