Skip to content

Commit 208f98b

Browse files
committed
initial support for expo config plugins
1 parent 403dd71 commit 208f98b

21 files changed

+704
-8
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# plugin-expo-config-plugins
2+
3+
This library was generated with [Nx](https://nx.dev).
4+
5+
## Building
6+
7+
Run `nx build plugin-expo-config-plugins` to build the library.
8+
9+
## Running unit tests
10+
11+
Run `nx test plugin-expo-config-plugins` to execute the unit tests via [Vitest](https://vitest.dev/).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import baseConfig from '../../eslint.config.js';
2+
3+
export default baseConfig;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "@rnef/plugin-expo-config-plugins",
3+
"version": "0.2.0",
4+
"type": "module",
5+
"exports": {
6+
"types": "./dist/src/index.d.ts",
7+
"import": "./dist/src/index.js"
8+
},
9+
"files": [
10+
"dist",
11+
"src",
12+
"template"
13+
],
14+
"scripts": {
15+
"publish:npm": "npm publish --access restricted",
16+
"publish:verdaccio": "npm publish --registry http://localhost:4873 --userconfig ../../.npmrc"
17+
},
18+
"dependencies": {
19+
"@rnef/tools": "^0.2.0",
20+
"@expo/config-plugins": "^9.0.15",
21+
"tslib": "^2.3.0"
22+
},
23+
"devDependencies": {
24+
"@rnef/config": "^0.2.0"
25+
},
26+
"publishConfig": {
27+
"access": "restricted"
28+
}
29+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "plugin-expo-config-plugins",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "packages/plugin-expo-config-plugins/src",
5+
"projectType": "library",
6+
"tags": [],
7+
"targets": {
8+
"build": {
9+
"executor": "@nx/js:tsc"
10+
}
11+
}
12+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { expect, test } from 'vitest';
2+
import { pluginExpoConfigPlugins } from '../lib/pluginExpoConfigPlugins.js';
3+
4+
const pluginApi = {
5+
registerCommand: vi.fn(),
6+
getProjectRoot: vi.fn(),
7+
getReactNativePath: vi.fn(),
8+
getReactNativeVersion: vi.fn(),
9+
getPlatforms: vi.fn(),
10+
};
11+
12+
test('plugin is called with correct arguments and returns its name and description', () => {
13+
const plugin = pluginExpoConfigPlugins()(pluginApi);
14+
15+
expect(plugin).toMatchObject({
16+
name: 'plugin-expo-config-plugins',
17+
description: 'RNEF plugin for Expo Config Plugins.',
18+
});
19+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/pluginExpoConfigPlugins.js';
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
3+
// '@expo/config-plugins' is a CommonJS module, which may not support all
4+
// module.exports as named exports. CommonJS modules can always be imported via
5+
// the default export.
6+
import ExpoConfigPlugins from "@expo/config-plugins";
7+
8+
const { BaseMods, evalModsAsync, withPlugins } = ExpoConfigPlugins;
9+
10+
export { BaseMods, evalModsAsync, withPlugins };
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
import * as fs from 'node:fs/promises';
3+
import { withPlugins } from './ExpoConfigPlugins.js';
4+
import { compileModsAsync } from './plugins/modCompiler.js';
5+
import { withInternal } from './plugins/withInternal.js';
6+
import type { ProjectInfo } from './types.js';
7+
8+
/**
9+
* Applies config plugins.
10+
*/
11+
export async function applyConfigPlugins({
12+
appJsonPath,
13+
...info
14+
}: ProjectInfo) {
15+
if (!appJsonPath) {
16+
return;
17+
}
18+
19+
const content = await fs.readFile(appJsonPath, { encoding: 'utf-8' });
20+
const { plugins, ...config } = JSON.parse(content);
21+
if (!Array.isArray(plugins) || plugins.length === 0) {
22+
return;
23+
}
24+
console.log(withPlugins(withInternal(config, info), plugins));
25+
return compileModsAsync(
26+
withPlugins(withInternal(config, info), plugins),
27+
info
28+
);
29+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import type { PluginApi, PluginOutput } from '@rnef/config';
4+
import { applyConfigPlugins } from './apply.js';
5+
import type { ProjectInfo } from './types.js';
6+
7+
type ConfigPluginsArgs = {
8+
platforms: string[];
9+
};
10+
11+
export const pluginExpoConfigPlugins =
12+
() =>
13+
(api: PluginApi): PluginOutput => {
14+
api.registerCommand({
15+
name: 'apply-config-plugins',
16+
description: 'Starts Metro dev server.',
17+
action: async (args: ConfigPluginsArgs) => {
18+
const packageJsonPath = path.join(api.getProjectRoot(), 'package.json');
19+
const content = fs.readFileSync(packageJsonPath, 'utf-8');
20+
21+
if (!content.includes('"@expo/config-plugins"')) {
22+
return;
23+
}
24+
25+
applyConfigPlugins({
26+
projectRoot: api.getProjectRoot(),
27+
platforms: args.platforms as ProjectInfo['platforms'],
28+
packageJsonPath,
29+
appJsonPath: path.join(api.getProjectRoot(), 'app.json'),
30+
});
31+
},
32+
options: [
33+
{
34+
name: '--platforms <list>',
35+
description: 'Platforms to apply config plugins',
36+
parse: (val: string) => val.split(','),
37+
},
38+
],
39+
});
40+
41+
return {
42+
name: 'plugin-expo-config-plugins',
43+
description: 'RNEF plugin for Expo Config Plugins.',
44+
};
45+
};
46+
47+
export default pluginExpoConfigPlugins;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { BaseMods } from '../ExpoConfigPlugins.js';
2+
import { makeNullProvider } from '../provider.js';
3+
import type { CustomModProvider, IosModFileProviders } from '../types.js';
4+
5+
export function createModFileProviders(
6+
modifyFilePath: CustomModProvider
7+
): IosModFileProviders {
8+
const nullProvider = makeNullProvider();
9+
10+
// https://github.com/expo/expo/blob/sdk-51/packages/%40expo/config-plugins/src/plugins/withIosBaseMods.ts
11+
const expoProviders = BaseMods.getIosModFileProviders();
12+
13+
const defaultProviders: IosModFileProviders = {
14+
dangerous: expoProviders.dangerous,
15+
finalized: expoProviders.finalized,
16+
appDelegate: modifyFilePath(
17+
expoProviders.appDelegate,
18+
// @todo rewrite template finding and copying logic
19+
'App76/AppDelegate.swift'
20+
),
21+
// @ts-expect-error todo fix
22+
expoPlist: nullProvider,
23+
xcodeproj: modifyFilePath(
24+
expoProviders.xcodeproj,
25+
// @todo rewrite template finding and copying logic
26+
'App76.xcodeproj/project.pbxproj'
27+
),
28+
infoPlist: modifyFilePath(expoProviders.infoPlist, 'Info.plist'),
29+
// @ts-expect-error todo fix
30+
entitlements: nullProvider,
31+
// @ts-expect-error todo fix
32+
podfile: makeNullProvider({
33+
path: '',
34+
language: /** @type {const} */ 'rb',
35+
contents: '',
36+
}),
37+
// @ts-expect-error todo fix
38+
podfileProperties: makeNullProvider(),
39+
};
40+
41+
return defaultProviders;
42+
}

0 commit comments

Comments
 (0)