✨ Cross Platform Tools ✨
Set of tools to help you build cross-platform libraries from a single codebase
@cross-platform-tools/vite-plugin
Vite plugin that abstracts out module resolutions so you can build for multiple platforms from a single codebase.
Button.potato.tsx
, Button.tomato.tsx
---> dist/potato/Button.js
, dist/tomato/Button.js
, dist/types/Button.d.ts
Getting Started
or scaffold locally -
-
Scaffold an example node-edge-library
npx degit saurabhdaware/node-edge-library node-edge-library
-
Install Dependencies
pnpm install
-
Run the example of the library usage
cd packages/node-edge-app node run.js # Runs the example with node bundle node --conditions=edge run.js # Runs the example with edge bundle
[!note]
The example is kept simple for better understanding of code. If your usecase requires you to build final bundles of app, you can also pass these conditions from resolve.conditions method in vite.
How does it work?
Module Resolutions in Build
@cross-platform-tools/vite-plugin
package takes care of resolving extensions such as .node.ts
, .client.ts
, .xyz.ts
and creates final bundles such as dist/node/
, dist/client/
, or dist/xyz
.
Module Resolutions in Tests
It also takes care of resolving extensions for tests when used with vitest
. So you can define your platform-specific tests with .client.test.ts
, .node.test.ts
, etc.
Base Library Setup
This plugin also takes care of basic things required to build a library such as type file generations for all platforms, base library configuration setup with vite, etc.
Manual Setup
This section explains the steps required to set up a library from scratch. Check out the Getting Started for Quick Start guide.
Step 1: Install the Vite Plugin
npm i --save-dev @cross-platform-tools/vite-plugin
Step 2: Add Vite Plugin to your configuration
// vite.config.ts
import { defineConfig } from "vite";
import { viteCrossPlatform } from '@cross-platform-tools/vite-plugin';
export default defineConfig({
plugins: [
viteCrossPlatform({
// We call `vite build` multiple times for each platform
platform: process.env.PLATFORM,
// This can be any string that is used in filenames.
// E.g. something.node.ts and something.client.ts in this example
supportedPlatforms: ['node', 'client'],
lib: {
entryDir: 'src',
outDir: 'dist'
}
}),
]
});
supportedPlatforms
above
Step 2: Create files with extensions used in // src/getData.node.ts
export const getData = () => 1234;
// src/getData.client.ts
export const getData = () => 4321;
// src/index.ts
export { getData } from './getData';
Step 3: Add build scripts and exports to your package.json
{
"scripts": "PLATFORM=node vite build && PLATFORM=client vite build",
"exports": {
"node": {
"default": "./dist/node/production/index.js",
"types": "./dist/node/types/index.d.ts"
},
"default": {
"default": "./dist/client/production/index.js",
"types": "./dist/client/types/index.d.ts"
}
}
}
You can follow the package.json exports documentation of node.js and export as per your use case.
Bundlers and tools have their own defined namespaces in exports from which they pick bundles. Such as React Native has react-native
namespace, while most bundlers use the browser
namespace for client bundling.
You can also define your custom namespace such as "xyz": "./path/to/bundle"
and use it with node --condition=xyz ./file.js
on consumer end or define it in your bundler (e.g. using resolve.conditions
)
Thanks for checking this out! Do drop your feedback on Twitter (@saurabhdawaree).
Like my work? You can sponsor me from GitHub Sponsors @saurabhdaware