From b5876a972ae12c82b55fdb7173c145c75dd8e2e5 Mon Sep 17 00:00:00 2001 From: Hokki Date: Tue, 29 Jul 2025 08:05:48 +0700 Subject: [PATCH 1/4] docs(start): createIsomorphicFn and envOnly --- docs/start/config.json | 8 ++ .../framework/react/environment-functions.md | 105 ++++++++++++++++++ .../start/framework/react/server-functions.md | 2 +- .../framework/solid/environment-functions.md | 3 + 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 docs/start/framework/react/environment-functions.md create mode 100644 docs/start/framework/solid/environment-functions.md diff --git a/docs/start/config.json b/docs/start/config.json index 02a44b30acc..882a3977f71 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -41,6 +41,10 @@ "label": "Static Server Functions", "to": "framework/react/static-server-functions" }, + { + "label": "Environment Functions", + "to": "framework/react/environment-functions" + }, { "label": "Middleware", "to": "framework/react/middleware" @@ -122,6 +126,10 @@ "label": "Static Server Functions", "to": "framework/solid/static-server-functions" }, + { + "label": "Environment Functions", + "to": "framework/react/environment-functions" + }, { "label": "Middleware", "to": "framework/solid/middleware" diff --git a/docs/start/framework/react/environment-functions.md b/docs/start/framework/react/environment-functions.md new file mode 100644 index 00000000000..c773f0dd153 --- /dev/null +++ b/docs/start/framework/react/environment-functions.md @@ -0,0 +1,105 @@ +--- +id: environment-functions +title: Environment Functions +--- + +## What Are Environment Functions? + +Environment functions are utilities designed to define and control function execution based on the runtime environment—whether the code is running on the client or the server. These utilities help ensure that environment-specific logic is executed safely and intentionally, preventing runtime errors and improving maintainability in fullstack or isomorphic applications. + +Start provide three core environment functions: + +- `createIsomorphicFn`: Compose a single function that adapts to both client and server environments. +- `serverOnly`: Ensures a function can only run on the server. +- `clientOnly`: Ensures a function can only run on the client. + +--- + +## Isomorphic Functions + +Use `createIsomorphicFn()` to define functions that behave differently depending on whether they are called on the client or the server. This is useful for safely sharing logic across environments while delegating environment-specific behavior to appropriate handlers. + +> [!WARNING] +> Unimplemented function results in a no-op function. + +### Complete Implementation + +```tsx +const getEnv = createIsomorphicFn() + .server(() => 'server') + .client(() => 'client') + +const env = getEnv() +// ℹ️ On the **server**, it returns `'server'`. +// ℹ️ On the **client**, it returns `'client'`. +``` + +### Partial Implementation (Server) + +Here is an example of `createIsomorphicFn()` with only server implementation: + +```tsx +const serverImplementationOnly = createIsomorphicFn().server(() => 'server') + +const server = serverImplementationOnly() +// ℹ️ On the **server**, it returns `'server'`. +// ℹ️ On the **client**, it is no-op (returns `undefined`) +``` + +### Partial Implementation (Client) + +Here is an example of `createIsomorphicFn()` with only client implementation: + +```tsx +const clientImplementationOnly = createIsomorphicFn().client(() => 'client') + +const client = clientImplementationOnly() +// ℹ️ On the **server**, it is no-op (returns `undefined`) +// ℹ️ On the **client**, it returns `'client'`. +``` + +### No Implementation + +Here is an example of `createIsomorphicFn()` without any environment specific implementation: + +```tsx +const noImplementation = createIsomorphicFn() + +const noop = noImplementation() +// ℹ️ On both **client** and **server**, it is no-op (returns `undefined`) +``` + +--- + +## `env`Only Functions + +The `serverOnly` and `clientOnly` helpers enforce strict environment-bound execution. They ensure the decorated function is only callable in the correct runtime context. If misused, they throw descriptive runtime errors to prevent unintentional logic execution. + +### `serverOnly` + +```tsx +const foo = serverOnly(() => 'bar') + +foo() // ✅ On server: returns "bar" +// ❌ On client: throws "serverOnly() functions can only be called on the server!" +``` + +### `clientOnly` + +```tsx +const foo = clientOnly(() => 'bar') + +foo() // ✅ On client: returns "bar" +// ❌ On server: throws "clientOnly() functions can only be called on the client!" +``` + +> [!NOTE] +> These functions are useful for API access, filesystem reads, using browser APIs, or other operations that are invalid or insecure outside their intended environment. + +## Tree Shaking + +Environment functions are tree-shaken based on the environment for each bundle produced. + +On the server, functions created using `createIsomorphicFn()` are tree-shaken. So, all code used inside `.client()` are not included in server bundle, and vice-versa. + +On the server, implementation of `clientOnly` functions are replaced with a function that throws an `Error`. The reverse is true for `serverOnly` functions on the client. diff --git a/docs/start/framework/react/server-functions.md b/docs/start/framework/react/server-functions.md index 2c6a075b35b..c2a607533b7 100644 --- a/docs/start/framework/react/server-functions.md +++ b/docs/start/framework/react/server-functions.md @@ -98,7 +98,7 @@ response?: 'data' | 'full' | 'raw' - From other server functions > [!WARNING] -> Server functions cannot be called from API Routes. If you need to share business logic between server functions and API Routes, extract the shared logic into utility functions that can be imported by both. +> Server functions cannot be called from API Routes. If you need to share business logic between server functions and API Routes, extract the shared logic into utility functions that can be imported by both. See [Environment Functions](../environment-functions.md) for more. ## Accepting Parameters diff --git a/docs/start/framework/solid/environment-functions.md b/docs/start/framework/solid/environment-functions.md new file mode 100644 index 00000000000..cf5d8678dd4 --- /dev/null +++ b/docs/start/framework/solid/environment-functions.md @@ -0,0 +1,3 @@ +--- +ref: docs/start/framework/react/environment-functions.md +--- From 02bf486e3ded46c30c40161748a03f90416a5532 Mon Sep 17 00:00:00 2001 From: Hokki Suwanda Date: Fri, 15 Aug 2025 21:31:58 +0700 Subject: [PATCH 2/4] docs(start): add imports and no-op explanation --- .../framework/react/environment-functions.md | 27 ++++++++++++++++--- .../start/framework/react/server-functions.md | 3 --- .../framework/solid/environment-functions.md | 1 + 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/docs/start/framework/react/environment-functions.md b/docs/start/framework/react/environment-functions.md index c773f0dd153..39d51890d69 100644 --- a/docs/start/framework/react/environment-functions.md +++ b/docs/start/framework/react/environment-functions.md @@ -19,12 +19,11 @@ Start provide three core environment functions: Use `createIsomorphicFn()` to define functions that behave differently depending on whether they are called on the client or the server. This is useful for safely sharing logic across environments while delegating environment-specific behavior to appropriate handlers. -> [!WARNING] -> Unimplemented function results in a no-op function. - ### Complete Implementation ```tsx +import { createIsomorphicFn } from '@tanstack/react-start'; + const getEnv = createIsomorphicFn() .server(() => 'server') .client(() => 'client') @@ -39,6 +38,8 @@ const env = getEnv() Here is an example of `createIsomorphicFn()` with only server implementation: ```tsx +import { createIsomorphicFn } from '@tanstack/react-start'; + const serverImplementationOnly = createIsomorphicFn().server(() => 'server') const server = serverImplementationOnly() @@ -51,6 +52,8 @@ const server = serverImplementationOnly() Here is an example of `createIsomorphicFn()` with only client implementation: ```tsx +import { createIsomorphicFn } from '@tanstack/react-start'; + const clientImplementationOnly = createIsomorphicFn().client(() => 'client') const client = clientImplementationOnly() @@ -63,12 +66,23 @@ const client = clientImplementationOnly() Here is an example of `createIsomorphicFn()` without any environment specific implementation: ```tsx +import { createIsomorphicFn } from '@tanstack/react-start'; + const noImplementation = createIsomorphicFn() const noop = noImplementation() // ℹ️ On both **client** and **server**, it is no-op (returns `undefined`) ``` +#### What is a no-op? + +A no-op (short for "no operation") is a function that does nothing when executed - it simply returns `undefined` without performing any operations. + +```tsx +// basic no-op implementation +function noop() {} +``` + --- ## `env`Only Functions @@ -78,6 +92,8 @@ The `serverOnly` and `clientOnly` helpers enforce strict environment-bound execu ### `serverOnly` ```tsx +import { serverOnly } from '@tanstack/react-start'; + const foo = serverOnly(() => 'bar') foo() // ✅ On server: returns "bar" @@ -87,6 +103,8 @@ foo() // ✅ On server: returns "bar" ### `clientOnly` ```tsx +import { clientOnly } from '@tanstack/react-start'; + const foo = clientOnly(() => 'bar') foo() // ✅ On client: returns "bar" @@ -100,6 +118,7 @@ foo() // ✅ On client: returns "bar" Environment functions are tree-shaken based on the environment for each bundle produced. -On the server, functions created using `createIsomorphicFn()` are tree-shaken. So, all code used inside `.client()` are not included in server bundle, and vice-versa. +Functions created using `createIsomorphicFn()` are tree-shaken. All codes inside `.client()` are not included in server bundle, and vice-versa. On the server, implementation of `clientOnly` functions are replaced with a function that throws an `Error`. The reverse is true for `serverOnly` functions on the client. + diff --git a/docs/start/framework/react/server-functions.md b/docs/start/framework/react/server-functions.md index c2a607533b7..1c129d12dc9 100644 --- a/docs/start/framework/react/server-functions.md +++ b/docs/start/framework/react/server-functions.md @@ -97,9 +97,6 @@ response?: 'data' | 'full' | 'raw' - From client-side code - From other server functions -> [!WARNING] -> Server functions cannot be called from API Routes. If you need to share business logic between server functions and API Routes, extract the shared logic into utility functions that can be imported by both. See [Environment Functions](../environment-functions.md) for more. - ## Accepting Parameters Server functions accept a single parameter, which can be a variety of types: diff --git a/docs/start/framework/solid/environment-functions.md b/docs/start/framework/solid/environment-functions.md index cf5d8678dd4..63a0d9ad22a 100644 --- a/docs/start/framework/solid/environment-functions.md +++ b/docs/start/framework/solid/environment-functions.md @@ -1,3 +1,4 @@ --- ref: docs/start/framework/react/environment-functions.md +replace: { 'react': 'solid' } --- From a9770e725130723cbf511cf342b23e3a8e1ec777 Mon Sep 17 00:00:00 2001 From: Hokki Suwanda Date: Fri, 15 Aug 2025 21:36:19 +0700 Subject: [PATCH 3/4] docs(start): fix solid reference --- docs/start/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/start/config.json b/docs/start/config.json index 882a3977f71..e95d0257d75 100644 --- a/docs/start/config.json +++ b/docs/start/config.json @@ -128,7 +128,7 @@ }, { "label": "Environment Functions", - "to": "framework/react/environment-functions" + "to": "framework/solid/environment-functions" }, { "label": "Middleware", From 8ac6f2820ea4872827529cb57fbe021ea19ae01e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 03:32:25 +0000 Subject: [PATCH 4/4] ci: apply automated fixes --- .../framework/react/environment-functions.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/start/framework/react/environment-functions.md b/docs/start/framework/react/environment-functions.md index 39d51890d69..6e0a5196ee2 100644 --- a/docs/start/framework/react/environment-functions.md +++ b/docs/start/framework/react/environment-functions.md @@ -17,12 +17,12 @@ Start provide three core environment functions: ## Isomorphic Functions -Use `createIsomorphicFn()` to define functions that behave differently depending on whether they are called on the client or the server. This is useful for safely sharing logic across environments while delegating environment-specific behavior to appropriate handlers. +Use `createIsomorphicFn()` to define functions that behave differently depending on whether they are called on the client or the server. This is useful for safely sharing logic across environments while delegating environment-specific behavior to appropriate handlers. ### Complete Implementation ```tsx -import { createIsomorphicFn } from '@tanstack/react-start'; +import { createIsomorphicFn } from '@tanstack/react-start' const getEnv = createIsomorphicFn() .server(() => 'server') @@ -38,7 +38,7 @@ const env = getEnv() Here is an example of `createIsomorphicFn()` with only server implementation: ```tsx -import { createIsomorphicFn } from '@tanstack/react-start'; +import { createIsomorphicFn } from '@tanstack/react-start' const serverImplementationOnly = createIsomorphicFn().server(() => 'server') @@ -52,7 +52,7 @@ const server = serverImplementationOnly() Here is an example of `createIsomorphicFn()` with only client implementation: ```tsx -import { createIsomorphicFn } from '@tanstack/react-start'; +import { createIsomorphicFn } from '@tanstack/react-start' const clientImplementationOnly = createIsomorphicFn().client(() => 'client') @@ -66,7 +66,7 @@ const client = clientImplementationOnly() Here is an example of `createIsomorphicFn()` without any environment specific implementation: ```tsx -import { createIsomorphicFn } from '@tanstack/react-start'; +import { createIsomorphicFn } from '@tanstack/react-start' const noImplementation = createIsomorphicFn() @@ -92,7 +92,7 @@ The `serverOnly` and `clientOnly` helpers enforce strict environment-bound execu ### `serverOnly` ```tsx -import { serverOnly } from '@tanstack/react-start'; +import { serverOnly } from '@tanstack/react-start' const foo = serverOnly(() => 'bar') @@ -103,7 +103,7 @@ foo() // ✅ On server: returns "bar" ### `clientOnly` ```tsx -import { clientOnly } from '@tanstack/react-start'; +import { clientOnly } from '@tanstack/react-start' const foo = clientOnly(() => 'bar') @@ -121,4 +121,3 @@ Environment functions are tree-shaken based on the environment for each bundle p Functions created using `createIsomorphicFn()` are tree-shaken. All codes inside `.client()` are not included in server bundle, and vice-versa. On the server, implementation of `clientOnly` functions are replaced with a function that throws an `Error`. The reverse is true for `serverOnly` functions on the client. -