Skip to content

Commit

Permalink
feat: hot module replacement
Browse files Browse the repository at this point in the history
- now supports HMR
- limitations
  - in node_modules/* modules are not supported HMR yet.
- custom runtime module system by `swc-plugin-global-module`
  - https://github.com/leegeunhyeok/swc-plugin-global-module

close #38
  • Loading branch information
leegeunhyeok committed Dec 30, 2023
1 parent 04fc154 commit 6f16063
Show file tree
Hide file tree
Showing 167 changed files with 2,834 additions and 2,354 deletions.
14 changes: 14 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
rules: {
semi: ['error', 'always'],
quotes: ['error', 'single'],
camelcase: 'off',
'new-cap': 'off',
'object-curly-spacing': ['error', 'always'],
'array-bracket-spacing': 'off',
Expand All @@ -43,6 +44,10 @@ module.exports = {
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-throw-literal': 'off',
'@typescript-eslint/no-unsafe-enum-comparison': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
{
Expand All @@ -51,6 +56,15 @@ module.exports = {
'no-console': 'off',
},
},
{
files: ['packages/hmr/lib/runtime/*.ts'],
rules: {
'no-console': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
},
},
{
files: ['packages/jest/lib/**/*.ts'],
rules: {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
- 💾 In-memory & Local File System Caching
- 🎨 Flexible & Extensible
- 🔥 Supports JSC & Hermes Runtime
- 🔄 Supports Live Reload
- 🔄 Supports HMR & Live Reload
- 🐛 Supports Debugging(Flipper, Chrome Debugger)
- 🌍 Supports All Platforms(Android, iOS, Web)
- ✨ New Architecture Ready
Expand Down
56 changes: 21 additions & 35 deletions docs/pages/configuration/basic-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ exports.default = {};
By default, follow the configuration below.

```js
/**
* @type {import('@react-native-esbuild/core').Config}
*/
exports.default = {
cache: true,
logger: {
Expand All @@ -28,13 +31,6 @@ exports.default = {
assetExtensions: [/* internal/lib/defaults.ts */],
},
transformer: {
jsc: {
transform: {
react: {
runtime: 'automatic',
},
},
},
stripFlowPackageNames: ['react-native'],
},
web: {
Expand Down Expand Up @@ -68,7 +64,6 @@ Resolver configurations.

Transformer configurations.

- `transformer.jsc`: [jsc](https://swc.rs/docs/configuration/compilation) config in swc.
- `transformer.stripFlowPackageNames`: Package names to strip flow syntax from (Defaults to `['react-native']`)
- `transformer.fullyTransformPackageNames`: Package names to fully transform with [metro-react-native-babel-preset](https://github.com/facebook/react-native/tree/main/packages/react-native-babel-preset) from
- `transformer.additionalTransformRules`: Additional transform rules. This rules will be applied before phase of transform to es5
Expand All @@ -90,6 +85,15 @@ Additional Esbuild plugins.

For more details, go to [Custom Plugins](/configuration/custom-plugins)

### experimental

<Callout>
Experimental configurations.
</Callout>

- `experimental.hmr`: Enable HMR(Hot Module Replacement) on development mode. (Defaults to `false`)
- For more details and limitations, go to [Hot Module Replacement](/limitations/hot-module-replacement).

## Types

<details>
Expand Down Expand Up @@ -210,9 +214,16 @@ interface Config {
*/
plugins?: EsbuildPlugin[];
/**
* Client event receiver
* Experimental configurations
*/
reporter?: (event: ReportableEvent) => void;
experimental?: {
/**
* Enable HMR(Hot Module Replacement) on development mode.
*
* Defaults to `false`.
*/
hmr?: boolean;
};
}
```

Expand All @@ -239,28 +250,3 @@ type BabelTransformRule = TransformRuleBase<import('@babel/core').Options>;
```

</details>

<details>
<summary>ReportableEvent</summary>

```ts
type ReportableEvent = ClientLogEvent;

interface ClientLogEvent {
type: 'client_log';
level:
| 'trace'
| 'info'
| 'warn'
| 'error'
| 'log'
| 'group'
| 'groupCollapsed'
| 'groupEnd'
| 'debug';
data: unknown[];
mode: 'BRIDGE' | 'NOBRIDGE';
}
```

</details>
2 changes: 1 addition & 1 deletion docs/pages/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ And create `react-native-esbuild.js` to project root.
exports.default = {};
```

for more details, go to [Configuration](/configuration/basic).
for more details, go to [Basic Configuration](/configuration/basic-configuration).

## Native Setup

Expand Down
5 changes: 0 additions & 5 deletions docs/pages/limitations/hot-module-replacement.md

This file was deleted.

30 changes: 30 additions & 0 deletions docs/pages/limitations/hot-module-replacement.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Callout } from 'nextra/components'

