crossbind
GitHub

Bundler plugins

A bundler plugin turns each header and .rs file you import into a JavaScript module. For the web it also compiles the wasm while you develop and emits it with your bundle; in a React Native app, the app's Xcode and Gradle builds compile the native code.

Which plugin

BundlerPackageBuilds for
Vite@crossbind/plugin-viteBrowser and Apps
Webpack@crossbind/plugin-webpack and @crossbind/plugin-webpack-loaderBrowser
Rspack@crossbind/plugin-webpack and @crossbind/plugin-webpack-loaderBrowser
Rollup@crossbind/plugin-rollupBrowser
Metro@crossbind/plugin-metro and @crossbind/plugin-react-nativeReact Native, and Expo's web build

Each one reads the project's crossbind.config.js, or crossbind.config.mjs, from the folder the bundler runs in. A page with no bundler builds with the CLI instead: see No bundler.

Vite

shell
npm install -D @crossbind/plugin-vite@beta
vite.config.js
import { defineConfig } from 'vite';
import viteCrossbindPlugin from '@crossbind/plugin-vite';
 
export default defineConfig({
plugins: [viteCrossbindPlugin()],
});

The plugin sits next to your framework's plugin; React, Vue and Svelte need nothing more.

Webpack

shell
npm install -D @crossbind/plugin-webpack@beta @crossbind/plugin-webpack-loader@beta
webpack.config.mjs
import CrossbindWebpackPlugin from '@crossbind/plugin-webpack';
 
const crossbindWebpackPlugin = new CrossbindWebpackPlugin();
 
export default {
module: {
rules: [crossbindWebpackPlugin.getRule()],
},
plugins: [crossbindWebpackPlugin],
devServer: crossbindWebpackPlugin.getDevServerConfig(),
};

getRule() hands headers and .rs files to the loader package, which is why both are installed. getDevServerConfig() serves the compiled module in dev and sends the COOP/COEP headers; if you already have a devServer block, spread it in and keep its headers and setupMiddlewares.

Keep the config an ES module
crossbind loads your project config with a top-level await, and Node cannot require() a module that does: a CommonJS webpack.config.js that requires the plugin fails with ERR_REQUIRE_ASYNC_MODULE. Name it webpack.config.mjs, or set "type": "module".

Rspack

Rspack takes the same two packages through its webpack-compatible plugin API.

shell
npm install -D @crossbind/plugin-webpack@beta @crossbind/plugin-webpack-loader@beta
rspack.config.mjs
import { defineConfig } from '@rspack/cli';
import CrossbindWebpackPlugin from '@crossbind/plugin-webpack';
 
const crossbindWebpackPlugin = new CrossbindWebpackPlugin();
 
export default defineConfig({
module: {
rules: [crossbindWebpackPlugin.getRule()],
},
plugins: [crossbindWebpackPlugin],
devServer: crossbindWebpackPlugin.getDevServerConfig(),
});

Rollup

The Rollup plugin is the core the Vite plugin wraps; use it directly only for a plain Rollup build. Rollup has no dev server: rollup -c -w rebuilds the wasm when a native file changes, and nothing reloads a page.

shell
npm install -D @crossbind/plugin-rollup@beta
rollup.config.mjs
import rollupCrossbindPlugin from '@crossbind/plugin-rollup';
 
export default {
input: 'src/index.js',
output: { file: 'dist/index.js', format: 'esm' },
plugins: [rollupCrossbindPlugin()],
};

The bundle step writes crossbind.js and crossbind.wasm into the output directory. Plain Rollup has no base path, so the generated code loads them from the site root.

Metro

@crossbind/plugin-metro turns header, .i and .rs imports into modules and writes the C++ bridge for each header while Metro bundles a React Native or Expo app; on iOS and Android the app's Xcode and Gradle builds compile the C++, through @crossbind/plugin-react-native. React Native shows its config, and Expo the web build it makes as well.

Another bundler

The plugins are thin: the work is done by functions crossbind exports, and @crossbind/plugin-rollup is the smallest complete plugin to copy. Each one starts by picking the browser target:

my-plugin.js
import {
state, buildDependencies, createBridgeFile, getCrossbindScript, getDependFilePath,
createLib, buildWasm, appSourceFiles, getTargetParams, getFilteredBuildTargets,
} from 'crossbind';
 
const targetParams = getTargetParams({ platform: ['wasm'], arch: ['wasm32'], runtime: ['st'], runtimeEnv: ['browser'] }, true);
const target = getFilteredBuildTargets(targetParams, { buildType: 'release' })?.[0];
  • Before the first transform, buildDependencies() puts the library headers in place.
  • Imported headers become modules through createBridgeFile() and getCrossbindScript(); a library header such as @crossbind/port-zlib/zlib.h resolves through getDependFilePath().
  • The bundle step compiles with createLib() and buildWasm() and emits the target's jsName, wasmName and dataTxtName from state.config.paths.build as crossbind.js, crossbind.wasm and crossbind.data.txt.
  • In dev, the plugin serves those files and watches state.config.paths.native and appSourceFiles(), whose imports decide what a library header binds.
Type to search every guide page and section.
↑↓ navigate↵ openesc close