Skip to content

Commit 02a8eae

Browse files
committed
feat(cargo): use cargo to build rust into wasm
㊙️ under the hood, it use rust-native-wasm-loader ✍️ use $Shape to relax Options type 👫 partially polyfill webpack specific context [ci skip]
1 parent e4468ae commit 02a8eae

File tree

6 files changed

+126
-14
lines changed

6 files changed

+126
-14
lines changed

flow-typed/rollupPluginRustDef.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
declare type Options = {
2+
target: string,
3+
release: boolean,
24
export: 'buffer' | 'instance' | 'module' | 'promise',
3-
include?: string[] | string, // rollup recommend to add this
4-
exclude?: string[] | string // rollup recommend to add this
5+
include: string[] | string, // rollup recommend to add this
6+
exclude: string[] | string // rollup recommend to add this
57
};

package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"rollup-plugin-eslint": "^5.0.0",
7373
"rollup-plugin-node-resolve": "^3.3.0",
7474
"rollup-plugin-prettier": "^0.4.0",
75+
"rust-native-wasm-loader": "^0.8.1",
7576
"standard-version": "^4.4.0"
7677
},
7778
"lint-staged": {

src/main.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,47 @@
11
// @flow
22
import { createFilter } from 'rollup-pluginutils';
3+
import { readFileSync } from 'fs';
4+
import {
5+
cargoCommand,
6+
findSrcDir,
7+
handleCargo
8+
} from 'rust-native-wasm-loader/dist/cargo';
9+
import { rollPack, execPermissive } from './util';
310
import wrap from './wrapper';
411
import predef from './options';
512

613
const extension = /\.rs$/; // rust file extension
714

8-
export default function(options: Options = predef) {
9-
options = Object.assign(predef, options); // diff and merge with predefined options
15+
export default function(options: $Shape<Options> = {}) {
16+
options = (Object.assign(predef, options): Options); // diff and merge with predefined options
1017
const filter = createFilter(options.include, options.exclude);
18+
const cmd = cargoCommand(options.target, options.release);
1119

1220
return {
1321
name: 'rust',
1422
async transform(code: string, id: string) {
1523
if (!extension.test(id)) return;
1624
if (!filter(id)) return;
1725

18-
const wasmCode = Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x01, 0, 0, 0]);
19-
switch (options.export) {
20-
case 'buffer':
21-
return wrap(wasmCode).asBuffer;
22-
case 'instance':
23-
return wrap(wasmCode).asWebAssembly.Instance;
24-
case 'module':
25-
return wrap(wasmCode).asWebAssembly.Module;
26-
case 'promise':
27-
return wrap(wasmCode).promiseWebAssembly;
26+
const cwd = await findSrcDir(id);
27+
if (!cwd) this.error('No Cargo.toml file found in any parent directory.');
28+
const result = await execPermissive(cmd, cwd);
29+
30+
if (options.target.includes('wasm')) {
31+
let { wasmFile } = await handleCargo(rollPack(this).toLoader, result);
32+
if (!wasmFile) this.error('No wasm file produced as build output');
33+
const wasmCode = readFileSync(wasmFile);
34+
35+
switch (options.export) {
36+
case 'buffer':
37+
return wrap(wasmCode).asBuffer;
38+
case 'instance':
39+
return wrap(wasmCode).asWebAssembly.Instance;
40+
case 'module':
41+
return wrap(wasmCode).asWebAssembly.Module;
42+
case 'promise':
43+
return wrap(wasmCode).promiseWebAssembly;
44+
}
2845
}
2946
}
3047
};

src/options.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// @flow
22
/** Default options */
33
export default ({
4+
target: 'wasm32-unknown-uknown',
5+
release: true,
46
export: 'promise',
57
include: ['**/*.rs'],
68
exclude: ['node_modules/**', 'target/**']

src/util.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// @flow
2+
import { promisify } from 'util';
3+
import { exec } from 'child_process';
4+
5+
/** Execute command in permissive environment
6+
* @param cmd command to execute
7+
* @param cwd working directory where the command executed
8+
*/
9+
export async function execPermissive(cmd: string, cwd: string) {
10+
try {
11+
return await promisify(exec)(cmd, {
12+
cwd,
13+
encoding: 'utf-8',
14+
maxBuffer: 2 * 1024 * 1024 * 1024
15+
});
16+
} catch (error) {
17+
return error;
18+
}
19+
}
20+
/** Transform rollup plugin context into webpack specific context
21+
* @param self rollup plugin context
22+
* @see https://github.com/rollup/rollup/wiki/Plugins#plugin-context
23+
*/
24+
export function rollPack(self: any) {
25+
return {
26+
/** @see https://webpack.js.org/api/loaders/#the-loader-context */
27+
toLoader: {
28+
emitWarning: (warning: string | Error) => self.warning(warning),
29+
emitError: (error: string | Error) => self.error(error),
30+
addDependency: (file: string) => (file ? undefined : undefined)
31+
}
32+
};
33+
}

0 commit comments

Comments
 (0)