@crossbind/plugin-metro 2.0.0-beta.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # @crossbind/plugin-metro
2
+
3
+ ## 2.0.0-beta.50
4
+
5
+ ### Patch Changes
6
+
7
+ - Aligned on the 2.0.0-beta.50 baseline. No source change; the version exists so every package in
8
+ the workspace names the same release.
9
+
10
+ ## 1.0.2
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+ - crossbind@1.0.4
16
+
17
+ ## 1.0.0
18
+
19
+ ### Major Changes
20
+
21
+ - 🚀 first stable release
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+ - crossbind@1.0.0
27
+
28
+ ## 1.0.0-beta.38
29
+
30
+ ### Patch Changes
31
+
32
+ - chore: add initial version of CHANGELOGS files
33
+ - Updated dependencies
34
+ - crossbind@1.0.0-beta.33
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Buğra Sarı and crossbind contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # @crossbind/plugin-metro
2
+ **crossbind Metro plugin**
3
+ A tool for seamless C++ integration with the Metro bundler.
4
+
5
+ <a href="https://www.npmjs.com/package/@crossbind/plugin-metro">
6
+ <img alt="NPM version" src="https://img.shields.io/npm/v/@crossbind/plugin-metro?style=for-the-badge" />
7
+ </a>
8
+ <a href="https://github.com/crossbind/crossbind/blob/main/LICENSE">
9
+ <img alt="License" src="https://img.shields.io/github/license/crossbind/crossbind?style=for-the-badge" />
10
+ </a>
11
+ <a href="https://crossbind.dev/docs/guide/integrate-into-existing-project/react-native">
12
+ <img alt="Docs - React Native" src="https://img.shields.io/badge/Docs_-_React%20Native-20B2AA?style=for-the-badge" />
13
+ </a>
14
+ <a href="https://crossbind.dev/docs/guide/integrate-into-existing-project/expo">
15
+ <img alt="Docs - Expo" src="https://img.shields.io/badge/Docs_-_Expo-20B2AA?style=for-the-badge" />
16
+ </a>
17
+
18
+ ## Integration
19
+ To integrate crossbind into your project using Metro as a bundler, you can utilize the @crossbind/plugin-metro plugin. Start by installing these package with the following command:
20
+
21
+ NPM
22
+ ```sh
23
+ npm install @crossbind/plugin-metro --save-dev
24
+ ```
25
+ or YARN
26
+ ```sh
27
+ yarn add @crossbind/plugin-metro --dev
28
+ ```
29
+ or PNPM
30
+ ```sh
31
+ pnpm add @crossbind/plugin-metro --save-dev
32
+ ```
33
+ or BUN
34
+ ```sh
35
+ bun add @crossbind/plugin-metro --dev
36
+ ```
37
+
38
+ To enable the plugin, modify the metro.config.js file as shown below.
39
+
40
+ **React Native**
41
+ ```diff
42
+ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
43
+ +const CrossbindMetroPlugin = require('@crossbind/plugin-metro');
44
+
45
+ /**
46
+ * Metro configuration
47
+ * https://reactnative.dev/docs/metro
48
+ *
49
+ * @type {import('metro-config').MetroConfig}
50
+ */
51
+ -const config = {};
52
+ +const config = {
53
+ + ...CrossbindMetroPlugin(getDefaultConfig(__dirname)),
54
+ +};
55
+
56
+ module.exports = mergeConfig(getDefaultConfig(__dirname), config);
57
+ ```
58
+
59
+ **Expo**
60
+ ```diff
61
+ // Learn more https://docs.expo.io/guides/customizing-metro
62
+ const { getDefaultConfig } = require('expo/metro-config');
63
+ +const { mergeConfig } = require('metro-config');
64
+ +const CrossbindMetroPlugin = require('@crossbind/plugin-metro');
65
+
66
+ /** @type {import('expo/metro-config').MetroConfig} */
67
+ const config = getDefaultConfig(__dirname);
68
+
69
+ +const newConfig = {
70
+ + ...CrossbindMetroPlugin(config),
71
+ +};
72
+
73
+ -module.exports = config;
74
+ +module.exports = mergeConfig(config, newConfig);
75
+ ```
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@crossbind/plugin-metro",
3
+ "version": "2.0.0-beta.50",
4
+ "description": "crossbind Metro plugin: A tool for seamless C++ integration with the Metro bundler.",
5
+ "homepage": "https://github.com/crossbind/crossbind/tree/main/plugins/metro#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/crossbind/crossbind.git"
9
+ },
10
+ "license": "MIT",
11
+ "main": "src/metro-plugin.cjs",
12
+ "peerDependencies": {
13
+ "metro": "*",
14
+ "@babel/core": "^7.0.0"
15
+ },
16
+ "dependencies": {
17
+ "crossbind": "^2.0.0-beta.50",
18
+ "@babel/runtime": "^7.29.7",
19
+ "@react-native/metro-babel-transformer": "^0.85.3",
20
+ "upath": "^3.0.8"
21
+ },
22
+ "keywords": [
23
+ "crossbind-plugin",
24
+ "metro",
25
+ "react-native",
26
+ "expo",
27
+ "bind",
28
+ "c++",
29
+ "android",
30
+ "ios",
31
+ "mobile"
32
+ ]
33
+ }
@@ -0,0 +1,51 @@
1
+ const path = require('node:path');
2
+
3
+ let getDependFilePathFunc;
4
+ let crossbindState;
5
+ import('crossbind').then(({ getDependFilePath, state }) => {
6
+ getDependFilePathFunc = getDependFilePath;
7
+ crossbindState = state;
8
+ });
9
+
10
+ module.exports = function CrossbindMetroPlugin(defaultConfig) {
11
+ // Generated proxy modules (e.g. a cargo package's lib.rs) originate in package workspaces
12
+ // outside the app's node_modules chain, so their bare imports (react-native, @babel/runtime,
13
+ // @crossbind/core-embind-jsi, ...) must fall back to the app's resolution. extraNodeModules is
14
+ // consulted only when normal resolution fails, so everything else is untouched.
15
+ const projectRoot = defaultConfig.projectRoot || process.cwd();
16
+ const appNodeModules = new Proxy({}, {
17
+ get: (_, name) => {
18
+ try {
19
+ return path.dirname(require.resolve(`${String(name)}/package.json`, { paths: [projectRoot] }));
20
+ } catch (e) {
21
+ return path.join(projectRoot, 'node_modules', String(name));
22
+ }
23
+ },
24
+ });
25
+
26
+ return {
27
+ resetCache: true,
28
+ resolver: {
29
+ extraNodeModules: appNodeModules,
30
+ sourceExts: [...defaultConfig.resolver.sourceExts, ...['h', 'hpp', 'hxx', 'hh'], ...['i'], ...['rs']],
31
+ resolveRequest: (context, moduleName, platform) => {
32
+ // The `crossbind` import above resolves asynchronously; until it lands (and for
33
+ // platforms with no matching target) fall back to Metro's default resolution
34
+ // instead of dereferencing undefined state.
35
+ if (crossbindState && getDependFilePathFunc) {
36
+ const target = crossbindState.targets.find((t) => t.platform === platform);
37
+ const dependFilePath = target && getDependFilePathFunc(moduleName, target);
38
+ if (dependFilePath) {
39
+ return context.resolveRequest(context, dependFilePath, platform);
40
+ }
41
+ }
42
+
43
+ return context.resolveRequest(context, moduleName, platform);
44
+ },
45
+ },
46
+ transformer: {
47
+ ...defaultConfig.transformer,
48
+ babelTransformerPath: require.resolve('./metro-transformer.cjs'),
49
+ },
50
+ };
51
+ };
@@ -0,0 +1,47 @@
1
+
2
+ const upstreamTransformer = (() => {
3
+ try {
4
+ return require('@expo/metro-config/babel-transformer');
5
+ } catch (error) {
6
+ try {
7
+ return require('@react-native/metro-babel-transformer');
8
+ } catch (e) {
9
+ return require('metro-react-native-babel-transformer');
10
+ }
11
+ }
12
+ })();
13
+
14
+ let compilerClassPromise;
15
+ const getCompilerClass = () => {
16
+ if (compilerClassPromise) return compilerClassPromise;
17
+ compilerClassPromise = new Promise((resolve, reject) => {
18
+ import('crossbind').then(({ state, getCrossbindScript, getRustJsScript, createBridgeFile }) => {
19
+ resolve({ state, getCrossbindScript, getRustJsScript, createBridgeFile });
20
+ }).catch((e) => reject(e));
21
+ });
22
+ return compilerClassPromise;
23
+ };
24
+
25
+ module.exports.transform = async ({ src, filename, ...rest }) => {
26
+ const { state, getCrossbindScript, getRustJsScript, createBridgeFile } = await getCompilerClass();
27
+ const headerRegex = new RegExp(`\\.(${state.config.ext.header.join('|')})$`);
28
+ const moduleRegex = new RegExp(`\\.(${state.config.ext.module.join('|')})$`);
29
+ if (headerRegex.test(filename) || moduleRegex.test(filename) || filename.endsWith('.rs')) {
30
+ let target;
31
+ if (rest.options.platform === 'ios') target = state.targets.find((t) => t.platform === 'ios');
32
+ else if (rest.options.platform === 'android') target = state.targets.find((t) => t.platform === 'android');
33
+ else target = state.targets.find((t) => t.platform === 'wasm');
34
+
35
+ // Rust surfaces need no bridge file here: the native bridge is the package's generated
36
+ // companion crate; this only emits the JS proxy module (same shape as the .h flow).
37
+ if (filename.endsWith('.rs')) {
38
+ return upstreamTransformer.transform({ src: getRustJsScript(target, filename), filename, ...rest });
39
+ }
40
+
41
+ const bridgeFile = createBridgeFile(filename, target);
42
+
43
+ return upstreamTransformer.transform({ src: getCrossbindScript(target, bridgeFile), filename, ...rest });
44
+ }
45
+
46
+ return upstreamTransformer.transform({ src, filename, ...rest });
47
+ };