# Hot Module Replacement

<Callout type="warning">
HMR(Hot Module Replacement) is experimental.
</Callout>

esbuild doesn't currently support Hot Module Replacement(HMR).

So, I working hard for implement custom HMR and it's partially available as an experimental feature.

You can enable HMR by `experimental.hmr` set to `true` in your configuration file.

```js
/**
* @type {import('@react-native-esbuild/core').Config}
*/
exports.default = {
// ...
experimental: {
hmr: true,
},
};
```

and here are some limitations.

- Detects changes in the `<projectRoot>/*` only.
- Changes detected in `<projectRoot>/node_modules/*` will be ignored and fully refreshed after rebuild.
3 changes: 3 additions & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ yarn-error.log
!.yarn/sdks
!.yarn/versions

# @swc
.swc

# @react-native-esbuild
.rne
.swc
Expand Down
3 changes: 3 additions & 0 deletions example/react-native-esbuild.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ exports.default = {
],
},
},
experimental: {
hmr: true,
},
};
18 changes: 2 additions & 16 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,13 @@ const config: Config = {
testMatch: ['<rootDir>/packages/core/**/*.test.ts'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
},
{
displayName: '@react-native-esbuild/config',
transform,
testEnvironment: 'node',
testMatch: ['<rootDir>/packages/config/**/*.test.ts'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
},
{
displayName: '@react-native-esbuild/dev-server',
transform,
testEnvironment: 'node',
testMatch: ['<rootDir>/packages/dev-server/**/*.test.ts'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
},
{
displayName: '@react-native-esbuild/internal',
transform,
testEnvironment: 'node',
testMatch: ['<rootDir>/packages/internal/**/*.test.ts'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
},
{
displayName: '@react-native-esbuild/plugins',
transform,
Expand All @@ -70,10 +56,10 @@ const config: Config = {
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
},
{
displayName: '@react-native-esbuild/utils',
displayName: '@react-native-esbuild/shared',
transform,
testEnvironment: 'node',
testMatch: ['<rootDir>/packages/utils/**/*.test.ts'],
testMatch: ['<rootDir>/packages/shared/**/*.test.ts'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
},
],
Expand Down
5 changes: 2 additions & 3 deletions packages/cli/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { hideBin } from 'yargs/helpers';
import {
DEFAULT_ENTRY_POINT,
DEFAULT_WEB_ENTRY_POINT,
} from '@react-native-esbuild/config';
import { VERSION } from './constants';
} from '@react-native-esbuild/shared';
import type { RawArgv } from './types';

const commonOptions = {
Expand All @@ -27,7 +26,7 @@ const commonOptions = {
export const cli = (): RawArgv | Promise<RawArgv> => {
return yargs(hideBin(process.argv))
.scriptName('rne')
.version(VERSION)
.version(self._version as string)
.usage('$0 <cmd> [args]')
.command(
'start',
Expand Down
11 changes: 3 additions & 8 deletions packages/cli/lib/commands/bundle.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import path from 'node:path';
import { ReactNativeEsbuildBundler } from '@react-native-esbuild/core';
import {
DEFAULT_ENTRY_POINT,
DEFAULT_WEB_ENTRY_POINT,
type BundleOptions,
} from '@react-native-esbuild/config';
} from '@react-native-esbuild/shared';
import { printDebugOptions } from '../helpers';
import { bundleArgvSchema } from '../schema';
import { presets } from '../presets';
import { logger } from '../shared';
import type { Command } from '../types';

Expand All @@ -16,7 +15,7 @@ import type { Command } from '../types';
export const bundle: Command = async (argv) => {
const bundleArgv = bundleArgvSchema.parse(argv);
const bundleOptions: Partial<BundleOptions> = {
entry: path.resolve(bundleArgv['entry-file'] ?? DEFAULT_ENTRY_POINT),
entry: path.resolve(bundleArgv['entry-file'] ?? DEFAULT_WEB_ENTRY_POINT),
sourcemap: bundleArgv['sourcemap-output'],
outfile: bundleArgv['bundle-output'],
assetsDir: bundleArgv['assets-dest'],
Expand All @@ -32,10 +31,6 @@ export const bundle: Command = async (argv) => {
printDebugOptions(bundleOptions);

const bundler = new ReactNativeEsbuildBundler(root);
(bundleOptions.platform === 'web' ? presets.web : presets.native).forEach(
bundler.addPlugin.bind(bundler),
);

await bundler.initialize();
await bundler.bundle(bundleOptions);
};
12 changes: 3 additions & 9 deletions packages/cli/lib/commands/serve.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ReactNativeWebServer } from '@react-native-esbuild/dev-server';
import type { BundleOptions } from '@react-native-esbuild/config';
import type { BundleOptions } from '@react-native-esbuild/shared';
import { printDebugOptions } from '../helpers';
import { serveArgvSchema } from '../schema';
import { presets } from '../presets';
import { logger } from '../shared';
import type { Command } from '../types';

Expand All @@ -28,12 +27,7 @@ export const serve: Command = async (argv): Promise<void> => {
logger.debug('bundle options');
printDebugOptions(bundleOptions);

const server = await new ReactNativeWebServer(
serveOptions,
bundleOptions,
).initialize((bundler) => {
presets.web.forEach(bundler.addPlugin.bind(bundler));
});

const server = new ReactNativeWebServer(serveOptions, bundleOptions);
await server.initialize();
await server.listen();
};
11 changes: 3 additions & 8 deletions packages/cli/lib/commands/start.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable quotes -- Allow quote in template literal */
import path from 'node:path';
import { ReactNativeAppServer } from '@react-native-esbuild/dev-server';
import { DEFAULT_ENTRY_POINT } from '@react-native-esbuild/config';
import { DEFAULT_ENTRY_POINT } from '@react-native-esbuild/shared';
import { enableInteractiveMode, printDebugOptions } from '../helpers';
import { startArgvSchema } from '../schema';
import { presets } from '../presets';
import { logger } from '../shared';
import type { Command } from '../types';

Expand All @@ -21,12 +20,8 @@ export const start: Command = async (argv) => {
logger.debug('start options');
printDebugOptions({ entry, ...serveOptions });

const server = await new ReactNativeAppServer(serveOptions).initialize(
(bundler) => {
presets.native.forEach(bundler.addPlugin.bind(bundler));
},
);

const server = new ReactNativeAppServer(serveOptions);
await server.initialize();
await server.listen(() => {
if (
enableInteractiveMode((keyName) => {
Expand Down
3 changes: 0 additions & 3 deletions packages/cli/lib/constants/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/cli/lib/helpers/cli.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors } from '@react-native-esbuild/utils';
import { colors } from '@react-native-esbuild/shared';
import { logger } from '../shared';

export const getCommand = <RawArgv extends { _: (string | number)[] }>(
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactNativeEsbuildBundler } from '@react-native-esbuild/core';
import { LogLevel } from '@react-native-esbuild/utils';
import { LogLevel } from '@react-native-esbuild/shared';
import { cli } from './cli';
import * as Commands from './commands';
import { getCommand, handleUncaughtException } from './helpers';
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/lib/schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import path from 'node:path';
import { SUPPORT_PLATFORMS } from '@react-native-esbuild/config';
import { SUPPORT_PLATFORMS } from '@react-native-esbuild/shared';
import { z } from 'zod';

const resolvePath = (filepath: string): string =>
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/lib/shared.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { Logger } from '@react-native-esbuild/utils';
import { Logger } from '@react-native-esbuild/shared';

export const logger = new Logger('cli');
3 changes: 1 addition & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@
"url": "https://github.com/leegeunhyeok/react-native-esbuild/issues"
},
"dependencies": {
"@react-native-esbuild/config": "workspace:*",
"@react-native-esbuild/core": "workspace:*",
"@react-native-esbuild/dev-server": "workspace:*",
"@react-native-esbuild/plugins": "workspace:*",
"@react-native-esbuild/utils": "workspace:*",
"@react-native-esbuild/shared": "workspace:*",
"yargs": "^17.7.2",
"zod": "^3.22.2"
},
Expand Down
Loading

0 comments on commit 6f16063

Please sign in to comment.