obytes / react-native-template-obytes

📱 A template for your next React Native project: Expo, PNPM, TypeScript, TailwindCSS, Husky, EAS, GitHub Actions, Env Vars, expo-router, react-query, react-hook-form.
https://starter.obytes.com
MIT License
2.11k stars 314 forks source link

You attempted to import the Node standard library module "path" from "env.js" #310

Closed iamhaaamed closed 2 months ago

iamhaaamed commented 4 months ago

Summary:

when trying to run the project i have this error:

You attempted to import the Node standard library module "path" from "env.js".

this is my env.js file:

/*
 * Env file to load and validate env variables
 * Be cautious; this file should not be imported into your source folder.
 * We split the env variables into two parts:
 * 1. Client variables: These variables are used in the client-side code (src folder).
 * 2. Build-time variables: These variables are used in the build process (app.config.ts file).
 * Import this file into the `app.config.ts` file to use environment variables during the build process. The client variables can then be passed to the client-side using the extra field in the `app.config.ts` file.
 * To access the client environment variables in your `src` folder, you can import them from `@env`. For example: `import Env from '@env'`.
 */
/**
 * 1st part: Import packages and Load your env variables
 * we use dotenv to load the correct variables from the .env file based on the APP_ENV variable (default is development)
 * APP_ENV is passed as an inline variable while executing the command, for example: APP_ENV=staging pnpm build:android
 */
const z = require('zod');

const packageJSON = require('./package.json');
const path = require('path');
const APP_ENV = process.env.APP_ENV ?? 'development';
const envPath = path.resolve(__dirname, `.env.${APP_ENV}`);

require('dotenv').config({
  path: envPath,
});

/**
 * 2nd part: Define some static variables for the app
 * Such as: bundle id, package name, app name.
 *
 * You can add them to the .env file but we think it's better to keep them here as as we use prefix to generate this values based on the APP_ENV
 * for example: if the APP_ENV is staging, the bundle id will be com.nordiccar.mypartslocator.staging
 */

// TODO: Replace these values with your own

const BUNDLE_ID = 'com.xxx'; // ios bundle id
const PACKAGE = 'com.xxx'; // android package name
const NAME = 'XXX'; // app name
const EXPO_ACCOUNT_OWNER = 'xxx'; // expo account owner
const EAS_PROJECT_ID = '7f2d8c22-xxxxxxxxxx'; // eas project id
const SCHEME = 'XXX'; // app scheme

/**
 * We declare a function withEnvSuffix that will add a suffix to the variable name based on the APP_ENV
 * Add a suffix to variable env based on APP_ENV
 * @param {string} name
 * @returns  {string}
 */

const withEnvSuffix = (name) => {
  return APP_ENV === 'production' ? name : `${name}.${APP_ENV}`;
};

/**
 * 2nd part: Define your env variables schema
 * we use zod to define our env variables schema
 *
 * we split the env variables into two parts:
 *    1. client: These variables are used in the client-side code (`src` folder).
 *    2. buildTime: These variables are used in the build process (app.config.ts file). You can think of them as server-side variables.
 *
 * Main rules:
 *    1. If you need your variable on the client-side, you should add it to the client schema; otherwise, you should add it to the buildTime schema.
 *    2. Whenever you want to add a new variable, you should add it to the correct schema based on the previous rule, then you should add it to the corresponding object (_clientEnv or _buildTimeEnv).
 *
 * Note: `z.string()` means that the variable exists and can be an empty string, but not `undefined`.
 * If you want to make the variable required, you should use `z.string().min(1)` instead.
 * Read more about zod here: https://zod.dev/?id=strings
 *
 */

const client = z.object({
  APP_ENV: z.enum(['development', 'staging', 'production']),
  NAME: z.string(),
  SCHEME: z.string(),
  BUNDLE_ID: z.string(),
  PACKAGE: z.string(),
  VERSION: z.string(),

  // ADD YOUR CLIENT ENV VARS HERE
  API_URL: z.string(),
  WS_HOST: z.string(),
  PLATE_RECOGNIZER_TOKEN: z.string(),
  IMAGE_BASE_URL: z.string(),
});

const buildTime = z.object({
  EXPO_ACCOUNT_OWNER: z.string(),
  EAS_PROJECT_ID: z.string(),
  // ADD YOUR BUILD TIME ENV VARS HERE
  SECRET_KEY: z.string(),
});

/**
 * @type {Record<keyof z.infer<typeof client> , unknown>}
 */
