Skip to content

Instantly share code, notes, and snippets.

@ezilox
Last active September 13, 2021 09:34
Show Gist options
  • Save ezilox/7255f179652daff811fd9c7d02f8eb29 to your computer and use it in GitHub Desktop.
Save ezilox/7255f179652daff811fd9c7d02f8eb29 to your computer and use it in GitHub Desktop.
import { withProjectBuildGradle } from '@expo/config-plugins';
const withFFmpegPackage = (config) => {
return withProjectBuildGradle(config, async config => {
const newContents = config.modResults.contents.replace(
'targetSdkVersion = 30\n',
'targetSdkVersion = 30\nreactNativeFFmpegPackage = "min-gpl-lts"\n'
);
config.modResults.contents = newContents
console.log(config);
return config;
});
};
@wodin
Copy link

wodin commented Sep 13, 2021

Hi. Coincidentally, I was also trying to get this working yesterday. These are also my first config plugins, so I am sure they can be improved.

By the way, how are you referencing your plugins? When I initially tried using import and export I got errors.

Anyway, here's what I managed to get working:

plugins/withMinAndroidSdkVersion.js:

const {
  withProjectBuildGradle,
  withPlugins,
} = require('@expo/config-plugins');

function setMinSdkVersion(buildGradle, minVersion) {
  const regexpMinSdkVersion = /\bminSdkVersion\s*=\s*(\d+)/;
  const match = buildGradle.match(regexpMinSdkVersion);

  if (match) {
    const version = parseInt(match[1], 10);

    if (version < minVersion) {
      buildGradle = buildGradle.replace(
        /\bminSdkVersion\s*=\s*\d+/,
        `minSdkVersion = ${minVersion}`
      );
    } else {
      console.warn(`WARN: minSdkVersion is already >= ${version}`);
    }
  }

  return buildGradle;
}

const withMinSdkVersion = (config, { minSdkVersion } = {}) => {
  return withProjectBuildGradle(config, (config) => {
    if (config.modResults.language === 'groovy') {
      config.modResults.contents = setMinSdkVersion(
        config.modResults.contents,
        minSdkVersion
      );
    } else {
      throw new Error(
        "Can't set minSdkVersion in the project build.gradle, because it's not groovy"
      );
    }
    return config;
  });
};

module.exports = (config, props) =>
  withPlugins(config, [
    [withMinSdkVersion, props],
  ]);

plugins/withFfmpegPackage.js:

const {
  withAppBuildGradle,
  withProjectBuildGradle,
  withPlugins,
} = require('@expo/config-plugins');

function setFfmpegPackage(buildGradle, packageName) {
  const regexpReactNativeFfmpegPackage =
    /\breactNativeFFmpegPackage\s*=\s*"([^"]*)"/;
  const match = buildGradle.match(regexpReactNativeFfmpegPackage);

  if (match) {
    return buildGradle.replace(
      regexpReactNativeFfmpegPackage,
      `reactNativeFFmpegPackage = "${packageName}"`
    );
  }

  // Set the ffmpeg native package
  return buildGradle.replace(
    /\bext\s?{/,
    `ext {
        reactNativeFFmpegPackage = "${packageName}"`
  );
}

function addPickFirst(buildGradle, paths) {
  const regexpPackagingOptions = /\bpackagingOptions\s*{[^}]*}/;
  const packagingOptionsMatch = buildGradle.match(
    regexpPackagingOptions
  );

  let bodyLines = [];
  paths.forEach((path) => {
    bodyLines.push(`        pickFirst '${path}'`);
  });
  let body = bodyLines.join('\n');

  if (packagingOptionsMatch) {
    console.warn(
      'WARN: Replacing packagingOptions in app build.gradle'
    );
    return buildGradle.replace(
      regexpPackagingOptions,
      `packagingOptions {
${body}
    }`
    );
  }

  const regexpAndroid = /\nandroid\s*{/;
  const androidMatch = buildGradle.match(regexpAndroid);

  if (androidMatch) {
    return buildGradle.replace(
      regexpAndroid,
      `
android {
    packagingOptions {
${body}
    }`
    );
  }

  throw new Error('Could not find where to add packagingOptions');
}

const withFfmpegPackage = (
  config,
  { ffmpegPackage: packageName = 'min' } = {}
) => {
  return withProjectBuildGradle(config, (config) => {
    if (config.modResults.language === 'groovy') {
      config.modResults.contents = setFfmpegPackage(
        config.modResults.contents,
        packageName
      );
    } else {
      throw new Error(
        "Can't set ffmpeg package name in the project build.gradle, because it's not groovy"
      );
    }
    return config;
  });
};

const withPickFirstFbjni = (config, props = {}) => {
  return withAppBuildGradle(config, (config) => {
    if (config.modResults.language === 'groovy') {
      config.modResults.contents = addPickFirst(
        config.modResults.contents,
        ['lib/**/libfbjni.so', 'lib/**/libc++_shared.so']
      );
    } else {
      throw new Error(
        "Can't add pickFirst '**/libfbjni.so' because app build.grandle is not groovy"
      );
    }
    return config;
  });
};

module.exports = (config, props) =>
  withPlugins(config, [
    [withFfmpegPackage, props],
    [withPickFirstFbjni, props],
  ]);

app.json:

    "plugins": [
      [
        "./plugins/withMinAndroidSdkVersion.js",
        {
          "minSdkVersion": 24
        }
      ],
      [
        "./plugins/withFfmpegPackage.js",
        {
          "ffmpegPackage": "full"
        }
      ]
    ]

I later created this one because I saw another forum post that needed these for various other dependencies:

plugins/withPickFirst.js:

const { withAppBuildGradle, withPlugins } = require('@expo/config-plugins');

function addPickFirst(buildGradle, paths) {
  const regexpPackagingOptions = /\bpackagingOptions\s*{[^}]*}/;
  const packagingOptionsMatch = buildGradle.match(regexpPackagingOptions);

  let bodyLines = [];
  paths.forEach((path) => {
    bodyLines.push(`        pickFirst '${path}'`);
  });
  let body = bodyLines.join('\n');

  if (packagingOptionsMatch) {
    console.warn(
      'WARN: withPickFirst: Replacing packagingOptions in app build.gradle'
    );
    return buildGradle.replace(
      regexpPackagingOptions,
      `packagingOptions {
${body}
    }`
    );
  }

  const regexpAndroid = /\nandroid\s*{/;
  const androidMatch = buildGradle.match(regexpAndroid);

  if (androidMatch) {
    return buildGradle.replace(
      regexpAndroid,
      `
android {
    packagingOptions {
${body}
    }`
    );
  }

  throw new Error(
    'withPickFirst: Could not find where to add packagingOptions'
  );
}

const withPickFirst = (config, props = {}) => {
  if (!props.paths) {
    throw new Error('withPickFirst: No paths specified!');
  }
  return withAppBuildGradle(config, (config) => {
    if (config.modResults.language === 'groovy') {
      config.modResults.contents = addPickFirst(
        config.modResults.contents,
        props.paths
      );
    } else {
      throw new Error(
        "withPickFirst: Can't add pickFirst(s) because app build.grandle is not groovy"
      );
    }
    return config;
  });
};

module.exports = (config, props) =>
  withPlugins(config, [[withPickFirst, props]]);

and you can use that from app.json as follows:

    "plugins": [
      [
        "./plugins/withPickFirst.js",
        {
          "paths": ["lib/**/libfbjni.so", "lib/**/libc++_shared.so"]
        }
      ]
    ]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment