Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/CodySwannGT/lisa --skill expo-env-configコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
any non-trivial request —…
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
SOC 職業分類に基づく
SKILL.md を表示中
| name | expo-env-config |
| description | creating, modifying, or… |
This skill enforces type-safe, validated environment variable management for Expo/React Native using Zod schemas. Environment variables are validated at build time and provide full TypeScript inference, regardless of their source (.env files, EAS Build secrets, CI/CD pipelines, or command-line exports).
Unlike NestJS's @nestjs/config, Expo has no official type-safe env solution. The Zod validation pattern provides:
z.infer<typeof schema>.env, EAS secrets, CI variablessrc/lib/env.ts)import { z } from "zod";
/**
* Environment variable schema with Zod validation.
* Variables are validated at module load time.
*/
const envSchema = z.object({
// Required variables
EXPO_PUBLIC_API_URL: z.string().url(),
EXPO_PUBLIC_APP_ENV: z.enum(["development", "staging", "production"]),
// Optional variables with defaults
EXPO_PUBLIC_SENTRY_DSN: z.string().optional(),
EXPO_PUBLIC_FEATURE_FLAG: z
.string()
.transform(v => v === "true")
.default("false"),
});
/**
* Validated environment configuration.
* Throws at module load if validation fails.
*/
export const env = envSchema.parse(process.env);
/**
* Type-safe environment configuration.
*/
export type Env = z.infer<typeof envSchema>;
import { env } from "@/lib/env";
// Full autocomplete and type safety
const apiUrl = env.EXPO_PUBLIC_API_URL; // string
const isDev = env.EXPO_PUBLIC_APP_ENV === "development"; // boolean
app.config.ts)For variables needed during the build process, validate in app.config.ts:
// app.config.ts
const { z } = require("zod");
const buildEnvSchema = z.object({
EXPO_PUBLIC_API_URL: z.string().url(),
EXPO_PUBLIC_APP_ENV: z.enum(["development", "staging", "production"]),
// Build-only secrets (not exposed to client)
SENTRY_AUTH_TOKEN: z.string().optional(),
});
// Throws during `eas build` if invalid
const env = buildEnvSchema.parse(process.env);
module.exports = {
name: "MyApp",
slug: "my-app",
extra: {
apiUrl: env.EXPO_PUBLIC_API_URL,
appEnv: env.EXPO_PUBLIC_APP_ENV,
},
};
Environment variables arrive in process.env from multiple sources:
| Source | When Available | How Set |
|---|---|---|
.env.local | Local dev | Expo CLI auto-loads |
.env.development | Local dev | Copied to .env.local via npm script |
eas.json env | EAS Build | build.production.env section |
| EAS Secrets | EAS Build | eas secret:create |
| CI Variables | CI builds | GitHub Actions / GitLab CI settings |
The Zod pattern validates process.env directly - it doesn't care how variables got there.
jest.setup.local.ts)// Mock the env module for all tests
jest.mock("@/lib/env", () => ({
env: {
EXPO_PUBLIC_API_URL: "https://test.example.com",
EXPO_PUBLIC_APP_ENV: "development",
EXPO_PUBLIC_SENTRY_DSN: undefined,
EXPO_PUBLIC_FEATURE_FLAG: false,
},
}));
import { env } from "@/lib/env";
jest.mock("@/lib/env");
describe("ProductionFeature", () => {
beforeEach(() => {
(env as jest.Mocked<typeof env>).EXPO_PUBLIC_APP_ENV = "production";
});
it("should behave differently in production", () => {
// Test production-specific behavior
});
});
This pattern is enforced by ESLint's no-restricted-syntax rule in eslint.config.mjs:
"no-restricted-syntax": [
"error",
{
selector: "MemberExpression[object.name='process'][property.name='env']",
message: "Direct process.env access is forbidden. Import { env } from '@/lib/env' instead.",
},
],
Exceptions (files allowed to use process.env):
lib/env.ts - The env validation module itselfapp.config.ts - Expo build configcodegen.ts - GraphQL codegen configplaywright.config.ts - E2E test configlighthouserc.js - Lighthouse CI configVariables without this prefix are not available in client code:
// CORRECT - available in client
EXPO_PUBLIC_API_URL=https://api.example.com
// INCORRECT - only available at build time
API_URL=https://api.example.com
Always use the validated env object:
// CORRECT - type-safe, validated
import { env } from "@/lib/env";
const url = env.EXPO_PUBLIC_API_URL;
// INCORRECT - untyped, unvalidated
const url = process.env.EXPO_PUBLIC_API_URL;
Validation happens at module load. If a required variable is missing, the app fails immediately with a clear error rather than at runtime.
Environment variables are always strings. Use Zod transforms:
const envSchema = z.object({
// Boolean from string
EXPO_PUBLIC_DEBUG: z
.string()
.transform(v => v === "true")
.default("false"),
// Number from string
EXPO_PUBLIC_TIMEOUT_MS: z
.string()
.transform(v => parseInt(v, 10))
.default("5000"),
// Array from comma-separated string
EXPO_PUBLIC_ALLOWED_HOSTS: z
.string()
.transform(v => v.split(",").map(s => s.trim()))
.default(""),
});
Keep sensitive build-time variables out of the client schema:
// Client variables (embedded in JS bundle)
const clientSchema = z.object({
EXPO_PUBLIC_API_URL: z.string().url(),
});
// Build-only variables (NOT in bundle)
const buildSchema = z.object({
SENTRY_AUTH_TOKEN: z.string(),
EAS_PROJECT_ID: z.string(),
});
src/
lib/
env.ts # Main env schema and exports
app.config.ts # Build-time validation (if needed)
.env.localhost # Local development (git-ignored)
.env.development # Development environment
.env.staging # Staging environment
.env.production # Production environment
For comprehensive patterns, transforms, and testing examples:
// WRONG - untyped, could be undefined
const Component = () => {
const url = process.env.EXPO_PUBLIC_API_URL;
// url is string | undefined, no validation
};
// CORRECT - validated and typed
import { env } from "@/lib/env";
const Component = () => {
const url = env.EXPO_PUBLIC_API_URL;
// url is string, guaranteed to be valid URL
};
// WRONG - skipping validation
export const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:3000";
// CORRECT - always validate
const envSchema = z.object({
EXPO_PUBLIC_API_URL: z.string().url().default("http://localhost:3000"),
});
export const { EXPO_PUBLIC_API_URL: API_URL } = envSchema.parse(process.env);
// WRONG - secrets exposed in client bundle
EXPO_PUBLIC_API_SECRET=super-secret-key
// CORRECT - secrets only at build time, passed securely
SENTRY_AUTH_TOKEN=secret # Build-only, not in bundle
When adding or modifying environment variables:
EXPO_PUBLIC_ (if needed in client code)src/lib/env.tsjest.setup.local.ts.env.example or .env.development.env files