Skip to content

Commit 25cdfae

Browse files
aweebitJulien-R44
authored andcommitted
feat: expose function for validation without running Vite
1 parent 15d4300 commit 25cdfae

File tree

3 files changed

+68
-25
lines changed

3 files changed

+68
-25
lines changed

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,33 @@ interface ImportMetaEnv extends ImportMetaEnvAugmented {
173173
}
174174
```
175175

176+
## Validation without Vite
177+
178+
In some cases, you might want to validate environment variables outside of Vite and reuse the same schema. You can do so by using the `loadAndValidateEnv` function directly. This function will validate and also load the environment variables inside the `process.env` object.
179+
180+
> ![WARNING]
181+
> `process.env` only accept string values, so don't be surprised if a `number` or `boolean` variable comes back as a string when accessing it after validation.
182+
183+
```ts
184+
import { loadAndValidateEnv } from '@julr/vite-plugin-validate-env';
185+
186+
const env = await loadAndValidateEnv(
187+
{
188+
mode: 'development', // required
189+
root: import.meta.dirname, // optional
190+
},
191+
{
192+
// Plugin options. Also optional if you
193+
// are using a dedicated `env.ts` file
194+
validator: 'builtin',
195+
schema: { VITE_MY_VAR: Schema.string() },
196+
},
197+
);
198+
199+
console.log(env.VITE_MY_VAR);
200+
console.log(process.env.VITE_MY_VAR)
201+
```
202+
176203
## 💖 Sponsors
177204

178205
If you find this useful, consider [sponsoring me](https://github.com/sponsors/Julien-R44)! It helps support and maintain the project 🙏

src/index.ts

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import path from 'node:path'
22
import { cwd } from 'node:process'
3+
import { type Plugin } from 'vite'
34
import { createConfigLoader as createLoader } from 'unconfig'
4-
import { type ConfigEnv, type Plugin, type UserConfig } from 'vite'
55

66
import { initUi, type UI } from './ui.js'
77
import { builtinValidation } from './validators/builtin/index.js'
88
import { standardValidation } from './validators/standard/index.js'
9-
import type { FullPluginOptions, PluginOptions, Schema } from './types.js'
9+
import type { ConfigOptions, FullPluginOptions, PluginOptions, Schema } from './types.js'
1010

1111
/**
1212
* Load schema defined in `env.ts` file using unconfig
@@ -71,42 +71,27 @@ function shouldLogVariables(options: PluginOptions) {
7171
/**
7272
* Main function. Will call each validator defined in the schema and throw an error if any of them fails.
7373
*/
74-
async function validateEnv(
75-
ui: UI,
76-
userConfig: UserConfig,
77-
envConfig: ConfigEnv,
78-
inlineOptions?: PluginOptions,
79-
) {
74+
async function validateEnv(ui: UI, config: ConfigOptions, inlineOptions?: PluginOptions) {
8075
/**
8176
* Dynamic import of Vite helpers to using the ESM build of Vite and
8277
* avoiding CJS since it will be deprecated
8378
* See : https://vitejs.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated
8479
*/
8580
const { normalizePath, loadEnv } = await import('vite')
86-
const rootDir = userConfig.root || cwd()
81+
const rootDir = config.root || cwd()
8782

88-
const resolvedRoot = normalizePath(
89-
userConfig.root ? path.resolve(userConfig.root) : process.cwd(),
90-
)
83+
const resolvedRoot = normalizePath(config.root ? path.resolve(config.root) : process.cwd())
9184

92-
const envDir = userConfig.envDir
93-
? normalizePath(path.resolve(resolvedRoot, userConfig.envDir))
85+
const envDir = config.envDir
86+
? normalizePath(path.resolve(resolvedRoot, config.envDir))
9487
: resolvedRoot
9588

96-
const env = loadEnv(envConfig.mode, envDir, userConfig.envPrefix)
89+
const env = loadEnv(config.mode, envDir, config.envPrefix)
9790

9891
const options = await loadOptions(rootDir, inlineOptions)
9992
const variables = await validateAndLog(ui, env, options)
10093

101-
return {
102-
define: variables.reduce(
103-
(acc, { key, value }) => {
104-
acc[`import.meta.env.${key}`] = JSON.stringify(value)
105-
return acc
106-
},
107-
{} as Record<string, unknown>,
108-
),
109-
}
94+
return variables
11095
}
11196

11297
async function validateAndLog(ui: UI, env: Record<string, string>, options: PluginOptions) {
@@ -139,10 +124,37 @@ export const ValidateEnv = (options?: PluginOptions): Plugin => {
139124
// @ts-expect-error - only used for testing as we need to keep each instance of the plugin unique to a test
140125
ui: process.env.NODE_ENV === 'testing' ? ui : undefined,
141126
name: 'vite-plugin-validate-env',
142-
config: (config, env) => validateEnv(ui, config, env, options),
127+
config: ({ envDir, envPrefix, root }, { mode }) =>
128+
validateEnv(ui, { envDir, envPrefix, root, mode }, options).then((variables) => ({
129+
define: variables.reduce(
130+
(acc, { key, value }) => {
131+
acc[`import.meta.env.${key}`] = JSON.stringify(value)
132+
return acc
133+
},
134+
{} as Record<string, string>,
135+
),
136+
})),
143137
}
144138
}
145139

140+
/**
141+
* Load environment variables using the provided Vite configuration
142+
* and validate them against a schema the same way `ValidateEnv` does it
143+
* @returns An object mapping environment variable names to validation results
144+
*/
145+
export const loadAndValidateEnv = (config: ConfigOptions, options?: PluginOptions) => {
146+
const ui = initUi()
147+
return validateEnv(ui, config, options).then((variables) =>
148+
variables.reduce(
149+
(acc, { key, value }) => {
150+
acc[key] = value
151+
return acc
152+
},
153+
{} as Record<string, any>,
154+
),
155+
)
156+
}
157+
146158
export const defineConfig = <T extends PluginOptions>(config: T): T => config
147159

148160
export { schema as Schema } from '@poppinss/validator-lite'

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { z } from 'zod'
2+
import type { ConfigEnv, UserConfig } from 'vite'
23
import type { StandardSchemaV1 } from '@standard-schema/spec'
34
import type { ValidateFn } from '@poppinss/validator-lite/types'
45

@@ -23,6 +24,9 @@ export type StandardSchema = RecordViteKeys<StandardSchemaV1>
2324

2425
export type Schema = PoppinsSchema | StandardSchema
2526

27+
export type ConfigOptions = Pick<UserConfig, 'envDir' | 'envPrefix' | 'root'> &
28+
Pick<ConfigEnv, 'mode'>
29+
2630
/**
2731
* Infer the schema type from the plugin options
2832
*/

0 commit comments

Comments
 (0)