用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/NTQ78/Todo-app --skill frontend-environment-variables命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | frontend-environment-variables |
| description | Use when working with environment variables in frontend code |
⚠️ CRITICAL: ZERO TOLERANCE FOR DIRECT ENV ACCESS ⚠️
ALL environment variable access MUST go through the ENVs utility. NO EXCEPTIONS.
Centralized, type-safe environment variable access through ENVs.ts utility. NEVER access environment variables directly via import.meta.env.* (including import.meta.env.DEV, import.meta.env.PROD, or import.meta.env.MODE) in TypeScript files.
Philosophy: Single source of truth for ALL environment variables with runtime validation and type safety via jet-env.
import.meta.env.* usage in TypeScript files (anti-pattern)import.meta.env.DEV, import.meta.env.PROD, or import.meta.env.MODE (anti-pattern)import.meta.env.VITE_* in TypeScript files (anti-pattern)process.env.* anywhere in frontend code (doesn't work in Vite)Location: spark/frontend/my-vite-app/src/utils/ENVs/ENVs.ts
import jetEnv, { str } from "jet-env";
const baseENVs = jetEnv(
{
ViteSupabaseUrl: str,
ViteSupabaseAnonKey: str,
ViteCdnBaseUrl: str,
},
{ getValue: (key) => import.meta.env[key] }
);
/**
* Typesafe environment variables utility.
* ALL environment variable access MUST go through this utility.
* NEVER use import.meta.env directly in your code.
*/
const ENVs = {
...baseENVs,
/** Development mode - Use instead of import.meta.env.DEV */
get isDev(): boolean {
return import.meta.env.DEV;
},
/** Production mode - Use instead of import.meta.env.PROD */
get isProd(): boolean {
return import.meta.env.PROD;
},
/** Current mode string - Use instead of import.meta.env.MODE */
get mode(): string {
return import.meta.env.MODE;
},
} as const;
export { ENVs };
Pattern:
jet-env for runtime validationstr, num, bool)isDev, isProd, mode)import.meta.env directly// ✅ CORRECT - Using ENVs utility
import { ENVs } from "@/utils/ENVs/ENVs";
const supabaseUrl = ENVs.ViteSupabaseUrl;
const supabaseKey = ENVs.ViteSupabaseAnonKey;
const cdnUrl = ENVs.ViteCdnBaseUrl;
// ✅ CORRECT - Using ENVs.isDev
import { ENVs } from "@/utils/ENVs/ENVs";
if (ENVs.isDev) {
console.debug("[Debug] Development mode only");
}
// ✅ CORRECT - Using ENVs.mode
if (ENVs.mode === "development") {
console.debug("[Dev] Development mode");
}
// ✅ CORRECT - Production check
if (ENVs.isProd) {
// Production-only code
}
Available ENVs Properties:
ENVs.mode - Current mode string ('development' | 'production')ENVs.isDev - boolean (true when mode === 'development')ENVs.isProd - boolean (true when mode === 'production')Rule: ALL environment access (including mode checks) goes through ENVs utility.
Step-by-step:
Add to .env file with VITE_ prefix
VITE_API_URL=https://api.example.com
VITE_CDN_BASE_URL=https://cdn.example.com
Add to baseENVs schema in src/utils/ENVs/ENVs.ts
import jetEnv, { str } from "jet-env";
const baseENVs = jetEnv(
{
ViteSupabaseUrl: str,
ViteSupabaseAnonKey: str,
ViteCdnBaseUrl: str,
ViteApiUrl: str, // New variable
},
{ getValue: (key) => import.meta.env[key] }
);
const ENVs = {
...baseENVs,
get isDev(): boolean {
return import.meta.env.DEV;
},
get isProd(): boolean {
return import.meta.env.PROD;
},
get mode(): string {
return import.meta.env.MODE;
},
} as const;
{ };
Naming Convention:
.env file: VITE_API_URL (SCREAMING*SNAKE_CASE with VITE* prefix)ViteApiUrl (PascalCase)ENVs.ViteApiUrlImportant: ALL frontend environment variables MUST have VITE_ prefix (Vite requirement for security).
// ❌ FORBIDDEN
const url = import.meta.env.VITE_SUPABASE_URL;
// ✅ CORRECT
import { ENVs } from "@/utils/ENVs/ENVs";
const url = ENVs.ViteSupabaseUrl;
// ❌ FORBIDDEN - Even Vite built-ins must go through ENVs
if (import.meta.env.DEV) {
console.log("This is forbidden!");
}
if (import.meta.env.PROD) {
console.log("This is also forbidden!");
}
// ✅ CORRECT - Use ENVs helpers
import { ENVs } from "@/utils/ENVs/ENVs";
if (ENVs.isDev) {
console.log("This is correct");
}
if (ENVs.isProd) {
console.log("This is also correct");
}
// ❌ FORBIDDEN - process.env doesn't work in Vite browser code
if (process.env.NODE_ENV === "development") {
console.log("This will NOT work correctly");
}
// ✅ CORRECT - Use ENVs.isDev
import { ENVs } from "@/utils/ENVs/ENVs";
if (ENVs.isDev) {
console.log("This works correctly");
}
Why process.env.NODE_ENV fails:
process.env is Node.js-only, not available in browserprocess.env to frontend codeENVs.isDev or ENVs.mode instead// ❌ WRONG - New variable not added to ENVs.ts
const apiUrl = import.meta.env.VITE_NEW_API_URL; // No type safety
// ✅ CORRECT - First add to ENVs.ts, then use
import { ENVs } from "@/utils/ENVs/ENVs";
const apiUrl = ENVs.ViteNewApiUrl; // Type-safe, validated
🚫 Violations to find and fix:
import.meta.env.* usage in TypeScript files (except in ENVs.ts itself)import.meta.env.VITE_* in TypeScript files (except in ENVs.ts itself)import.meta.env.DEV in TypeScript files (except in ENVs.ts itself)import.meta.env.PROD in TypeScript files (except in ENVs.ts itself)import.meta.env.MODE in TypeScript files (except in ENVs.ts itself)process.env.NODE_ENV in TypeScript filesprocess.env.* anywhere in frontend code✅ Correct patterns to enforce:
ENVs.* (including mode checks)ENVs.isDev (NOT import.meta.env.DEV)ENVs.isProd (NOT import.meta.env.PROD)ENVs.mode (NOT import.meta.env.MODE).env have VITE_ prefiximport.meta.env accessFiles where direct env access is acceptable:
vite.config.js - Vite configuration (Node.js environment)src/utils/ENVs/ENVs.ts - The utility file itself.eslintrc.js)Rule: Only TypeScript/TSX files in src/ directory must use ENVs utility.
🚫 NO EXCEPTIONS - All env access goes through ENVs:
Development/Production Checks (use ENVs helpers):
import.meta.env.DEV ❌ → Use ENVs.isDev ✅import.meta.env.PROD ❌ → Use ENVs.isProd ✅import.meta.env.MODE ❌ → Use ENVs.mode ✅Custom Variables (use ENVs properties):
import.meta.env.VITE_* ❌ → Use ENVs.* ✅The ONLY exception: The ENVs.ts file itself, which is the single source of truth.
When fixing violations:
import.meta.env.DEV → Replace with ENVs.isDevimport.meta.env.PROD → Replace with ENVs.isProdimport.meta.env.MODE → Replace with ENVs.modeimport.meta.env.VITE_* → Replace with ENVs.*process.env.NODE_ENV → Replace with ENVs.isDevbaseENVs schema in ENVs.ts firstENVs.*import { ENVs } from '@/utils/ENVs/ENVs'docs/spark/frontend/my-vite-app/practices.md (Environment Variables section)spark/frontend/my-vite-app/src/utils/ENVs/ENVs.tsAccess via ENVs throughout app
import { ENVs } from "@/utils/ENVs/ENVs";
const apiUrl = ENVs.ViteApiUrl;
const cdnUrl = ENVs.ViteCdnBaseUrl;
Use when deploying Cloudflare Workers, managing R2 storage, or working with Cloudflare infrastructure
Use when working with ANTD components, theme tokens, icons, forms, or feedback components (message/notification/modal)
Use when adding, referencing, or serving static assets (images, fonts, videos, 3D models) through the R2 CDN pipeline with type-safe imports