From 52c8182ce9088356d6c5c0e18e459a989cf6679e Mon Sep 17 00:00:00 2001
From: denchiklut <ollylut@gmail.com>
Date: Sat, 28 Sep 2024 19:45:14 -0700
Subject: [PATCH] chore: cleanup

---
 README.md                                     |  2 +-
 config/spec/setup.ts                          |  4 +-
 src/client/components/@shared/error/index.tsx |  3 +-
 src/common/env/create-env.ts                  | 42 -------------
 src/common/env/env.util.ts                    | 62 ++++++++++---------
 src/common/env/index.ts                       | 38 +++++++++++-
 src/common/env/parse.util.ts                  |  4 +-
 7 files changed, 77 insertions(+), 78 deletions(-)
 delete mode 100644 src/common/env/create-env.ts

diff --git a/README.md b/README.md
index 5c8a0bb..450a780 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ You can use .env file to specify environment variables. This file is ignored by
 #### Adding new `env` variable
 
 1. Add it to `.env` file
-2. For TS completion and validation add it to `envSchema` in `src/common/env/env.util.ts`
+2. For TS completion and validation add it to `envSchema` in `src/common/env/index.ts`
 3. If this variable needs to be accessible from both `client` & `server` make sure it's name starts with prefix `CLIENT_`
 4. You can access environment variable via `getENV` function.
    This function will return a proper value based on environment (client/server) and cast it to a proper type based on `envSchema` from `step 2` (string/number/boolean)
diff --git a/config/spec/setup.ts b/config/spec/setup.ts
index 7e7d177..271f630 100644
--- a/config/spec/setup.ts
+++ b/config/spec/setup.ts
@@ -5,8 +5,8 @@ window.IS_DEV = false
 window.IS_SPA = true
 window.clientPrefix = 'PUBLIC_'
 
