Skip to content

Commit

Permalink
feat: refactor strategy for env define
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien-R44 committed Nov 25, 2023
1 parent 8ed66d3 commit 3b9eeea
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 27 deletions.
1 change: 1 addition & 0 deletions bin/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ processCLIArgs(process.argv.slice(2))
configure({
files: ['tests/**/*.spec.ts'],
plugins: [assert(), fileSystem()],
importer: (filePath) => import(filePath.toString()),
})

/*
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@
"format": "prettier --write .",
"prepublishOnly": "pnpm build",
"release": "bumpp --commit --push --tag && pnpm publish",
"start": "node --loader=ts-node/esm src/index.ts",
"test": "cross-env NODE_NO_WARNINGS=1 NODE_ENV=testing node --loader ts-node/esm bin/test.ts",
"dev:playground": "pnpm vite -c playground/vite.config.ts",
"quick:test": "node --loader ts-node/esm bin/test.ts",
"test": "node --loader ts-node/esm bin/test.ts",
"typecheck": "tsc --noEmit",
"checks": "pnpm lint && pnpm typecheck"
},
Expand Down Expand Up @@ -90,6 +91,7 @@
"typescript": "^5.3.2",
"unbuild": "^2.0.0",
"vite": "^5.0.2",
"vite-node": "^0.34.6",
"zod": "^3.22.4"
},
"pnpm": {
Expand Down
25 changes: 25 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,17 @@ async function validateEnv(userConfig: UserConfig, envConfig: ConfigEnv, options
throw new Error(`Invalid validator "${validator}"`)
}

await validatorFn(env, schema as any)
const variables = await validatorFn(env, schema as any)

return {
define: variables.reduce(
(acc, { key, value }) => {
acc[`import.meta.env.${key}`] = JSON.stringify(value)
return acc
},
{} as Record<string, unknown>,
),
}
}

/**
Expand Down
11 changes: 5 additions & 6 deletions src/validators/builtin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,16 @@ export function errorReporter(errors: any[]) {
*/
export function builtinValidation(env: Record<string, string>, schema: PoppinsSchema) {
const errors = []
const variables = []

for (const [key, validator] of Object.entries(schema!)) {
try {
const res = validator(key, env[key])

// Handle undefined aka optional results
if (typeof res === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete process.env[key]
continue
}
if (typeof res === 'undefined') continue

process.env[key] = res
variables.push({ key, value: res })
} catch (err) {
errors.push({ key, err })
}
Expand All @@ -41,4 +38,6 @@ export function builtinValidation(env: Record<string, string>, schema: PoppinsSc
if (errors.length) {
throw new Error(errorReporter(errors))
}

return variables
}
11 changes: 5 additions & 6 deletions src/validators/zod/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function errorReporter(errors: any[]) {
*/
export async function zodValidation(env: Record<string, string>, schema: ZodSchema) {
const errors = []
const variables = []

for (const [key, validator] of Object.entries(schema!)) {
const result = validator.safeParse(env[key])
Expand All @@ -31,16 +32,14 @@ export async function zodValidation(env: Record<string, string>, schema: ZodSche
}

// Handle undefined aka optional results
if (typeof result.data === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete process.env[key]
continue
}
if (typeof result.data === 'undefined') continue

process.env[key] = result.data
variables.push({ key, value: result.data })
}

if (errors.length) {
throw new Error(errorReporter(errors))
}

return variables
}
46 changes: 39 additions & 7 deletions tests/common.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ test.group('vite-plugin-validate-env', () => {
await fs.create(`.env.development`, `VITE_URL_TRAILING=test.com`)

// @ts-ignore
await plugin.config!({ root: fs.basePath }, viteEnvConfig)
assert.equal(process.env.VITE_URL_TRAILING, 'test.com/')
const { define } = await plugin.config!({ root: fs.basePath }, viteEnvConfig)

assert.deepEqual(define['import.meta.env.VITE_URL_TRAILING'], '"test.com/"')
})

test('Dedicated config file', async ({ assert, fs }) => {
Expand Down Expand Up @@ -134,9 +135,12 @@ test.group('vite-plugin-validate-env', () => {
await fs.create(`./env-directory/.env.development`, `VITE_XXX=bonjour`)

// @ts-ignore
await plugin.config({ root: fs.basePath, envDir: './env-directory' }, viteEnvConfig)
const { define } = await plugin.config(
{ root: fs.basePath, envDir: './env-directory' },
viteEnvConfig,
)

assert.equal(process.env.VITE_XXX, 'bonjour')
assert.deepEqual(define['import.meta.env.VITE_XXX'], '"bonjour"')
})

test('Display multiple errors', async ({ assert, fs }) => {
Expand Down Expand Up @@ -195,9 +199,37 @@ test.group('vite-plugin-validate-env', () => {

await fs.create('.env.development', 'VITE_MY_VAR=hello')
// @ts-ignore
await plugin.config({ root: fs.basePath }, viteEnvConfig)
const { define } = await plugin.config({ root: fs.basePath }, viteEnvConfig)

assert.equal(process.env.VITE_OPTIONAL, undefined)
assert.equal(process.env.VITE_MY_VAR, 'hello')
assert.equal(define['import.meta.env.VITE_OPTIONAL'], undefined)
assert.equal(define['import.meta.env.VITE_MY_VAR'], '"hello"')
})

test('number value', async ({ assert, fs }) => {
const plugin = ValidateEnv({
validator: 'builtin',
schema: { VITE_NUMBER: Schema.number() },
})

await fs.create('.env.development', 'VITE_NUMBER=43')

// @ts-ignore
const { define } = await plugin.config({ root: fs.basePath }, viteEnvConfig)

assert.deepEqual(define['import.meta.env.VITE_NUMBER'], '43')
})

test('boolean value', async ({ assert, fs }) => {
const plugin = ValidateEnv({
validator: 'builtin',
schema: { VITE_BOOLEAN: Schema.boolean() },
})

await fs.create('.env.development', 'VITE_BOOLEAN=true')

// @ts-ignore
const { define } = await plugin.config({ root: fs.basePath }, viteEnvConfig)

assert.deepEqual(define['import.meta.env.VITE_BOOLEAN'], 'true')
})
})
46 changes: 41 additions & 5 deletions tests/zod.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ test.group('Zod validation adaptater', () => {
await fs.create(ENV_FILENAME, 'VITE_TEST=hello')

// @ts-expect-error - 'config' is the handler
await plugin.config!({ root: fs.basePath }, viteEnvConfig)
assert.equal(process.env.VITE_TEST, 'HELLO')
const { define } = await plugin.config!({ root: fs.basePath }, viteEnvConfig)
assert.equal(define['import.meta.env.VITE_TEST'], '"HELLO"')
})

test('Custom error message', async ({ assert, fs }) => {
Expand Down Expand Up @@ -148,9 +148,45 @@ test.group('Zod validation adaptater', () => {

await fs.create(ENV_FILENAME, 'VITE_MY_VAR=hello')
// @ts-ignore
await plugin.config({ root: fs.basePath }, viteEnvConfig)
const { define } = await plugin.config({ root: fs.basePath }, viteEnvConfig)

assert.equal(process.env.VITE_OPTIONAL_ZOD, undefined)
assert.equal(process.env.VITE_MY_VAR, 'hello')
assert.equal(define['import.meta.env.VITE_OPTIONAL_ZOD'], undefined)
assert.equal(define['import.meta.env.VITE_MY_VAR'], '"hello"')
})

test('number value', async ({ assert, fs }) => {
assert.plan(1)

const plugin = ValidateEnv({
validator: 'zod',
schema: { VITE_NUMBER: z.preprocess((value) => Number(value), z.number()) },
})

await fs.create(ENV_FILENAME, 'VITE_NUMBER=4323')

// @ts-ignore
const { define } = await plugin.config({ root: fs.basePath }, viteEnvConfig)
assert.equal(define['import.meta.env.VITE_NUMBER'], '4323')
})

test('boolean value', async ({ assert, fs }) => {
assert.plan(2)

const plugin = ValidateEnv({
validator: 'zod',
schema: {
VITE_BOOLEAN: z.preprocess((value) => value === 'true' || value === '1', z.boolean()),
},
})

await fs.create(ENV_FILENAME, 'VITE_BOOLEAN=true')
// @ts-ignore
const { define } = await plugin.config({ root: fs.basePath }, viteEnvConfig)
assert.equal(define['import.meta.env.VITE_BOOLEAN'], 'true')

await fs.create(ENV_FILENAME, 'VITE_BOOLEAN=1')
// @ts-ignore
const { define: define2 } = await plugin.config({ root: fs.basePath }, viteEnvConfig)
assert.equal(define2['import.meta.env.VITE_BOOLEAN'], 'true')
})
})

0 comments on commit 3b9eeea

Please sign in to comment.