Skip to content

Commit bf796ce

Browse files
authored
Merge pull request #1423 from yamcodes/1328-env-module-transform
feat: (v1) env-module transform — canonical env object surface
2 parents a3d93be + 1d0ce6f commit bf796ce

43 files changed

Lines changed: 1166 additions & 251 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
"@arkenv/vite-plugin": minor
3+
---
4+
5+
#### Add `env.ts` transform for Vite fullstack apps
6+
7+
Let the Vite plugin discover your `env.ts` and expose a shared `env` object that works in both client and server code. On the client, public (`VITE_*`) values are inlined at build time and server-only keys throw if read; on the server/SSR, `env.ts` still runs normally and validates against the real environment at boot. The plugin does not rewrite your `env.ts` file on disk.
8+
9+
Works with `@arkenv/vite-plugin` and `@arkenv/vite-plugin/standard`.
10+
11+
Usage:
12+
13+
```ts
14+
// vite.config.ts
15+
import arkenv from "@arkenv/vite-plugin";
16+
17+
export default {
18+
plugins: [arkenv()], // finds src/env.ts or env.ts
19+
// or: arkenv({ schemaPath: "src/env.ts", clientPrefix: "VITE_" })
20+
};
21+
```
22+
23+
```ts
24+
// src/env.ts
25+
import arkenv from "@arkenv/core";
26+
27+
export const env = arkenv({
28+
DATABASE_URL: "string",
29+
VITE_API_URL: "string",
30+
});
31+
```
32+
33+
```ts
34+
import { env } from "./env";
35+
36+
env.VITE_API_URL; // available on client and server
37+
env.DATABASE_URL; // server only — throws if read in the browser
38+
```
39+
40+
Passing a schema to `arkenv(schema)` (the previous `import.meta.env` define API) continues to work unchanged.
Lines changed: 21 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,45 @@
11
# ArkEnv + SolidStart Example
22

