Repository: expo/expo
File: packages/expo-build-properties/src/pluginConfig.ts (TypeScript)

Code before:
}

/**
 * @ignore
 */
export function validateConfig(config: unknown, projectRoot?: string): PluginConfigType {
  fixupDeprecatedEnableProguardInReleaseBuilds(config);
  validate(schema, config);
  maybeThrowInvalidVersions(config);

  if (
    config.android?.enableShrinkResourcesInReleaseBuilds === true &&
    config.android?.enableMinifyInReleaseBuilds !== true
  ) {
    throw new Error(
      '`android.enableShrinkResourcesInReleaseBuilds` requires `android.enableMinifyInReleaseBuilds` to be enabled.'
    );
  }

  const androidUseHermesV1 = resolveConfigValue(config, 'android', 'useHermesV1');
  const iosUseHermesV1 = resolveConfigValue(config, 'ios', 'useHermesV1');

  // Hermes v1 requires a specific hermes-compiler version
  if ((androidUseHermesV1 || iosUseHermesV1) && projectRoot) {
    const hermesCompilerVersion = getHermesCompilerVersion(projectRoot);
    if (hermesCompilerVersion !== HERMES_V1_COMPILER_VERSION) {
      throw new Error(
        `\`useHermesV1\` requires setting the hermes-compiler version to ${HERMES_V1_COMPILER_VERSION} through resolutions. ` +
          (hermesCompilerVersion
            ? `Found version "${hermesCompilerVersion}" instead.`
            : 'hermes-compiler dependency not found.')
      );
    }
  }

  // Validate useHermesV1 requires buildReactNativeFromSource for Android
  const androidBuildFromSource =
    resolveConfigValue(config, 'android', 'buildReactNativeFromSource') ??
    config.android?.buildFromSource; // Deprecated fallback
  if (androidUseHermesV1 === true && androidBuildFromSource !== true) {
    throw new Error(
      '`useHermesV1` requires `buildReactNativeFromSource` to be `true` for Android.'
    );
  }

  // Validate useHermesV1 requires buildReactNativeFromSource for iOS
  const iosBuildFromSource = resolveConfigValue(config, 'ios', 'buildReactNativeFromSource');
  if (iosUseHermesV1 === true && iosBuildFromSource !== true) {
    throw new Error('`useHermesV1` requires `buildReactNativeFromSource` to be `true` for iOS.');
  }


Review comment (bug):
We ideally should leave a note or todo comment here to explicitly limit the time this code should stick around, so that when we're coming back to bump the version, it's actually going to be deleted for SDK 56.

We should also handle `hermesCompilerVersion === null` differently here. I'd prefer if the build fails over bailing out unnecessarily here, when the resolution fails for whatever reason 😅 

Code after:

/**
 * @ignore
 */
export function validateConfig(config: unknown, projectRoot?: string): PluginConfigType {
  fixupDeprecatedEnableProguardInReleaseBuilds(config);
  validate(schema, config);
  maybeThrowInvalidVersions(config);

  if (
    config.android?.enableShrinkResourcesInReleaseBuilds === true &&
    config.android?.enableMinifyInReleaseBuilds !== true
  ) {
    throw new Error(
      '`android.enableShrinkResourcesInReleaseBuilds` requires `android.enableMinifyInReleaseBuilds` to be enabled.'
    );
  }

  const androidUseHermesV1 = resolveConfigValue(config, 'android', 'useHermesV1');
  const iosUseHermesV1 = resolveConfigValue(config, 'ios', 'useHermesV1');

  // TODO(gabrieldonadel): Revisit this before releasing SDK 56
  // Hermes v1 requires a specific hermes-compiler version
  if ((androidUseHermesV1 || iosUseHermesV1) && projectRoot) {
    const hermesCompilerVersion = getHermesCompilerVersion(projectRoot);
    if (hermesCompilerVersion && hermesCompilerVersion !== HERMES_V1_COMPILER_VERSION) {
      throw new Error(
        `\`useHermesV1\` requires setting the hermes-compiler version to ${HERMES_V1_COMPILER_VERSION} through resolutions. ` +
          `Found version "${hermesCompilerVersion}" instead.`
      );
    }
  }

  // Validate useHermesV1 requires buildReactNativeFromSource for Android
  const androidBuildFromSource =
    resolveConfigValue(config, 'android', 'buildReactNativeFromSource') ??
    config.android?.buildFromSource; // Deprecated fallback
  if (androidUseHermesV1 === true && androidBuildFromSource !== true) {
    throw new Error(
      '`useHermesV1` requires `buildReactNativeFromSource` to be `true` for Android.'
    );
  }

  // Validate useHermesV1 requires buildReactNativeFromSource for iOS
  const iosBuildFromSource = resolveConfigValue(config, 'ios', 'buildReactNativeFromSource');
  if (iosUseHermesV1 === true && iosBuildFromSource !== true) {
    throw new Error('`useHermesV1` requires `buildReactNativeFromSource` to be `true` for iOS.');
  }

  return config;
}