const _clientEnv = {
  APP_ENV,
  NAME: NAME,
  SCHEME: SCHEME,
  BUNDLE_ID: withEnvSuffix(BUNDLE_ID),
  PACKAGE: withEnvSuffix(PACKAGE),
  VERSION: packageJSON.version,

  // ADD YOUR ENV VARS HERE TOO
  API_URL: process.env.API_URL,
  WS_HOST: process.env.WS_HOST,
  PLATE_RECOGNIZER_TOKEN: process.env.PLATE_RECOGNIZER_TOKEN,

  IMAGE_BASE_URL: process.env.IMAGE_BASE_URL,
};

/**
 * @type {Record<keyof z.infer<typeof buildTime> , unknown>}
 */
const _buildTimeEnv = {
  EXPO_ACCOUNT_OWNER,
  EAS_PROJECT_ID,
  // ADD YOUR ENV VARS HERE TOO
  SECRET_KEY: process.env.SECRET_KEY,
};

/**
 * 3rd part: Merge and Validate your env variables
 * We use zod to validate our env variables based on the schema we defined above
 * If the validation fails we throw an error and log the error to the console with a detailed message about missed variables
 * If the validation passes we export the merged and parsed env variables to be used in the app.config.ts file as well as a ClientEnv object to be used in the client-side code
 **/
const _env = {
  ..._clientEnv,
  ..._buildTimeEnv,
};

const merged = buildTime.merge(client);
const parsed = merged.safeParse(_env);

if (parsed.success === false) {
  console.error(
    '❌ Invalid environment variables:',
    parsed.error.flatten().fieldErrors,

    `\n❌ Missing variables in .env.${APP_ENV} file, Make sure all required variables are defined in the .env.${APP_ENV} file.`,
    `\n💡 Tip: If you recently updated the .env.${APP_ENV} file and the error still persists, try restarting the server with the -cc flag to clear the cache.`
  );
  throw new Error(
    'Invalid environment variables, Check terminal for more details '
  );
}

const Env = parsed.data;
const ClientEnv = client.parse(_clientEnv);

module.exports = {
  Env,
  ClientEnv,
  withEnvSuffix,
};

and this is my app.config.ts:

/* eslint-disable max-lines-per-function */
import type { ConfigContext, ExpoConfig } from '@expo/config';

import { ClientEnv, Env } from './env';

export default ({ config }: ConfigContext): ExpoConfig => ({
  ...config,
  name: Env.NAME,
  description: `${Env.NAME} Mobile App`,
  owner: Env.EXPO_ACCOUNT_OWNER,
  scheme: Env.SCHEME,
  slug: 'mypartslocator',
  version: Env.VERSION.toString(),
  orientation: 'portrait',
  icon: './assets/icon.png',
  userInterfaceStyle: 'automatic',
  // splash: {
  //   image: './assets/splash.png',
  //   resizeMode: 'cover',
  //   backgroundColor: '#2E3C4B',
  // },
  updates: {
    fallbackToCacheTimeout: 0,
  },
  assetBundlePatterns: ['**/*'],
  ios: {
    supportsTablet: true,
    bundleIdentifier: Env.BUNDLE_ID,
    googleServicesFile: './ios/GoogleService-Info.plist',
  },
  experiments: {
    typedRoutes: true,
  },
  android: {
    googleServicesFile: './android/app/google-services.json',
    adaptiveIcon: {
      foregroundImage: './assets/icon',
      backgroundColor: '#FFFFFF',
    },
    package: Env.PACKAGE,
  },
  web: {
    favicon: './assets/favicon.png',
    bundler: 'metro',
  },
  plugins: [
    [
      'expo-font',
      {
        fonts: ['./assets/fonts/Urbanist.ttf'],
      },
    ],
    'expo-localization',
    'expo-router',
    '@react-native-firebase/app',
    '@react-native-firebase/crashlytics',
    [
      'expo-build-properties',
      {
        android: {
          kotlinVersion: '1.7.22', // this is for softinput package
        },
        ios: {
          useFrameworks: 'static',
        },
      },
    ],
    [
      'app-icon-badge',
      {
        enabled: Env.APP_ENV !== 'production',
        badges: [
          {
            text: Env.APP_ENV,
            type: 'banner',
            color: 'white',
          },
          {
            text: Env.VERSION.toString(),
            type: 'ribbon',
            color: 'white',
          },
        ],
      },
    ],
  ],
  extra: {
    ...ClientEnv,
    eas: {
      projectId: Env.EAS_PROJECT_ID,
    },
  },
});
thozh commented 3 months ago

DId you find a solution ?

iamhaaamed commented 3 months ago

DId you find a solution ?

No, not yet

Cyberlane commented 3 months ago

