rollipop 1.0.0-alpha.21 → 1.0.0-alpha.23
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/CHANGELOG.md +19 -0
- package/dist/{chunk-DEq-mXcV.js → _virtual/_rolldown/runtime.js} +1 -1
- package/dist/commands.d.ts +2 -4
- package/dist/commands.js +10 -3957
- package/dist/common/code.js +21 -0
- package/dist/common/constants.js +5 -0
- package/dist/common/env.js +33 -0
- package/dist/common/logger.d.ts +34 -0
- package/dist/common/logger.js +82 -0
- package/dist/common/logo.js +54 -0
- package/dist/common/progress-bar.js +167 -0
- package/dist/common/transformer.js +13 -0
- package/dist/common/types.d.ts +10 -0
- package/dist/config/compose-override.js +18 -0
- package/dist/config/defaults.d.ts +74 -0
- package/dist/config/defaults.js +74 -0
- package/dist/config/define-config.d.ts +13 -0
- package/dist/config/define-config.js +6 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +5 -0
- package/dist/config/load-config.d.ts +19 -0
- package/dist/config/load-config.js +73 -0
- package/dist/config/merge-config.d.ts +12 -0
- package/dist/config/merge-config.js +20 -0
- package/dist/config/types.d.ts +452 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.js +146 -0
- package/dist/core/assets.d.ts +91 -0
- package/dist/core/assets.js +244 -0
- package/dist/core/bundler.d.ts +15 -0
- package/dist/core/bundler.js +80 -0
- package/dist/core/env.d.ts +11 -0
- package/dist/core/env.js +36 -0
- package/dist/core/fs/data.js +9 -0
- package/dist/core/fs/storage.d.ts +15 -0
- package/dist/core/fs/storage.js +31 -0
- package/dist/core/plugins/babel-plugin.d.ts +22 -0
- package/dist/core/plugins/babel-plugin.js +74 -0
- package/dist/core/plugins/context.d.ts +10 -0
- package/dist/core/plugins/context.js +24 -0
- package/dist/core/plugins/dev-server-plugin.d.ts +13 -0
- package/dist/core/plugins/dev-server-plugin.js +27 -0
- package/dist/core/plugins/index.d.ts +13 -0
- package/dist/core/plugins/index.js +18 -0
- package/dist/core/plugins/prelude-plugin.d.ts +10 -0
- package/dist/core/plugins/prelude-plugin.js +23 -0
- package/dist/core/plugins/react-native-plugin.d.ts +36 -0
- package/dist/core/plugins/react-native-plugin.js +81 -0
- package/dist/core/plugins/reporter-plugin.d.ts +11 -0
- package/dist/core/plugins/reporter-plugin.js +87 -0
- package/dist/core/plugins/shared/filters.js +5 -0
- package/dist/core/plugins/swc-plugin.d.ts +26 -0
- package/dist/core/plugins/swc-plugin.js +108 -0
- package/dist/core/plugins/types.d.ts +18 -0
- package/dist/core/plugins/utils/source.js +10 -0
- package/dist/core/plugins/utils/transform-utils.js +56 -0
- package/dist/core/rolldown.js +313 -0
- package/dist/core/settings.js +19 -0
- package/dist/core/types.d.ts +83 -0
- package/dist/filter.d.ts +1 -0
- package/dist/filter.js +2 -0
- package/dist/hmr-runtime.iife.js +5 -5
- package/dist/index.d.ts +24 -1221
- package/dist/index.js +19 -4029
- package/dist/internal/react-native.js +24 -0
- package/dist/logger.js +5 -0
- package/dist/node/cli-utils.d.ts +10 -0
- package/dist/node/cli-utils.js +28 -0
- package/dist/node/cli.d.ts +6 -0
- package/dist/node/cli.js +23 -0
- package/dist/node/commands/agent/action.js +91 -0
- package/dist/node/commands/agent/command.js +10 -0
- package/dist/node/commands/agent/index.js +2 -0
- package/dist/node/commands/bundle/action.js +33 -0
- package/dist/node/commands/bundle/command.js +96 -0
- package/dist/node/commands/bundle/index.js +2 -0
- package/dist/node/commands/start/action.js +37 -0
- package/dist/node/commands/start/command.js +93 -0
- package/dist/node/commands/start/debugger.js +79 -0
- package/dist/node/commands/start/index.js +2 -0
- package/dist/node/commands/start/setup-interactive-mode.d.ts +20 -0
- package/dist/node/commands/start/setup-interactive-mode.js +107 -0
- package/dist/node/constants.js +4 -0
- package/dist/node/logger.js +5 -0
- package/dist/node/types.d.ts +23 -0
- package/dist/node/utils.js +23 -0
- package/dist/package.js +4 -0
- package/dist/runtime.js +1 -1
- package/dist/server/bundle.d.ts +12 -0
- package/dist/server/bundle.js +55 -0
- package/dist/server/bundler-pool.d.ts +51 -0
- package/dist/server/bundler-pool.js +197 -0
- package/dist/server/common/schema.js +19 -0
- package/dist/server/constants.d.ts +6 -0
- package/dist/server/constants.js +6 -0
- package/dist/server/create-dev-server.d.ts +6 -0
- package/dist/server/create-dev-server.js +185 -0
- package/dist/server/error.js +9 -0
- package/dist/server/events/event-bus.d.ts +12 -0
- package/dist/server/events/event-bus.js +16 -0
- package/dist/server/events/types.d.ts +37 -0
- package/dist/server/events/types.js +6 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +3 -0
- package/dist/server/logger.js +33 -0
- package/dist/server/mcp/context.js +14 -0
- package/dist/server/mcp/server.js +86 -0
- package/dist/server/mcp/tools/app-log-diagnostics.js +37 -0
- package/dist/server/mcp/tools/build-diagnostics.js +97 -0
- package/dist/server/mcp/tools/build-info.js +33 -0
- package/dist/server/mcp/tools/device-diagnostics.js +52 -0
- package/dist/server/mcp/tools/index.js +277 -0
- package/dist/server/middlewares/request-logger.js +15 -0
- package/dist/server/middlewares/serve-assets.js +49 -0
- package/dist/server/middlewares/serve-bundle.js +72 -0
- package/dist/server/middlewares/sse.js +34 -0
- package/dist/server/middlewares/symbolicate.js +71 -0
- package/dist/server/sse/adapter.js +74 -0
- package/dist/server/sse/event-bus.js +26 -0
- package/dist/server/symbolicate.js +93 -0
- package/dist/server/types.d.ts +125 -0
- package/dist/server/wss/hmr-server.js +209 -0
- package/dist/server/wss/server.d.ts +9 -0
- package/dist/server/wss/server.js +70 -0
- package/dist/{runtime.d.cts → types/hmr.d.ts} +1 -12
- package/dist/types.d.ts +78 -0
- package/dist/utils/babel.js +11 -0
- package/dist/utils/build-options.js +17 -0
- package/dist/utils/bundle.js +6 -0
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +32 -0
- package/dist/utils/dev-server.js +51 -0
- package/dist/utils/env.js +7 -0
- package/dist/utils/errors.js +9 -0
- package/dist/utils/hash.js +8 -0
- package/dist/utils/id.js +28 -0
- package/dist/utils/node-resolve.js +42 -0
- package/dist/utils/promise.js +15 -0
- package/dist/utils/reporters.js +120 -0
- package/dist/utils/reset-cache.d.ts +8 -0
- package/dist/utils/reset-cache.js +25 -0
- package/dist/utils/response.js +91 -0
- package/dist/utils/run-build.d.ts +8 -0
- package/dist/utils/run-build.js +7 -0
- package/dist/utils/run-server.d.ts +6 -0
- package/dist/utils/run-server.js +20 -0
- package/dist/utils/runtime-target.js +9 -0
- package/dist/utils/serialize.js +10 -0
- package/dist/utils/server.js +6 -0
- package/dist/utils/storage.js +6 -0
- package/dist/utils/string.js +6 -0
- package/dist/utils/swc.js +10 -0
- package/dist/utils/terminal.js +86 -0
- package/dist/utils/url.js +23 -0
- package/package.json +56 -68
- package/dist/commands.cjs +0 -4008
- package/dist/commands.d.cts +0 -5
- package/dist/pluginutils.d.ts +0 -1
- package/dist/pluginutils.js +0 -2
- package/dist/runtime.cjs +0 -34
- /package/dist/{chunk-DXpK5_cz.js → chunk-DJV587Yu.js} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { parseUrl } from "../utils/url.js";
|
|
2
|
+
import { codeFrameColumns } from "@babel/code-frame";
|
|
3
|
+
//#region src/server/symbolicate.ts
|
|
4
|
+
/**
|
|
5
|
+
* @see https://github.com/facebook/react-native/blob/0.83-stable/packages/metro-config/src/index.flow.js#L17
|
|
6
|
+
*/
|
|
7
|
+
const INTERNAL_CALLSITES_REGEX = new RegExp([
|
|
8
|
+
"/Libraries/BatchedBridge/MessageQueue\\.js$",
|
|
9
|
+
"/Libraries/Core/.+\\.js$",
|
|
10
|
+
"/Libraries/LogBox/.+\\.js$",
|
|
11
|
+
"/Libraries/Network/.+\\.js$",
|
|
12
|
+
"/Libraries/Pressability/.+\\.js$",
|
|
13
|
+
"/Libraries/Renderer/implementations/.+\\.js$",
|
|
14
|
+
"/Libraries/Utilities/.+\\.js$",
|
|
15
|
+
"/Libraries/vendor/.+\\.js$",
|
|
16
|
+
"/Libraries/WebSocket/.+\\.js$",
|
|
17
|
+
"/src/private/renderer/errorhandling/.+\\.js$",
|
|
18
|
+
"/metro-runtime/.+\\.js$",
|
|
19
|
+
"/node_modules/@babel/runtime/.+\\.js$",
|
|
20
|
+
"/node_modules/@react-native/js-polyfills/.+\\.js$",
|
|
21
|
+
"/node_modules/invariant/.+\\.js$",
|
|
22
|
+
"/node_modules/react-devtools-core/.+\\.js$",
|
|
23
|
+
"/node_modules/react-native/index.js$",
|
|
24
|
+
"/node_modules/react-refresh/.+\\.js$",
|
|
25
|
+
"/node_modules/scheduler/.+\\.js$",
|
|
26
|
+
"^\\[native code\\]$"
|
|
27
|
+
].map((pathPattern) => pathPattern.replaceAll("/", "[/\\\\]")).join("|"));
|
|
28
|
+
async function symbolicate(bundleStore, stack) {
|
|
29
|
+
const sourceMapConsumer = await bundleStore.sourceMapConsumer;
|
|
30
|
+
const symbolicatedStack = stack.filter((frame) => frame.file?.startsWith("http")).map((frame) => sourceMapConsumer ? originalPositionFor(sourceMapConsumer, frame) : frame).map((frame) => collapseFrame(frame));
|
|
31
|
+
return {
|
|
32
|
+
stack: symbolicatedStack,
|
|
33
|
+
codeFrame: getCodeFrame(symbolicatedStack, bundleStore, sourceMapConsumer)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function originalPositionFor(sourceMapConsumer, frame) {
|
|
37
|
+
if (frame.column == null || frame.lineNumber == null) return frame;
|
|
38
|
+
const originalPosition = sourceMapConsumer.originalPositionFor({
|
|
39
|
+
column: frame.column,
|
|
40
|
+
line: frame.lineNumber
|
|
41
|
+
});
|
|
42
|
+
return Object.entries(originalPosition).reduce((frame, [key, value]) => {
|
|
43
|
+
const targetKey = convertFrameKey(key);
|
|
44
|
+
return {
|
|
45
|
+
...frame,
|
|
46
|
+
...value ? { [targetKey]: value } : null
|
|
47
|
+
};
|
|
48
|
+
}, frame);
|
|
49
|
+
}
|
|
50
|
+
function collapseFrame(frame) {
|
|
51
|
+
return {
|
|
52
|
+
...frame,
|
|
53
|
+
collapse: Boolean(frame.file && INTERNAL_CALLSITES_REGEX.test(frame.file))
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function isCollapsed(frame) {
|
|
57
|
+
return "collapse" in frame && frame.collapse;
|
|
58
|
+
}
|
|
59
|
+
function convertFrameKey(key) {
|
|
60
|
+
if (key === "line") return "lineNumber";
|
|
61
|
+
else if (key === "source") return "file";
|
|
62
|
+
else if (key === "name") return "methodName";
|
|
63
|
+
return key;
|
|
64
|
+
}
|
|
65
|
+
function getCodeFrame(frames, bundleStore, sourceMapConsumer) {
|
|
66
|
+
const frame = frames.find((frame) => {
|
|
67
|
+
return frame.lineNumber != null && frame.column != null && !isCollapsed(frame);
|
|
68
|
+
});
|
|
69
|
+
if (frame?.file == null || frame.column == null || frame.lineNumber == null) return null;
|
|
70
|
+
try {
|
|
71
|
+
const { lineNumber, column, file } = frame;
|
|
72
|
+
const unresolved = file.startsWith("http");
|
|
73
|
+
const source = sourceMapConsumer == null || unresolved ? bundleStore.code : sourceMapConsumer.sourceContentFor(frame.file);
|
|
74
|
+
const fileName = unresolved ? parseUrl(file).pathname ?? "unknown" : file;
|
|
75
|
+
let content = "";
|
|
76
|
+
if (source) content = codeFrameColumns(source, { start: {
|
|
77
|
+
column,
|
|
78
|
+
line: lineNumber
|
|
79
|
+
} }, { highlightCode: true });
|
|
80
|
+
return {
|
|
81
|
+
content,
|
|
82
|
+
fileName,
|
|
83
|
+
location: {
|
|
84
|
+
column,
|
|
85
|
+
row: lineNumber
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { symbolicate };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { BuildOptions } from "../core/types.js";
|
|
2
|
+
import { WebSocketClient } from "./wss/server.js";
|
|
3
|
+
import { ServerEventBus } from "./events/event-bus.js";
|
|
4
|
+
import { BundlerPool } from "./bundler-pool.js";
|
|
5
|
+
import { ResolvedConfig } from "../config/defaults.js";
|
|
6
|
+
import { FastifyInstance } from "fastify";
|
|
7
|
+
import { Emitter } from "mitt";
|
|
8
|
+
import * as ws from "ws";
|
|
9
|
+
import * as fastifyMiddie from "@fastify/middie";
|
|
10
|
+
|
|
11
|
+
//#region src/server/types.d.ts
|
|
12
|
+
type FastifyInstance$1 = FastifyInstance & {
|
|
13
|
+
use(fn: fastifyMiddie.Handler): FastifyInstance$1;
|
|
14
|
+
use(route: string, fn: fastifyMiddie.Handler): FastifyInstance$1;
|
|
15
|
+
use(routes: string[], fn: fastifyMiddie.Handler): FastifyInstance$1;
|
|
16
|
+
};
|
|
17
|
+
interface ServerOptions {
|
|
18
|
+
port?: number;
|
|
19
|
+
host?: string;
|
|
20
|
+
https?: boolean;
|
|
21
|
+
key?: string;
|
|
22
|
+
cert?: string;
|
|
23
|
+
buildOptions?: Pick<BuildOptions, 'cache'>;
|
|
24
|
+
mcp?: boolean;
|
|
25
|
+
}
|
|
26
|
+
interface DevServerContext {
|
|
27
|
+
/**
|
|
28
|
+
* The base URL of the development server.
|
|
29
|
+
*/
|
|
30
|
+
serverBaseUrl: string;
|
|
31
|
+
/**
|
|
32
|
+
* Resolved Rollipop config.
|
|
33
|
+
*/
|
|
34
|
+
config: ResolvedConfig;
|
|
35
|
+
/**
|
|
36
|
+
* Server options.
|
|
37
|
+
*/
|
|
38
|
+
options: ServerOptions;
|
|
39
|
+
/**
|
|
40
|
+
* The bundler pool.
|
|
41
|
+
*/
|
|
42
|
+
bundlerPool: BundlerPool;
|
|
43
|
+
/**
|
|
44
|
+
* The event bus.
|
|
45
|
+
*/
|
|
46
|
+
eventBus: ServerEventBus;
|
|
47
|
+
/**
|
|
48
|
+
* The message websocket server API.
|
|
49
|
+
*/
|
|
50
|
+
message: ws.Server & {
|
|
51
|
+
/**
|
|
52
|
+
* Broadcast a message to all connected devices.
|
|
53
|
+
*/
|
|
54
|
+
broadcast: (method: string, params?: Record<string, any>) => void;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The events websocket server API.
|
|
58
|
+
*/
|
|
59
|
+
events: ws.Server & {
|
|
60
|
+
/**
|
|
61
|
+
* Report an event to the reporter.
|
|
62
|
+
*/
|
|
63
|
+
reportEvent: (event: {
|
|
64
|
+
type: string;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}) => void;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* HMR websocket server API
|
|
70
|
+
*/
|
|
71
|
+
hot: ws.Server & {
|
|
72
|
+
send: (client: ws.WebSocket, eventName: string, payload?: unknown) => void;
|
|
73
|
+
sendAll: (eventName: string, payload?: unknown) => void;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
type DevServerEvents = {
|
|
77
|
+
'device.connected': {
|
|
78
|
+
client: WebSocketClient;
|
|
79
|
+
};
|
|
80
|
+
'device.message': {
|
|
81
|
+
client: WebSocketClient;
|
|
82
|
+
data: ws.RawData;
|
|
83
|
+
};
|
|
84
|
+
'device.error': {
|
|
85
|
+
client: WebSocketClient;
|
|
86
|
+
error: Error;
|
|
87
|
+
};
|
|
88
|
+
'device.disconnected': {
|
|
89
|
+
client: WebSocketClient;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
interface Middlewares {
|
|
93
|
+
/**
|
|
94
|
+
* Register a middleware to the Fastify instance.
|
|
95
|
+
*
|
|
96
|
+
* **NOTE**: This is a wrapper of `instance.use`.
|
|
97
|
+
*/
|
|
98
|
+
use: FastifyInstance$1['use'];
|
|
99
|
+
}
|
|
100
|
+
type DevServer = {
|
|
101
|
+
/**
|
|
102
|
+
* The Fastify instance.
|
|
103
|
+
*/
|
|
104
|
+
instance: FastifyInstance$1;
|
|
105
|
+
/**
|
|
106
|
+
* `express` and `connect` style middleware registration API.
|
|
107
|
+
*/
|
|
108
|
+
middlewares: Middlewares;
|
|
109
|
+
} & DevServerContext & Emitter<DevServerEvents>;
|
|
110
|
+
interface BundleDetails {
|
|
111
|
+
bundleType: string;
|
|
112
|
+
dev: boolean;
|
|
113
|
+
entryFile: string;
|
|
114
|
+
minify: boolean;
|
|
115
|
+
platform?: string;
|
|
116
|
+
}
|
|
117
|
+
interface FormattedError {
|
|
118
|
+
type: string;
|
|
119
|
+
message: string;
|
|
120
|
+
errors: {
|
|
121
|
+
description: string;
|
|
122
|
+
}[];
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { BundleDetails, DevServer, DevServerContext, DevServerEvents, FastifyInstance$1 as FastifyInstance, FormattedError, Middlewares, ServerOptions };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { isBundlerEventForId } from "../events/types.js";
|
|
2
|
+
import { WebSocketServer } from "./server.js";
|
|
3
|
+
import { invariant } from "es-toolkit";
|
|
4
|
+
//#region src/server/wss/hmr-server.ts
|
|
5
|
+
var HMRServer = class extends WebSocketServer {
|
|
6
|
+
bundlerPool;
|
|
7
|
+
eventBus;
|
|
8
|
+
instances = /* @__PURE__ */ new Map();
|
|
9
|
+
bindings = /* @__PURE__ */ new Map();
|
|
10
|
+
constructor({ bundlerPool, eventBus }) {
|
|
11
|
+
super("hmr", { noServer: true });
|
|
12
|
+
this.bundlerPool = bundlerPool;
|
|
13
|
+
this.eventBus = eventBus;
|
|
14
|
+
}
|
|
15
|
+
parseClientMessage(data) {
|
|
16
|
+
const parsedData = JSON.parse(this.rawDataToString(data));
|
|
17
|
+
const clientMessage = "type" in parsedData ? parsedData : null;
|
|
18
|
+
invariant(clientMessage, "Invalid HMR client message");
|
|
19
|
+
return clientMessage;
|
|
20
|
+
}
|
|
21
|
+
async handleConnected(client, platform, bundleEntry) {
|
|
22
|
+
try {
|
|
23
|
+
this.logger.trace(`HMR client connected (clientId: ${client.id})`, {
|
|
24
|
+
platform,
|
|
25
|
+
bundleEntry
|
|
26
|
+
});
|
|
27
|
+
const devEngineInstance = this.bundlerPool.get(bundleEntry, {
|
|
28
|
+
platform,
|
|
29
|
+
dev: true
|
|
30
|
+
});
|
|
31
|
+
this.bindEvents(client, devEngineInstance);
|
|
32
|
+
this.instances.set(client.id, devEngineInstance);
|
|
33
|
+
this.logger.trace(`Bundler instance prepared (bundlerId: ${devEngineInstance.id})`);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
this.logger.error(`Failed to prepare bundler instance`, error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
bindEvents(client, instance) {
|
|
39
|
+
if (this.bindings.get(client.id) == null) {
|
|
40
|
+
const unsubscribe = this.eventBus.subscribe((event) => {
|
|
41
|
+
if (!isBundlerEventForId(event, instance.id)) return;
|
|
42
|
+
switch (event.type) {
|
|
43
|
+
case "hmr_updates":
|
|
44
|
+
this.handleUpdates(client, event.updates);
|
|
45
|
+
break;
|
|
46
|
+
case "hmr_failed":
|
|
47
|
+
this.sendBuildFailed(client, event.error);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.bindings.set(client.id, { unsubscribe });
|
|
52
|
+
this.logger.trace(`HMR event binding established (clientId: ${client.id})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
sendBuildFailed(client, error) {
|
|
56
|
+
this.send(client, JSON.stringify({
|
|
57
|
+
type: "hmr:error",
|
|
58
|
+
payload: {
|
|
59
|
+
type: "BuildError",
|
|
60
|
+
errors: [{ description: error.message }],
|
|
61
|
+
message: error.message
|
|
62
|
+
}
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
async handleModuleRegistered(client, modules) {
|
|
66
|
+
try {
|
|
67
|
+
const instance = this.instances.get(client.id);
|
|
68
|
+
invariant(instance != null, `Bundler instance not found for client clientId: ${client.id}`);
|
|
69
|
+
await instance.ensureInitialized;
|
|
70
|
+
await instance.devEngine.registerModules(client.id.toString(), modules);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this.logger.error(`Failed to handle module registered`, error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async handleInvalidate(client, moduleId) {
|
|
76
|
+
try {
|
|
77
|
+
const instance = this.instances.get(client.id);
|
|
78
|
+
invariant(instance != null, `Bundler instance not found for client clientId: ${client.id}`);
|
|
79
|
+
await instance.ensureInitialized;
|
|
80
|
+
const updates = await instance.devEngine.invalidate(moduleId);
|
|
81
|
+
await this.handleUpdates(client, updates);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
this.logger.error(`Failed to handle invalidate`, error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async handleUpdates(client, updates) {
|
|
87
|
+
this.logger.trace(`HMR updates found (clientId: ${client.id})`, { updatesCount: updates.length });
|
|
88
|
+
const actionableUpdates = updates.filter((clientUpdate) => clientUpdate.update.type !== "Noop");
|
|
89
|
+
if (actionableUpdates.length === 0) return;
|
|
90
|
+
this.send(client, JSON.stringify({ type: "hmr:update-start" }));
|
|
91
|
+
for (const clientUpdate of actionableUpdates) {
|
|
92
|
+
const update = clientUpdate.update;
|
|
93
|
+
switch (update.type) {
|
|
94
|
+
case "Patch":
|
|
95
|
+
this.sendUpdateToClient(client, update);
|
|
96
|
+
break;
|
|
97
|
+
case "FullReload":
|
|
98
|
+
this.sendReloadToClient(client);
|
|
99
|
+
break;
|
|
100
|
+
case "Noop": break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
sendUpdateToClient(client, update) {
|
|
105
|
+
invariant(update.type === "Patch", "Invalid HMR update type");
|
|
106
|
+
const updateMessage = {
|
|
107
|
+
type: "hmr:update",
|
|
108
|
+
code: update.code
|
|
109
|
+
};
|
|
110
|
+
this.send(client, JSON.stringify(updateMessage));
|
|
111
|
+
this.done(client);
|
|
112
|
+
}
|
|
113
|
+
sendReloadToClient(client) {
|
|
114
|
+
this.logger.trace(`Sending HMR reload message to client (clientId: ${client.id})`);
|
|
115
|
+
this.send(client, JSON.stringify({ type: "hmr:reload" }));
|
|
116
|
+
this.done(client);
|
|
117
|
+
}
|
|
118
|
+
done(client) {
|
|
119
|
+
this.send(client, JSON.stringify({ type: "hmr:update-done" }));
|
|
120
|
+
}
|
|
121
|
+
sendError(client, error) {
|
|
122
|
+
try {
|
|
123
|
+
this.send(client, JSON.stringify(error));
|
|
124
|
+
} catch (error) {
|
|
125
|
+
this.logger.error(`Failed to send HMR error message to client (clientId: ${client.id})`, error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
cleanup(client) {
|
|
129
|
+
this.logger.trace(`HMR client cleanup (clientId: ${client.id})`);
|
|
130
|
+
const binding = this.bindings.get(client.id);
|
|
131
|
+
const instance = this.instances.get(client.id);
|
|
132
|
+
if (binding != null) binding.unsubscribe();
|
|
133
|
+
if (instance != null) try {
|
|
134
|
+
instance.devEngine.removeClient(String(client.id));
|
|
135
|
+
} catch (error) {
|
|
136
|
+
this.logger.warn(`Skipped devEngine.removeClient for client ${client.id}: ` + (error instanceof Error ? error.message : String(error)));
|
|
137
|
+
}
|
|
138
|
+
this.bindings.delete(client.id);
|
|
139
|
+
this.instances.delete(client.id);
|
|
140
|
+
}
|
|
141
|
+
onMessage(client, data) {
|
|
142
|
+
let message;
|
|
143
|
+
try {
|
|
144
|
+
message = this.parseClientMessage(data);
|
|
145
|
+
let traceMessage = message;
|
|
146
|
+
if (message.type === "hmr:module-registered") traceMessage = {
|
|
147
|
+
...message,
|
|
148
|
+
modules: `[${message.modules.length} modules]`
|
|
149
|
+
};
|
|
150
|
+
else if (message.type === "hmr:log") traceMessage = {
|
|
151
|
+
...message,
|
|
152
|
+
data: `(${message.data.length} items)`
|
|
153
|
+
};
|
|
154
|
+
this.logger.trace("HMR client message received", traceMessage);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const message = "Failed to parse HMR client message";
|
|
157
|
+
this.logger.error(message, error);
|
|
158
|
+
this.sendError(client, {
|
|
159
|
+
type: "InternalError",
|
|
160
|
+
errors: [{ description: error instanceof Error ? error.message : String(error) }],
|
|
161
|
+
message
|
|
162
|
+
});
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (isCustomHMRMessage(message)) {
|
|
166
|
+
this.wss.emit(message.type, message.payload);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
switch (message.type) {
|
|
170
|
+
case "hmr:connected":
|
|
171
|
+
this.handleConnected(client, message.platform, message.bundleEntry);
|
|
172
|
+
break;
|
|
173
|
+
case "hmr:module-registered":
|
|
174
|
+
this.handleModuleRegistered(client, message.modules);
|
|
175
|
+
break;
|
|
176
|
+
case "hmr:invalidate":
|
|
177
|
+
this.handleInvalidate(client, message.moduleId);
|
|
178
|
+
break;
|
|
179
|
+
case "hmr:log": {
|
|
180
|
+
const instance = this.instances.get(client.id);
|
|
181
|
+
this.eventBus.emit({
|
|
182
|
+
type: "client_log",
|
|
183
|
+
...instance != null ? { bundlerId: instance.id } : {},
|
|
184
|
+
level: message.level,
|
|
185
|
+
data: message.data
|
|
186
|
+
});
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
onConnection(client) {
|
|
192
|
+
this.logger.trace(`connection established (clientId: ${client.id})`);
|
|
193
|
+
}
|
|
194
|
+
onError(client, error) {
|
|
195
|
+
this.logger.error(`connection error (clientId: ${client.id})`, error);
|
|
196
|
+
this.cleanup(client);
|
|
197
|
+
}
|
|
198
|
+
onClose(client) {
|
|
199
|
+
this.logger.trace(`connection closed (clientId: ${client.id})`);
|
|
200
|
+
this.cleanup(client);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
function isCustomHMRMessage(message) {
|
|
204
|
+
if (typeof message !== "object" || message == null) return false;
|
|
205
|
+
if ("type" in message && typeof message.type === "string" && message.type.startsWith("hmr:")) return false;
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
export { HMRServer };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { logger } from "../logger.js";
|
|
2
|
+
import { parseUrl } from "../../utils/url.js";
|
|
3
|
+
import EventEmitter from "node:events";
|
|
4
|
+
import * as ws from "ws";
|
|
5
|
+
//#region src/server/wss/server.ts
|
|
6
|
+
var WebSocketServer = class extends EventEmitter {
|
|
7
|
+
clientId = 0;
|
|
8
|
+
wss;
|
|
9
|
+
logger;
|
|
10
|
+
constructor(name, options) {
|
|
11
|
+
super();
|
|
12
|
+
const logger$1 = logger.child(name);
|
|
13
|
+
const wss = new ws.WebSocketServer(options);
|
|
14
|
+
wss.on("connection", (socket) => {
|
|
15
|
+
const client = Object.defineProperty(socket, "id", {
|
|
16
|
+
value: this.clientId++,
|
|
17
|
+
writable: false
|
|
18
|
+
});
|
|
19
|
+
this.onConnection(client);
|
|
20
|
+
this.emit("connection", client);
|
|
21
|
+
client.on("message", (data) => {
|
|
22
|
+
this.onMessage(client, data);
|
|
23
|
+
this.emit("message", client, data);
|
|
24
|
+
});
|
|
25
|
+
client.on("error", (error) => {
|
|
26
|
+
this.onError(client, error);
|
|
27
|
+
this.emit("error", client, error);
|
|
28
|
+
});
|
|
29
|
+
client.on("close", () => {
|
|
30
|
+
this.onClose(client);
|
|
31
|
+
this.emit("close", client);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
this.wss = wss;
|
|
35
|
+
this.logger = logger$1;
|
|
36
|
+
}
|
|
37
|
+
get server() {
|
|
38
|
+
return this.wss;
|
|
39
|
+
}
|
|
40
|
+
send(client, data) {
|
|
41
|
+
if (client.readyState === ws.WebSocket.OPEN) client.send(data);
|
|
42
|
+
}
|
|
43
|
+
sendAll(data) {
|
|
44
|
+
this.wss.clients.forEach((client) => {
|
|
45
|
+
if (client.readyState === ws.WebSocket.OPEN) client.send(data);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
rawDataToString(data) {
|
|
49
|
+
if (Buffer.isBuffer(data)) return data.toString("utf8");
|
|
50
|
+
if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
|
|
51
|
+
return Buffer.from(data).toString("utf8");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function getWebSocketUpgradeHandler(websocketEndpoints) {
|
|
55
|
+
return (request, socket, head) => {
|
|
56
|
+
if (request.url == null) {
|
|
57
|
+
socket.destroy();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const { pathname } = parseUrl(request.url);
|
|
61
|
+
if (pathname != null && websocketEndpoints[pathname]) {
|
|
62
|
+
const wss = websocketEndpoints[pathname];
|
|
63
|
+
wss.handleUpgrade(request, socket, head, (socket) => {
|
|
64
|
+
wss.emit("connection", socket, request);
|
|
65
|
+
});
|
|
66
|
+
} else socket.destroy();
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { WebSocketServer, getWebSocketUpgradeHandler };
|
|
@@ -73,15 +73,4 @@ interface DevRuntimeMessenger {
|
|
|
73
73
|
send(message: HMRClientMessage): void;
|
|
74
74
|
}
|
|
75
75
|
//#endregion
|
|
76
|
-
|
|
77
|
-
declare global {
|
|
78
|
-
var __ROLLIPOP_CUSTOM_HMR_HANDLER__: HMRCustomHandler | undefined;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Set a custom HMR handler.
|
|
82
|
-
*
|
|
83
|
-
* @param handler - The custom HMR handler to set.
|
|
84
|
-
*/
|
|
85
|
-
declare function setCustomHMRHandler(handler: HMRCustomHandler): void;
|
|
86
|
-
//#endregion
|
|
87
|
-
export { setCustomHMRHandler };
|
|
76
|
+
export { type DevRuntime, DevRuntimeInterface, DevRuntimeMessenger, DevRuntimeModule, HMRClientLogLevel, HMRClientMessage, HMRContext, HMRCustomHandler, HMRCustomMessage, HMRServerError, HMRServerMessage };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
3
|
+
type NullValue<T = void> = T | undefined | null | void;
|
|
4
|
+
type DeepRequired<T> = { [K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K] };
|
|
5
|
+
interface Reporter {
|
|
6
|
+
update(event: ReportableEvent): void;
|
|
7
|
+
}
|
|
8
|
+
interface OptionalBundlerEvent {
|
|
9
|
+
bundlerId?: string;
|
|
10
|
+
}
|
|
11
|
+
interface BuildDiagnosticLog {
|
|
12
|
+
code?: string;
|
|
13
|
+
plugin?: string;
|
|
14
|
+
message: string;
|
|
15
|
+
stack?: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
hook?: string;
|
|
18
|
+
frame?: string;
|
|
19
|
+
loc?: {
|
|
20
|
+
column: number;
|
|
21
|
+
file?: string;
|
|
22
|
+
line: number;
|
|
23
|
+
};
|
|
24
|
+
meta?: unknown;
|
|
25
|
+
}
|
|
26
|
+
type ReportableEvent = ({
|
|
27
|
+
type: 'bundle_build_started';
|
|
28
|
+
} & OptionalBundlerEvent) | ({
|
|
29
|
+
type: 'bundle_build_done';
|
|
30
|
+
totalModules: number;
|
|
31
|
+
transformedModules: number;
|
|
32
|
+
cacheHitModules: number;
|
|
33
|
+
duration: number;
|
|
34
|
+
bundleFilePath?: string;
|
|
35
|
+
} & OptionalBundlerEvent) | ({
|
|
36
|
+
type: 'bundle_build_failed';
|
|
37
|
+
error: Error;
|
|
38
|
+
} & OptionalBundlerEvent) | ({
|
|
39
|
+
type: 'hmr_failed';
|
|
40
|
+
error: Error;
|
|
41
|
+
} & OptionalBundlerEvent) | ({
|
|
42
|
+
type: 'transform';
|
|
43
|
+
id: string;
|
|
44
|
+
totalModules: number | undefined;
|
|
45
|
+
transformedModules: number;
|
|
46
|
+
} & OptionalBundlerEvent) | ({
|
|
47
|
+
type: 'watch_change';
|
|
48
|
+
id: string;
|
|
49
|
+
} & OptionalBundlerEvent) | ({
|
|
50
|
+
type: 'build_log';
|
|
51
|
+
level: 'debug' | 'info';
|
|
52
|
+
log: BuildDiagnosticLog;
|
|
53
|
+
} & OptionalBundlerEvent) | ({
|
|
54
|
+
type: 'build_error';
|
|
55
|
+
level: 'warn' | 'error';
|
|
56
|
+
log: BuildDiagnosticLog;
|
|
57
|
+
} & OptionalBundlerEvent) | MetroCompatibleClientLogEvent;
|
|
58
|
+
type MetroCompatibleClientLogEvent = {
|
|
59
|
+
type: 'client_log';
|
|
60
|
+
level: 'trace' | 'info' | 'warn' | 'log' | 'group' | 'groupCollapsed' | 'groupEnd' | 'debug'
|
|
61
|
+
/**
|
|
62
|
+
* In react-native, ReportableEvent['level'] does not defined `error` type.
|
|
63
|
+
* But, Flipper supports the `error` type.
|
|
64
|
+
*
|
|
65
|
+
* @see https://github.com/facebook/flipper/blob/v0.273.0/desktop/flipper-common/src/server-types.tsx#L74
|
|
66
|
+
*/
|
|
67
|
+
| 'error';
|
|
68
|
+
data: any[];
|
|
69
|
+
bundlerId?: string;
|
|
70
|
+
};
|
|
71
|
+
interface PackageJson {
|
|
72
|
+
name: string;
|
|
73
|
+
version?: string;
|
|
74
|
+
dependencies?: Record<string, string>;
|
|
75
|
+
devDependencies?: Record<string, string>;
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { BuildDiagnosticLog, DeepRequired, MaybePromise, MetroCompatibleClientLogEvent, NullValue, PackageJson, ReportableEvent, Reporter };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { mergeWith } from "es-toolkit";
|
|
2
|
+
//#region src/utils/babel.ts
|
|
3
|
+
function mergeBabelOptions(options) {
|
|
4
|
+
return options.reduce((acc, options) => mergeWith(acc, options, merge$1), {});
|
|
5
|
+
}
|
|
6
|
+
function merge$1(target, source, key) {
|
|
7
|
+
if (key === "plugins") return [...target ?? [], ...source ?? []];
|
|
8
|
+
if (key === "presets") return [...target ?? [], ...source ?? []];
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { mergeBabelOptions };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { merge } from "es-toolkit";
|
|
3
|
+
//#region src/utils/build-options.ts
|
|
4
|
+
const DEFAULT_BUILD_OPTIONS = {
|
|
5
|
+
cache: true,
|
|
6
|
+
minify: false
|
|
7
|
+
};
|
|
8
|
+
function resolveBuildOptions(config, buildOptions) {
|
|
9
|
+
if (buildOptions.outfile) buildOptions.outfile = path.resolve(config.root, buildOptions.outfile);
|
|
10
|
+
if ((buildOptions.sourcemap === true || buildOptions.sourcemap === "hidden") && buildOptions.sourcemapOutfile) buildOptions.sourcemapOutfile = path.resolve(config.root, buildOptions.sourcemapOutfile);
|
|
11
|
+
return merge(DEFAULT_BUILD_OPTIONS, {
|
|
12
|
+
...buildOptions,
|
|
13
|
+
dev: buildOptions.dev ?? config.mode === "development"
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { resolveBuildOptions };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { __require } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
//#region src/utils/config.ts
|
|
4
|
+
function bindReporter(config, onEvent) {
|
|
5
|
+
const reporter = { update(event) {
|
|
6
|
+
onEvent?.(event);
|
|
7
|
+
} };
|
|
8
|
+
return {
|
|
9
|
+
...config,
|
|
10
|
+
reporter
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function resolveHmrConfig(config) {
|
|
14
|
+
if (config.mode !== "development") return null;
|
|
15
|
+
const defaultRuntimeImplements = getDefaultRuntimeImplements();
|
|
16
|
+
if (typeof config.devMode.hmr === "boolean") return config.devMode.hmr ? defaultRuntimeImplements : null;
|
|
17
|
+
const { runtimeImplement = defaultRuntimeImplements.runtimeImplement, clientImplement = defaultRuntimeImplements.clientImplement } = config.devMode.hmr;
|
|
18
|
+
return {
|
|
19
|
+
runtimeImplement,
|
|
20
|
+
clientImplement
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
getDefaultRuntimeImplements.cache = null;
|
|
24
|
+
function getDefaultRuntimeImplements() {
|
|
25
|
+
if (getDefaultRuntimeImplements.cache == null) getDefaultRuntimeImplements.cache = {
|
|
26
|
+
runtimeImplement: fs.readFileSync(__require.resolve("rollipop/hmr-runtime"), "utf-8"),
|
|
27
|
+
clientImplement: fs.readFileSync(__require.resolve("rollipop/hmr-client"), "utf-8")
|
|
28
|
+
};
|
|
29
|
+
return getDefaultRuntimeImplements.cache;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { bindReporter, resolveHmrConfig };
|