Skip to content

Commit

Permalink
future module including taint
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusmarminge committed Jan 24, 2024
1 parent f1e0791 commit 94129d3
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 5 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 2 additions & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"devDependencies": {
"@types/mdx": "^2.0.5",
"@types/node": "^18.15.13",
"@types/react": "18.0.38",
"@types/react-dom": "18.0.11",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.21",
"tailwindcss": "^3.3.1",
Expand Down
2 changes: 1 addition & 1 deletion examples/nextjs/app/env.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createEnv } from "@t3-oss/env-nextjs";
import { createEnv } from "@t3-oss/env-nextjs/future";
import { vercel } from "@t3-oss/env-nextjs/presets";
import { z } from "zod";

Expand Down
6 changes: 5 additions & 1 deletion examples/nextjs/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Inter } from "next/font/google";

import { env } from "~/env";
import { ClientComponent } from "./client-component";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });
Expand All @@ -11,10 +12,13 @@ export const metadata = {
};

export default function RootLayout(props: { children: React.ReactNode }) {
console.log("Server says", env.SECRET);

return (
<html lang="en">
<body className={inter.className}>
<div>Server says {env.SECRET}!</div>
{/* 👇👇👇 rendering the server secret will cause error because it's tained 👇👇👇 */}
{/* <div>Server says {env.SECRET}!</div> */}
<main>{props.children}</main>
</body>
</html>
Expand Down
5 changes: 4 additions & 1 deletion examples/nextjs/next.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import "./app/env.js";
// import "./app/env.js";

/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
taint: true,
},
};

export default nextConfig;
16 changes: 16 additions & 0 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@
"default": "./dist/index.cjs"
}
},
"./future": {
"import": {
"types": "./dist/future.d.ts",
"default": "./dist/future.js"
},
"require": {
"types": "./dist/future.d.cts",
"default": "./dist/future.cjs"
}
},
"./presets": {
"import": {
"types": "./dist/presets.d.ts",
Expand All @@ -48,16 +58,22 @@
"@t3-oss/env-core": "workspace:*"
},
"peerDependencies": {
"react": "experimental",
"typescript": ">=5.0.0",
"zod": "^3.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"typescript": {
"optional": true
}
},
"devDependencies": {
"@types/react": "18.2.48",
"bunchee": "^4.3.3",
"react": "experimental",
"typescript": "^5.3.3",
"zod": "^3.20.2"
}
Expand Down
109 changes: 109 additions & 0 deletions packages/nextjs/src/future.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/// <reference types="react/experimental" />

/**
* This contains a potential future version of `createEnv` that
* uses the new Taint API to prevent server variables from being
* passed to client components.
*/

import type { ServerClientOptions, StrictOptions } from "@t3-oss/env-core";
import { createEnv as createEnvCore } from "@t3-oss/env-core";
import * as React from "react";
import type { ZodType } from "zod";

const CLIENT_PREFIX = "NEXT_PUBLIC_" as const;
type ClientPrefix = typeof CLIENT_PREFIX;

type Options<
TServer extends Record<string, ZodType>,
TClient extends Record<`${ClientPrefix}${string}`, ZodType>,
TShared extends Record<string, ZodType>,
TExtends extends Array<Record<string, unknown>>,
> = Omit<
StrictOptions<ClientPrefix, TServer, TClient, TShared, TExtends> &
ServerClientOptions<ClientPrefix, TServer, TClient>,
"runtimeEnvStrict" | "runtimeEnv" | "clientPrefix"
> &
(
| {
/**
* Manual destruction of `process.env`. Required for Next.js < 13.4.4.
*/
runtimeEnv: StrictOptions<
ClientPrefix,
TServer,
TClient,
TShared,
TExtends
>["runtimeEnvStrict"];
experimental__runtimeEnv?: never;
}
| {
runtimeEnv?: never;
/**
* Can be used for Next.js ^13.4.4 since they stopped static analysis of server side `process.env`.
* Only client side `process.env` is statically analyzed and needs to be manually destructured.
*/
experimental__runtimeEnv: Record<
| {
[TKey in keyof TClient]: TKey extends `${ClientPrefix}${string}`
? TKey
: never;
}[keyof TClient]
| {
[TKey in keyof TShared]: TKey extends string ? TKey : never;
}[keyof TShared],
string | boolean | number | undefined
>;
}
);

export function createEnv<
TServer extends Record<string, ZodType> = NonNullable<unknown>,
TClient extends Record<
`${ClientPrefix}${string}`,
ZodType
> = NonNullable<unknown>,
TShared extends Record<string, ZodType> = NonNullable<unknown>,
const TExtends extends Array<Record<string, unknown>> = [],
>(opts: Options<TServer, TClient, TShared, TExtends>) {
const client = typeof opts.client === "object" ? opts.client : {};
const server = typeof opts.server === "object" ? opts.server : {};
const shared = opts.shared;

const runtimeEnv = opts.runtimeEnv
? opts.runtimeEnv
: {
...process.env,
...opts.experimental__runtimeEnv,
};

const proxy = createEnvCore<
ClientPrefix,
TServer,
TClient,
TShared,
TExtends
>({
...opts,
shared,
client,
server,
clientPrefix: CLIENT_PREFIX,
runtimeEnv,
});

for (const key in proxy) {
if (key in server) {
try {
React.experimental_taintUniqueValue(
"Passing server variables is not supported",
proxy,
proxy[key],
);
} catch {}
}
}

return proxy;
}

0 comments on commit 94129d3

Please sign in to comment.