rollipop 1.0.0-alpha.32 → 1.0.0-alpha.34
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/dist/common/transformer.js +34 -7
- package/dist/config/defaults.js +5 -5
- package/dist/config/types.d.ts +3 -2
- package/dist/constants.js +1 -1
- package/dist/core/plugins/react-native-plugin.d.ts +1 -0
- package/dist/core/plugins/react-native-plugin.js +28 -2
- package/dist/core/plugins/swc-plugin.js +1 -1
- package/dist/core/rolldown.js +2 -1
- package/dist/hmr-runtime.iife.js +30 -31
- package/dist/internal/react-native.js +38 -7
- package/dist/node/commands/agent/action.js +11 -7
- package/dist/package.js +1 -1
- package/dist/server/bundler-pool.d.ts +5 -2
- package/dist/server/bundler-pool.js +23 -6
- package/dist/server/create-dev-server.js +9 -3
- package/dist/server/devframe/index.js +20 -59
- package/dist/server/devframe/setup.js +5 -6
- package/dist/server/middlewares/symbolicate.js +2 -1
- package/dist/server/wss/hmr-server.js +13 -3
- package/dist/storage/file-storage.js +1 -1
- package/package.json +5 -5
- package/skills/configuration.md +2 -2
- package/skills/debugging.md +1 -1
- package/skills/dev-server.md +2 -2
- package/src/runtime/__tests__/hmr-runtime.spec.ts +14 -5
- package/src/runtime/hmr-runtime.ts +3 -4
|
@@ -1,13 +1,40 @@
|
|
|
1
|
+
import * as swc from "@swc/core";
|
|
1
2
|
import transform from "fast-flow-transform";
|
|
2
3
|
//#region src/common/transformer.ts
|
|
3
4
|
async function stripFlowTypes(id, code) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
try {
|
|
6
|
+
return await transform({
|
|
7
|
+
filename: id,
|
|
8
|
+
source: code,
|
|
9
|
+
sourcemap: true,
|
|
10
|
+
dialect: "flow",
|
|
11
|
+
format: "pretty"
|
|
12
|
+
});
|
|
13
|
+
} catch {
|
|
14
|
+
const result = swc.transformSync(code, {
|
|
15
|
+
filename: id,
|
|
16
|
+
configFile: false,
|
|
17
|
+
swcrc: false,
|
|
18
|
+
sourceMaps: true,
|
|
19
|
+
inputSourceMap: false,
|
|
20
|
+
jsc: {
|
|
21
|
+
target: "esnext",
|
|
22
|
+
parser: {
|
|
23
|
+
syntax: "flow",
|
|
24
|
+
all: true,
|
|
25
|
+
jsx: true,
|
|
26
|
+
enums: true,
|
|
27
|
+
components: true,
|
|
28
|
+
patternMatching: true
|
|
29
|
+
},
|
|
30
|
+
minify: { format: { comments: false } }
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
code: result.code,
|
|
35
|
+
map: result.map ? JSON.parse(result.map) : void 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
11
38
|
}
|
|
12
39
|
//#endregion
|
|
13
40
|
export { stripFlowTypes };
|
package/dist/config/defaults.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { stripFlowTypes } from "../common/transformer.js";
|
|
2
|
-
import { DEFAULT_ANALYZE_FILE, DEFAULT_ANALYZE_REPORT_FILE, DEFAULT_ASSET_EXTENSIONS,
|
|
2
|
+
import { DEFAULT_ANALYZE_FILE, DEFAULT_ANALYZE_REPORT_FILE, DEFAULT_ASSET_EXTENSIONS, DEFAULT_ENV_FILE, DEFAULT_ENV_PREFIX, DEFAULT_HMR_CLIENT_PATH, DEFAULT_RESOLVER_ALIAS_FIELDS, DEFAULT_RESOLVER_CONDITION_NAMES, DEFAULT_RESOLVER_MAIN_FIELDS, DEFAULT_RUNTIME_TARGET, DEFAULT_SOURCE_EXTENSIONS } from "../constants.js";
|
|
3
3
|
import { ClientLogReporter } from "../events/builtin-reporters.js";
|
|
4
|
-
import { getInitializeCorePath, getPolyfillScriptPaths } from "../internal/react-native.js";
|
|
5
4
|
import { resolvePackagePath } from "../utils/node-resolve.js";
|
|
5
|
+
import { resolveAssetRegistryPath, resolveInitializeCorePath, resolvePolyfillScriptPaths } from "../internal/react-native.js";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
//#region src/config/defaults.ts
|
|
8
8
|
async function getDefaultConfig(projectRoot, mode) {
|
|
@@ -26,8 +26,8 @@ async function getDefaultConfig(projectRoot, mode) {
|
|
|
26
26
|
symlinks: true
|
|
27
27
|
},
|
|
28
28
|
transform: { swc: { native: { externalHelpers: true } } },
|
|
29
|
-
prelude: [
|
|
30
|
-
polyfills: await Promise.all(
|
|
29
|
+
prelude: [resolveInitializeCorePath(projectRoot)],
|
|
30
|
+
polyfills: await Promise.all(resolvePolyfillScriptPaths(projectRoot, reactNativePath).map(async (path) => {
|
|
31
31
|
const code = fs.readFileSync(path, "utf-8");
|
|
32
32
|
return {
|
|
33
33
|
type: "iife",
|
|
@@ -38,7 +38,7 @@ async function getDefaultConfig(projectRoot, mode) {
|
|
|
38
38
|
treeshake: true,
|
|
39
39
|
reactNative: {
|
|
40
40
|
reactNativePath,
|
|
41
|
-
assetRegistryPath:
|
|
41
|
+
assetRegistryPath: resolveAssetRegistryPath(projectRoot),
|
|
42
42
|
hmrClientPath: DEFAULT_HMR_CLIENT_PATH
|
|
43
43
|
},
|
|
44
44
|
dev: {
|
package/dist/config/types.d.ts
CHANGED
|
@@ -5,11 +5,11 @@ import "../core/plugins/index.js";
|
|
|
5
5
|
import { InteractiveCommand } from "../node/commands/start/setup-interactive-mode.js";
|
|
6
6
|
import "../node/cli-utils.js";
|
|
7
7
|
import { Plugin } from "../core/plugins/types.js";
|
|
8
|
+
import * as swc from "@swc/core";
|
|
8
9
|
import { DevWatchOptions, RollipopReactNativeFlowConfig, RollipopReactNativeSwcConfig, RollipopReactNativeWorkletsConfig } from "@rollipop/rolldown/experimental";
|
|
9
10
|
import * as rolldown from "@rollipop/rolldown";
|
|
10
11
|
import { TopLevelFilterExpression } from "@rollipop/rolldown/filter";
|
|
11
12
|
import * as babel from "@babel/core";
|
|
12
|
-
import * as swc from "@swc/core";
|
|
13
13
|
//#region src/config/types.d.ts
|
|
14
14
|
type RolldownExperimentalOptions = NonNullable<rolldown.InputOptions['experimental']>;
|
|
15
15
|
type RolldownTransformOptions = NonNullable<rolldown.InputOptions['transform']>;
|
|
@@ -267,7 +267,8 @@ export interface ReactNativeConfig {
|
|
|
267
267
|
/**
|
|
268
268
|
* Path to asset registry file.
|
|
269
269
|
*
|
|
270
|
-
* Defaults to: `react-native/
|
|
270
|
+
* Defaults to: `react-native/asset-registry`,
|
|
271
|
+
* with the legacy `react-native/Libraries/Image/AssetRegistry.js` path as a fallback.
|
|
271
272
|
*/
|
|
272
273
|
assetRegistryPath?: string | ((root: string) => MaybePromise<string>);
|
|
273
274
|
/**
|
package/dist/constants.js
CHANGED
|
@@ -20,7 +20,7 @@ var constants_exports = /* @__PURE__ */ __exportAll({
|
|
|
20
20
|
ROLLIPOP_VIRTUAL_ENTRY_ID: () => ROLLIPOP_VIRTUAL_ENTRY_ID,
|
|
21
21
|
ROLLIPOP_VIRTUAL_PREFIX: () => ROLLIPOP_VIRTUAL_PREFIX
|
|
22
22
|
});
|
|
23
|
-
const ROLLIPOP_VERSION = "1.0.0-alpha.
|
|
23
|
+
const ROLLIPOP_VERSION = "1.0.0-alpha.34";
|
|
24
24
|
const ROLLIPOP_VIRTUAL_PREFIX = "\0rollipop/";
|
|
25
25
|
const ROLLIPOP_VIRTUAL_BOOTSTRAP_ID = `${ROLLIPOP_VIRTUAL_PREFIX}bootstrap`;
|
|
26
26
|
const ROLLIPOP_VIRTUAL_ENTRY_ID = `${ROLLIPOP_VIRTUAL_PREFIX}entry`;
|
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { copyAssetsToDestination, generateAssetRegistryCode, resolveScaledAssets } from "../assets.js";
|
|
2
2
|
import { setFlag } from "./utils/transform-utils.js";
|
|
3
|
+
import path from "node:path";
|
|
3
4
|
import { rollipopReactNativePlugin } from "@rollipop/rolldown/experimental";
|
|
4
5
|
import { id, include } from "@rollipop/rolldown/filter";
|
|
5
6
|
//#region src/core/plugins/react-native-plugin.ts
|
|
6
7
|
function reactNativePlugin(options) {
|
|
7
|
-
const { projectRoot, platform, preferNativePlatform, buildType, context, assetsDir, assetExtensions, assetRegistryPath, builtinPluginConfig } = options;
|
|
8
|
+
const { projectRoot, reactNativePath, platform, preferNativePlatform, buildType, context, assetsDir, assetExtensions, assetRegistryPath, builtinPluginConfig } = options;
|
|
9
|
+
const privateModulePlugin = {
|
|
10
|
+
name: "rollipop:react-native-private-module",
|
|
11
|
+
resolveId: {
|
|
12
|
+
order: "post",
|
|
13
|
+
filter: [include(id(new RegExp(`^react-native/src/private/`)))],
|
|
14
|
+
async handler(source, importer, extraOptions) {
|
|
15
|
+
const resolved = await this.resolve(source, importer, {
|
|
16
|
+
...extraOptions,
|
|
17
|
+
skipSelf: true
|
|
18
|
+
});
|
|
19
|
+
if (resolved != null) return resolved;
|
|
20
|
+
const candidate = path.resolve(reactNativePath, source.slice(13));
|
|
21
|
+
const relativeCandidate = path.relative(reactNativePath, candidate);
|
|
22
|
+
if (relativeCandidate.startsWith("..") || path.isAbsolute(relativeCandidate)) return null;
|
|
23
|
+
return this.resolve(candidate, importer, {
|
|
24
|
+
...extraOptions,
|
|
25
|
+
skipSelf: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
8
30
|
const assets = [];
|
|
9
31
|
const assetPlugin = {
|
|
10
32
|
name: "rollipop:react-native-asset",
|
|
@@ -42,7 +64,11 @@ function reactNativePlugin(options) {
|
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
};
|
|
45
|
-
return [
|
|
67
|
+
return [
|
|
68
|
+
privateModulePlugin,
|
|
69
|
+
rollipopReactNativePlugin(builtinPluginConfig),
|
|
70
|
+
assetPlugin
|
|
71
|
+
];
|
|
46
72
|
}
|
|
47
73
|
//#endregion
|
|
48
74
|
export { reactNativePlugin as reactNative };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getFlag } from "./utils/transform-utils.js";
|
|
2
2
|
import { ROLLDOWN_RUNTIME_EXCLUDE_FILTER, withRuntimeExclude } from "./shared/filters.js";
|
|
3
3
|
import { mergeSwcOptions } from "../../utils/swc.js";
|
|
4
|
+
import * as swc from "@swc/core";
|
|
4
5
|
import { partition } from "es-toolkit";
|
|
5
6
|
import { id, include } from "@rollipop/rolldown/filter";
|
|
6
|
-
import * as swc from "@swc/core";
|
|
7
7
|
//#region src/core/plugins/swc-plugin.ts
|
|
8
8
|
function swcPlugin({ context, transformConfig }) {
|
|
9
9
|
const { rules = [] } = transformConfig ?? {};
|
package/dist/core/rolldown.js
CHANGED
|
@@ -2,8 +2,8 @@ import { ROLLIPOP_VIRTUAL_ENTRY_ID } from "../constants.js";
|
|
|
2
2
|
import { isDebugEnabled } from "../common/env.js";
|
|
3
3
|
import { CompatStatusReporter, ProgressBarStatusReporter } from "../events/builtin-reporters.js";
|
|
4
4
|
import { asLiteral, iife, nodeEnvironment } from "../common/code.js";
|
|
5
|
-
import { getGlobalVariables } from "../internal/react-native.js";
|
|
6
5
|
import { resolveFrom, resolvePackageJson } from "../utils/node-resolve.js";
|
|
6
|
+
import { getGlobalVariables } from "../internal/react-native.js";
|
|
7
7
|
import { printPluginLog } from "./plugins/context.js";
|
|
8
8
|
import { applyRolldownOptionsConfig } from "../config/compose-override.js";
|
|
9
9
|
import { createVirtualModuleId, escapeVirtualModuleId } from "../utils/id.js";
|
|
@@ -200,6 +200,7 @@ async function resolveReactNativePluginOptions(config, context, buildOptions) {
|
|
|
200
200
|
return {
|
|
201
201
|
context,
|
|
202
202
|
projectRoot: config.root,
|
|
203
|
+
reactNativePath: config.reactNative.reactNativePath,
|
|
203
204
|
platform: buildOptions.platform,
|
|
204
205
|
preferNativePlatform: config.resolve.preferNativePlatform,
|
|
205
206
|
buildType: context.buildType,
|
package/dist/hmr-runtime.iife.js
CHANGED
|
@@ -261,6 +261,14 @@ var __rolldown_runtime__ = (function() {
|
|
|
261
261
|
} });
|
|
262
262
|
if (t) _set_prototype_of(e, t);
|
|
263
263
|
}
|
|
264
|
+
function _is_native_reflect_construct() {
|
|
265
|
+
try {
|
|
266
|
+
var e = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
|
|
267
|
+
} catch (e) {}
|
|
268
|
+
return (_is_native_reflect_construct = function() {
|
|
269
|
+
return !!e;
|
|
270
|
+
})();
|
|
271
|
+
}
|
|
264
272
|
function _iterable_to_array(e) {
|
|
265
273
|
if (typeof Symbol !== "undefined" && e[Symbol.iterator] != null || e["@@iterator"] != null) return Array.from(e);
|
|
266
274
|
}
|
|
@@ -289,10 +297,10 @@ var __rolldown_runtime__ = (function() {
|
|
|
289
297
|
return n;
|
|
290
298
|
}
|
|
291
299
|
function _non_iterable_rest() {
|
|
292
|
-
throw new TypeError("Invalid attempt to destructure non-iterable instance
|
|
300
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
293
301
|
}
|
|
294
302
|
function _non_iterable_spread() {
|
|
295
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance
|
|
303
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
296
304
|
}
|
|
297
305
|
function _possible_constructor_return(e, t) {
|
|
298
306
|
if (t && (_type_of(t) === "object" || typeof t === "function")) return t;
|
|
@@ -311,26 +319,6 @@ var __rolldown_runtime__ = (function() {
|
|
|
311
319
|
function _to_consumable_array(e) {
|
|
312
320
|
return _array_without_holes(e) || _iterable_to_array(e) || _unsupported_iterable_to_array(e) || _non_iterable_spread();
|
|
313
321
|
}
|
|
314
|
-
function _type_of(e) {
|
|
315
|
-
"@swc/helpers - typeof";
|
|
316
|
-
return e && typeof Symbol !== "undefined" && e.constructor === Symbol ? "symbol" : typeof e;
|
|
317
|
-
}
|
|
318
|
-
function _unsupported_iterable_to_array(e, t) {
|
|
319
|
-
if (!e) return;
|
|
320
|
-
if (typeof e === "string") return _array_like_to_array(e, t);
|
|
321
|
-
var r = Object.prototype.toString.call(e).slice(8, -1);
|
|
322
|
-
if (r === "Object" && e.constructor) r = e.constructor.name;
|
|
323
|
-
if (r === "Map" || r === "Set") return Array.from(r);
|
|
324
|
-
if (r === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)) return _array_like_to_array(e, t);
|
|
325
|
-
}
|
|
326
|
-
function _is_native_reflect_construct() {
|
|
327
|
-
try {
|
|
328
|
-
var e = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
|
|
329
|
-
} catch (e) {}
|
|
330
|
-
return (_is_native_reflect_construct = function() {
|
|
331
|
-
return !!e;
|
|
332
|
-
})();
|
|
333
|
-
}
|
|
334
322
|
function _ts_generator(e, t) {
|
|
335
323
|
var r, n, o, a = {
|
|
336
324
|
label: 0,
|
|
@@ -411,6 +399,18 @@ var __rolldown_runtime__ = (function() {
|
|
|
411
399
|
};
|
|
412
400
|
}
|
|
413
401
|
}
|
|
402
|
+
function _type_of(e) {
|
|
403
|
+
"@swc/helpers - typeof";
|
|
404
|
+
return e && typeof Symbol !== "undefined" && e.constructor === Symbol ? "symbol" : typeof e;
|
|
405
|
+
}
|
|
406
|
+
function _unsupported_iterable_to_array(e, t) {
|
|
407
|
+
if (!e) return;
|
|
408
|
+
if (typeof e === "string") return _array_like_to_array(e, t);
|
|
409
|
+
var r = Object.prototype.toString.call(e).slice(8, -1);
|
|
410
|
+
if (r === "Object" && e.constructor) r = e.constructor.name;
|
|
411
|
+
if (r === "Map" || r === "Set") return Array.from(r);
|
|
412
|
+
if (r === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)) return _array_like_to_array(e, t);
|
|
413
|
+
}
|
|
414
414
|
var _globalThis;
|
|
415
415
|
var ___rollipop_runtime__;
|
|
416
416
|
var BaseDevRuntime = DevRuntime;
|
|
@@ -616,14 +616,14 @@ var __rolldown_runtime__ = (function() {
|
|
|
616
616
|
]);
|
|
617
617
|
return e;
|
|
618
618
|
}();
|
|
619
|
-
var ReactNativeDevRuntime = /*#__PURE__*/ function(
|
|
619
|
+
var ReactNativeDevRuntime = /*#__PURE__*/ function(BaseDevRuntime) {
|
|
620
620
|
"use strict";
|
|
621
|
-
_inherits(
|
|
622
|
-
function
|
|
623
|
-
_class_call_check(this,
|
|
621
|
+
_inherits(ReactNativeDevRuntime, BaseDevRuntime);
|
|
622
|
+
function ReactNativeDevRuntime() {
|
|
623
|
+
_class_call_check(this, ReactNativeDevRuntime);
|
|
624
624
|
var e;
|
|
625
625
|
var t = new SocketHolder();
|
|
626
|
-
e = _call_super(this,
|
|
626
|
+
e = _call_super(this, ReactNativeDevRuntime, [createClientId()]), e.moduleHotContexts = /* @__PURE__ */ new Map(), e.moduleHotContextsToBeUpdated = /* @__PURE__ */ new Map(), e.moduleHotData = /* @__PURE__ */ new Map(), e.applyQueue = Promise.resolve(), e.updatesEnabled = Promise.resolve(), e.reloadRequested = false, e.lastSeq = 0;
|
|
627
627
|
e.socketHolder = t;
|
|
628
628
|
e.hooks = {
|
|
629
629
|
createModuleHotContext: function t(t) {
|
|
@@ -635,7 +635,7 @@ var __rolldown_runtime__ = (function() {
|
|
|
635
635
|
};
|
|
636
636
|
return e;
|
|
637
637
|
}
|
|
638
|
-
_create_class(
|
|
638
|
+
_create_class(ReactNativeDevRuntime, [
|
|
639
639
|
{
|
|
640
640
|
key: "createHotContext",
|
|
641
641
|
value: function e(e) {
|
|
@@ -756,7 +756,6 @@ var __rolldown_runtime__ = (function() {
|
|
|
756
756
|
}
|
|
757
757
|
this.lastSeq = o;
|
|
758
758
|
var i = this.computeHmrUpdate(t);
|
|
759
|
-
if (i.type === "noop") return;
|
|
760
759
|
if (i.type === "full-reload") {
|
|
761
760
|
this.reload(i.reason);
|
|
762
761
|
return;
|
|
@@ -766,7 +765,7 @@ var __rolldown_runtime__ = (function() {
|
|
|
766
765
|
type: "hmr:payload-delivered",
|
|
767
766
|
filename: n
|
|
768
767
|
}));
|
|
769
|
-
this.applyUpdate(i);
|
|
768
|
+
if (i.type === "boundaries") this.applyUpdate(i);
|
|
770
769
|
}
|
|
771
770
|
},
|
|
772
771
|
{
|
|
@@ -1025,7 +1024,7 @@ var __rolldown_runtime__ = (function() {
|
|
|
1025
1024
|
}
|
|
1026
1025
|
}
|
|
1027
1026
|
]);
|
|
1028
|
-
return
|
|
1027
|
+
return ReactNativeDevRuntime;
|
|
1029
1028
|
}(BaseDevRuntime);
|
|
1030
1029
|
function createClientId() {
|
|
1031
1030
|
return "".concat(Date.now().toString(36), "-").concat(Math.random().toString(36).slice(2));
|
|
@@ -1,14 +1,45 @@
|
|
|
1
|
-
import { __require } from "../_virtual/_rolldown/runtime.js";
|
|
2
1
|
import { asLiteral } from "../common/code.js";
|
|
2
|
+
import { resolvePackagePath } from "../utils/node-resolve.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { isNotNil } from "es-toolkit";
|
|
5
6
|
//#region src/internal/react-native.ts
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
const ASSET_REGISTRY_PATH = "react-native/asset-registry";
|
|
8
|
+
const LEGACY_ASSET_REGISTRY_PATH = "react-native/Libraries/Image/AssetRegistry.js";
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
function resolveInitializeCorePath(basePath) {
|
|
11
|
+
return resolveOptional("react-native/setup-env", [basePath]) ?? require.resolve("react-native/Libraries/Core/InitializeCore", { paths: [basePath] });
|
|
8
12
|
}
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
function resolveAssetRegistryPath(basePath) {
|
|
14
|
+
return resolveOptional(ASSET_REGISTRY_PATH, [basePath]) ? ASSET_REGISTRY_PATH : LEGACY_ASSET_REGISTRY_PATH;
|
|
15
|
+
}
|
|
16
|
+
function resolvePolyfillScriptPaths(basePath, reactNativePath) {
|
|
17
|
+
const polyfillPath = resolveOptional("@react-native/js-polyfills", [reactNativePath, basePath]);
|
|
18
|
+
if (polyfillPath) return loadPolyfillScriptPaths(polyfillPath);
|
|
19
|
+
let metroConfigPath;
|
|
20
|
+
try {
|
|
21
|
+
metroConfigPath = resolvePackagePath(basePath, "@react-native/metro-config");
|
|
22
|
+
} catch {}
|
|
23
|
+
if (metroConfigPath) {
|
|
24
|
+
const requireFromMetroConfig = createRequire(path.join(metroConfigPath, "package.json"));
|
|
25
|
+
const metroPolyfillPath = resolveOptionalWith(() => requireFromMetroConfig.resolve("@react-native/js-polyfills"));
|
|
26
|
+
if (metroPolyfillPath) return loadPolyfillScriptPaths(metroPolyfillPath, requireFromMetroConfig);
|
|
27
|
+
}
|
|
28
|
+
return loadPolyfillScriptPaths(require.resolve(path.join(reactNativePath, "rn-get-polyfills")));
|
|
29
|
+
}
|
|
30
|
+
function loadPolyfillScriptPaths(modulePath, load = require) {
|
|
31
|
+
return load(modulePath)();
|
|
32
|
+
}
|
|
33
|
+
function resolveOptional(moduleId, paths) {
|
|
34
|
+
return resolveOptionalWith(() => require.resolve(moduleId, { paths }));
|
|
35
|
+
}
|
|
36
|
+
function resolveOptionalWith(resolve) {
|
|
37
|
+
try {
|
|
38
|
+
return resolve();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (error instanceof Error && "code" in error && (error.code === "MODULE_NOT_FOUND" || error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED")) return;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
12
43
|
}
|
|
13
44
|
function getGlobalVariables(dev) {
|
|
14
45
|
return [
|
|
@@ -20,4 +51,4 @@ function getGlobalVariables(dev) {
|
|
|
20
51
|
].filter(isNotNil);
|
|
21
52
|
}
|
|
22
53
|
//#endregion
|
|
23
|
-
export { getGlobalVariables,
|
|
54
|
+
export { getGlobalVariables, resolveAssetRegistryPath, resolveInitializeCorePath, resolvePolyfillScriptPaths };
|
|
@@ -17,6 +17,10 @@ function getAgentGuide(baseUrl = defaultBaseUrl) {
|
|
|
17
17
|
" rollipop skills get <name>",
|
|
18
18
|
"",
|
|
19
19
|
"Rollipop exposes build events, diagnostics, and client logs through Devframe MCP.",
|
|
20
|
+
"MCP requires the optional @devframes/agentic peer dependency:",
|
|
21
|
+
"",
|
|
22
|
+
" yarn add -D @devframes/agentic@1.0.0",
|
|
23
|
+
"",
|
|
20
24
|
"Keep the dev server running while your agent uses the MCP tools.",
|
|
21
25
|
"",
|
|
22
26
|
"Start the dev server:",
|
|
@@ -27,8 +31,7 @@ function getAgentGuide(baseUrl = defaultBaseUrl) {
|
|
|
27
31
|
"",
|
|
28
32
|
` MCP server: ${baseUrl}/__rollipop/__mcp`,
|
|
29
33
|
"",
|
|
30
|
-
"
|
|
31
|
-
"adjust these URLs to match the running dev server.",
|
|
34
|
+
"For a custom loopback --host or --port, adjust both the MCP URL and Origin.",
|
|
32
35
|
"",
|
|
33
36
|
"MCP setup:",
|
|
34
37
|
"",
|
|
@@ -44,13 +47,12 @@ function getAgentGuide(baseUrl = defaultBaseUrl) {
|
|
|
44
47
|
" }",
|
|
45
48
|
" }",
|
|
46
49
|
"",
|
|
47
|
-
" Devframe
|
|
50
|
+
" Devframe requires a loopback Origin and a connection from the same machine.",
|
|
48
51
|
"",
|
|
49
52
|
" Tool discovery:",
|
|
50
53
|
" MCP clients discover supported tools by calling tools/list.",
|
|
51
54
|
" The response includes tool names, descriptions, and input schemas.",
|
|
52
|
-
"
|
|
53
|
-
" the returned mcp-session-id header.",
|
|
55
|
+
" The endpoint is stateless; no mcp-session-id header is returned or required.",
|
|
54
56
|
"",
|
|
55
57
|
"External debugging:",
|
|
56
58
|
"",
|
|
@@ -65,9 +67,11 @@ function getAgentGuide(baseUrl = defaultBaseUrl) {
|
|
|
65
67
|
"Agent workflow:",
|
|
66
68
|
"",
|
|
67
69
|
" 1. Run rollipop start and keep it running while using MCP tools.",
|
|
68
|
-
" 2.
|
|
70
|
+
" 2. Start get_build_events, then make edits while it is listening.",
|
|
71
|
+
" Only new events are collected; bundler-scoped events include bundlerId.",
|
|
69
72
|
" 3. Wait for bundle_build_done or bundle_build_failed for bundle builds.",
|
|
70
|
-
" For HMR edits, watch for watch_change
|
|
73
|
+
" For HMR edits, watch for watch_change and hmr_failed.",
|
|
74
|
+
" Use build_logs and build_errors when events occurred before listening.",
|
|
71
75
|
" 4. Use get_bundler_status when you need the latest state for a known bundlerId.",
|
|
72
76
|
" 5. Use get_console_logs, clear_console_logs, and clear_build_* for focused diagnostics.",
|
|
73
77
|
" 6. Use bundleFilePath from bundle_build_done when bundle-level debugging is needed.",
|
package/dist/package.js
CHANGED
|
@@ -44,19 +44,22 @@ export declare class BundlerDevEngine {
|
|
|
44
44
|
getBundle(): Promise<BundleStore>;
|
|
45
45
|
getSourceMap(): Promise<string | undefined>;
|
|
46
46
|
triggerFullBuild(): Promise<void>;
|
|
47
|
+
close(): Promise<void>;
|
|
47
48
|
}
|
|
48
49
|
export declare class BundlerPool {
|
|
49
50
|
private readonly config;
|
|
50
51
|
private readonly resolvedServerOptions;
|
|
51
52
|
private readonly eventBus;
|
|
52
|
-
private
|
|
53
|
+
private readonly instances;
|
|
54
|
+
private closeTask;
|
|
53
55
|
readonly hotUpdateStore: HotUpdateStore;
|
|
54
56
|
constructor(config: ResolvedConfig, resolvedServerOptions: Required<Pick<ServerOptions, 'host' | 'port'>> & Pick<ServerOptions, 'buildOptions'>, eventBus: EventBus);
|
|
55
|
-
get(bundleName: string, buildOptions: Pick<BuildOptions, 'platform' | 'dev'>): BundlerDevEngine;
|
|
57
|
+
get(bundleName: string, buildOptions: Pick<BuildOptions, 'platform' | 'dev' | 'minify'>): BundlerDevEngine;
|
|
56
58
|
/**
|
|
57
59
|
* Look up a cached bundler by the id carried as `bundlerId` in events such as `bundle_build_done`. Returns `undefined` when no instance with that id has been created yet.
|
|
58
60
|
*/
|
|
59
61
|
getInstanceById(id: string): BundlerDevEngine | undefined;
|
|
60
62
|
getInstances(): BundlerDevEngine[];
|
|
63
|
+
close(): Promise<void>;
|
|
61
64
|
}
|
|
62
65
|
//#endregion
|
|
@@ -218,12 +218,19 @@ var BundlerDevEngine = class {
|
|
|
218
218
|
await this.ensureInitialized;
|
|
219
219
|
this.devEngine.triggerFullBuild();
|
|
220
220
|
}
|
|
221
|
+
async close() {
|
|
222
|
+
await this.initializeTask.catch(() => {});
|
|
223
|
+
const devEngine = this._devEngine;
|
|
224
|
+
this._devEngine = null;
|
|
225
|
+
await devEngine?.close();
|
|
226
|
+
}
|
|
221
227
|
};
|
|
222
|
-
var BundlerPool = class
|
|
228
|
+
var BundlerPool = class {
|
|
223
229
|
config;
|
|
224
230
|
resolvedServerOptions;
|
|
225
231
|
eventBus;
|
|
226
|
-
|
|
232
|
+
instances = /* @__PURE__ */ new Map();
|
|
233
|
+
closeTask;
|
|
227
234
|
hotUpdateStore;
|
|
228
235
|
constructor(config, resolvedServerOptions, eventBus) {
|
|
229
236
|
this.config = config;
|
|
@@ -233,13 +240,14 @@ var BundlerPool = class BundlerPool {
|
|
|
233
240
|
this.hotUpdateStore.clear();
|
|
234
241
|
}
|
|
235
242
|
get(bundleName, buildOptions) {
|
|
243
|
+
invariant(this.closeTask == null, "Bundler pool is closed");
|
|
236
244
|
const baseBundleName = getBaseBundleName(bundleName);
|
|
237
245
|
const resolvedBuildOptions = resolveBuildOptions(this.config, {
|
|
238
246
|
...this.resolvedServerOptions.buildOptions,
|
|
239
247
|
...buildOptions
|
|
240
248
|
});
|
|
241
249
|
const key = `${baseBundleName}-${Bundler.createId(this.config, resolvedBuildOptions)}`;
|
|
242
|
-
const instance =
|
|
250
|
+
const instance = this.instances.get(key);
|
|
243
251
|
if (instance) return instance;
|
|
244
252
|
else {
|
|
245
253
|
logger.debug("Preparing new bundler instance", {
|
|
@@ -248,7 +256,7 @@ var BundlerPool = class BundlerPool {
|
|
|
248
256
|
});
|
|
249
257
|
const instance = new BundlerDevEngine(baseBundleName, { server: this.resolvedServerOptions }, this.config, resolvedBuildOptions, this.eventBus, this.hotUpdateStore);
|
|
250
258
|
logger.debug("Setting new bundler instance", { key });
|
|
251
|
-
|
|
259
|
+
this.instances.set(key, instance);
|
|
252
260
|
return instance;
|
|
253
261
|
}
|
|
254
262
|
}
|
|
@@ -256,10 +264,19 @@ var BundlerPool = class BundlerPool {
|
|
|
256
264
|
* Look up a cached bundler by the id carried as `bundlerId` in events such as `bundle_build_done`. Returns `undefined` when no instance with that id has been created yet.
|
|
257
265
|
*/
|
|
258
266
|
getInstanceById(id) {
|
|
259
|
-
for (const instance of
|
|
267
|
+
for (const instance of this.instances.values()) if (instance.id === id && instance.projectRoot === this.config.root) return instance;
|
|
260
268
|
}
|
|
261
269
|
getInstances() {
|
|
262
|
-
return Array.from(
|
|
270
|
+
return Array.from(this.instances.values());
|
|
271
|
+
}
|
|
272
|
+
close() {
|
|
273
|
+
this.closeTask ??= (async () => {
|
|
274
|
+
const instances = this.getInstances();
|
|
275
|
+
this.instances.clear();
|
|
276
|
+
const errors = (await Promise.allSettled(instances.map((instance) => instance.close()))).flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
277
|
+
if (errors.length > 0) throw new AggregateError(errors, "Failed to close bundler engines");
|
|
278
|
+
})();
|
|
279
|
+
return this.closeTask;
|
|
263
280
|
}
|
|
264
281
|
};
|
|
265
282
|
//#endregion
|
|
@@ -17,6 +17,7 @@ import { DevServerState } from "./state/store.js";
|
|
|
17
17
|
import { getWebSocketUpgradeHandler } from "./wss/server.js";
|
|
18
18
|
import { HMRServer } from "./wss/hmr-server.js";
|
|
19
19
|
import url from "node:url";
|
|
20
|
+
import { format } from "node:util";
|
|
20
21
|
import { createDevServerMiddleware } from "@react-native-community/cli-server-api";
|
|
21
22
|
import { createDevMiddleware } from "@react-native/dev-middleware";
|
|
22
23
|
import Fastify, { LogController } from "fastify";
|
|
@@ -61,10 +62,14 @@ async function createDevServer(config, options) {
|
|
|
61
62
|
logger: {
|
|
62
63
|
info(...args) {
|
|
63
64
|
if (args[0].includes("JavaScript logs have moved")) return;
|
|
64
|
-
logger.info(...args);
|
|
65
|
+
logger.info(format(...args));
|
|
65
66
|
},
|
|
66
|
-
warn
|
|
67
|
-
|
|
67
|
+
warn(...args) {
|
|
68
|
+
logger.warn(format(...args));
|
|
69
|
+
},
|
|
70
|
+
error(...args) {
|
|
71
|
+
logger.error(format(...args));
|
|
72
|
+
}
|
|
68
73
|
},
|
|
69
74
|
unstable_experiments: {
|
|
70
75
|
enableNetworkInspector: true,
|
|
@@ -142,6 +147,7 @@ async function createDevServer(config, options) {
|
|
|
142
147
|
server.close(() => resolve());
|
|
143
148
|
})));
|
|
144
149
|
});
|
|
150
|
+
fastify.addHook("onClose", () => bundlerPool.close());
|
|
145
151
|
await invokePostConfigureServer();
|
|
146
152
|
eventBus.emit({
|
|
147
153
|
type: "server_ready",
|
|
@@ -4,27 +4,23 @@ import { parseUrl } from "../../utils/url.js";
|
|
|
4
4
|
import { symbolicate } from "../symbolicate.js";
|
|
5
5
|
import { toDevframeEvent } from "./events.js";
|
|
6
6
|
import { registerAgentTools } from "./agent/tools/index.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getConfigInfo, getDevice, getFeatureFlags, getSnapshot } from "./data.js";
|
|
8
8
|
import { defineDevframe, defineRpcFunction } from "devframe";
|
|
9
9
|
//#region src/server/devframe/index.ts
|
|
10
10
|
const ROLLIPOP_DEVFRAME_SCOPE = "rollipop";
|
|
11
|
-
const
|
|
11
|
+
const DASHBOARD_EVENTS_STATE_KEY = "events";
|
|
12
12
|
var RollipopDevframeController = class {
|
|
13
13
|
context;
|
|
14
14
|
sequence = 0;
|
|
15
|
-
|
|
16
|
-
updateSharedState;
|
|
15
|
+
updateEventState;
|
|
17
16
|
unsubscribeEventBus;
|
|
18
|
-
refreshQueue;
|
|
19
17
|
definition;
|
|
20
18
|
constructor(context, agentToolContext) {
|
|
21
19
|
this.context = context;
|
|
22
|
-
const initialState = createInitialDashboardState(context);
|
|
23
|
-
this.refreshQueue = Promise.resolve(initialState);
|
|
24
20
|
this.definition = defineDevframe({
|
|
25
21
|
id: "rollipop",
|
|
26
22
|
name: "Rollipop",
|
|
27
|
-
version: "1.0.0-alpha.
|
|
23
|
+
version: "1.0.0-alpha.34",
|
|
28
24
|
packageName: "rollipop",
|
|
29
25
|
homepage: "https://github.com/rollipop-dev/rollipop",
|
|
30
26
|
description: "React Native development dashboard powered by Rollipop.",
|
|
@@ -34,13 +30,10 @@ var RollipopDevframeController = class {
|
|
|
34
30
|
},
|
|
35
31
|
setup: async (devframeContext) => {
|
|
36
32
|
const scope = devframeContext.scope(ROLLIPOP_DEVFRAME_SCOPE);
|
|
37
|
-
const sharedState = await scope.rpc.sharedState(
|
|
38
|
-
this.
|
|
33
|
+
const sharedState = await scope.rpc.sharedState(DASHBOARD_EVENTS_STATE_KEY, { initialValue: { lastEvent: null } });
|
|
34
|
+
this.updateEventState = (event) => {
|
|
39
35
|
sharedState.mutate((current) => {
|
|
40
|
-
current.
|
|
41
|
-
current.builds = state.builds;
|
|
42
|
-
current.featureFlags = state.featureFlags;
|
|
43
|
-
current.lastEvent = state.lastEvent;
|
|
36
|
+
current.lastEvent = event;
|
|
44
37
|
});
|
|
45
38
|
};
|
|
46
39
|
registerDashboardRpcFunctions(scope.rpc, this);
|
|
@@ -48,26 +41,19 @@ var RollipopDevframeController = class {
|
|
|
48
41
|
this.unsubscribeEventBus = context.eventBus.subscribe((event) => {
|
|
49
42
|
const data = toDevframeEvent(event);
|
|
50
43
|
if (data == null) return;
|
|
51
|
-
this.
|
|
52
|
-
sequence: ++this.sequence,
|
|
53
|
-
data
|
|
54
|
-
};
|
|
55
|
-
this.refresh();
|
|
44
|
+
this.notify(data);
|
|
56
45
|
});
|
|
57
46
|
}
|
|
58
47
|
});
|
|
59
48
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.updateSharedState?.(state);
|
|
65
|
-
return state;
|
|
49
|
+
notify(data) {
|
|
50
|
+
this.updateEventState?.({
|
|
51
|
+
sequence: ++this.sequence,
|
|
52
|
+
data
|
|
66
53
|
});
|
|
67
|
-
return this.refreshQueue;
|
|
68
54
|
}
|
|
69
|
-
|
|
70
|
-
return (
|
|
55
|
+
getSnapshot() {
|
|
56
|
+
return getSnapshot(this.context);
|
|
71
57
|
}
|
|
72
58
|
getBuilds() {
|
|
73
59
|
return this.context.state.getBuilds();
|
|
@@ -78,9 +64,8 @@ var RollipopDevframeController = class {
|
|
|
78
64
|
if (this.context.bundlerPool.getInstanceById(bundlerId) == null) throw new Error(`Build logs not found: ${bundlerId}`);
|
|
79
65
|
return [];
|
|
80
66
|
}
|
|
81
|
-
|
|
67
|
+
deleteBuildLogs(bundlerId) {
|
|
82
68
|
if (!this.context.state.clearBuildLogs(bundlerId) && this.context.bundlerPool.getInstanceById(bundlerId) == null) throw new Error(`Build logs not found: ${bundlerId}`);
|
|
83
|
-
await this.refresh();
|
|
84
69
|
}
|
|
85
70
|
getConfig() {
|
|
86
71
|
return getConfigInfo(this.context);
|
|
@@ -98,7 +83,8 @@ var RollipopDevframeController = class {
|
|
|
98
83
|
if (pathname == null || query.platform == null || query.dev == null) throw new Error("Bundle URL must include pathname, platform, and dev query parameters");
|
|
99
84
|
const bundle = await this.context.bundlerPool.get(getBaseBundleName(pathname), {
|
|
100
85
|
platform: String(query.platform),
|
|
101
|
-
dev: query.dev === "true"
|
|
86
|
+
dev: query.dev === "true",
|
|
87
|
+
...query.minify == null ? {} : { minify: query.minify === "true" }
|
|
102
88
|
}).getBundle();
|
|
103
89
|
return symbolicate(bundle, [{
|
|
104
90
|
file: bundleUrl,
|
|
@@ -111,33 +97,21 @@ var RollipopDevframeController = class {
|
|
|
111
97
|
if (bundler == null) throw new Error(`Bundler not found: ${bundlerId}`);
|
|
112
98
|
setTimeout(() => this.context.message.broadcast("reload"), 0);
|
|
113
99
|
await bundler.triggerFullBuild();
|
|
114
|
-
await this.refresh();
|
|
115
100
|
}
|
|
116
|
-
|
|
101
|
+
reload() {
|
|
117
102
|
this.context.message.broadcast("reload");
|
|
118
|
-
await this.refresh();
|
|
119
103
|
}
|
|
120
104
|
async resetCache() {
|
|
121
105
|
await resetCache();
|
|
122
106
|
this.context.eventBus.emit({ type: "cache_reset" });
|
|
123
|
-
await this.refresh();
|
|
124
107
|
}
|
|
125
|
-
|
|
108
|
+
resetBundlerState() {
|
|
126
109
|
this.context.state.resetBufferedState();
|
|
127
|
-
await this.refresh();
|
|
128
110
|
}
|
|
129
111
|
dispose() {
|
|
130
112
|
this.unsubscribeEventBus?.();
|
|
131
113
|
this.unsubscribeEventBus = void 0;
|
|
132
114
|
}
|
|
133
|
-
async readDashboardState(lastEvent) {
|
|
134
|
-
return {
|
|
135
|
-
snapshot: await getSnapshot(this.context),
|
|
136
|
-
builds: this.context.state.getBuilds(),
|
|
137
|
-
featureFlags: getFeatureFlags(this.context),
|
|
138
|
-
lastEvent
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
115
|
};
|
|
142
116
|
function registerDashboardRpcFunctions(rpc, controller) {
|
|
143
117
|
rpc.register(defineRpcFunction({
|
|
@@ -201,18 +175,5 @@ function registerDashboardRpcFunctions(rpc, controller) {
|
|
|
201
175
|
handler: () => controller.resetBundlerState()
|
|
202
176
|
}));
|
|
203
177
|
}
|
|
204
|
-
function createInitialDashboardState(context) {
|
|
205
|
-
return {
|
|
206
|
-
snapshot: {
|
|
207
|
-
project: getProjectInfo(context),
|
|
208
|
-
bundlers: getBundlers(context),
|
|
209
|
-
devices: [],
|
|
210
|
-
buildSummary: getBuildSummary(context)
|
|
211
|
-
},
|
|
212
|
-
builds: context.state.getBuilds(),
|
|
213
|
-
featureFlags: getFeatureFlags(context),
|
|
214
|
-
lastEvent: null
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
178
|
//#endregion
|
|
218
|
-
export {
|
|
179
|
+
export { DASHBOARD_EVENTS_STATE_KEY, ROLLIPOP_DEVFRAME_SCOPE, RollipopDevframeController };
|
|
@@ -16,7 +16,7 @@ async function setupDevframe(context, server, inspectorDeviceServer) {
|
|
|
16
16
|
const controller = new RollipopDevframeController(context, createAgentToolContext(context));
|
|
17
17
|
const devtools = initHub({
|
|
18
18
|
name: DEVFRAME_NAME,
|
|
19
|
-
version: "1.0.0-alpha.
|
|
19
|
+
version: "1.0.0-alpha.34",
|
|
20
20
|
base: ROLLIPOP_DEVFRAME_BASE,
|
|
21
21
|
cwd: context.config.root,
|
|
22
22
|
ui: createUi({
|
|
@@ -44,11 +44,11 @@ async function setupDevframe(context, server, inspectorDeviceServer) {
|
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
await devtools.ready;
|
|
47
|
-
const
|
|
48
|
-
controller.
|
|
47
|
+
const notifyDevicesChanged = () => {
|
|
48
|
+
controller.notify({ type: "devices_changed" });
|
|
49
49
|
};
|
|
50
50
|
const onDeviceConnection = (socket) => {
|
|
51
|
-
socket.once("close",
|
|
51
|
+
socket.once("close", notifyDevicesChanged);
|
|
52
52
|
};
|
|
53
53
|
inspectorDeviceServer?.on("connection", onDeviceConnection);
|
|
54
54
|
server.instance.addHook("onListen", () => {
|
|
@@ -56,11 +56,10 @@ async function setupDevframe(context, server, inspectorDeviceServer) {
|
|
|
56
56
|
const mcpUrl = getServerDisplayUrl(server.instance.listeningOrigin, "/__rollipop/__mcp");
|
|
57
57
|
logger.info(`MCP server listening at ${mcpUrl}`);
|
|
58
58
|
}
|
|
59
|
-
controller.refresh();
|
|
60
59
|
});
|
|
61
60
|
server.instance.addHook("preClose", async () => {
|
|
62
61
|
inspectorDeviceServer?.off("connection", onDeviceConnection);
|
|
63
|
-
for (const socket of inspectorDeviceServer?.clients ?? []) socket.off("close",
|
|
62
|
+
for (const socket of inspectorDeviceServer?.clients ?? []) socket.off("close", notifyDevicesChanged);
|
|
64
63
|
controller.dispose();
|
|
65
64
|
await devtools.close();
|
|
66
65
|
});
|
|
@@ -55,7 +55,8 @@ function getBundleStoresByFrameUrl(stack, context) {
|
|
|
55
55
|
const bundleName = getBaseBundleName(pathname);
|
|
56
56
|
const bundler = context.bundlerPool.get(bundleName, {
|
|
57
57
|
platform,
|
|
58
|
-
dev
|
|
58
|
+
dev,
|
|
59
|
+
...query.minify == null ? {} : { minify: query.minify === "true" }
|
|
59
60
|
});
|
|
60
61
|
bundleStores.set(file, bundler.getBundle());
|
|
61
62
|
}
|
|
@@ -4,6 +4,16 @@ import { WebSocketServer } from "./server.js";
|
|
|
4
4
|
import { invariant } from "es-toolkit";
|
|
5
5
|
//#region src/server/wss/hmr-server.ts
|
|
6
6
|
const MAX_PENDING_PAYLOADS_PER_CLIENT = 8;
|
|
7
|
+
const RESERVED_WEBSOCKET_EVENTS = /* @__PURE__ */ new Set([
|
|
8
|
+
"close",
|
|
9
|
+
"connection",
|
|
10
|
+
"error",
|
|
11
|
+
"headers",
|
|
12
|
+
"listening",
|
|
13
|
+
"wsClientError",
|
|
14
|
+
"newListener",
|
|
15
|
+
"removeListener"
|
|
16
|
+
]);
|
|
7
17
|
var HMRServer = class extends WebSocketServer {
|
|
8
18
|
bundlerPool;
|
|
9
19
|
eventBus;
|
|
@@ -19,9 +29,9 @@ var HMRServer = class extends WebSocketServer {
|
|
|
19
29
|
}
|
|
20
30
|
parseClientMessage(data) {
|
|
21
31
|
const parsedData = JSON.parse(this.rawDataToString(data));
|
|
22
|
-
|
|
23
|
-
invariant(
|
|
24
|
-
return
|
|
32
|
+
invariant(typeof parsedData?.type === "string", "Invalid HMR client message");
|
|
33
|
+
invariant(!RESERVED_WEBSOCKET_EVENTS.has(parsedData.type), `Reserved HMR event name: ${parsedData.type}`);
|
|
34
|
+
return parsedData;
|
|
25
35
|
}
|
|
26
36
|
async handleConnected(client, runtimeClientId, platform, bundleEntry) {
|
|
27
37
|
const connectionToken = Symbol();
|
|
@@ -28,7 +28,7 @@ var FileStorage = class FileStorage {
|
|
|
28
28
|
this.dataFilePath = path.join(FileStorage.getPath(basePath, { prepare: true }), DATA_FILENAME);
|
|
29
29
|
if (fs.existsSync(this.dataFilePath)) {
|
|
30
30
|
const loadedData = JSON.parse(fs.readFileSync(this.dataFilePath, "utf-8"));
|
|
31
|
-
if ("version" in loadedData && loadedData.version === "1.0.0-alpha.
|
|
31
|
+
if ("version" in loadedData && loadedData.version === "1.0.0-alpha.34") this.data = loadedData;
|
|
32
32
|
else {
|
|
33
33
|
this.data = createDefaultData();
|
|
34
34
|
this.flush();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rollipop",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.34",
|
|
4
4
|
"homepage": "https://github.com/rollipop-dev/rollipop#readme",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/rollipop-dev/rollipop/issues"
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"@react-native-community/cli-server-api": "20.2.0",
|
|
68
68
|
"@react-native-community/cli-types": "20.2.0",
|
|
69
69
|
"@react-native/dev-middleware": "0.87.1",
|
|
70
|
-
"@rollipop/dashboard": "1.0.0-alpha.
|
|
71
|
-
"@rollipop/rolldown": "1.0.
|
|
72
|
-
"@swc/core": "1.
|
|
70
|
+
"@rollipop/dashboard": "1.0.0-alpha.34",
|
|
71
|
+
"@rollipop/rolldown": "1.0.34",
|
|
72
|
+
"@swc/core": "1.16.13",
|
|
73
73
|
"@swc/helpers": "0.5.23",
|
|
74
74
|
"@types/ws": "8.18.1",
|
|
75
75
|
"arktype": "2.2.3",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
119
|
"engines": {
|
|
120
|
-
"node": "
|
|
120
|
+
"node": "^22.18.0 || ^24.11.0 || >=26.0.0"
|
|
121
121
|
},
|
|
122
122
|
"installConfig": {
|
|
123
123
|
"hoistingLimits": "none"
|
package/skills/configuration.md
CHANGED
|
@@ -18,7 +18,7 @@ Use https://rollipop.dev/docs/features/env.md when changing env file loading, pr
|
|
|
18
18
|
|
|
19
19
|
## Experimental Features
|
|
20
20
|
|
|
21
|
-
Use https://rollipop.dev/docs/
|
|
21
|
+
Use https://rollipop.dev/docs/experimental/overview.md before enabling experimental options. Keep the change explicit and mention stability/version risk. For worklets specifically, read https://rollipop.dev/docs/experimental/reanimated-worklets.md.
|
|
22
22
|
|
|
23
23
|
## Resolve Changes
|
|
24
24
|
|
|
@@ -27,5 +27,5 @@ When changing module resolution behavior, verify package metadata (`exports`, `r
|
|
|
27
27
|
## Watchpoints
|
|
28
28
|
|
|
29
29
|
- Translate intent from existing Metro config instead of copying Metro fields mechanically.
|
|
30
|
-
-
|
|
30
|
+
- Configured `prelude`, `polyfills`, `plugins`, `resolve.sourceExtensions`, and `resolve.assetExtensions` arrays are appended to the defaults with duplicates removed. Supplying an empty array does not remove the defaults.
|
|
31
31
|
- If config is produced dynamically, inspect both the source config and the resolved behavior through dev-server diagnostics when available.
|
package/skills/debugging.md
CHANGED
|
@@ -10,7 +10,7 @@ Use this skill when Rollipop is installed or running and the task needs diagnost
|
|
|
10
10
|
|
|
11
11
|
## MCP Server
|
|
12
12
|
|
|
13
|
-
Devframe MCP exposes structured Rollipop diagnostics
|
|
13
|
+
Devframe MCP exposes structured Rollipop diagnostics while the dev server is running when the optional `@devframes/agentic` peer dependency is installed. Use it for build events, logs, cache actions, app logs, device status, reloads, build info, and symbolication. Read https://rollipop.dev/docs/features/mcp.md for installation and setup.
|
|
14
14
|
|
|
15
15
|
## React Native DevTools
|
|
16
16
|
|
package/skills/dev-server.md
CHANGED
|
@@ -18,7 +18,7 @@ Use Rollipop's dev server for React Native HMR, source maps, multi-platform deve
|
|
|
18
18
|
|
|
19
19
|
## MCP Server
|
|
20
20
|
|
|
21
|
-
The Devframe MCP endpoint is available
|
|
21
|
+
The Devframe MCP endpoint is available while the dev server is running when the optional `@devframes/agentic` peer dependency is installed. Read https://rollipop.dev/docs/features/mcp.md for installation and setup.
|
|
22
22
|
|
|
23
23
|
## Interactive Tools
|
|
24
24
|
|
|
@@ -26,6 +26,6 @@ React Native DevTools and custom terminal commands belong to the dev-server work
|
|
|
26
26
|
|
|
27
27
|
## Watchpoints
|
|
28
28
|
|
|
29
|
-
- Keep
|
|
29
|
+
- Keep the host and port consistent with device/app URLs. HTTPS is not supported; `--https` fails even though the CLI accepts it alongside `--key` and `--cert`.
|
|
30
30
|
- Interactive mode is useful for humans; disable it for automation when needed.
|
|
31
31
|
- If dev-server startup or HMR fails, switch to `troubleshooting.md`; use `debugging.md` when diagnostics tooling is needed.
|
|
@@ -522,10 +522,14 @@ describe('HMR runtime', () => {
|
|
|
522
522
|
expect(reload).not.toHaveBeenCalled();
|
|
523
523
|
});
|
|
524
524
|
|
|
525
|
-
it('
|
|
526
|
-
|
|
525
|
+
it('updates unexecuted modules without executing them until their first import', async () => {
|
|
526
|
+
// oxlint-disable-next-line no-eval
|
|
527
|
+
const evaluate = vi.fn((source: string) => (0, eval)(source));
|
|
527
528
|
globalThis.globalEvalWithSourceUrl = evaluate;
|
|
528
|
-
const { socket } = await setupRuntime();
|
|
529
|
+
const { runtime, socket } = await setupRuntime();
|
|
530
|
+
runtime.registerFactory('dep.js', 'esm', (id) => {
|
|
531
|
+
runtime.registerModule(id, { exports: { value: 'before' } });
|
|
532
|
+
});
|
|
529
533
|
|
|
530
534
|
socket.emit('message', {
|
|
531
535
|
data: JSON.stringify({
|
|
@@ -539,8 +543,13 @@ describe('HMR runtime', () => {
|
|
|
539
543
|
});
|
|
540
544
|
|
|
541
545
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
542
|
-
expect(
|
|
543
|
-
expect(
|
|
546
|
+
expect(runtime.isExecuted('dep.js')).toBe(false);
|
|
547
|
+
expect(runtime.initModule('dep.js')).toEqual({ value: 'after' });
|
|
548
|
+
expect(runtime.getImporters('dep.js')).toEqual(['entry.js']);
|
|
549
|
+
expect(evaluate).toHaveBeenCalledOnce();
|
|
550
|
+
expect(socket.sent.map((message) => JSON.parse(message))).toEqual([
|
|
551
|
+
{ type: 'hmr:payload-delivered', filename: 'hmr_patch_1.js' },
|
|
552
|
+
]);
|
|
544
553
|
expect(reload).not.toHaveBeenCalled();
|
|
545
554
|
});
|
|
546
555
|
|
|
@@ -299,9 +299,6 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
|
|
|
299
299
|
this.lastSeq = seq;
|
|
300
300
|
|
|
301
301
|
const update = this.computeHmrUpdate(changedIds);
|
|
302
|
-
if (update.type === 'noop') {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
302
|
if (update.type === 'full-reload') {
|
|
306
303
|
this.reload(update.reason);
|
|
307
304
|
return;
|
|
@@ -311,7 +308,9 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
|
|
|
311
308
|
this.socketHolder.send(
|
|
312
309
|
JSON.stringify({ type: 'hmr:payload-delivered', filename } satisfies HMRClientMessage),
|
|
313
310
|
);
|
|
314
|
-
|
|
311
|
+
if (update.type === 'boundaries') {
|
|
312
|
+
this.applyUpdate(update);
|
|
313
|
+
}
|
|
315
314
|
}
|
|
316
315
|
|
|
317
316
|
private applyInvalidate(moduleId: string, firstInvalidatedBy: string) {
|