Skip to content

Commit

Permalink
feat: export babel and metro configs to reduce boilerplate (#600)
Browse files Browse the repository at this point in the history
### Summary

Currently the example app has metro and babel configs setup to work
correctly in the library setup. However, the files are long and verbose
- increasing the maintenance cost as the consumer needs to manually
update them when we make any changes.

So this moves the configs to `react-native-builder-bob` package, so
users can import and use them. They still have the full flexibility to
customize anything they need.

### Test plan

Tested by creating an Expo app and app with native modules.
  • Loading branch information
satya164 committed Jul 26, 2024
1 parent 626b83f commit d6cb1ce
Show file tree
Hide file tree
Showing 11 changed files with 1,081 additions and 153 deletions.
2 changes: 1 addition & 1 deletion packages/create-react-native-library/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import generateExampleApp, {
import { spawn } from './utils/spawn';
import { version } from '../package.json';

const FALLBACK_BOB_VERSION = '0.26.0';
const FALLBACK_BOB_VERSION = '0.28.0';

const BINARIES = [
/(gradlew|\.(jar|keystore|png|jpg|gif))$/,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,6 @@ export default async function generateExampleApp({
});

const PACKAGES_TO_ADD_DEV = {
'babel-plugin-module-resolver': '^5.0.0',
'react-native-builder-bob': `^${bobVersion}`,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,18 @@
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
const escape = require('escape-string-regexp');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const pak = require('../package.json');
const { getDefaultConfig } = require('@react-native/metro-config');
const { getConfig } = require('react-native-builder-bob/metro-config');
const pkg = require('../package.json');

const root = path.resolve(__dirname, '..');
const modules = Object.keys({ ...pak.peerDependencies });

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {
watchFolders: [root],

// We need to make sure that only one version is loaded for peerDependencies
// So we block them at the root, and alias them to the versions in example's node_modules
resolver: {
blacklistRE: exclusionList(
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),

extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name);
return acc;
}, {}),
},

transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);
module.exports = getConfig(getDefaultConfig(__dirname), {
root,
pkg,
project: __dirname,
});
Original file line number Diff line number Diff line change
@@ -1,40 +1,16 @@
const path = require('path');
const pak = require('../package.json');
const { getConfig } = require('react-native-builder-bob/babel-config');
const pkg = require('../package.json');

const root = path.resolve(__dirname, '..');

module.exports = function (api) {
api.cache(true);

return {
overrides: [
{
exclude: path.join(root, 'src'),
presets: ['babel-preset-expo'],
},
{
include: path.join(root, 'src'),
presets: [
[
'module:react-native-builder-bob/babel-preset',
{ modules: 'commonjs' },
],
],
},
{
exclude: /\/node_modules\//,
plugins: [
[
'module-resolver',
{
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
alias: {
[pak.name]: path.join(root, pak.source),
},
},
],
],
},
],
};
return getConfig(
{
presets: ['babel-preset-expo'],
},
{ root, pkg }
);
};
Original file line number Diff line number Diff line change
@@ -1,43 +1,18 @@
const path = require('path');
const escape = require('escape-string-regexp');
const { getDefaultConfig } = require('@expo/metro-config');
const exclusionList = require('metro-config/src/defaults/exclusionList');
const pak = require('../package.json');
const { getConfig } = require('react-native-builder-bob/metro-config');
const pkg = require('../package.json');

const root = path.resolve(__dirname, '..');
const modules = Object.keys({ ...pak.peerDependencies });

const defaultConfig = getDefaultConfig(__dirname);

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
const config = {
...defaultConfig,

projectRoot: __dirname,
watchFolders: [root],

// We need to make sure that only one version is loaded for peerDependencies
// So we block them at the root, and alias them to the versions in example's node_modules
resolver: {
...defaultConfig.resolver,

blacklistRE: exclusionList(
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),

extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(__dirname, 'node_modules', name);
return acc;
}, {}),
},
};

module.exports = config;
module.exports = getConfig(getDefaultConfig(__dirname), {
root,
pkg,
project: __dirname,
});
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
const path = require('path');
const pak = require('../package.json');
const { getConfig } = require('react-native-builder-bob/babel-config');
const pkg = require('../package.json');

const root = path.resolve(__dirname, '..');