@iamhaaamed - what does your babel config look like?

iamhaaamed commented 3 months ago

@iamhaaamed - what does your babel config look like?

This is my babel.config file:

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      ['babel-preset-expo', { jsxImportSource: 'nativewind' }],
      'nativewind/babel',
    ],
    plugins: [
      [
        'module-resolver',
        {
          root: ['./'],
          alias: {
            '@': './src',
            '@env': './src/core/env.js',
          },
          extensions: [
            '.ios.ts',
            '.android.ts',
            '.ts',
            '.ios.tsx',
            '.android.tsx',
            '.tsx',
            '.jsx',
            '.js',
            '.json',
          ],
        },
      ],
      'react-native-reanimated/plugin',
    ],
  };
};
Cyberlane commented 3 months ago

I had this exact issue, so I am trying to remember which thing was incorrect...

Do you by chance have your package.json configured to be a module?

iamhaaamed commented 3 months ago

I had this exact issue, so I am trying to remember which thing was incorrect...

Do you by chance have your package.json configured to be a module?

No, my package.json is not configured to be a module.

This is my package.json file:

{
  "name": "mypartslocator",
  "version": "0.0.1",
  "private": true,
  "main": "expo-router/entry",
  "scripts": {
    "start": "cross-env EXPO_NO_DOTENV=1 expo start",
    "prebuild": "cross-env EXPO_NO_DOTENV=1 pnpm expo prebuild",
    "android": "cross-env EXPO_NO_DOTENV=1 expo run:android",
    "ios": "cross-env EXPO_NO_DOTENV=1 expo run:ios",
    "doctor": "npx expo-doctor@latest",
    "preinstall": "npx only-allow pnpm",
    "start:staging": "cross-env APP_ENV=staging pnpm run start",
    "prebuild:staging": "cross-env APP_ENV=staging pnpm run prebuild",
    "android:staging": "cross-env APP_ENV=staging pnpm run android",
    "ios:staging": "cross-env APP_ENV=staging pnpm run ios",
    "start:production": "cross-env APP_ENV=production pnpm run start",
    "prebuild:production": "cross-env APP_ENV=production pnpm run prebuild",
    "android:production": "cross-env APP_ENV=production pnpm run android",
    "ios:production": "cross-env APP_ENV=production pnpm run ios",
    "build:development:ios": "cross-env APP_ENV=development EXPO_NO_DOTENV=1 eas build --profile development --platform ios",
    "build:development:android": "cross-env APP_ENV=development EXPO_NO_DOTENV=1 eas build --profile development --platform android ",
    "build:staging:ios": "cross-env APP_ENV=staging EXPO_NO_DOTENV=1 eas build --profile staging --platform ios",
    "build:staging:android": "cross-env APP_ENV=staging EXPO_NO_DOTENV=1 eas build --profile staging --platform android ",
    "build:production:ios": "cross-env APP_ENV=production EXPO_NO_DOTENV=1 eas build --profile production --platform ios",
    "build:production:android": "cross-env APP_ENV=production EXPO_NO_DOTENV=1 eas build --profile production --platform android ",
    "postinstall": "husky install",
    "app-release": "cross-env SKIP_BRANCH_PROTECTION=true np --no-publish --no-cleanup --no-release-draft",
    "version": "pnpm run prebuild && git add .",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
    "type-check": "tsc  --noemit",
    "lint:translations": "eslint ./src/translations/ --fix --ext .json  ",
    "test": "jest",
    "test:ci": "pnpm run test --coverage",
    "test:watch": "pnpm run test --watch",
    "install-maestro": "curl -Ls 'https://get.maestro.mobile.dev' | bash",
    "e2e-test": "maestro test .maestro/ -e APP_ID=com.obytes.development"
  },
  "dependencies": {
    "@expo/metro-runtime": "^3.1.3",
    "@gorhom/bottom-sheet": "^4.6.0",
    "@hookform/resolvers": "^2.9.11",
    "@react-native-community/datetimepicker": "^7.7.0",
    "@react-native-community/netinfo": "^11.3.1",
    "@react-native-community/push-notification-ios": "^1.11.0",
    "@react-native-firebase/analytics": "^19.2.2",
    "@react-native-firebase/app": "^19.2.2",
    "@react-native-firebase/crashlytics": "^19.2.2",
    "@react-native-firebase/dynamic-links": "^19.2.2",
    "@react-native-firebase/messaging": "^19.2.2",
    "@react-navigation/bottom-tabs": "^6.5.11",
    "@react-navigation/native": "^6.1.9",
    "@react-navigation/native-stack": "^6.9.17",
    "@shopify/flash-list": "1.6.3",
    "@tanstack/react-query": "^4.36.1",
    "app-icon-badge": "^0.0.15",
    "axios": "^1.6.5",
    "dayjs": "^1.11.10",
    "expo": "~50.0.17",
    "expo-build-properties": "~0.11.1",
    "expo-constants": "~15.4.6",
    "expo-dev-client": "~3.3.11",
    "expo-font": "~11.10.3",
    "expo-image": "~1.10.6",
    "expo-linking": "~6.2.2",
    "expo-localization": "~14.8.4",
    "expo-router": "~3.4.8",
    "expo-splash-screen": "0.26.4",
    "expo-status-bar": "~1.11.1",
    "expo-system-ui": "~2.9.4",
    "i18next": "^22.5.1",
    "lodash.memoize": "^4.1.2",
    "mime": "^4.0.1",
    "moti": "^0.28.1",
    "nativewind": "^4.0.36",
    "navigation-utilities": "link:@/navigation/navigation-utilities",
    "pusher-js": "8.4.0-rc2",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-error-boundary": "^3.1.4",
    "react-hook-form": "^7.49.3",
    "react-i18next": "^12.3.1",
    "react-icomoon": "^2.5.7",
    "react-native": "0.73.6",
    "react-native-audio-recorder-player": "^3.6.7",
    "react-native-avoid-softinput": "^4.0.2",
    "react-native-biometrics": "^3.0.1",
    "react-native-curved-bottom-bar": "^3.2.7",
    "react-native-device-info": "^10.13.2",
    "react-native-flash-message": "^0.4.2",
    "react-native-gesture-handler": "~2.14.1",
    "react-native-image-crop-picker": "^0.40.3",
    "react-native-mmkv": "2.6.3",
    "react-native-modal-datetime-picker": "^17.1.0",
    "react-native-push-notification": "^8.1.1",
    "react-native-reanimated": "~3.6.2",
    "react-native-restart": "0.0.27",
    "react-native-safe-area-context": "4.8.2",
    "react-native-screens": "~3.29.0",
    "react-native-svg": "14.1.0",
    "react-native-swiper": "^1.6.0",
    "react-native-web": "~0.19.10",
    "react-query-kit": "^3.2.0",
    "tailwind-variants": "^0.1.20",
    "zod": "^3.22.4",
    "zustand": "^4.5.0"
  },
  "devDependencies": {
    "@babel/core": "^7.23.7",
    "@commitlint/cli": "^17.8.1",
    "@commitlint/config-conventional": "^17.8.1",
    "@dev-plugins/react-navigation": "^0.0.5",
    "@dev-plugins/react-query": "^0.0.5",
    "@expo/config": "~8.5.4",
    "@react-native-community/eslint-config": "^3.2.0",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react-native": "^12.4.3",
    "@types/i18n-js": "^3.8.9",
    "@types/jest": "^29.5.11",
    "@types/lodash.memoize": "^4.1.9",
    "@types/react": "~18.2.48",
    "@types/react-native-push-notification": "^8.1.4",
    "@types/react-test-renderer": "^18.0.7",
    "@typescript-eslint/eslint-plugin": "^5.62.0",
    "@typescript-eslint/parser": "^5.62.0",
    "babel-plugin-module-resolver": "^5.0.0",
    "cross-env": "^7.0.3",
    "dotenv": "^16.3.2",
    "eslint": "^8.57.0",
    "eslint-plugin-i18n-json": "^4.0.0",
    "eslint-plugin-simple-import-sort": "^10.0.0",
    "eslint-plugin-tailwindcss": "^3.10.1",
    "eslint-plugin-testing-library": "^6.2.0",
    "eslint-plugin-unicorn": "^46.0.1",
    "eslint-plugin-unused-imports": "^2.0.0",
    "husky": "^8.0.3",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "jest-expo": "~50.0.4",
    "jest-junit": "^16.0.0",
    "lint-staged": "^13.3.0",
    "metro-babel-register": "^0.73.10",
    "np": "^7.7.0",
    "prettier": "^2.8.8",
    "react-test-renderer": "^18.2.0",
    "tailwindcss": "^3.3.2",
    "ts-jest": "^29.1.1",
    "typescript": "^5.3.3"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/user/repo-name.git"
  },
  "packageManager": "pnpm@8.6.5",
  "osMetadata": {
    "initVersion": "5.3.0"
  }
}
yjose commented 2 months ago

Mostly you are importing env.js file inside src folder. make sure you are importing env vars from '@env' and not directly from './env.js' file