@zntc/react-native 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -19
- package/dist/dev-server/bun-http-adapter.d.ts +10 -0
- package/dist/dev-server/hmr-bridge.d.ts +16 -1
- package/dist/dev-server/http-server.d.ts +1 -0
- package/dist/dev-server/index.d.ts +1 -1
- package/dist/dev-server/platform-state.d.ts +24 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +256 -69
- package/dist/preset.d.ts +11 -0
- package/package.json +3 -3
- package/runtime/zntc-hmr-client.cjs +22 -10
package/README.md
CHANGED
|
@@ -1,48 +1,108 @@
|
|
|
1
1
|
# @zntc/react-native
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English · **[한국어](./README_KO.md)**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> ZNTC React Native platform layer — RN preset + Metro-compatible dev server + Reanimated worklets / Flow / Hermes.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
- **RN 상수 / helpers** — `RN_GLOBAL_IDENTIFIERS` / `tryResolve` / `resolveRnPolyfills`.
|
|
7
|
+
[](https://www.npmjs.com/package/@zntc/react-native)
|
|
8
|
+
[](https://github.com/ohah/zntc/blob/main/LICENSE)
|
|
9
|
+
|
|
10
|
+
`@zntc/react-native` adapts the [ZNTC](https://github.com/ohah/zntc) toolchain to React Native. It turns a small user input into Metro-compatible NAPI build options, ships a Metro-compatible HMR dev server, and wires in the RN-specific transforms (Flow, Reanimated worklets, Hermes target) — all built into the ZNTC core, **without Babel**.
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
What it provides:
|
|
13
|
+
|
|
14
|
+
- **RN preset** — `buildRnBundleOptions(input)` / `bundleRn(input)` / `watchRn(input)`. RN-specific build options (Hermes/ES5 target, Flow, automatic-dev JSX, worklets, dev mode, Fast Refresh, polyfills, RN prelude banner) are applied automatically.
|
|
15
|
+
- **Metro-compatible dev server** — `serveRn(options)` / `buildRnDevServerOptions(input)`. Per-platform watch, HMR bridge over the `/hot` endpoint, and terminal actions, wired together for you.
|
|
16
|
+
- **Metro HMR adapter** — `createMetroHmrAdapter()` emits messages compatible with the RN runtime's HMRClient interface (`hmr:update-start` / `hmr:update` / `hmr:update-done` / `hmr:reload` / `hmr:error` / `log`).
|
|
17
|
+
- **RN runtime** — `runtime/zntc-hmr-client.cjs`, an HMRClient-compatible client for the RN runtime.
|
|
18
|
+
- **Plugin factories** — `createAssetPlugin` / `createBabelPlugin` / `createCodegenPlugin` / `createRequireContextPlugin` / `createMetroResolveRequestPlugin`.
|
|
19
|
+
- **RN constants / helpers** — `RN_GLOBAL_IDENTIFIERS` / `tryResolve` / `resolveRnPolyfills`.
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
- iOS / Android native build orchestration (run-android / run-ios / autolinking) — `@react-native-community/cli` 영역
|
|
21
|
+
Out of scope (handled elsewhere): iOS / Android native build orchestration (`run-android` / `run-ios` / autolinking) belongs to `@react-native-community/cli`.
|
|
17
22
|
|
|
18
|
-
##
|
|
23
|
+
## Installation
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
26
|
bun add -D @zntc/react-native @zntc/core
|
|
22
|
-
#
|
|
23
|
-
#
|
|
27
|
+
# npm i -D @zntc/react-native @zntc/core
|
|
28
|
+
# pnpm add -D @zntc/react-native @zntc/core
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Some features rely on optional peer packages — install the ones your setup needs:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bun add -D @babel/core @react-native/babel-preset metro-resolver react-native
|
|
24
35
|
```
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
`@react-native-community/cli-server-api` is required for the dev server's reload / dev-menu broadcasts.
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Attaching to an existing React Native CLI project
|
|
42
|
+
|
|
43
|
+
The simplest path is the scaffolder, which rewrites the `start` / `bundle:*` scripts of an existing RN CLI app to use ZNTC (Metro fallback is preserved):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx @zntc/init
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See the [React Native guide](https://ohah.github.io/zntc/guides/react-native/) for details.
|
|
50
|
+
|
|
51
|
+
### RN preset — `buildRnBundleOptions`
|
|
52
|
+
|
|
53
|
+
Convert a small RN input into ZNTC NAPI build options, then run a build:
|
|
27
54
|
|
|
28
55
|
```ts
|
|
29
56
|
import { init, build } from '@zntc/core';
|
|
30
57
|
import { buildRnBundleOptions } from '@zntc/react-native';
|
|
31
58
|
|
|
32
59
|
await init();
|
|
60
|
+
|
|
33
61
|
const result = await build(
|
|
34
62
|
buildRnBundleOptions({
|
|
35
63
|
entry: '/abs/path/index.ts',
|
|
36
64
|
projectRoot: '/abs/path',
|
|
37
|
-
rnPlatform: 'ios',
|
|
65
|
+
rnPlatform: 'ios', // 'ios' | 'android'
|
|
38
66
|
dev: false,
|
|
39
67
|
sourcemap: true,
|
|
40
68
|
}),
|
|
41
69
|
);
|
|
42
70
|
```
|
|
43
71
|
|
|
44
|
-
|
|
72
|
+
`bundleRn(input)` is a one-call shorthand for `build(buildRnBundleOptions(input))`, and `watchRn(input)` starts a watching build.
|
|
73
|
+
|
|
74
|
+
The preset auto-enables the RN-compatible defaults (Hermes/ES5 target, Flow, worklets, polyfills, RN prelude banner, asset loaders, and so on). In dev mode it additionally enables automatic-dev JSX, Fast Refresh, and the dev-mode runtime. You can layer user overrides on top via `input.override` (dictionaries deep-merge, arrays/primitives replace).
|
|
75
|
+
|
|
76
|
+
### Metro-compatible dev server — `serveRn`
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { buildRnDevServerOptions, serveRn } from '@zntc/react-native';
|
|
80
|
+
|
|
81
|
+
const handle = await serveRn(
|
|
82
|
+
buildRnDevServerOptions({
|
|
83
|
+
bundle: {
|
|
84
|
+
entry: '/abs/path/index.ts',
|
|
85
|
+
projectRoot: '/abs/path',
|
|
86
|
+
rnPlatform: 'ios',
|
|
87
|
+
dev: true,
|
|
88
|
+
},
|
|
89
|
+
port: 8081,
|
|
90
|
+
host: 'localhost',
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// handle.url / handle.port — connect the RN app to this server
|
|
95
|
+
// await handle.stop(); — graceful shutdown
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`serveRn` lazily loads `@react-native-community/cli-server-api` and the RN dev middleware, runs a per-platform watching build, serves HMR over `/hot`, and sets up terminal actions (reload / dev menu). The HMR messages are Metro HMRClient-compatible, so the standard RN runtime connects without changes.
|
|
99
|
+
|
|
100
|
+
## Documentation
|
|
101
|
+
|
|
102
|
+
- Monorepo: <https://github.com/ohah/zntc>
|
|
103
|
+
- Docs: <https://ohah.github.io/zntc>
|
|
104
|
+
- React Native guide: <https://ohah.github.io/zntc/guides/react-native/>
|
|
105
|
+
|
|
106
|
+
## License
|
|
45
107
|
|
|
46
|
-
|
|
47
|
-
- #2540 — 본 패키지 신설
|
|
48
|
-
- #2538 — Zig 단일 dev server (후속)
|
|
108
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Middleware } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* 미들웨어 체인(node req/res 기반)을 Bun `Request` 에 대해 실행하고 단일
|
|
4
|
+
* `Response` 로 변환. terminal next() / next(err) 도 node 경로(chainToHandler)와
|
|
5
|
+
* 동일하게 404 / 500 으로 매핑한다.
|
|
6
|
+
*
|
|
7
|
+
* route handler 가 비동기로 res.end() 를 호출하는 경우(.catch(next) 패턴)도
|
|
8
|
+
* Promise 가 end / next 둘 중 먼저 오는 쪽에서 resolve 되도록 한다.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runMiddlewareForBun(middleware: Middleware, req: Request): Promise<Response>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IncomingMessage } from 'node:http';
|
|
2
2
|
import type { Socket } from 'node:net';
|
|
3
|
+
import type { BunHmrClient } from '@zntc/server';
|
|
3
4
|
import { type MetroHmrAdapter } from '../metro-hmr-adapter.ts';
|
|
4
5
|
import type { PlatformStateCallbacks } from './platform-state.ts';
|
|
5
6
|
export interface HmrBridgeOptions {
|
|
@@ -14,8 +15,22 @@ export interface HmrBridge {
|
|
|
14
15
|
readonly adapter: MetroHmrAdapter;
|
|
15
16
|
readonly callbacks: PlatformStateCallbacks;
|
|
16
17
|
readonly path: string;
|
|
17
|
-
/** http upgrade chain 안에서 호출 — channel.accept + initial greeting. */
|
|
18
|
+
/** http upgrade chain 안에서 호출 (Node) — channel.accept + initial greeting. */
|
|
18
19
|
acceptUpgrade(req: IncomingMessage, socket: Socket): void;
|
|
20
|
+
/**
|
|
21
|
+
* Bun.serve websocket `open(ws)` 에서 호출 — Bun client 등록 + initial greeting.
|
|
22
|
+
* Node 의 `acceptUpgrade` 와 대칭이지만 raw socket 핸드셰이크가 없다 (Bun.serve
|
|
23
|
+
* 의 `server.upgrade(req)` 가 RFC6455 핸드셰이크를 native 로 처리하므로).
|
|
24
|
+
*/
|
|
25
|
+
acceptBun(ws: BunHmrClient): void;
|
|
26
|
+
/** Bun.serve websocket `close(ws)` 에서 호출 — Bun client 정리. */
|
|
27
|
+
removeBun(ws: BunHmrClient): void;
|
|
28
|
+
/**
|
|
29
|
+
* Bun.serve websocket `message(ws, msg)` 에서 호출 — client → server text 를
|
|
30
|
+
* incoming 핸들러(register-entrypoints ACK / log forwarding)로 dispatch.
|
|
31
|
+
* Node 경로는 `channel.accept` 의 `socket.on('data')` 가 같은 역할.
|
|
32
|
+
*/
|
|
33
|
+
handleBunMessage(ws: BunHmrClient, text: string): void;
|
|
19
34
|
/** RN runtime console.log forwarding 출력 표시 toggle. 새 상태 반환. */
|
|
20
35
|
toggleLogs(): boolean;
|
|
21
36
|
}
|
|
@@ -30,3 +30,4 @@ export interface DevHttpServerHandle {
|
|
|
30
30
|
*/
|
|
31
31
|
export declare function createBaseMiddleware(options: RnDevServerOptions, deps: DevHttpServerDeps): Middleware;
|
|
32
32
|
export declare function createDevHttpServer(options: RnDevServerOptions, deps: DevHttpServerDeps): Promise<DevHttpServerHandle>;
|
|
33
|
+
export declare function createBunDevHttpServer(options: RnDevServerOptions, deps: DevHttpServerDeps): Promise<DevHttpServerHandle>;
|
|
@@ -2,7 +2,7 @@ export { createHmrBridge, type HmrBridge, type HmrBridgeOptions } from './hmr-br
|
|
|
2
2
|
export { type RnDevServerHandle, serveRn, type ServeRnExtras } from './serve.ts';
|
|
3
3
|
export { type CliServerApi, type CliWebsocketEndpoint, loadCliServerApi, type LoadCliServerApiOptions, } from './middleware/cli-server-api.ts';
|
|
4
4
|
export { type DevMiddleware, loadDevMiddleware, type LoadDevMiddlewareOptions, } from './middleware/dev-middleware.ts';
|
|
5
|
-
export { createBaseMiddleware, createDevHttpServer, type DevHttpServerDeps, type DevHttpServerHandle, } from './http-server.ts';
|
|
5
|
+
export { createBaseMiddleware, createBunDevHttpServer, createDevHttpServer, type DevHttpServerDeps, type DevHttpServerHandle, } from './http-server.ts';
|
|
6
6
|
export { parseRequestUrl, readJsonBody, sendJson, sendText } from './http-utils.ts';
|
|
7
7
|
export type { CustomizeFrame, RnDevServerOptions, RnDevServerOptionsInput } from './options.ts';
|
|
8
8
|
export { buildRnDevServerOptions } from './options.ts';
|
|
@@ -20,6 +20,30 @@ export interface PlatformStateCallbacks {
|
|
|
20
20
|
onReady?: (state: PlatformState, event: WatchReadyEvent) => void;
|
|
21
21
|
onRebuild?: (state: PlatformState, event: WatchRebuildEvent) => void;
|
|
22
22
|
}
|
|
23
|
+
export interface BundleRefresher {
|
|
24
|
+
/** stale 신호 — generation 을 bump 해 in-flight build 가 이 변경을 마스킹하지 못하게 한다. */
|
|
25
|
+
markStale(): void;
|
|
26
|
+
/** stale 이면 build, in-flight 면 coalesce. */
|
|
27
|
+
refresh(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* in-flight build coalescing + generation guard. `build` 는 시작 시점 소스를 빌드하므로,
|
|
31
|
+
* 진행 중 `markStale()` 이 또 불리면(파일 재변경) 그 build 완료가 stale 을 clear 하면 안 된다
|
|
32
|
+
* (더 새로운 변경을 마스킹 → stale bundle 제공). generation 으로 가드: build 시작 시 generation
|
|
33
|
+
* 을 캡처, 완료 시 generation 이 그대로일 때만 `clearStale`. 새 staleness 가 들어왔으면 stale 을
|
|
34
|
+
* 유지해 다음 `refresh()` 가 최신 소스로 rebuild 한다. `bundleRn`/state 의존을 콜백으로 주입해
|
|
35
|
+
* race-safety 로직만 결정적으로 유닛 테스트할 수 있게 분리.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createBundleRefresher(deps: {
|
|
38
|
+
/** 이미 신선(!bundleStale && bundle≠null)하면 build 스킵. */
|
|
39
|
+
isFresh: () => boolean;
|
|
40
|
+
/** bundleRn + state.bundle/buildError 갱신. 내부 try/catch 라 reject 하지 않음. */
|
|
41
|
+
build: () => Promise<void>;
|
|
42
|
+
/** stale=true. */
|
|
43
|
+
setStale: () => void;
|
|
44
|
+
/** stale=false. */
|
|
45
|
+
clearStale: () => void;
|
|
46
|
+
}): BundleRefresher;
|
|
23
47
|
/**
|
|
24
48
|
* platform 별 watch + state 생성. 첫 build 는 비동기 — caller 가
|
|
25
49
|
* `waitForBuild(state)` 로 대기. RN runtime 이 ios+android 동시 요청 시
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { HMR_RN_MSG, type HmrRnErrorMessage, type HmrRnLogMessage, type HmrRnMessage, type HmrRnMessageType, type HmrRnReloadMessage, type HmrRnUpdateDoneMessage, type HmrRnUpdateMessage, type HmrRnUpdateModule, type HmrRnUpdateStartMessage, } from '@zntc/server';
|
|
2
|
-
export { type AssetResolverOptions, type Broadcast, buildRnDevServerOptions, createBaseMiddleware, createDevHttpServer, createHmrBridge, createPlatformState, type CliServerApi, type CliWebsocketEndpoint, type DevMiddleware, loadCliServerApi, loadDevMiddleware, type LoadCliServerApiOptions, type LoadDevMiddlewareOptions, createPlatformStateRegistry, type CustomizeFrame, type DevHttpServerDeps, type DevHttpServerHandle, type FrameInfo, getCachedSourceMap, type HmrBridge, type HmrBridgeOptions, applyCustomizeFrame, createSourceMapConsumer, extractCodeFrame, handleAssetRequest, handleBundleRequest, handleHmrMapRequest, handleIndexPage, handleMapRequest, handleSymbolicateRequest, isIndexRoute, isAssetRoute, isBundleRoute, isHmrMapRoute, isMapRoute, isSymbolicateRoute, normalizeFrame, symbolicateFrame, type SymbolicateCodeFrame, type SymbolicateRequest, type SymbolicateResponse, type Middleware, type MiddlewareEnhanceContext, type PlatformState, type PlatformStateCallbacks, type PlatformStateRegistry, colors, formatLogBadge, logBundle, logError, logInfo, logWarn, postProcessSourceMap, type SourcemapPathOptions, printZntcRnBanner, resolveAssetPath, type ServeRnExtras, serveRn, setupTerminalActions, type TerminalActionsCallbacks, type TerminalActionsOptions, type RnDevServerHandle, type RnDevServerOptions, type RnDevServerOptionsInput, waitForBuild, } from './dev-server/index.ts';
|
|
2
|
+
export { type AssetResolverOptions, type Broadcast, buildRnDevServerOptions, createBaseMiddleware, createBunDevHttpServer, createDevHttpServer, createHmrBridge, createPlatformState, type CliServerApi, type CliWebsocketEndpoint, type DevMiddleware, loadCliServerApi, loadDevMiddleware, type LoadCliServerApiOptions, type LoadDevMiddlewareOptions, createPlatformStateRegistry, type CustomizeFrame, type DevHttpServerDeps, type DevHttpServerHandle, type FrameInfo, getCachedSourceMap, type HmrBridge, type HmrBridgeOptions, applyCustomizeFrame, createSourceMapConsumer, extractCodeFrame, handleAssetRequest, handleBundleRequest, handleHmrMapRequest, handleIndexPage, handleMapRequest, handleSymbolicateRequest, isIndexRoute, isAssetRoute, isBundleRoute, isHmrMapRoute, isMapRoute, isSymbolicateRoute, normalizeFrame, symbolicateFrame, type SymbolicateCodeFrame, type SymbolicateRequest, type SymbolicateResponse, type Middleware, type MiddlewareEnhanceContext, type PlatformState, type PlatformStateCallbacks, type PlatformStateRegistry, colors, formatLogBadge, logBundle, logError, logInfo, logWarn, postProcessSourceMap, type SourcemapPathOptions, printZntcRnBanner, resolveAssetPath, type ServeRnExtras, serveRn, setupTerminalActions, type TerminalActionsCallbacks, type TerminalActionsOptions, type RnDevServerHandle, type RnDevServerOptions, type RnDevServerOptionsInput, waitForBuild, } from './dev-server/index.ts';
|
|
3
3
|
export { createMetroHmrAdapter, type MetroHmrAdapter } from './metro-hmr-adapter.ts';
|
|
4
4
|
export type { CustomResolver, MetroPlatform, Resolution, ResolutionContext, } from './metro-resolver-types.ts';
|
|
5
5
|
export { createAssetPlugin } from './plugins/asset.ts';
|
package/dist/index.js
CHANGED
|
@@ -87,7 +87,7 @@ const URI_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
|
|
87
87
|
if (parsedUrl.path.indexOf("//&") === -1) {
|
|
88
88
|
return urlToNormalize;
|
|
89
89
|
}
|
|
90
|
-
return
|
|
90
|
+
return parsedUrl.schemeAndAuthority + parsedUrl.path.replace("//&", "?") + (parsedUrl.queryWithoutQuestionMark.length > 0 ? "&" + parsedUrl.queryWithoutQuestionMark : "") + parsedUrl.fragmentWithHash;
|
|
91
91
|
}
|
|
92
92
|
function toJscSafeUrl(urlToConvert) {
|
|
93
93
|
if (!_rfc3986Parse(urlToConvert).hasQueryPart) {
|
|
@@ -97,7 +97,7 @@ const URI_REGEX = /^(([^:/?#]+):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
|
|
97
97
|
if (parsedUrl.queryWithoutQuestionMark.length > 0 && (parsedUrl.path === "" || parsedUrl.path === "/")) {
|
|
98
98
|
throw new Error(`The given URL "${urlToConvert}" has an empty path and cannot be converted to a JSC-safe format.`);
|
|
99
99
|
}
|
|
100
|
-
return
|
|
100
|
+
return parsedUrl.schemeAndAuthority + parsedUrl.path + (parsedUrl.queryWithoutQuestionMark.length > 0 ? "//&" + parsedUrl.queryWithoutQuestionMark.replace(/\?/g, "%3F") : "") + parsedUrl.fragmentWithHash;
|
|
101
101
|
}
|
|
102
102
|
module.exports = { isJscSafeUrl, toNormalUrl, toJscSafeUrl };
|
|
103
103
|
|
|
@@ -110,15 +110,6 @@ var require__disabled__url = __commonJS({
|
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
112
|
//#endregion
|
|
113
|
-
//#region (optional-missing):
|
|
114
|
-
var require__optional_missing__ = __commonJS({
|
|
115
|
-
"(optional-missing):"(exports, module) {
|
|
116
|
-
var e = new Error("Cannot find module \"" + "" + "\"");
|
|
117
|
-
e.code = "MODULE_NOT_FOUND";
|
|
118
|
-
throw e;
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
//#endregion
|
|
122
113
|
//#region array-set.js
|
|
123
114
|
var require_source_map_lib_array_set = __commonJS({
|
|
124
115
|
"array-set.js"(exports, module) {
|
|
@@ -260,7 +251,7 @@ var require_source_map_lib_mapping_list = __commonJS({
|
|
|
260
251
|
const util = require_source_map_lib_util();
|
|
261
252
|
function generatedPositionAfter(mappingA,mappingB) {
|
|
262
253
|
const lineA = mappingA.generatedLine,lineB = mappingB.generatedLine,columnA = mappingA.generatedColumn,columnB = mappingB.generatedColumn;
|
|
263
|
-
return
|
|
254
|
+
return lineB > lineA || lineB == lineA && columnB >= columnA || util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0;
|
|
264
255
|
}
|
|
265
256
|
class MappingList {
|
|
266
257
|
constructor() {
|
|
@@ -525,7 +516,7 @@ const util = require_source_map_lib_util(),binarySearch = require_source_map_lib
|
|
|
525
516
|
bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
|
|
526
517
|
}
|
|
527
518
|
let mapping;
|
|
528
|
-
this._wasm.withMappingCallback((m) =>
|
|
519
|
+
this._wasm.withMappingCallback((m) => mapping = m, () => {
|
|
529
520
|
this._wasm.exports.original_location_for(this._getMappingsPtr(), needle.generatedLine - 1, needle.generatedColumn, bias);
|
|
530
521
|
});
|
|
531
522
|
if (mapping) {
|
|
@@ -547,9 +538,9 @@ const util = require_source_map_lib_util(),binarySearch = require_source_map_lib
|
|
|
547
538
|
if (!this.sourcesContent) {
|
|
548
539
|
return false;
|
|
549
540
|
}
|
|
550
|
-
return
|
|
541
|
+
return this.sourcesContent.length >= this._sources.size() && !this.sourcesContent.some(function(sc) {
|
|
551
542
|
return sc == null;
|
|
552
|
-
})
|
|
543
|
+
});
|
|
553
544
|
}
|
|
554
545
|
sourceContentFor(aSource,nullOnMissing) {
|
|
555
546
|
if (!this.sourcesContent) {
|
|
@@ -582,7 +573,7 @@ const util = require_source_map_lib_util(),binarySearch = require_source_map_lib
|
|
|
582
573
|
bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
|
|
583
574
|
}
|
|
584
575
|
let mapping;
|
|
585
|
-
this._wasm.withMappingCallback((m) =>
|
|
576
|
+
this._wasm.withMappingCallback((m) => mapping = m, () => {
|
|
586
577
|
this._wasm.exports.generated_location_for(this._getMappingsPtr(), needle.source, needle.originalLine - 1, needle.originalColumn, bias);
|
|
587
578
|
});
|
|
588
579
|
if (mapping) {
|
|
@@ -616,7 +607,7 @@ const util = require_source_map_lib_util(),binarySearch = require_source_map_lib
|
|
|
616
607
|
throw new Error("Support for url field in sections not implemented.");
|
|
617
608
|
}
|
|
618
609
|
const offset = util.getArg(s, "offset"),offsetLine = util.getArg(offset, "line"),offsetColumn = util.getArg(offset, "column");
|
|
619
|
-
if (offsetLine < lastOffset.line ||
|
|
610
|
+
if (offsetLine < lastOffset.line || offsetLine === lastOffset.line && offsetColumn < lastOffset.column) {
|
|
620
611
|
throw new Error("Section offsets must be ordered and non-overlapping.");
|
|
621
612
|
}
|
|
622
613
|
lastOffset = offset;
|
|
@@ -645,7 +636,7 @@ const util = require_source_map_lib_util(),binarySearch = require_source_map_lib
|
|
|
645
636
|
if (cmp) {
|
|
646
637
|
return cmp;
|
|
647
638
|
}
|
|
648
|
-
return
|
|
639
|
+
return aNeedle.generatedColumn - (section.generatedOffset.generatedColumn - 1);
|
|
649
640
|
}),section = this._sections[sectionIndex];
|
|
650
641
|
if (!section) {
|
|
651
642
|
return { source: null, line: null, column: null, name: null };
|
|
@@ -1511,13 +1502,13 @@ exports.SourceMapGenerator = require_source_map_lib_source_map_generator().Sourc
|
|
|
1511
1502
|
});
|
|
1512
1503
|
//#endregion
|
|
1513
1504
|
//#region protocol.ts
|
|
1514
|
-
const HMR_MSG = Object.freeze({ Connected: "connected", CssUpdate: "css-update", ClearError: "clear-error", Error: "error", FullReload: "full-reload" }),HMR_WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
1505
|
+
const HMR_MSG = Object.freeze({ Connected: "connected", CssUpdate: "css-update", ClearError: "clear-error", Error: "error", FullReload: "full-reload", UpdateStart: "update-start", Update: "update", UpdateDone: "update-done" }),HMR_WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
1515
1506
|
function normalizeHmrErrors(errors) {
|
|
1516
1507
|
if (!Array.isArray(errors) || errors.length === 0) {
|
|
1517
1508
|
return [{ file: "", message: "Unknown build error" }];
|
|
1518
1509
|
}
|
|
1519
1510
|
return errors.map((error) => {
|
|
1520
|
-
const e =
|
|
1511
|
+
const e = error ?? {},file = typeof e.location?.file == "string" ? e.location.file : "",message = String(e.text ?? e.message ?? error);
|
|
1521
1512
|
return { file, message };
|
|
1522
1513
|
});
|
|
1523
1514
|
}
|
|
@@ -1585,8 +1576,8 @@ function parseTextFrame(buffer) {
|
|
|
1585
1576
|
//#endregion
|
|
1586
1577
|
//#region hmr-channel.ts
|
|
1587
1578
|
function extractErrorText(error) {
|
|
1588
|
-
const e =
|
|
1589
|
-
return
|
|
1579
|
+
const e = error ?? {};
|
|
1580
|
+
return typeof e.stack == "string" && e.stack || typeof e.message == "string" && e.message || String(error);
|
|
1590
1581
|
}
|
|
1591
1582
|
function createHmrChannel() {
|
|
1592
1583
|
const nodeSockets = new Set(),bunClients = new Set(),incomingHandlers = [],connectedText = JSON.stringify({ type: HMR_MSG.Connected });
|
|
@@ -1614,14 +1605,15 @@ function createHmrChannel() {
|
|
|
1614
1605
|
let recvBuffer = Buffer.alloc(0);
|
|
1615
1606
|
socket.on("data", (chunk) => {
|
|
1616
1607
|
if (incomingHandlers.length === 0)return;
|
|
1617
|
-
recvBuffer =
|
|
1608
|
+
recvBuffer = recvBuffer.length === 0 ? chunk : Buffer.concat([recvBuffer, chunk]);
|
|
1609
|
+
const reply = (text) => writeTextFrame(socket, text);
|
|
1618
1610
|
while (recvBuffer.length > 0) {
|
|
1619
1611
|
const parsed = parseTextFrame(recvBuffer);
|
|
1620
1612
|
if (!parsed)break;
|
|
1621
1613
|
recvBuffer = recvBuffer.subarray(parsed.consumed);
|
|
1622
1614
|
for (const handler of incomingHandlers) {
|
|
1623
1615
|
try {
|
|
1624
|
-
handler(parsed.text,
|
|
1616
|
+
handler(parsed.text, reply);
|
|
1625
1617
|
} catch {
|
|
1626
1618
|
}
|
|
1627
1619
|
}
|
|
@@ -1635,6 +1627,15 @@ function createHmrChannel() {
|
|
|
1635
1627
|
greetBun(ws);
|
|
1636
1628
|
}, removeBunClient(ws) {
|
|
1637
1629
|
bunClients.delete(ws);
|
|
1630
|
+
}, dispatchBunIncoming(ws,text) {
|
|
1631
|
+
if (incomingHandlers.length === 0)return;
|
|
1632
|
+
const reply = (out) => ws.send(out);
|
|
1633
|
+
for (const handler of incomingHandlers) {
|
|
1634
|
+
try {
|
|
1635
|
+
handler(text, reply);
|
|
1636
|
+
} catch {
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1638
1639
|
}, onIncoming(handler) {
|
|
1639
1640
|
incomingHandlers.push(handler);
|
|
1640
1641
|
}, broadcast(message) {
|
|
@@ -1698,7 +1699,7 @@ const BANNER_WIDTH = 59;
|
|
|
1698
1699
|
function bannerLine(content) {
|
|
1699
1700
|
const visibleLen = content.replace(/\x1b\[[0-9;]*m/g, "").length,padding = Math.max(0, BANNER_WIDTH - visibleLen),left = Math.floor(padding / 2);
|
|
1700
1701
|
;
|
|
1701
|
-
return `${colors.cyan} ║${colors.reset}${" ".repeat(left)}${content}${" ".repeat(
|
|
1702
|
+
return `${colors.cyan} ║${colors.reset}${" ".repeat(left)}${content}${" ".repeat(padding - left)}${colors.cyan}║${colors.reset}`;
|
|
1702
1703
|
}
|
|
1703
1704
|
const ZNTC_ASCII = ["▀▀▀▀▀▀▀▀", "▀▀▀▀▀▀▀▀▀▀▀▀", "▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀", "▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀", "▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"],ZNTC_GRADIENT = [colors.brightYellow, colors.brightYellow, colors.yellow, colors.brightRed, colors.brightRed];
|
|
1704
1705
|
if (ZNTC_ASCII.length !== ZNTC_GRADIENT.length) {
|
|
@@ -1736,7 +1737,8 @@ function buildOnRebuild(adapter,opts={}) {
|
|
|
1736
1737
|
const annotated = annotateUpdates(event.updates, state.platform);
|
|
1737
1738
|
adapter.sendUpdate(annotated);
|
|
1738
1739
|
if (!opts.silent) {
|
|
1739
|
-
|
|
1740
|
+
const profileOn = process.env.ZNTC_PROFILE != null && process.env.ZNTC_PROFILE !== "",pd = profileOn ? event.phaseDurations : undefined,r = (n) => n == null ? "-" : n.toFixed(0),bd = pd ? ` ${colors.dim}[det=${r(pd.detect)} grph=${r(pd.graph)} lnk=${r(pd.link)} shk=${r(pd.shake)} emt=${r(pd.emit)} dlt=${r(pd.delta)} | gDisc=${r(pd.graphDiscover)} gBld=${r(pd.graphBuild)} eOut=${r(pd.emitOutput)} eMod=${r(pd.emitModulePass)} eCat=${r(pd.emitConcat)}]${colors.reset}` : "";
|
|
1741
|
+
logInfo(`HMR update ${colors.dim}[${state.platform}] ${event.updates.length} module(s) (${ms}ms)${colors.reset}${bd}`);
|
|
1740
1742
|
}
|
|
1741
1743
|
return;
|
|
1742
1744
|
}
|
|
@@ -1746,7 +1748,7 @@ function buildOnRebuild(adapter,opts={}) {
|
|
|
1746
1748
|
};
|
|
1747
1749
|
}
|
|
1748
1750
|
function buildIncomingHandler(adapter,getLogsEnabled) {
|
|
1749
|
-
return (text,
|
|
1751
|
+
return (text, reply) => {
|
|
1750
1752
|
let msg = null;
|
|
1751
1753
|
try {
|
|
1752
1754
|
msg = JSON.parse(text);
|
|
@@ -1755,7 +1757,7 @@ function buildIncomingHandler(adapter,getLogsEnabled) {
|
|
|
1755
1757
|
}
|
|
1756
1758
|
if (!msg || typeof msg.type != "string")return;
|
|
1757
1759
|
if (msg.type === "register-entrypoints") {
|
|
1758
|
-
|
|
1760
|
+
reply(ACK_TEXT);
|
|
1759
1761
|
return;
|
|
1760
1762
|
}
|
|
1761
1763
|
if (msg.type === "log") {
|
|
@@ -1776,10 +1778,6 @@ function buildIncomingHandler(adapter,getLogsEnabled) {
|
|
|
1776
1778
|
};
|
|
1777
1779
|
}
|
|
1778
1780
|
const ACK_TEXT = JSON.stringify({ type: "bundle-registered" });
|
|
1779
|
-
function buildAckFrame() {
|
|
1780
|
-
const payload = Buffer.from(ACK_TEXT);
|
|
1781
|
-
return Buffer.concat([Buffer.from([0x81, payload.length]), payload]);
|
|
1782
|
-
}
|
|
1783
1781
|
function createHmrBridge(options) {
|
|
1784
1782
|
const adapter = createMetroHmrAdapter(),onRebuild = buildOnRebuild(adapter, { silent: options.silent });
|
|
1785
1783
|
let logsEnabled = options.forwardClientLogs !== false;
|
|
@@ -1787,12 +1785,118 @@ function createHmrBridge(options) {
|
|
|
1787
1785
|
return { adapter, callbacks: { onRebuild }, path: options.path, acceptUpgrade(req,socket) {
|
|
1788
1786
|
adapter.channel.accept(req, socket);
|
|
1789
1787
|
adapter.sendInitialGreeting();
|
|
1788
|
+
}, acceptBun(ws) {
|
|
1789
|
+
adapter.channel.addBunClient(ws);
|
|
1790
|
+
adapter.sendInitialGreeting();
|
|
1791
|
+
}, removeBun(ws) {
|
|
1792
|
+
adapter.channel.removeBunClient(ws);
|
|
1793
|
+
}, handleBunMessage(ws,text) {
|
|
1794
|
+
adapter.channel.dispatchBunIncoming(ws, text);
|
|
1790
1795
|
}, toggleLogs() {
|
|
1791
1796
|
logsEnabled = !logsEnabled;
|
|
1792
1797
|
return logsEnabled;
|
|
1793
1798
|
} };
|
|
1794
1799
|
}
|
|
1795
1800
|
//#endregion
|
|
1801
|
+
//#region bun-http-adapter.ts
|
|
1802
|
+
function createResponseShim(done) {
|
|
1803
|
+
const captured = { statusCode: 200, headers: {}, chunks: [] };
|
|
1804
|
+
let ended = false,headersSent = false;
|
|
1805
|
+
const applyHeaders = (headers) => {
|
|
1806
|
+
if (!headers)return;
|
|
1807
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
1808
|
+
captured.headers[k.toLowerCase()] = Array.isArray(v) ? v.join(", ") : String(v);
|
|
1809
|
+
}
|
|
1810
|
+
},shim = { get statusCode() {
|
|
1811
|
+
return captured.statusCode;
|
|
1812
|
+
}, set statusCode(code) {
|
|
1813
|
+
captured.statusCode = code;
|
|
1814
|
+
}, get headersSent() {
|
|
1815
|
+
return headersSent;
|
|
1816
|
+
}, get writableEnded() {
|
|
1817
|
+
return ended;
|
|
1818
|
+
}, setHeader(name,value) {
|
|
1819
|
+
captured.headers[String(name).toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1820
|
+
}, getHeader(name) {
|
|
1821
|
+
return captured.headers[String(name).toLowerCase()];
|
|
1822
|
+
}, writeHead(statusCode,headersOrReason,maybeHeaders) {
|
|
1823
|
+
captured.statusCode = statusCode;
|
|
1824
|
+
const headers = headersOrReason && typeof headersOrReason == "object" ? headersOrReason : maybeHeaders ?? undefined;
|
|
1825
|
+
applyHeaders(headers);
|
|
1826
|
+
headersSent = true;
|
|
1827
|
+
return shim;
|
|
1828
|
+
}, write(chunk) {
|
|
1829
|
+
if (chunk != null) {
|
|
1830
|
+
captured.chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
1831
|
+
}
|
|
1832
|
+
headersSent = true;
|
|
1833
|
+
return true;
|
|
1834
|
+
}, end(chunk) {
|
|
1835
|
+
if (ended)return shim;
|
|
1836
|
+
if (chunk != null) {
|
|
1837
|
+
captured.chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
1838
|
+
}
|
|
1839
|
+
ended = true;
|
|
1840
|
+
headersSent = true;
|
|
1841
|
+
done(captured);
|
|
1842
|
+
return shim;
|
|
1843
|
+
} };
|
|
1844
|
+
return { res: shim, captured };
|
|
1845
|
+
}
|
|
1846
|
+
function createRequestShim(req,rawBody) {
|
|
1847
|
+
const u = new URL(req.url),headers = {};
|
|
1848
|
+
req.headers.forEach((value, key) => {
|
|
1849
|
+
headers[key.toLowerCase()] = value;
|
|
1850
|
+
});
|
|
1851
|
+
const shim = { url: u.pathname + u.search, method: req.method, headers, complete: true, readable: false, rawBody, on() {
|
|
1852
|
+
return shim;
|
|
1853
|
+
} };
|
|
1854
|
+
return shim;
|
|
1855
|
+
}
|
|
1856
|
+
async function runMiddlewareForBun(middleware,req) {
|
|
1857
|
+
let rawBody = null;
|
|
1858
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
1859
|
+
try {
|
|
1860
|
+
rawBody = await req.text();
|
|
1861
|
+
} catch {
|
|
1862
|
+
rawBody = null;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
const reqShim = createRequestShim(req, rawBody);
|
|
1866
|
+
return new Promise((resolve) => {
|
|
1867
|
+
let settled = false;
|
|
1868
|
+
const toResponse = (c) => {
|
|
1869
|
+
const body = c.chunks.length === 0 ? null : new Uint8Array(Buffer.concat(c.chunks));
|
|
1870
|
+
return new Response(body, { status: c.statusCode, headers: c.headers });
|
|
1871
|
+
},{ res:res } = createResponseShim((captured) => {
|
|
1872
|
+
if (settled)return;
|
|
1873
|
+
settled = true;
|
|
1874
|
+
resolve(toResponse(captured));
|
|
1875
|
+
});
|
|
1876
|
+
try {
|
|
1877
|
+
middleware(reqShim, res, (err) => {
|
|
1878
|
+
if (settled)return;
|
|
1879
|
+
settled = true;
|
|
1880
|
+
if (err) {
|
|
1881
|
+
const msg = err?.message ?? String(err);
|
|
1882
|
+
resolve(new Response(`Internal Server Error: ${msg}`, { status: 500 }));
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
if (!res.headersSent && !res.writableEnded) {
|
|
1886
|
+
resolve(new Response("Not Found", { status: 404 }));
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
resolve(new Response(null, { status: res.statusCode }));
|
|
1890
|
+
});
|
|
1891
|
+
} catch (err) {
|
|
1892
|
+
if (settled)return;
|
|
1893
|
+
settled = true;
|
|
1894
|
+
const msg = err?.message ?? String(err);
|
|
1895
|
+
resolve(new Response(`Internal Server Error: ${msg}`, { status: 500 }));
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
//#endregion
|
|
1796
1900
|
//#region http-utils.ts
|
|
1797
1901
|
function sendText(res,statusCode,text,contentType="text/plain") {
|
|
1798
1902
|
res.writeHead(statusCode, { "Content-Type": contentType, "Content-Length": Buffer.byteLength(text) });
|
|
@@ -1979,7 +2083,7 @@ function resolveAssetPath(urlPathname,opts) {
|
|
|
1979
2083
|
}
|
|
1980
2084
|
if (normalizedPath.startsWith("node_modules")) {
|
|
1981
2085
|
try {
|
|
1982
|
-
const modulePath = normalizedPath.replace(/^node_modules[/\\]/, ""),packageName = modulePath.startsWith("@") ?
|
|
2086
|
+
const modulePath = normalizedPath.replace(/^node_modules[/\\]/, ""),packageName = modulePath.startsWith("@") ? modulePath.match(/^(@[^/\\]+[/\\][^/\\]+)/)?.[1] ?? modulePath.split(sep)[0] : modulePath.split(sep)[0];
|
|
1983
2087
|
if (!packageName)return null;
|
|
1984
2088
|
const packageRelativePath = modulePath.slice(packageName.length + 1);
|
|
1985
2089
|
let packageJsonPath;
|
|
@@ -2024,7 +2128,7 @@ async function handleAssetRequest(_req,res,url,opts) {
|
|
|
2024
2128
|
const ext = extname(resolved).toLowerCase();
|
|
2025
2129
|
;
|
|
2026
2130
|
const content = await readFile(resolved);
|
|
2027
|
-
res.writeHead(200, { "Content-Type":
|
|
2131
|
+
res.writeHead(200, { "Content-Type": CONTENT_TYPE_MAP[ext] ?? "application/octet-stream", "Cache-Control": "public, max-age=31536000", "Content-Length": content.length });
|
|
2028
2132
|
res.end(content);
|
|
2029
2133
|
} catch {
|
|
2030
2134
|
sendText(res, 500, "Internal Server Error");
|
|
@@ -2122,7 +2226,10 @@ function createMetroResolveRequestPlugin(opts) {
|
|
|
2122
2226
|
try {
|
|
2123
2227
|
const result = resolveRequest({ originModulePath: args.importer ?? "", platform: metroPlatform, resolveRequest: fallbackResolver }, args.path, metroPlatform);
|
|
2124
2228
|
if (result.type === "sourceFile")return { path: result.filePath };
|
|
2125
|
-
if (result.type === "assetFiles")
|
|
2229
|
+
if (result.type === "assetFiles") {
|
|
2230
|
+
const assetPath = result.filePaths[0];
|
|
2231
|
+
return assetPath ? { path: assetPath } : null;
|
|
2232
|
+
}
|
|
2126
2233
|
if (result.type === "empty")return { disabled: true };
|
|
2127
2234
|
} catch (err) {
|
|
2128
2235
|
if (err.message === DELEGATE_TO_DEFAULT_SENTINEL)return null;
|
|
@@ -2276,7 +2383,7 @@ function deepMerge(base,override) {
|
|
|
2276
2383
|
return result;
|
|
2277
2384
|
}
|
|
2278
2385
|
function buildRnBundleOptions(input) {
|
|
2279
|
-
const { entry:entry, projectRoot:projectRoot, rnPlatform:rnPlatform, dev:dev, sourcemap:sourcemap, minify:minify, dropConsole:dropConsole, dropDebugger:dropDebugger, extra:extra } = input;
|
|
2386
|
+
const { entry:entry, projectRoot:projectRoot, rnPlatform:rnPlatform, dev:dev, sourcemap:sourcemap, minify:minify, watchDelay:watchDelay, dropConsole:dropConsole, dropDebugger:dropDebugger, extra:extra } = input;
|
|
2280
2387
|
if (extra?.platforms && !extra.platforms.includes(rnPlatform)) {
|
|
2281
2388
|
throw new Error(`extra.platforms (${JSON.stringify(extra.platforms)}) does not include the active rnPlatform '${rnPlatform}'`);
|
|
2282
2389
|
}
|
|
@@ -2299,7 +2406,7 @@ function buildRnBundleOptions(input) {
|
|
|
2299
2406
|
if (extra?.prelude && extra.prelude.length > 0) {
|
|
2300
2407
|
for (const p of extra.prelude)runBeforeMain.push(resolve$4(projectRoot, p));
|
|
2301
2408
|
}
|
|
2302
|
-
const define = { global: "__ZNTC_RN_GLOBAL__", __DEV__: String(dev), "process.env.NODE_ENV": `"${dev ? "development" : "production"}"`, "process.env.EXPO_ROUTER_APP_ROOT": JSON.stringify(resolve$4(projectRoot, "app")), "process.env.EXPO_ROUTER_IMPORT_MODE": "\"sync\"", "process.env.EXPO_OS": `"${rnPlatform}"` },baseLoader = buildAssetLoaders(assetExts),preset = { entryPoints: [resolve$4(projectRoot, entry)], platform: "react-native", sourcemap: sourcemap ?? dev, minify: minify ?? false, dropConsole: dropConsole ?? false, dropDebugger: dropDebugger ?? false, plugins, emitDiskSourcemap: !dev, target: "es5", flow: true, jsxInJs: true, configurableExports: true, strictExecutionOrder: true, inlineDynamicImports: true, workletTransform: true, codegenTransform: true, resolveExtensions: buildResolveExtensions(rnPlatform, sourceExts), mainFields: ["react-native", "browser", "main"], loader: baseLoader, alias: buildRnSingletonAliases(projectRoot), preserveSymlinks: true, resolveSymlinkSiblings: true, define, banner: buildPrelude(input), globalIdentifiers: [...RN_GLOBAL_IDENTIFIERS] };
|
|
2409
|
+
const define = { global: "__ZNTC_RN_GLOBAL__", __DEV__: String(dev), "process.env.NODE_ENV": `"${dev ? "development" : "production"}"`, "process.env.EXPO_ROUTER_APP_ROOT": JSON.stringify(resolve$4(projectRoot, "app")), "process.env.EXPO_ROUTER_IMPORT_MODE": "\"sync\"", "process.env.EXPO_OS": `"${rnPlatform}"` },baseLoader = buildAssetLoaders(assetExts),preset = { entryPoints: [resolve$4(projectRoot, entry)], platform: "react-native", sourcemap: sourcemap ?? dev, minify: minify ?? false, ...watchDelay !== undefined ? { watchDelay } : {}, dropConsole: dropConsole ?? false, dropDebugger: dropDebugger ?? false, plugins, emitDiskSourcemap: !dev, target: "es5", flow: true, jsxInJs: true, configurableExports: true, strictExecutionOrder: true, inlineDynamicImports: true, workletTransform: true, codegenTransform: true, preserveSafePlugins: extra?.preserveSafePlugins ?? !(extra?.additionalPlugins && extra.additionalPlugins.length > 0 || extra?.metroResolveRequest || extra?.babelTransformerPath), resolveExtensions: buildResolveExtensions(rnPlatform, sourceExts), mainFields: ["react-native", "browser", "main"], loader: baseLoader, alias: buildRnSingletonAliases(projectRoot), preserveSymlinks: true, resolveSymlinkSiblings: true, define, banner: buildPrelude(input), globalIdentifiers: [...RN_GLOBAL_IDENTIFIERS] };
|
|
2303
2410
|
if (polyfills.length > 0)preset.polyfills = polyfills;
|
|
2304
2411
|
if (runBeforeMain.length > 0)preset.runBeforeMain = runBeforeMain;
|
|
2305
2412
|
const workletVersion = resolveWorkletPluginVersion(projectRoot, input.workletPluginVersion);
|
|
@@ -2366,7 +2473,7 @@ function isVirtualSource(stripped) {
|
|
|
2366
2473
|
function isFrameworkSource(src) {
|
|
2367
2474
|
if (typeof src != "string")return false;
|
|
2368
2475
|
const stripped = stripVirtualPrefix(src);
|
|
2369
|
-
return
|
|
2476
|
+
return stripped === "__prelude__" || stripped.includes("?ctx=") || /(?:^|[/\\])node_modules[/\\]/.test(stripped) || stripped.startsWith("zntc:");
|
|
2370
2477
|
}
|
|
2371
2478
|
function postProcessSourceMap(rawJson,pathOpts) {
|
|
2372
2479
|
try {
|
|
@@ -2376,7 +2483,7 @@ function postProcessSourceMap(rawJson,pathOpts) {
|
|
|
2376
2483
|
if (pathOpts?.sourceRoot === undefined && map.sourceRoot === "") {
|
|
2377
2484
|
delete map.sourceRoot;
|
|
2378
2485
|
}
|
|
2379
|
-
const existing = new Set([...
|
|
2486
|
+
const existing = new Set([...Array.isArray(map.x_google_ignoreList) ? map.x_google_ignoreList : [], ...Array.isArray(map.ignoreList) ? map.ignoreList : []].filter((v) => Number.isInteger(v) && v >= 0));
|
|
2380
2487
|
for (let i = 0; i < map.sources.length; i++) {
|
|
2381
2488
|
if (isFrameworkSource(map.sources[i]))existing.add(i);
|
|
2382
2489
|
}
|
|
@@ -2427,28 +2534,42 @@ function getBundleText(result) {
|
|
|
2427
2534
|
function getBundleSourceMapText(result) {
|
|
2428
2535
|
return result.outputFiles.find((file) => file.path.endsWith(".map"))?.text ?? null;
|
|
2429
2536
|
}
|
|
2430
|
-
function
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
state.bundleStale = false;
|
|
2443
|
-
}).catch((err) => {
|
|
2444
|
-
state.bundle = null;
|
|
2445
|
-
state.buildError = err instanceof Error ? err.message : String(err);
|
|
2446
|
-
state.bundleStale = false;
|
|
2537
|
+
function createBundleRefresher(deps) {
|
|
2538
|
+
let inFlight = null,generation = 0;
|
|
2539
|
+
function markStale() {
|
|
2540
|
+
deps.setStale();
|
|
2541
|
+
generation += 1;
|
|
2542
|
+
}
|
|
2543
|
+
function refresh() {
|
|
2544
|
+
if (deps.isFresh())return Promise.resolve();
|
|
2545
|
+
if (inFlight)return inFlight;
|
|
2546
|
+
const startGen = generation;
|
|
2547
|
+
inFlight = deps.build().then(() => {
|
|
2548
|
+
if (generation === startGen)deps.clearStale();
|
|
2447
2549
|
}).finally(() => {
|
|
2448
|
-
|
|
2550
|
+
inFlight = null;
|
|
2449
2551
|
});
|
|
2450
|
-
return
|
|
2552
|
+
return inFlight;
|
|
2451
2553
|
}
|
|
2554
|
+
return { markStale, refresh };
|
|
2555
|
+
}
|
|
2556
|
+
function createPlatformState(options,platform,callbacks) {
|
|
2557
|
+
const outputDir = mkdtempSync(join$2(tmpdir(), `zntc-rn-${platform}-`)),outputPath = join$2(outputDir, "bundle.js"),platformBundle = { ...options.bundle, rnPlatform: platform };
|
|
2558
|
+
let refresher;
|
|
2559
|
+
const state = { platform, outputDir, outputPath, handle: undefined, bundle: null, bundleStale: false, refreshBundle: () => refresher.refresh(), sourceMapCache: null, buildError: null, fileCount: 1, lastRebuildTime: Date.now() };
|
|
2560
|
+
refresher = createBundleRefresher({ isFresh: () => !state.bundleStale && state.bundle !== null, build: () => bundleRn(platformBundle).then((result) => {
|
|
2561
|
+
state.bundle = getBundleText(result);
|
|
2562
|
+
const sourceMap = getBundleSourceMapText(result);
|
|
2563
|
+
state.sourceMapCache = sourceMap ? postProcessSourceMap(sourceMap) : null;
|
|
2564
|
+
state.buildError = null;
|
|
2565
|
+
}).catch((err) => {
|
|
2566
|
+
state.bundle = null;
|
|
2567
|
+
state.buildError = err instanceof Error ? err.message : String(err);
|
|
2568
|
+
}), setStale: () => {
|
|
2569
|
+
state.bundleStale = true;
|
|
2570
|
+
}, clearStale: () => {
|
|
2571
|
+
state.bundleStale = false;
|
|
2572
|
+
} });
|
|
2452
2573
|
if (process.env.ZNTC_DEBUG_TERMINAL === "1") {
|
|
2453
2574
|
process.stderr.write(`[zntc:rn-dev:debug] watchRn[${platform}] sourcemap=${platformBundle.sourcemap} dev=${platformBundle.dev} outfile=${outputPath}\n`);
|
|
2454
2575
|
}
|
|
@@ -2472,7 +2593,7 @@ function createPlatformState(options,platform,callbacks) {
|
|
|
2472
2593
|
state.sourceMapCache = null;
|
|
2473
2594
|
if (platformBundle.dev) {
|
|
2474
2595
|
if (event.graphChanged) {
|
|
2475
|
-
|
|
2596
|
+
refresher.markStale();
|
|
2476
2597
|
return state.refreshBundle().then(() => {
|
|
2477
2598
|
if (state.buildError) {
|
|
2478
2599
|
callbacks?.onRebuild?.(state, { ...event, success: false, error: state.buildError });
|
|
@@ -2482,7 +2603,7 @@ function createPlatformState(options,platform,callbacks) {
|
|
|
2482
2603
|
});
|
|
2483
2604
|
}
|
|
2484
2605
|
if (event.updates && event.updates.length > 0) {
|
|
2485
|
-
|
|
2606
|
+
refresher.markStale();
|
|
2486
2607
|
}
|
|
2487
2608
|
callbacks?.onRebuild?.(state, event);
|
|
2488
2609
|
return;
|
|
@@ -2527,7 +2648,7 @@ function createPlatformStateRegistry(options,callbacks) {
|
|
|
2527
2648
|
function resolvePlatform(url,registry,defaultPlatform) {
|
|
2528
2649
|
const param = url.searchParams.get("platform");
|
|
2529
2650
|
;
|
|
2530
|
-
return registry.getOrCreate(
|
|
2651
|
+
return registry.getOrCreate(param === "ios" || param === "android" ? param : defaultPlatform);
|
|
2531
2652
|
}
|
|
2532
2653
|
//#endregion
|
|
2533
2654
|
//#region bundle.ts
|
|
@@ -2653,9 +2774,23 @@ function isOpenUrlRoute(pathname,method) {
|
|
|
2653
2774
|
}
|
|
2654
2775
|
function resolveOpener(platform=process.platform) {
|
|
2655
2776
|
if (platform === "darwin")return { command: "open", args: (t) => [t] };
|
|
2656
|
-
if (platform === "win32")return { command: "
|
|
2777
|
+
if (platform === "win32")return { command: "rundll32", args: (t) => ["url.dll,FileProtocolHandler", t] };
|
|
2657
2778
|
return { command: "xdg-open", args: (t) => [t] };
|
|
2658
2779
|
}
|
|
2780
|
+
const ALLOWED_PROTOCOLS = new Set(["http:", "https:"]);
|
|
2781
|
+
function isSafeBrowserUrl(target) {
|
|
2782
|
+
for (let i = 0; i < target.length; i += 1) {
|
|
2783
|
+
const c = target.charCodeAt(i);
|
|
2784
|
+
if (c <= 0x20 || c === 0x22 || c === 0x27 || c === 0x5c)return false;
|
|
2785
|
+
}
|
|
2786
|
+
let parsed;
|
|
2787
|
+
try {
|
|
2788
|
+
parsed = new URL(target);
|
|
2789
|
+
} catch {
|
|
2790
|
+
return false;
|
|
2791
|
+
}
|
|
2792
|
+
return ALLOWED_PROTOCOLS.has(parsed.protocol);
|
|
2793
|
+
}
|
|
2659
2794
|
async function handleOpenUrl(req,res,spawner=spawn,platform=process.platform) {
|
|
2660
2795
|
let body = {};
|
|
2661
2796
|
try {
|
|
@@ -2669,6 +2804,10 @@ async function handleOpenUrl(req,res,spawner=spawn,platform=process.platform) {
|
|
|
2669
2804
|
sendJson(res, 400, { error: "Invalid URL" });
|
|
2670
2805
|
return;
|
|
2671
2806
|
}
|
|
2807
|
+
if (!isSafeBrowserUrl(target)) {
|
|
2808
|
+
sendJson(res, 400, { error: "URL must be an http(s) URL without control characters" });
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2672
2811
|
try {
|
|
2673
2812
|
const { command:command, args:args } = resolveOpener(platform),child = spawner(command, args(target), { detached: true, stdio: "ignore" });
|
|
2674
2813
|
child.unref();
|
|
@@ -2706,7 +2845,7 @@ async function createSourceMapConsumer(sourceMapJson) {
|
|
|
2706
2845
|
return null;
|
|
2707
2846
|
}
|
|
2708
2847
|
try {
|
|
2709
|
-
const mod =
|
|
2848
|
+
const mod = await Promise.resolve().then(()=>require_source_map_source_map());
|
|
2710
2849
|
return await new mod.SourceMapConsumer(parsed);
|
|
2711
2850
|
} catch {
|
|
2712
2851
|
return null;
|
|
@@ -2914,7 +3053,53 @@ async function createDevHttpServer(options,deps) {
|
|
|
2914
3053
|
return { server, url: `http://${options.host}:${options.port}`, port: options.port, stop: () => new Promise((resolve, reject) => {
|
|
2915
3054
|
server.removeListener("request", requestHandler);
|
|
2916
3055
|
if (upgradeHandler)server.removeListener("upgrade", upgradeHandler);
|
|
2917
|
-
server.close((err) =>
|
|
3056
|
+
server.close((err) => err ? reject(err) : resolve());
|
|
3057
|
+
}) };
|
|
3058
|
+
}
|
|
3059
|
+
function createBunHttpServerShim() {
|
|
3060
|
+
let warned = false;
|
|
3061
|
+
const shim = { on(event) {
|
|
3062
|
+
if (event === "upgrade" && !warned) {
|
|
3063
|
+
warned = true;
|
|
3064
|
+
process.stderr.write(`[zntc:rn-dev] Bun runtime: enhanceMiddleware 의 httpServer.on('upgrade') 는 ` + `Bun.serve 에서 지원되지 않습니다(raw socket 미노출). HMR(/hot)은 정상 동작하나 ` + `WebSocket upgrade 에 의존하는 enhanceMiddleware 기능(Rozenite 등)은 제한됩니다.\n`);
|
|
3065
|
+
}
|
|
3066
|
+
return shim;
|
|
3067
|
+
}, once() {
|
|
3068
|
+
return shim;
|
|
3069
|
+
}, removeListener() {
|
|
3070
|
+
return shim;
|
|
3071
|
+
}, off() {
|
|
3072
|
+
return shim;
|
|
3073
|
+
}, address() {
|
|
3074
|
+
return { port: 0 };
|
|
3075
|
+
} };
|
|
3076
|
+
return shim;
|
|
3077
|
+
}
|
|
3078
|
+
async function createBunDevHttpServer(options,deps) {
|
|
3079
|
+
const Bun = globalThis.Bun;
|
|
3080
|
+
if (!Bun)throw new Error("createBunDevHttpServer는 Bun runtime에서만 호출 가능합니다.");
|
|
3081
|
+
const baseMiddleware = createBaseMiddleware(options, deps),enhanced = options.enhanceMiddleware ? options.enhanceMiddleware(baseMiddleware, { httpServer: createBunHttpServerShim() }) : baseMiddleware,hmr = deps.hmrBridge,serveOpts = { port: options.port, hostname: options.host, async fetch(req,server) {
|
|
3082
|
+
const url = new URL(req.url);
|
|
3083
|
+
if (hmr && (url.pathname === hmr.path || url.pathname.startsWith(`${hmr.path}?`))) {
|
|
3084
|
+
if (server.upgrade(req))return undefined;
|
|
3085
|
+
return new Response("Upgrade required", { status: 426 });
|
|
3086
|
+
}
|
|
3087
|
+
return runMiddlewareForBun(enhanced, req);
|
|
3088
|
+
} };
|
|
3089
|
+
if (hmr) {
|
|
3090
|
+
serveOpts.websocket = { open(ws) {
|
|
3091
|
+
hmr.acceptBun(ws);
|
|
3092
|
+
}, message(ws,message) {
|
|
3093
|
+
const text = typeof message == "string" ? message : message.toString("utf-8");
|
|
3094
|
+
hmr.handleBunMessage(ws, text);
|
|
3095
|
+
}, close(ws) {
|
|
3096
|
+
hmr.removeBun(ws);
|
|
3097
|
+
} };
|
|
3098
|
+
}
|
|
3099
|
+
const server = Bun.serve(serveOpts);
|
|
3100
|
+
return { server: server, url: `http://${options.host}:${server.port}`, port: server.port, stop: () => new Promise((resolve) => {
|
|
3101
|
+
server.stop(true);
|
|
3102
|
+
resolve();
|
|
2918
3103
|
}) };
|
|
2919
3104
|
}
|
|
2920
3105
|
//#endregion
|
|
@@ -2937,7 +3122,7 @@ function resolveCliServerApiPath(projectRoot) {
|
|
|
2937
3122
|
}
|
|
2938
3123
|
async function loadCliServerApi(options) {
|
|
2939
3124
|
try {
|
|
2940
|
-
const resolvedPath = resolveCliServerApiPath(options.projectRoot),mod =
|
|
3125
|
+
const resolvedPath = resolveCliServerApiPath(options.projectRoot),mod = await import(resolvedPath),result = mod.createDevServerMiddleware({ port: options.port, host: options.host, watchFolders: [] });
|
|
2941
3126
|
return { websocketEndpoints: result.websocketEndpoints, broadcast: result.messageSocketEndpoint.broadcast };
|
|
2942
3127
|
} catch (err) {
|
|
2943
3128
|
if (process.env.ZNTC_DEBUG_TERMINAL === "1") {
|
|
@@ -3130,7 +3315,9 @@ async function serveRn(options,extras={}) {
|
|
|
3130
3315
|
process.stderr.write(`[zntc:rn-dev:debug] cli-server-api: ${cliServerApi ? "loaded" : "null (peer 미설치 또는 load 실패)"}\n`);
|
|
3131
3316
|
process.stderr.write(`[zntc:rn-dev:debug] dev-middleware: ${devMiddleware ? "loaded" : "null (peer 미설치)"}\n`);
|
|
3132
3317
|
}
|
|
3133
|
-
const hmrBridge = options.hmr ? createHmrBridge({ path: HMR_PATH, silent: extras.silent, forwardClientLogs: options.bundle.extra?.forwardClientLogs }) : undefined,platforms = createPlatformStateRegistry(options, hmrBridge?.callbacks),
|
|
3318
|
+
const hmrBridge = options.hmr ? createHmrBridge({ path: HMR_PATH, silent: extras.silent, forwardClientLogs: options.bundle.extra?.forwardClientLogs }) : undefined,platforms = createPlatformStateRegistry(options, hmrBridge?.callbacks),isBun = typeof globalThis.Bun < "u";
|
|
3319
|
+
;
|
|
3320
|
+
const httpHandle = await (isBun ? createBunDevHttpServer : createDevHttpServer)(options, { broadcast, platforms, hmrBridge, devMiddleware: devMiddleware ?? undefined, cliServerApi: cliServerApi ?? undefined }),buildStart = Date.now(),firstState = platforms.getOrCreate(options.bundle.rnPlatform);
|
|
3134
3321
|
await waitForBuild(firstState);
|
|
3135
3322
|
if (!extras.silent) {
|
|
3136
3323
|
if (firstState.buildError) {
|
|
@@ -3238,7 +3425,7 @@ function parserPluginsFor(filename) {
|
|
|
3238
3425
|
}
|
|
3239
3426
|
function isRootImportPluginName(name) {
|
|
3240
3427
|
const normalized = name.replace(/\\/g, "/");
|
|
3241
|
-
return
|
|
3428
|
+
return normalized === "root-import" || normalized === "babel-plugin-root-import" || normalized.endsWith("/babel-plugin-root-import") || normalized.includes("/babel-plugin-root-import/");
|
|
3242
3429
|
}
|
|
3243
3430
|
function injectRootImportRootOption(options,projectRoot) {
|
|
3244
3431
|
const next = options ? { ...options } : {},paths = next.paths;
|
|
@@ -3463,9 +3650,9 @@ function withExpo(config) {
|
|
|
3463
3650
|
if (winter)expoModules.push(winter);
|
|
3464
3651
|
if (metroRuntime)expoModules.push(metroRuntime);
|
|
3465
3652
|
const existingAssetExts = config.resolver?.assetExts ?? [],existingNormalized = new Set(existingAssetExts.map(normalizeExt));
|
|
3466
|
-
return { ...config, resolver: { ...config.resolver, assetExts: [...existingAssetExts, ...EXPO_ASSET_EXTS.filter((ext) => !existingNormalized.has(ext))], blockList: [...
|
|
3653
|
+
return { ...config, resolver: { ...config.resolver, assetExts: [...existingAssetExts, ...EXPO_ASSET_EXTS.filter((ext) => !existingNormalized.has(ext))], blockList: [...config.resolver?.blockList ?? [], ...EXPO_BLOCK_LIST] }, serializer: { ...config.serializer, prelude: [...config.serializer?.prelude ?? [], ...expoModules] }, server: { ...config.server, silentConsoleErrorPatterns: [...config.server?.silentConsoleErrorPatterns ?? [], WINTER_POLYFILL_WARNING_PATTERN] } };
|
|
3467
3654
|
}
|
|
3468
3655
|
//#endregion
|
|
3469
3656
|
//#region index.ts
|
|
3470
|
-
export { HMR_RN_MSG, buildRnDevServerOptions, createBaseMiddleware, createDevHttpServer, createHmrBridge, createPlatformState, loadCliServerApi, loadDevMiddleware, createPlatformStateRegistry, getCachedSourceMap, applyCustomizeFrame, createSourceMapConsumer, extractCodeFrame, handleAssetRequest, handleBundleRequest, handleHmrMapRequest, handleIndexPage, handleMapRequest, handleSymbolicateRequest, isIndexRoute, isAssetRoute, isBundleRoute, isHmrMapRoute, isMapRoute, isSymbolicateRoute, normalizeFrame, symbolicateFrame, colors, formatLogBadge, logBundle, logError, logInfo, logWarn, postProcessSourceMap, printZntcRnBanner, resolveAssetPath, serveRn, setupTerminalActions, waitForBuild, createMetroHmrAdapter, createAssetPlugin, createBabelPlugin, createBabelTransformer, detectCustomPlugins, isZntcNativePlugin, ZNTC_NATIVE_PLUGIN_PATTERNS, CODEGEN_NATIVE_COMPONENT_MARKER, createCodegenPlugin, createCodegenTransformer, escapeRegex, createMetroResolveRequestPlugin, createRequireContextPlugin, createStyledComponentsNativePlugin, disableStyledComponentsNativeDomProbe, STYLED_COMPONENTS_NATIVE_PATH_RE, buildRnBundleOptions, bundleRn, DEFAULT_ASSET_EXTS, watchRn, resolveRnPolyfills, RN_GLOBAL_IDENTIFIERS, tryResolve, HMR_CLIENT_SUFFIX, ZNTC_HMR_CLIENT_CODE, detectExpo, WINTER_POLYFILL_WARNING_PATTERN, withExpo };
|
|
3657
|
+
export { HMR_RN_MSG, buildRnDevServerOptions, createBaseMiddleware, createBunDevHttpServer, createDevHttpServer, createHmrBridge, createPlatformState, loadCliServerApi, loadDevMiddleware, createPlatformStateRegistry, getCachedSourceMap, applyCustomizeFrame, createSourceMapConsumer, extractCodeFrame, handleAssetRequest, handleBundleRequest, handleHmrMapRequest, handleIndexPage, handleMapRequest, handleSymbolicateRequest, isIndexRoute, isAssetRoute, isBundleRoute, isHmrMapRoute, isMapRoute, isSymbolicateRoute, normalizeFrame, symbolicateFrame, colors, formatLogBadge, logBundle, logError, logInfo, logWarn, postProcessSourceMap, printZntcRnBanner, resolveAssetPath, serveRn, setupTerminalActions, waitForBuild, createMetroHmrAdapter, createAssetPlugin, createBabelPlugin, createBabelTransformer, detectCustomPlugins, isZntcNativePlugin, ZNTC_NATIVE_PLUGIN_PATTERNS, CODEGEN_NATIVE_COMPONENT_MARKER, createCodegenPlugin, createCodegenTransformer, escapeRegex, createMetroResolveRequestPlugin, createRequireContextPlugin, createStyledComponentsNativePlugin, disableStyledComponentsNativeDomProbe, STYLED_COMPONENTS_NATIVE_PATH_RE, buildRnBundleOptions, bundleRn, DEFAULT_ASSET_EXTS, watchRn, resolveRnPolyfills, RN_GLOBAL_IDENTIFIERS, tryResolve, HMR_CLIENT_SUFFIX, ZNTC_HMR_CLIENT_CODE, detectExpo, WINTER_POLYFILL_WARNING_PATTERN, withExpo };
|
|
3471
3658
|
//#endregion
|
package/dist/preset.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface RnBundleInput {
|
|
|
12
12
|
dev: boolean;
|
|
13
13
|
/** sourcemap emit. dev 시 inline 권장. */
|
|
14
14
|
sourcemap?: boolean;
|
|
15
|
+
/** watch 디바운스(ms) — dev server watch 의 첫 이벤트 후 idle 병합 윈도우. 기본 16(native). */
|
|
16
|
+
watchDelay?: number;
|
|
15
17
|
/** prod build 의 minify. */
|
|
16
18
|
minify?: boolean;
|
|
17
19
|
/** console.* 호출 제거. Metro production Babel plugin 과 같은 정책을 원하는 release 경로에서 사용. */
|
|
@@ -49,6 +51,15 @@ export interface RnBundleInput {
|
|
|
49
51
|
metroResolveRequest?: CustomResolver;
|
|
50
52
|
/** Metro 호환 babel transformer path (svg-transformer 등). */
|
|
51
53
|
babelTransformerPath?: string;
|
|
54
|
+
/**
|
|
55
|
+
* HMR 위상 보존 plugin 게이트 명시 override. 미지정(undefined)이면 preset 이 보수적으로
|
|
56
|
+
* 판정한다 — 내장 plugin 만이면 true, additionalPlugins/metroResolveRequest/
|
|
57
|
+
* babelTransformerPath 중 하나라도 있으면 false. 사용자가 "내 resolver/transformer 가
|
|
58
|
+
* 결정적·모듈별 순수(같은 입력→같은 출력, 전역 상태 무의존)"임을 보장할 수 있으면 true 로
|
|
59
|
+
* 명시해 보존을 강제(=HMR rebuild 가속)할 수 있다. 비결정이면 stale 번들 위험은 사용자 책임.
|
|
60
|
+
* false 명시로 강제 비활성도 가능.
|
|
61
|
+
*/
|
|
62
|
+
preserveSafePlugins?: boolean;
|
|
52
63
|
/** RN sourceExts override (default RN preset). */
|
|
53
64
|
sourceExts?: string[];
|
|
54
65
|
/** RN assetExts override (default RN preset). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zntc/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "ZNTC React Native platform — Metro HMR adapter + RN preset (buildRnBundleOptions) + plugin factories + RN runtime",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bundler",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@react-native-community/cli-server-api": "^15.0.0",
|
|
51
|
-
"@zntc/core": "0.1.
|
|
51
|
+
"@zntc/core": "0.1.3",
|
|
52
52
|
"jsc-safe-url": "^0.2.4",
|
|
53
53
|
"source-map": "^0.7.4"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/node": "^25.5.2",
|
|
57
|
-
"@zntc/server": "0.1.
|
|
57
|
+
"@zntc/server": "0.1.3"
|
|
58
58
|
},
|
|
59
59
|
"optionalDependencies": {
|
|
60
60
|
"@babel/core": "^7.26.0",
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
var
|
|
8
|
+
// JSI 는 색상을 uint32 로 기대 — 음수(-1)는 HostFunction throw 위험. unsigned hex 사용.
|
|
9
|
+
var REFRESH_TEXT_COLOR = 0xffffffff; // #ffffff
|
|
10
|
+
var REFRESH_BACKGROUND_COLOR = 0xff2584e8; // #2584e8
|
|
10
11
|
var BUFFER_LIMIT = 1024 * 1024;
|
|
11
12
|
var prettyFormat = require('pretty-format');
|
|
12
13
|
var prettyFormatImpl = prettyFormat && (prettyFormat.default || prettyFormat);
|
|
@@ -68,13 +69,10 @@ function getDirectNativeDevLoadingView() {
|
|
|
68
69
|
|
|
69
70
|
function wrapNativeDevLoadingView(nativeDevLoadingView) {
|
|
70
71
|
return {
|
|
71
|
-
showMessage: function (message, _type,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
REFRESH_BACKGROUND_COLOR,
|
|
76
|
-
!!(options && options.dismissButton),
|
|
77
|
-
);
|
|
72
|
+
showMessage: function (message, _type, _options) {
|
|
73
|
+
// native DevLoadingView.showMessage 시그니처는 (message, color, backgroundColor) 3개.
|
|
74
|
+
// 4번째 인자를 넘기면 JSI HostFunction 이 arg-count mismatch 로 throw → 배너 미표시.
|
|
75
|
+
nativeDevLoadingView.showMessage(message, REFRESH_TEXT_COLOR, REFRESH_BACKGROUND_COLOR);
|
|
78
76
|
},
|
|
79
77
|
hide: function () {
|
|
80
78
|
nativeDevLoadingView.hide();
|
|
@@ -136,11 +134,25 @@ var HMRClient = {
|
|
|
136
134
|
},
|
|
137
135
|
|
|
138
136
|
_showRefreshing: function () {
|
|
137
|
+
this._showTime = Date.now();
|
|
139
138
|
this._safeCallDlv('showMessage', ['Refreshing...', 'refresh']);
|
|
140
139
|
},
|
|
141
140
|
|
|
141
|
+
// ZNTC 의 HMR apply 는 동기라 update-start→update-done 이 거의 즉시 일어난다.
|
|
142
|
+
// 그대로 hide 하면 'Refreshing...' 배너가 한 프레임도 못 그려지고 사라진다.
|
|
143
|
+
// 최소 표시 시간(MIN_SHOW_MS)을 보장해 Metro 와 체감을 맞춘다. 그 사이 새 update 가
|
|
144
|
+
// 시작되면(_pendingUpdates>0) hide 를 건너뛰어 배너를 유지한다.
|
|
142
145
|
_hideRefreshing: function () {
|
|
143
|
-
this
|
|
146
|
+
var self = this;
|
|
147
|
+
var MIN_SHOW_MS = 300;
|
|
148
|
+
var elapsed = Date.now() - (self._showTime || 0);
|
|
149
|
+
if (elapsed >= MIN_SHOW_MS) {
|
|
150
|
+
self._safeCallDlv('hide', []);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
setTimeout(function () {
|
|
154
|
+
if (self._pendingUpdates === 0) self._safeCallDlv('hide', []);
|
|
155
|
+
}, MIN_SHOW_MS - elapsed);
|
|
144
156
|
},
|
|
145
157
|
|
|
146
158
|
enable: function () {
|