module.exports = {
overrides: [
{
exclude: path.join(root, 'src'),
presets: ['module:@react-native/babel-preset'],
},
{
include: path.join(root, 'src'),
presets: [
[
'module:react-native-builder-bob/babel-preset',
{ modules: 'commonjs' },
],
],
},
{
exclude: /\/node_modules\//,
plugins: [
[
'module-resolver',
{
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
alias: {
[pak.name]: path.join(root, pak.source),
},
},
],
],
},
],
};
module.exports = getConfig(
{
presets: ['module:@react-native/babel-preset'],
},
{ root, pkg }
);
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require('path');
const pak = require('../package.json');
const pkg = require('../package.json');
<% if (example === 'test-app') { -%>
const { configureProjects } = require('react-native-test-app');
<% } -%>
Expand All @@ -23,7 +23,7 @@ module.exports = {
},
<% } -%>
dependencies: {
[pak.name]: {
[pkg.name]: {
root: path.join(__dirname, '..'),
},
},
Expand Down
63 changes: 63 additions & 0 deletions packages/react-native-builder-bob/babel-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable import/no-commonjs */

const path = require('path');

/**
* Get Babel configuration for the example project.
* This sets up appropriate presets and plugins for the library.
* It also aliases the library to the source directory.
*
* @param {import('@babel/core').TransformOptions} defaultConfig Default Babel configuration
* @param {object} options Options to customize the configuration
* @param {string} options.root Root directory of the monorepo
* @param {object} options.pkg Content of package.json of the library
* @returns {import('@babel/core').TransformOptions} Babel configuration
*/
const getConfig = (defaultConfig, { root, pkg }) => {
let src;

if (pkg.source.includes('/')) {
const segments = pkg.source.split('/');

if (segments[0] === '.') {
segments.shift();
}

src = segments[0];
}

if (src == null) {
throw new Error(
"Couldn't determine the source directory. Does the 'source' field in your 'package.json' point to a file within a directory?"
);
}

return {
overrides: [
{
...defaultConfig,
exclude: path.join(root, src),
},
{
include: path.join(root, src),
presets: [[require.resolve('./babel-preset'), { modules: 'commonjs' }]],
},
{
exclude: /\/node_modules\//,
plugins: [
[
require.resolve('babel-plugin-module-resolver'),
{
extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
alias: {
[pkg.name]: path.join(root, pkg.source),
},
},
],
],
},
],
};
};

exports.getConfig = getConfig;
54 changes: 54 additions & 0 deletions packages/react-native-builder-bob/metro-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-disable import/no-commonjs */

const path = require('path');
const escape = require('escape-string-regexp');
const exclusionList = require('metro-config/src/defaults/exclusionList');

/**
* Get Metro configuration for the example project.
* This sets up appropriate root and watch folders for the library.
* It also excludes conflicting modules and aliases them to the correct place.
*
* @param {import('metro-config').MetroConfig} defaultConfig Default Metro configuration
* @param {object} options Options to customize the configuration
* @param {string} options.root Root directory of the monorepo
* @param {object} options.pkg Content of package.json of the library
* @param {string} options.project Directory containing the example project
* @returns {import('metro-config').MetroConfig} Metro configuration
*/
const getConfig = (defaultConfig, { root, pkg, project }) => {
const modules = Object.keys({ ...pkg.peerDependencies });

/**
* Metro configuration
* https://facebook.github.io/metro/docs/configuration
*
* @type {import('metro-config').MetroConfig}
*/
return {
...defaultConfig,

projectRoot: project,
watchFolders: [root],

// We need to make sure that only one version is loaded for peerDependencies
// So we block them at the root, and alias them to the versions in example project's node_modules
resolver: {
...defaultConfig.resolver,

blacklistRE: exclusionList(
modules.map(
(m) =>
new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`)
)
),

extraNodeModules: modules.reduce((acc, name) => {
acc[name] = path.join(project, 'node_modules', name);
return acc;
}, {}),
},
};
};

exports.getConfig = getConfig;
7 changes: 6 additions & 1 deletion packages/react-native-builder-bob/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
"files": [
"bin",
"lib",
"babel-preset.js"
"babel-preset.js",
"metro-config.js",
"babel-config.js"
],
"engines": {
"node": ">= 18.0.0"
Expand All @@ -49,16 +51,19 @@
"@babel/preset-flow": "^7.17.12",
"@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12",
"babel-plugin-module-resolver": "^5.0.2",
"browserslist": "^4.20.4",
"cosmiconfig": "^9.0.0",
"cross-spawn": "^7.0.3",
"dedent": "^0.7.0",
"del": "^6.1.1",
"escape-string-regexp": "^4.0.0",
"fs-extra": "^10.1.0",
"glob": "^8.0.3",
"is-git-dirty": "^2.0.1",
"json5": "^2.2.1",
"kleur": "^4.1.4",
"metro-config": "^0.80.9",
"prompts": "^2.4.2",
"which": "^2.0.2",
"yargs": "^17.5.1"
Expand Down
Loading

0 comments on commit d6cb1ce

Please sign in to comment.