react-native-esbuild
react-native-esbuild copied to clipboard
Supports hot reload
Description
Supports hot reload on development environment.
Tasks
- [x] Implement custom file watcher instead esbuild.context.watch()
- [x] On some files change, trigger rebuild() and get the metadata(path, etc..) of the changed module.
- [x] Implement custom file system watcher using chokidar
- [x] Transformer
- [x] Skip if file is not changed (transform changed file only)
- [x] Wrap modules with react-refresh context boundary
- [x] Implement swc plugin (https://github.com/leegeunhyeok/swc-plugin-react-refresh)
- [x] Custom module system
- [x] Send HMR update message to client via web socket
- [x] Evaluate updated code on client runtime
- [x] Get updated module code from bundle
- [x] Add experimental config for toggle hot reload
- [ ] #41
References
- react-refresh
- https://swc.rs/docs/configuration/compilation#jsctransformreactrefresh
- https://github.com/FredKSchott/esm-hmr/blob/master/src/client.ts
- PoC: https://github.com/leegeunhyeok/esbuild-hmr
Limitations
- Esbuild isn't supports HMR(Hot Module Replacement).
- Cannot skip transform(js, ts) loader in Esbuild.
Notes
- 0.1.0-alpha.42
// before
var __commonJS = (cb, mod) => function __require2() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
// after
global.cachedModule = {};
var __commonJS = (cb, mod) => {
var name = __getOwnPropNames(cb)[0];
Object.defineProperty(global.cachedModule, name, {
get: () => mod
});
return function __require2() {
return mod || (0, cb[name])((mod = { exports: {} }).exports, mod), mod.exports;
};
};
// when file changes detected -> don't rebuild, just transform target file with swc only.
// `import { a, b, c } from 'module-path';`
try {
const { a, b, c } = global.cachedModule['module-path'];
// ...
} catch (error) {
// hot reload failed. fully reload instead
}
// Esbuild metafile
interface Metafile {
// `inputs` key is module file path.
inputs: Record<string, {
byte: number;
imports: {
path: string; // eg. 'node_modules/react/cjs/index.js'
original: string; // eg. 'react'
kind: '...',
}[];
}>[],
// ...
}
// {
// react: 'node_modules/react/cjs/index.js',
// '../components': 'src/components/index.ts',
// }
const mappedModules = metafile.inputs[changedFilePath]?.imports.reduce((prev, curr) => {
return { ...prev, [curr.original]: curr.path };
}, {});
// Example usage
import { transform } from '@swc/core';
await transform(code, {
jsc: {
experimental: {
plugins: [
// https://github.com/leegeunhyeok/swc-plugin-react-native-esbuild-module
['react-native-esbuild-module-plugin', {
alias: mappedModules ?? {},
}],
],
},
},
});