-jest.mock('src/common/env/create-env', () => ({
-	...jest.requireActual('src/common/env/create-env'),
+jest.mock('src/common/env/env.util', () => ({
+	...jest.requireActual('src/common/env/env.util'),
 	createEnv: jest.fn(() => ({
 		CLIENT_HOST: 'http://localhost:3000',
 		CLIENT_PUBLIC_PATH: '/',
diff --git a/src/client/components/@shared/error/index.tsx b/src/client/components/@shared/error/index.tsx
index d7341c3..8fc9d1c 100644
--- a/src/client/components/@shared/error/index.tsx
+++ b/src/client/components/@shared/error/index.tsx
@@ -1,8 +1,9 @@
 import { useRouteError } from 'react-router'
+import { logger } from 'src/common'
 
 export const Fallback = () => {
 	const error = useRouteError()
-	console.error(error)
+	logger.error(error)
 
 	return <p>Something went wrong</p>
 }
diff --git a/src/common/env/create-env.ts b/src/common/env/create-env.ts
deleted file mode 100644
index 9597ce6..0000000
--- a/src/common/env/create-env.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import type { ObjectSchema, InferType, AnyObject, AnySchema } from 'yup'
-import { parse } from 'src/common/env/parse.util'
-
-interface Props<T extends AnyObject> {
-	schema: ObjectSchema<T>
-	envs: Partial<Record<keyof InferType<ObjectSchema<T>>, unknown>>
-	clientPrefix?: string
-}
-export function createEnv<S extends AnyObject>({
-	schema,
-	envs,
-	clientPrefix = 'CLIENT_'
-}: Props<S>) {
-	const client = schema.pick(Object.keys(schema.shape).filter(k => k.startsWith(clientPrefix)))
-	const { data, error } = parse((IS_SERVER ? schema : client) as AnySchema<S>, envs)
-
-	if (error) {
-		console.error('❌ Invalid environment variables:', error.errors)
-
-		throw new Error('Invalid environment variables')
-	}
-
-	return new Proxy(data, {
-		get(target, prop, receiver) {
-			if (typeof prop !== 'string') {
-				return undefined
-			}
-
-			if (
-				!IS_SERVER &&
-				(!clientPrefix || !prop.startsWith(clientPrefix)) &&
-				!['toJSON', 'toString'].includes(prop)
-			) {
-				throw new Error(
-					`❌ Attempted to access a server-side environment variable "${prop}" on the client`
-				)
-			}
-
-			return Reflect.get(target, prop, receiver)
-		}
-	}) as InferType<ObjectSchema<S>>
-}
diff --git a/src/common/env/env.util.ts b/src/common/env/env.util.ts
index bcf5ac3..66fd647 100644
--- a/src/common/env/env.util.ts
+++ b/src/common/env/env.util.ts
@@ -1,35 +1,39 @@
-import { string, mixed, object, type InferType } from 'yup'
-import { getOrDefault } from './get.util'
-import { createEnv } from './create-env'
+import type { ObjectSchema, InferType, AnyObject } from 'yup'
+import { parse } from './parse.util'
 
-if (IS_SERVER) require('dotenv/config')
-
-const envSchema = object({
-	CLIENT_HOST: string().default('http://localhost:3000'),
-	CLIENT_PUBLIC_PATH: string().default('0.0.0'),
-	APP_VERSION: string().default('0.0.0'),
-	NODE_ENV: mixed<'production' | 'development' | 'test'>()
-		.oneOf(['production', 'development', 'test'])
-		.default('development')
-})
+interface Props<T extends AnyObject> {
+	schema: ObjectSchema<T>
+	envs: Partial<Record<keyof InferType<ObjectSchema<T>>, unknown>>
+	clientPrefix?: string
+}
+export function createEnv<S extends AnyObject>({
+	schema,
+	envs,
+	clientPrefix = 'CLIENT_'
+}: Props<S>) {
+	const client = schema.pick(Object.keys(schema.shape).filter(k => k.startsWith(clientPrefix)))
+	const { data, error } = parse((IS_SERVER ? schema : client) as ObjectSchema<S>, envs)
 
-export type Env = InferType<typeof envSchema>
+	if (error) {
+		console.error('❌ Invalid environment variables:', error.errors)
+		throw new Error('Invalid environment variables')
+	}
 
-export const getENV = getOrDefault(
-	createEnv({
-		clientPrefix,
-		schema: envSchema,
-		envs: IS_SERVER ? process.env : window.env_vars
-	})
-)
+	return new Proxy(data, {
+		get(target, prop, receiver) {
+			if (typeof prop !== 'string') return undefined
 
-export const setEnvVars = (nonce: string) => {
-	const clientEnv = Object.entries(getENV())
-		.filter(([k]) => k.startsWith(clientPrefix))
-		.reduce<Collection<string, unknown>>((res, [k, v]) => {
-			res[k] = v
-			return res
-		}, {})
+			if (
+				!IS_SERVER &&
+				(!clientPrefix || !prop.startsWith(clientPrefix)) &&
+				!['toJSON', 'toString'].includes(prop)
+			) {
+				throw new Error(
+					`❌ Attempted to access a server-side environment variable "${prop}" on the client`
+				)
+			}
 
-	return `<script nonce='${nonce}'>window.env_vars = Object.freeze(${JSON.stringify(clientEnv)})</script>`
+			return Reflect.get(target, prop, receiver)
+		}
+	}) as InferType<ObjectSchema<S>>
 }
diff --git a/src/common/env/index.ts b/src/common/env/index.ts
index bebcc62..a891630 100644
--- a/src/common/env/index.ts
+++ b/src/common/env/index.ts
@@ -1 +1,37 @@
-export * from './env.util'
+import { string, mixed, object, type InferType } from 'yup'
+import { getOrDefault } from './get.util'
+import { createEnv } from './env.util'
+
+if (IS_SERVER) require('dotenv/config')
+
+// you can find implementation for a `zod` library
+// in the `feat/pipable-stream` git branch
+const envSchema = object({
+	CLIENT_HOST: string().default('http://localhost:3000'),
+	CLIENT_PUBLIC_PATH: string().default('0.0.0'),
+	APP_VERSION: string().default('0.0.0'),
+	NODE_ENV: mixed<'production' | 'development' | 'test'>()
+		.oneOf(['production', 'development', 'test'])
+		.default('development')
+})
+
+export type Env = InferType<typeof envSchema>
+
+export const getENV = getOrDefault(
+	createEnv({
+		clientPrefix,
+		schema: envSchema,
+		envs: IS_SERVER ? process.env : window.env_vars
+	})
+)
+
+export const setEnvVars = (nonce: string) => {
+	const clientEnv = Object.entries(getENV())
+		.filter(([k]) => k.startsWith(clientPrefix))
+		.reduce<Collection<string, unknown>>((res, [k, v]) => {
+			res[k] = v
+			return res
+		}, {})
+
+	return `<script nonce='${nonce}'>window.env_vars=Object.freeze(${JSON.stringify(clientEnv)})</script>`
+}
diff --git a/src/common/env/parse.util.ts b/src/common/env/parse.util.ts
index da721ab..7ae3b6b 100644
--- a/src/common/env/parse.util.ts
+++ b/src/common/env/parse.util.ts
@@ -1,8 +1,8 @@
-import { type AnySchema, ValidationError } from 'yup'
+import { type AnyObject, type ObjectSchema, ValidationError } from 'yup'
 
 type ParseResult<T> = { data: T; error: null } | { data: null; error: ValidationError }
 
-export function parse<T>(schema: AnySchema<T>, envs: unknown): ParseResult<T> {
+export function parse<T extends AnyObject>(schema: ObjectSchema<T>, envs: unknown): ParseResult<T> {
 	try {
 		const data = schema.validateSync(envs, { abortEarly: false })
 		return { data, error: null } as ParseResult<T>