3-
This example demonstrates how to use [@arkenv/vite-plugin](https://arkenv.js.org/docs/vite-plugin) with [SolidStart](https://start.solidjs.com). It showcases:
3+
This example demonstrates `@arkenv/vite-plugin` with [SolidStart](https://start.solidjs.com):
44

5-
- **Environment variable validation** at build-time with ArkEnv
6-
- **Typesafe `import.meta.env`** with full TypeScript support
7-
- **Client-side environment variables** with automatic filtering of `VITE_*` prefixed variables
5+
- A single `src/env.ts` is the typed source of truth (`import { env } from "./env"`)
6+
- **Client graph**: plugin inlines coerced `VITE_*` literals and guards server-only keys
7+
- **SSR graph**: `env.ts` runs as-is → boot-time validation against the real environment
8+
- Reading `env.DATABASE_URL` in the browser throws
89

910
## Setup
1011

11-
The example uses a single schema definition in `app.config.ts` that defines the shape of your environment variables:
12+
```ts title="src/env.ts"
13+
import arkenv from "@arkenv/core";
14+
15+
export const env = arkenv({
16+
DATABASE_URL: "string = 'postgres://localhost:5432/solidstart'",
17+
VITE_TEST: "string = 'Hello from SolidStart'",
18+
VITE_NUMERIC: "string.numeric = '42'",
19+
VITE_BOOLEAN: "boolean = true",
20+
});
21+
```
1222

1323
```ts title="app.config.ts"
1424
import arkenvVitePlugin from "@arkenv/vite-plugin";
1525
import { defineConfig } from "@solidjs/start/config";
16-
import { type } from "arkenv";
17-
18-
// Define the schema
19-
export const Env = type({
20-
VITE_TEST: "string",
21-
VITE_NUMERIC: "string.numeric",
22-
VITE_BOOLEAN: "boolean",
23-
});
2426

2527
export default defineConfig({
2628
vite: {
27-
// Pass the schema to the plugin
28-
plugins: [arkenvVitePlugin(Env)],
29+
plugins: [arkenvVitePlugin()],
2930
},
3031
});
3132
```
3233

33-
## Typesafe `import.meta.env`
34-
35-
The example includes type augmentation for `import.meta.env` in `src/global.d.ts`. This ensures correct TypeScript inference for all variables defined in your schema.
36-
37-
```ts title="src/global.d.ts"
38-
/// <reference types="@solidjs/start/env" />
39-
40-
type ImportMetaEnvAugmented =
41-
import("@arkenv/vite-plugin").ImportMetaEnvAugmented<
42-
typeof import("../app.config").Env
43-
>;
44-
45-
// Augment import.meta.env with your schema
46-
// Only `VITE_*` prefixed variables will be included
47-
interface ImportMetaEnv extends ImportMetaEnvAugmented {}
48-
```
49-
50-
This makes usage in your Solid components fully typesafe:
34+
## Usage
5135

5236
```tsx
53-
const test = import.meta.env.VITE_TEST; // ✅ string
54-
const num = import.meta.env.VITE_NUMERIC; // ✅ number
55-
const bool = import.meta.env.VITE_BOOLEAN; // ✅ boolean
56-
```
57-
58-
## Environment Variables
37+
import { env } from "./env";
5938

60-
You can verify the validation by looking at the `.env.production` file (or creating a `.env` file):
61-
62-
```env title=".env.production"
63-
VITE_TEST=Hello from SolidStart (Production)
64-
VITE_NUMERIC=3
65-
VITE_BOOLEAN=false
39+
env.VITE_TEST; // string (inlined on the client)
40+
env.DATABASE_URL; // throws in the browser; works on the server
6641
```
6742

68-
The plugin automatically:
69-
70-
- Validates all variables at build-time
71-
- Filters to only expose `VITE_*` variables to the client
72-
- Ensures `import.meta.env` matches your schema
73-
7443
## Running the Example
7544

7645
```bash
@@ -89,10 +58,7 @@ pnpm start
8958

9059
## Documentation
9160

92-
For more information, see the [@arkenv/vite-plugin documentation](https://arkenv.js.org/docs/vite-plugin):
93-
94-
- [Introduction](https://arkenv.js.org/docs/vite-plugin)
61+
- [Vite plugin docs](https://arkenv.js.org/docs/vite-plugin)
9562
- [Typing import.meta.env](https://arkenv.js.org/docs/vite-plugin/typing-import-meta-env)
96-
- [Using ArkEnv in Vite config](https://arkenv.js.org/docs/vite-plugin/arkenv-in-viteconfig)
9763

9864
## this project was created with the [Solid CLI](https://github.com/solidjs-community/solid-cli)
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
import { type } from "@arkenv/core";
21
import arkenvVitePlugin from "@arkenv/vite-plugin";
32
import { defineConfig } from "@solidjs/start/config";
43

5-
export const Env = type({
6-
VITE_TEST: "string",
7-
VITE_NUMERIC: "string.numeric",
8-
VITE_BOOLEAN: "boolean",
9-
});
10-
114
export default defineConfig({
125
vite: {
13-
plugins: [arkenvVitePlugin(Env)],
6+
plugins: [arkenvVitePlugin()],
147
},
158
});

apps/playgrounds/solid-start/src/app.tsx

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { createSignal } from "solid-js";
2+
import { isServer } from "solid-js/web";
3+
import { env } from "./env";
24
import "./app.css";
35

6+
const serverDatabaseUrl = isServer ? env.DATABASE_URL : null;
7+
48
export default function App() {
59
const [count, setCount] = createSignal(0);
10+
const [serverError, setServerError] = createSignal<string | null>(null);
611

712
return (
813
<main>
@@ -26,17 +31,43 @@ export default function App() {
2631
to learn how to build SolidStart apps.
2732
<br />
2833
<br />
29-
<code>import.meta.env.VITE_TEST</code>:{" "}
30-
{String(import.meta.env.VITE_TEST)} (of type{" "}
31-
{typeof import.meta.env.VITE_TEST})
34+
<code>env.VITE_TEST</code>: {String(env.VITE_TEST)} (of type{" "}
35+
{typeof env.VITE_TEST})
36+
<br />
37+
<code>env.VITE_NUMERIC</code>: {String(env.VITE_NUMERIC)} (of type{" "}
38+
{typeof env.VITE_NUMERIC})
3239
<br />
33-
<code>import.meta.env.VITE_NUMERIC</code>:{" "}
34-
{String(import.meta.env.VITE_NUMERIC)} (of type{" "}
35-
{typeof import.meta.env.VITE_NUMERIC})
40+
<code>env.VITE_BOOLEAN</code>: {String(env.VITE_BOOLEAN)} (of type{" "}
41+
{typeof env.VITE_BOOLEAN})
3642
<br />
37-
<code>import.meta.env.VITE_BOOLEAN</code>:{" "}
38-
{String(import.meta.env.VITE_BOOLEAN)} (of type{" "}
39-
{typeof import.meta.env.VITE_BOOLEAN})
43+
{serverDatabaseUrl ? (
44+
<>
45+
<code>env.DATABASE_URL</code> (SSR): {serverDatabaseUrl}
46+
<br />
47+
</>
48+
) : null}
49+
<br />
50+
<button
51+
type="button"
52+
onClick={() => {
53+
try {
54+
void env.DATABASE_URL;
55+
setServerError(null);
56+
} catch (error) {
57+
setServerError(
58+
error instanceof Error ? error.message : String(error),
59+
);
60+
}
61+
}}
62+
>
63+
Read env.DATABASE_URL on the client
64+
</button>
65+
{serverError() ? (
66+
<>
67+
<br />
68+
<code>{serverError()}</code>
69+
</>
70+
) : null}
4071
</p>
4172
</main>
4273
);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import arkenv from "@arkenv/core";
2+
3+
/**
4+
* Canonical env object for the SolidStart example.
5+
* Client (`VITE_*`) keys are inlined by `@arkenv/vite-plugin` in the browser graph;
6+
* server-only keys validate at boot in the SSR graph.
7+
*/
8+
export const env = arkenv({
9+
DATABASE_URL: "string = 'postgres://localhost:5432/solidstart'",
10+
VITE_TEST: "string = 'Hello from SolidStart'",
11+
VITE_NUMERIC: "string.numeric = '42'",
12+
VITE_BOOLEAN: "boolean = true",
13+
NODE_ENV: "'development' | 'production' | 'test' = 'development'",
14+
});
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1 @@
11
/// <reference types="@solidjs/start/env" />
2-
3-
type ImportMetaEnvAugmented =
4-
import("@arkenv/vite-plugin").ImportMetaEnvAugmented<
5-
typeof import("../app.config").Env
6-
>;
7-
8-
// Augment import.meta.env with your schema
9-
// Only `VITE_*` prefixed variables will be included
10-
interface ImportMetaEnv extends ImportMetaEnvAugmented {}

examples/basic-js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"clean": "rimraf dist node_modules"
99
},
1010
"dependencies": {
11-
"@arkenv/core": "1.0.0-alpha.7",
11+
"@arkenv/core": "1.0.0-alpha.4",
1212
"arktype": "^2.2.0"
1313
},
1414
"engines": {

examples/basic/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"clean": "rimraf dist node_modules"
1010
},
1111
"dependencies": {
12-
"@arkenv/core": "1.0.0-alpha.7",
12+
"@arkenv/core": "1.0.0-alpha.4",
1313
"arktype": "^2.2.0",
1414
"zod": "^4.4.1"
1515
},

examples/with-bun-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"dependencies": {
1414
"@arkenv/bun-plugin": "1.0.0-alpha.6",
15-
"@arkenv/core": "1.0.0-alpha.7",
15+
"@arkenv/core": "1.0.0-alpha.4",
1616
"arktype": "^2.2.0",
1717
"react": "^19.2.5",
1818
"react-dom": "^19.2.5"

examples/with-bun/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"clean": "rimraf dist node_modules"
1111
},
1212
"dependencies": {
13-
"@arkenv/core": "1.0.0-alpha.7",
13+
"@arkenv/core": "1.0.0-alpha.4",
1414
"arktype": "^2.2.0"
1515
},
1616
"devDependencies": {

0 commit comments

Comments
 (0)