vxrn 1.21.1 → 1.21.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/dist/runtime/native-prelude.mjs +0 -14
- package/dist/runtime/native-prelude.mjs.map +1 -1
- package/dist/runtime/native-prelude.native.js +0 -14
- package/dist/runtime/native-prelude.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.mjs +48 -9
- package/dist/utils/createNativeDevEngine.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.native.js +48 -11
- package/dist/utils/createNativeDevEngine.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.test.mjs +54 -1
- package/dist/utils/createNativeDevEngine.test.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.test.native.js +57 -1
- package/dist/utils/createNativeDevEngine.test.native.js.map +1 -1
- package/expo-plugin.cjs +52 -1
- package/package.json +12 -12
- package/src/runtime/hmr-types.ts +6 -2
- package/src/runtime/native-prelude.ts +0 -14
- package/src/utils/createNativeDevEngine.test.ts +109 -0
- package/src/utils/createNativeDevEngine.ts +109 -30
- package/types/runtime/hmr-types.d.ts +5 -2
- package/types/runtime/hmr-types.d.ts.map +1 -1
- package/types/runtime/native-prelude.d.ts.map +1 -1
- package/types/utils/createNativeDevEngine.d.ts +28 -0
- package/types/utils/createNativeDevEngine.d.ts.map +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { getNativeTransformConfig, wrapNativeBundleModuleScope } from "./createNativeDevEngine.native.js";
|
|
2
|
+
import { getHermesSWCIncludes, getNativeTransformConfig, hmrClientNoopPlugin, vxrnCompilerPlugin, wrapNativeBundleModuleScope } from "./createNativeDevEngine.native.js";
|
|
3
3
|
var root = "/tmp/vxrn-native-env-define-test-nonexistent";
|
|
4
4
|
describe("getNativeTransformConfig platform env defines", function () {
|
|
5
5
|
var _loop = function (_i2, _iter2) {
|
|
@@ -50,4 +50,60 @@ globalThis.__rolldown_runtime__ = {};
|
|
|
50
50
|
expect(wrapNativeBundleModuleScope(input)).toBe(input);
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
|
+
describe("getHermesSWCIncludes", function () {
|
|
54
|
+
var CLASS_SET = ["transform-classes", "transform-class-properties", "transform-class-static-block", "transform-private-methods", "transform-private-property-in-object"];
|
|
55
|
+
it("always includes the full Hermes class-transform set (dev and prod)", function () {
|
|
56
|
+
expect(getHermesSWCIncludes(true)).toEqual(expect.arrayContaining(CLASS_SET));
|
|
57
|
+
expect(getHermesSWCIncludes(false)).toEqual(expect.arrayContaining(CLASS_SET));
|
|
58
|
+
});
|
|
59
|
+
it("adds transform-async-to-generator only in production", function () {
|
|
60
|
+
expect(getHermesSWCIncludes(true)).not.toContain("transform-async-to-generator");
|
|
61
|
+
expect(getHermesSWCIncludes(false)).toContain("transform-async-to-generator");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("hmrClientNoopPlugin", function () {
|
|
65
|
+
var plugin = hmrClientNoopPlugin();
|
|
66
|
+
var resolveId = plugin.resolveId;
|
|
67
|
+
var load = plugin.load;
|
|
68
|
+
var VIRTUAL_ID = "\0vxrn-hmr-client-noop";
|
|
69
|
+
it.each([["react-native/Libraries/Utilities/HMRClient", void 0], ["../Utilities/HMRClient", "/project/node_modules/react-native/Libraries/Core.js"], ["../../Utilities/HMRClient.js", "/project/react-native/Libraries/Core.js"], ["..\\Utilities\\HMRClient.js", "C:\\project\\react-native\\Libraries\\Core.js"], ["../Utilities/HMRClient.ts", "/project/node_modules/react-native/Core.js"], ["../Utilities/HMRClient.tsx", "/project/node_modules/react-native/Core.js"], ["../Utilities/HMRClient.cjs", "/project/node_modules/react-native/Core.js"]])("aliases RN HMRClient specifier %j to the no-op virtual module", function (source, importer) {
|
|
70
|
+
expect(resolveId(source, importer)).toEqual({
|
|
71
|
+
id: VIRTUAL_ID,
|
|
72
|
+
external: false
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it.each(["react-native/Libraries/Utilities/HMRClientRegistry", "some/MyUtilities/HMRClient", "react-native/Libraries/Core/setUpDeveloperTools", "react"])("does not touch unrelated specifier %j", function (source) {
|
|
76
|
+
expect(resolveId(source)).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
it("does not alias an app-authored Utilities/HMRClient module", function () {
|
|
79
|
+
expect(resolveId("../Utilities/HMRClient", "/project/src/App.tsx")).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
it("loads a no-op module exposing every HMRClient method RN calls", function () {
|
|
82
|
+
var result = load(VIRTUAL_ID);
|
|
83
|
+
expect(result === null || result === void 0 ? void 0 : result.moduleType).toBe("js");
|
|
84
|
+
for (var _i = 0, _iter = ["setup", "enable", "disable", "registerBundle", "log", "isEnabled"]; _i < _iter.length; _i++) {
|
|
85
|
+
var method = _iter[_i];
|
|
86
|
+
expect(result.code).toContain(method);
|
|
87
|
+
}
|
|
88
|
+
expect(result.code).toContain("export default HMRClient");
|
|
89
|
+
});
|
|
90
|
+
it("does not load unrelated ids", function () {
|
|
91
|
+
expect(load("\0some-other-virtual")).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe("vxrnCompilerPlugin React Refresh registration", function () {
|
|
95
|
+
it("keeps initial-bundle registrations visible to rolldown", async function () {
|
|
96
|
+
var previousNodeEnv = process.env.NODE_ENV;
|
|
97
|
+
process.env.NODE_ENV = "test";
|
|
98
|
+
try {
|
|
99
|
+
var transform = vxrnCompilerPlugin("ios", true).transform;
|
|
100
|
+
var code = (await transform("export const marker = \"$RefreshReg$(\"; export function Probe() { return <div>probe</div> }", "/project/src/Probe.tsx")).code;
|
|
101
|
+
expect(code).toContain("var __vxrnRefreshReg = globalThis.$RefreshReg$");
|
|
102
|
+
expect(code).toContain("__vxrnRefreshReg(");
|
|
103
|
+
expect(code).toContain("\"$RefreshReg$(\"");
|
|
104
|
+
} finally {
|
|
105
|
+
process.env.NODE_ENV = previousNodeEnv;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
53
109
|
//# sourceMappingURL=createNativeDevEngine.test.native.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["describe","expect","it","getNativeTransformConfig","wrapNativeBundleModuleScope","root","_loop","_i2","_iter2","_loop2","_i12","_iter12","dev","platform","define","toBe","JSON","stringify","envObject","parse","TAMAGUI_TARGET","TAMAGUI_ENVIRONMENT","_i1","_iter1","length","_i","_iter","RUNTIME_MARKER","out","openIdx","indexOf","toBeGreaterThan","toBeLessThan","trimEnd","endsWith","Function","not","toThrow","input"],"sources":["createNativeDevEngine.test.native.js"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { getNativeTransformConfig, wrapNativeBundleModuleScope } from \"./createNativeDevEngine\";\n\nvar root = \"/tmp/vxrn-native-env-define-test-nonexistent\";\ndescribe(\"getNativeTransformConfig platform env defines\", function() {\n\tvar _loop = function(_i2, _iter2) {\n\t\tvar _loop2 = function(_i12, _iter12) {\n\t\t\tvar dev = _iter12[_i12];\n\t\t\tit(`injects TAMAGUI_TARGET=native for ${platform} (dev=${dev})`, function() {\n\t\t\t\tvar { define } = getNativeTransformConfig(platform, dev, root);\n\t\t\t\texpect(define[\"import.meta.env.TAMAGUI_TARGET\"]).toBe(\"\\\"native\\\"\");\n\t\t\t\texpect(define[\"process.env.TAMAGUI_TARGET\"]).toBe(\"\\\"native\\\"\");\n\t\t\t\texpect(define[\"import.meta.env.TAMAGUI_ENVIRONMENT\"]).toBe(JSON.stringify(platform));\n\t\t\t\texpect(define[\"import.meta.env.VITE_ENVIRONMENT\"]).toBe(JSON.stringify(platform));\n\t\t\t\texpect(define[\"import.meta.env.VITE_NATIVE\"]).toBe(\"\\\"1\\\"\");\n\t\t\t\texpect(define[\"import.meta.env.EXPO_OS\"]).toBe(JSON.stringify(platform));\n\t\t\t\tvar envObject = JSON.parse(define[\"import.meta.env\"]);\n\t\t\t\texpect(envObject.TAMAGUI_TARGET).toBe(\"native\");\n\t\t\t\texpect(envObject.TAMAGUI_ENVIRONMENT).toBe(platform);\n\t\t\t});\n\t\t};\n\t\tvar platform = _iter2[_i2];\n\t\tfor (var _i1 = 0, _iter1 = [true, false]; _i1 < _iter1.length; _i1++) _loop2(_i1, _iter1);\n\t};\n\tfor (var _i = 0, _iter = [\"ios\", \"android\"]; _i < _iter.length; _i++) _loop(_i, _iter);\n});\ndescribe(\"wrapNativeBundleModuleScope\", function() {\n\tvar RUNTIME_MARKER = \"//#region \\\\0rolldown/runtime.js\";\n\tit(\"wraps module code after the prelude so top-level vars do not leak to global\", function() {\n\t\tvar out = wrapNativeBundleModuleScope(`globalThis.global = globalThis;\nglobalThis.__DEV__ = true;\n${RUNTIME_MARKER}\nvar fetch_hot, fetch$1, Headers, Request, Response$1;\nglobalThis.__rolldown_runtime__ = {};\n\n//# sourceMappingURL=x.js.map`);\n\t\tvar openIdx = out.indexOf(\";(function() {\");\n\t\texpect(openIdx).toBeGreaterThan(-1);\n\t\texpect(out.indexOf(\"globalThis.__DEV__\")).toBeLessThan(openIdx);\n\t\texpect(out.indexOf(\"var fetch_hot\")).toBeGreaterThan(openIdx);\n\t\texpect(out.indexOf(\"})();\")).toBeLessThan(out.indexOf(\"//# sourceMappingURL\"));\n\t\texpect(out.trimEnd().endsWith(\"//# sourceMappingURL=x.js.map\")).toBe(true);\n\t\texpect(function() {\n\t\t\treturn new Function(out);\n\t\t}).not.toThrow();\n\t});\n\tit(\"is a no-op when the runtime marker is absent (e.g. prod bundle)\", function() {\n\t\tvar input = \"var x = 1;\\nconsole.log(x);\\n\";\n\t\texpect(wrapNativeBundleModuleScope(input)).toBe(input);\n\t});\n});\n\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["describe","expect","it","getHermesSWCIncludes","getNativeTransformConfig","hmrClientNoopPlugin","vxrnCompilerPlugin","wrapNativeBundleModuleScope","root","_loop","_i2","_iter2","_loop2","_i12","_iter12","dev","platform","define","toBe","JSON","stringify","envObject","parse","TAMAGUI_TARGET","TAMAGUI_ENVIRONMENT","_i1","_iter1","length","_i","_iter","RUNTIME_MARKER","out","openIdx","indexOf","toBeGreaterThan","toBeLessThan","trimEnd","endsWith","Function","not","toThrow","input","CLASS_SET","toEqual","arrayContaining","toContain","plugin","resolveId","load","VIRTUAL_ID","each","source","importer","id","external","toBeUndefined","result","moduleType","method","code","previousNodeEnv","process","env","NODE_ENV","transform"],"sources":["createNativeDevEngine.test.native.js"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { getHermesSWCIncludes, getNativeTransformConfig, hmrClientNoopPlugin, vxrnCompilerPlugin, wrapNativeBundleModuleScope } from \"./createNativeDevEngine\";\n\nvar root = \"/tmp/vxrn-native-env-define-test-nonexistent\";\ndescribe(\"getNativeTransformConfig platform env defines\", function() {\n\tvar _loop = function(_i2, _iter2) {\n\t\tvar _loop2 = function(_i12, _iter12) {\n\t\t\tvar dev = _iter12[_i12];\n\t\t\tit(`injects TAMAGUI_TARGET=native for ${platform} (dev=${dev})`, function() {\n\t\t\t\tvar { define } = getNativeTransformConfig(platform, dev, root);\n\t\t\t\texpect(define[\"import.meta.env.TAMAGUI_TARGET\"]).toBe(\"\\\"native\\\"\");\n\t\t\t\texpect(define[\"process.env.TAMAGUI_TARGET\"]).toBe(\"\\\"native\\\"\");\n\t\t\t\texpect(define[\"import.meta.env.TAMAGUI_ENVIRONMENT\"]).toBe(JSON.stringify(platform));\n\t\t\t\texpect(define[\"import.meta.env.VITE_ENVIRONMENT\"]).toBe(JSON.stringify(platform));\n\t\t\t\texpect(define[\"import.meta.env.VITE_NATIVE\"]).toBe(\"\\\"1\\\"\");\n\t\t\t\texpect(define[\"import.meta.env.EXPO_OS\"]).toBe(JSON.stringify(platform));\n\t\t\t\tvar envObject = JSON.parse(define[\"import.meta.env\"]);\n\t\t\t\texpect(envObject.TAMAGUI_TARGET).toBe(\"native\");\n\t\t\t\texpect(envObject.TAMAGUI_ENVIRONMENT).toBe(platform);\n\t\t\t});\n\t\t};\n\t\tvar platform = _iter2[_i2];\n\t\tfor (var _i1 = 0, _iter1 = [true, false]; _i1 < _iter1.length; _i1++) _loop2(_i1, _iter1);\n\t};\n\tfor (var _i = 0, _iter = [\"ios\", \"android\"]; _i < _iter.length; _i++) _loop(_i, _iter);\n});\ndescribe(\"wrapNativeBundleModuleScope\", function() {\n\tvar RUNTIME_MARKER = \"//#region \\\\0rolldown/runtime.js\";\n\tit(\"wraps module code after the prelude so top-level vars do not leak to global\", function() {\n\t\tvar out = wrapNativeBundleModuleScope(`globalThis.global = globalThis;\nglobalThis.__DEV__ = true;\n${RUNTIME_MARKER}\nvar fetch_hot, fetch$1, Headers, Request, Response$1;\nglobalThis.__rolldown_runtime__ = {};\n\n//# sourceMappingURL=x.js.map`);\n\t\tvar openIdx = out.indexOf(\";(function() {\");\n\t\texpect(openIdx).toBeGreaterThan(-1);\n\t\texpect(out.indexOf(\"globalThis.__DEV__\")).toBeLessThan(openIdx);\n\t\texpect(out.indexOf(\"var fetch_hot\")).toBeGreaterThan(openIdx);\n\t\texpect(out.indexOf(\"})();\")).toBeLessThan(out.indexOf(\"//# sourceMappingURL\"));\n\t\texpect(out.trimEnd().endsWith(\"//# sourceMappingURL=x.js.map\")).toBe(true);\n\t\texpect(function() {\n\t\t\treturn new Function(out);\n\t\t}).not.toThrow();\n\t});\n\tit(\"is a no-op when the runtime marker is absent (e.g. prod bundle)\", function() {\n\t\tvar input = \"var x = 1;\\nconsole.log(x);\\n\";\n\t\texpect(wrapNativeBundleModuleScope(input)).toBe(input);\n\t});\n});\ndescribe(\"getHermesSWCIncludes\", function() {\n\tvar CLASS_SET = [\n\t\t\"transform-classes\",\n\t\t\"transform-class-properties\",\n\t\t\"transform-class-static-block\",\n\t\t\"transform-private-methods\",\n\t\t\"transform-private-property-in-object\"\n\t];\n\tit(\"always includes the full Hermes class-transform set (dev and prod)\", function() {\n\t\texpect(getHermesSWCIncludes(true)).toEqual(expect.arrayContaining(CLASS_SET));\n\t\texpect(getHermesSWCIncludes(false)).toEqual(expect.arrayContaining(CLASS_SET));\n\t});\n\tit(\"adds transform-async-to-generator only in production\", function() {\n\t\texpect(getHermesSWCIncludes(true)).not.toContain(\"transform-async-to-generator\");\n\t\texpect(getHermesSWCIncludes(false)).toContain(\"transform-async-to-generator\");\n\t});\n});\ndescribe(\"hmrClientNoopPlugin\", function() {\n\tvar plugin = hmrClientNoopPlugin();\n\tvar resolveId = plugin.resolveId;\n\tvar load = plugin.load;\n\tvar VIRTUAL_ID = \"\\0vxrn-hmr-client-noop\";\n\tit.each([\n\t\t[\"react-native/Libraries/Utilities/HMRClient\", void 0],\n\t\t[\"../Utilities/HMRClient\", \"/project/node_modules/react-native/Libraries/Core.js\"],\n\t\t[\"../../Utilities/HMRClient.js\", \"/project/react-native/Libraries/Core.js\"],\n\t\t[\"..\\\\Utilities\\\\HMRClient.js\", \"C:\\\\project\\\\react-native\\\\Libraries\\\\Core.js\"],\n\t\t[\"../Utilities/HMRClient.ts\", \"/project/node_modules/react-native/Core.js\"],\n\t\t[\"../Utilities/HMRClient.tsx\", \"/project/node_modules/react-native/Core.js\"],\n\t\t[\"../Utilities/HMRClient.cjs\", \"/project/node_modules/react-native/Core.js\"]\n\t])(\"aliases RN HMRClient specifier %j to the no-op virtual module\", function(source, importer) {\n\t\texpect(resolveId(source, importer)).toEqual({\n\t\t\tid: VIRTUAL_ID,\n\t\t\texternal: false\n\t\t});\n\t});\n\tit.each([\n\t\t\"react-native/Libraries/Utilities/HMRClientRegistry\",\n\t\t\"some/MyUtilities/HMRClient\",\n\t\t\"react-native/Libraries/Core/setUpDeveloperTools\",\n\t\t\"react\"\n\t])(\"does not touch unrelated specifier %j\", function(source) {\n\t\texpect(resolveId(source)).toBeUndefined();\n\t});\n\tit(\"does not alias an app-authored Utilities/HMRClient module\", function() {\n\t\texpect(resolveId(\"../Utilities/HMRClient\", \"/project/src/App.tsx\")).toBeUndefined();\n\t});\n\tit(\"loads a no-op module exposing every HMRClient method RN calls\", function() {\n\t\tvar result = load(VIRTUAL_ID);\n\t\texpect(result === null || result === void 0 ? void 0 : result.moduleType).toBe(\"js\");\n\t\tfor (var _i = 0, _iter = [\n\t\t\t\"setup\",\n\t\t\t\"enable\",\n\t\t\t\"disable\",\n\t\t\t\"registerBundle\",\n\t\t\t\"log\",\n\t\t\t\"isEnabled\"\n\t\t]; _i < _iter.length; _i++) {\n\t\t\tvar method = _iter[_i];\n\t\t\texpect(result.code).toContain(method);\n\t\t}\n\t\texpect(result.code).toContain(\"export default HMRClient\");\n\t});\n\tit(\"does not load unrelated ids\", function() {\n\t\texpect(load(\"\\0some-other-virtual\")).toBeUndefined();\n\t});\n});\ndescribe(\"vxrnCompilerPlugin React Refresh registration\", function() {\n\tit(\"keeps initial-bundle registrations visible to rolldown\", async function() {\n\t\tvar previousNodeEnv = process.env.NODE_ENV;\n\t\tprocess.env.NODE_ENV = \"test\";\n\t\ttry {\n\t\t\tvar transform = vxrnCompilerPlugin(\"ios\", true).transform;\n\t\t\tvar code = (await transform(\"export const marker = \\\"$RefreshReg$(\\\"; export function Probe() { return <div>probe</div> }\", \"/project/src/Probe.tsx\")).code;\n\t\t\texpect(code).toContain(\"var __vxrnRefreshReg = globalThis.$RefreshReg$\");\n\t\t\texpect(code).toContain(\"__vxrnRefreshReg(\");\n\t\t\texpect(code).toContain(\"\\\"$RefreshReg$(\\\"\");\n\t\t} finally {\n\t\t\tprocess.env.NODE_ENV = previousNodeEnv;\n\t\t}\n\t});\n});\n\n"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAC7C,SAASC,oBAAoB,EAAEC,wBAAwB,EAAEC,mBAAmB,EAAEC,kBAAkB,EAAEC,2BAA2B,QAAQ,mCAAyB;AAE9J,IAAIC,IAAI,GAAG,8CAA8C;AACzDR,QAAQ,CAAC,+CAA+C,EAAE,YAAW;EACpE,IAAIS,KAAK,GAAG,SAAAA,CAASC,GAAG,EAAEC,MAAM,EAAE;IACjC,IAAIC,MAAM,GAAG,SAAAA,CAASC,IAAI,EAAEC,OAAO,EAAE;MACpC,IAAIC,GAAG,GAAGD,OAAO,CAACD,IAAI,CAAC;MACvBX,EAAE,CAAC,qCAAqCc,QAAQ,SAASD,GAAG,GAAG,EAAE,YAAW;QAC3E,IAAI;UAAEE;QAAO,CAAC,GAAGb,wBAAwB,CAACY,QAAQ,EAAED,GAAG,EAAEP,IAAI,CAAC;QAC9DP,MAAM,CAACgB,MAAM,CAAC,gCAAgC,CAAC,CAAC,CAACC,IAAI,CAAC,YAAY,CAAC;QACnEjB,MAAM,CAACgB,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAACC,IAAI,CAAC,YAAY,CAAC;QAC/DjB,MAAM,CAACgB,MAAM,CAAC,qCAAqC,CAAC,CAAC,CAACC,IAAI,CAACC,IAAI,CAACC,SAAS,CAACJ,QAAQ,CAAC,CAAC;QACpFf,MAAM,CAACgB,MAAM,CAAC,kCAAkC,CAAC,CAAC,CAACC,IAAI,CAACC,IAAI,CAACC,SAAS,CAACJ,QAAQ,CAAC,CAAC;QACjFf,MAAM,CAACgB,MAAM,CAAC,6BAA6B,CAAC,CAAC,CAACC,IAAI,CAAC,OAAO,CAAC;QAC3DjB,MAAM,CAACgB,MAAM,CAAC,yBAAyB,CAAC,CAAC,CAACC,IAAI,CAACC,IAAI,CAACC,SAAS,CAACJ,QAAQ,CAAC,CAAC;QACxE,IAAIK,SAAS,GAAGF,IAAI,CAACG,KAAK,CAACL,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACrDhB,MAAM,CAACoB,SAAS,CAACE,cAAc,CAAC,CAACL,IAAI,CAAC,QAAQ,CAAC;QAC/CjB,MAAM,CAACoB,SAAS,CAACG,mBAAmB,CAAC,CAACN,IAAI,CAACF,QAAQ,CAAC;MACrD,CAAC,CAAC;IACH,CAAC;IACD,IAAIA,QAAQ,GAAGL,MAAM,CAACD,GAAG,CAAC;IAC1B,KAAK,IAAIe,GAAG,GAAG,CAAC,EAAEC,MAAM,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,EAAED,GAAG,GAAGC,MAAM,CAACC,MAAM,EAAEF,GAAG,EAAE,EAAEb,MAAM,CAACa,GAAG,EAAEC,MAAM,CAAC;EAC1F,CAAC;EACD,KAAK,IAAIE,EAAE,GAAG,CAAC,EAAEC,KAAK,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,EAAED,EAAE,GAAGC,KAAK,CAACF,MAAM,EAAEC,EAAE,EAAE,EAAEnB,KAAK,CAACmB,EAAE,EAAEC,KAAK,CAAC;AACvF,CAAC,CAAC;AACF7B,QAAQ,CAAC,6BAA6B,EAAE,YAAW;EAClD,IAAI8B,cAAc,GAAG,kCAAkC;EACvD5B,EAAE,CAAC,6EAA6E,EAAE,YAAW;IAC5F,IAAI6B,GAAG,GAAGxB,2BAA2B,CAAC;AACxC;AACA,EAAEuB,cAAc;AAChB;AACA;AACA;AACA,8BAA8B,CAAC;IAC7B,IAAIE,OAAO,GAAGD,GAAG,CAACE,OAAO,CAAC,gBAAgB,CAAC;IAC3ChC,MAAM,CAAC+B,OAAO,CAAC,CAACE,eAAe,CAAC,CAAC,CAAC,CAAC;IACnCjC,MAAM,CAAC8B,GAAG,CAACE,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAACE,YAAY,CAACH,OAAO,CAAC;IAC/D/B,MAAM,CAAC8B,GAAG,CAACE,OAAO,CAAC,eAAe,CAAC,CAAC,CAACC,eAAe,CAACF,OAAO,CAAC;IAC7D/B,MAAM,CAAC8B,GAAG,CAACE,OAAO,CAAC,OAAO,CAAC,CAAC,CAACE,YAAY,CAACJ,GAAG,CAACE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC9EhC,MAAM,CAAC8B,GAAG,CAACK,OAAO,CAAC,CAAC,CAACC,QAAQ,CAAC,+BAA+B,CAAC,CAAC,CAACnB,IAAI,CAAC,IAAI,CAAC;IAC1EjB,MAAM,CAAC,YAAW;MACjB,OAAO,IAAIqC,QAAQ,CAACP,GAAG,CAAC;IACzB,CAAC,CAAC,CAACQ,GAAG,CAACC,OAAO,CAAC,CAAC;EACjB,CAAC,CAAC;EACFtC,EAAE,CAAC,iEAAiE,EAAE,YAAW;IAChF,IAAIuC,KAAK,GAAG,+BAA+B;IAC3CxC,MAAM,CAACM,2BAA2B,CAACkC,KAAK,CAAC,CAAC,CAACvB,IAAI,CAACuB,KAAK,CAAC;EACvD,CAAC,CAAC;AACH,CAAC,CAAC;AACFzC,QAAQ,CAAC,sBAAsB,EAAE,YAAW;EAC3C,IAAI0C,SAAS,GAAG,CACf,mBAAmB,EACnB,4BAA4B,EAC5B,8BAA8B,EAC9B,2BAA2B,EAC3B,sCAAsC,CACtC;EACDxC,EAAE,CAAC,oEAAoE,EAAE,YAAW;IACnFD,MAAM,CAACE,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAACwC,OAAO,CAAC1C,MAAM,CAAC2C,eAAe,CAACF,SAAS,CAAC,CAAC;IAC7EzC,MAAM,CAACE,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAACwC,OAAO,CAAC1C,MAAM,CAAC2C,eAAe,CAACF,SAAS,CAAC,CAAC;EAC/E,CAAC,CAAC;EACFxC,EAAE,CAAC,sDAAsD,EAAE,YAAW;IACrED,MAAM,CAACE,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAACoC,GAAG,CAACM,SAAS,CAAC,8BAA8B,CAAC;IAChF5C,MAAM,CAACE,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC0C,SAAS,CAAC,8BAA8B,CAAC;EAC9E,CAAC,CAAC;AACH,CAAC,CAAC;AACF7C,QAAQ,CAAC,qBAAqB,EAAE,YAAW;EAC1C,IAAI8C,MAAM,GAAGzC,mBAAmB,CAAC,CAAC;EAClC,IAAI0C,SAAS,GAAGD,MAAM,CAACC,SAAS;EAChC,IAAIC,IAAI,GAAGF,MAAM,CAACE,IAAI;EACtB,IAAIC,UAAU,GAAG,wBAAwB;EACzC/C,EAAE,CAACgD,IAAI,CAAC,CACP,CAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC,EACtD,CAAC,wBAAwB,EAAE,sDAAsD,CAAC,EAClF,CAAC,8BAA8B,EAAE,yCAAyC,CAAC,EAC3E,CAAC,6BAA6B,EAAE,+CAA+C,CAAC,EAChF,CAAC,2BAA2B,EAAE,4CAA4C,CAAC,EAC3E,CAAC,4BAA4B,EAAE,4CAA4C,CAAC,EAC5E,CAAC,4BAA4B,EAAE,4CAA4C,CAAC,CAC5E,CAAC,CAAC,+DAA+D,EAAE,UAASC,MAAM,EAAEC,QAAQ,EAAE;IAC9FnD,MAAM,CAAC8C,SAAS,CAACI,MAAM,EAAEC,QAAQ,CAAC,CAAC,CAACT,OAAO,CAAC;MAC3CU,EAAE,EAAEJ,UAAU;MACdK,QAAQ,EAAE;IACX,CAAC,CAAC;EACH,CAAC,CAAC;EACFpD,EAAE,CAACgD,IAAI,CAAC,CACP,oDAAoD,EACpD,4BAA4B,EAC5B,iDAAiD,EACjD,OAAO,CACP,CAAC,CAAC,uCAAuC,EAAE,UAASC,MAAM,EAAE;IAC5DlD,MAAM,CAAC8C,SAAS,CAACI,MAAM,CAAC,CAAC,CAACI,aAAa,CAAC,CAAC;EAC1C,CAAC,CAAC;EACFrD,EAAE,CAAC,2DAA2D,EAAE,YAAW;IAC1ED,MAAM,CAAC8C,SAAS,CAAC,wBAAwB,EAAE,sBAAsB,CAAC,CAAC,CAACQ,aAAa,CAAC,CAAC;EACpF,CAAC,CAAC;EACFrD,EAAE,CAAC,+DAA+D,EAAE,YAAW;IAC9E,IAAIsD,MAAM,GAAGR,IAAI,CAACC,UAAU,CAAC;IAC7BhD,MAAM,CAACuD,MAAM,KAAK,IAAI,IAAIA,MAAM,KAAK,KAAK,CAAC,GAAG,KAAK,CAAC,GAAGA,MAAM,CAACC,UAAU,CAAC,CAACvC,IAAI,CAAC,IAAI,CAAC;IACpF,KAAK,IAAIU,EAAE,GAAG,CAAC,EAAEC,KAAK,GAAG,CACxB,OAAO,EACP,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,KAAK,EACL,WAAW,CACX,EAAED,EAAE,GAAGC,KAAK,CAACF,MAAM,EAAEC,EAAE,EAAE,EAAE;MAC3B,IAAI8B,MAAM,GAAG7B,KAAK,CAACD,EAAE,CAAC;MACtB3B,MAAM,CAACuD,MAAM,CAACG,IAAI,CAAC,CAACd,SAAS,CAACa,MAAM,CAAC;IACtC;IACAzD,MAAM,CAACuD,MAAM,CAACG,IAAI,CAAC,CAACd,SAAS,CAAC,0BAA0B,CAAC;EAC1D,CAAC,CAAC;EACF3C,EAAE,CAAC,6BAA6B,EAAE,YAAW;IAC5CD,MAAM,CAAC+C,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAACO,aAAa,CAAC,CAAC;EACrD,CAAC,CAAC;AACH,CAAC,CAAC;AACFvD,QAAQ,CAAC,+CAA+C,EAAE,YAAW;EACpEE,EAAE,CAAC,wDAAwD,EAAE,kBAAiB;IAC7E,IAAI0D,eAAe,GAAGC,OAAO,CAACC,GAAG,CAACC,QAAQ;IAC1CF,OAAO,CAACC,GAAG,CAACC,QAAQ,GAAG,MAAM;IAC7B,IAAI;MACH,IAAIC,SAAS,GAAG1D,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC0D,SAAS;MACzD,IAAIL,IAAI,GAAG,CAAC,MAAMK,SAAS,CAAC,8FAA8F,EAAE,wBAAwB,CAAC,EAAEL,IAAI;MAC3J1D,MAAM,CAAC0D,IAAI,CAAC,CAACd,SAAS,CAAC,gDAAgD,CAAC;MACxE5C,MAAM,CAAC0D,IAAI,CAAC,CAACd,SAAS,CAAC,mBAAmB,CAAC;MAC3C5C,MAAM,CAAC0D,IAAI,CAAC,CAACd,SAAS,CAAC,mBAAmB,CAAC;IAC5C,CAAC,SAAS;MACTgB,OAAO,CAACC,GAAG,CAACC,QAAQ,GAAGH,eAAe;IACvC;EACD,CAAC,CAAC;AACH,CAAC,CAAC","ignoreList":[]}
|
package/expo-plugin.cjs
CHANGED
|
@@ -19,6 +19,13 @@ function getPodfilePropsOptOut(propsPath, key) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const plugin = (config, options = {}) => {
|
|
22
|
+
// fail fast on an Expo SDK mismatch before any native project is generated:
|
|
23
|
+
// one/vxrn pin expo-linking to a single SDK, and its native module is
|
|
24
|
+
// compiled against that SDK's expo-modules-core ABI. When the app's expo
|
|
25
|
+
// brings a newer expo-modules-core, the build/install/launch pipeline all
|
|
26
|
+
// succeed and the app then dies at ReactInstance creation with
|
|
27
|
+
// NoSuchMethodError in ExpoLinkingModule — invisible until runtime.
|
|
28
|
+
assertExpoModulesCoreMatchesExpoLinking(process.cwd())
|
|
22
29
|
return withPlugins(config, [
|
|
23
30
|
// auto-inject swift 6 workaround for expo-modules-core into Podfile (version-gated, opt-out-able)
|
|
24
31
|
[
|
|
@@ -693,8 +700,12 @@ function addReactNativeScreensFix(input) {
|
|
|
693
700
|
}
|
|
694
701
|
|
|
695
702
|
function getExpoModulesCoreVersion(projectRoot) {
|
|
703
|
+
return getInstalledPackageVersion('expo-modules-core', projectRoot)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function getInstalledPackageVersion(packageName, projectRoot) {
|
|
696
707
|
try {
|
|
697
|
-
const pkgPath = require.resolve(
|
|
708
|
+
const pkgPath = require.resolve(`${packageName}/package.json`, {
|
|
698
709
|
paths: [projectRoot],
|
|
699
710
|
})
|
|
700
711
|
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version
|
|
@@ -703,6 +714,44 @@ function getExpoModulesCoreVersion(projectRoot) {
|
|
|
703
714
|
}
|
|
704
715
|
}
|
|
705
716
|
|
|
717
|
+
/**
|
|
718
|
+
* expo-linking (pinned by one/vxrn to a single Expo SDK) calls into
|
|
719
|
+
* expo-modules-core's Kotlin/Swift ABI; mixing majors compiles and installs
|
|
720
|
+
* fine but crashes at ReactInstance creation (NoSuchMethodError in
|
|
721
|
+
* ExpoLinkingModule). The project-root resolution sees the copy native
|
|
722
|
+
* autolinking will actually compile, so comparing the two majors here turns
|
|
723
|
+
* an opaque runtime crash into an actionable prebuild error.
|
|
724
|
+
*
|
|
725
|
+
* Escape hatch: ONE_SKIP_EXPO_SDK_CHECK=1 (e.g. for intentionally patched
|
|
726
|
+
* setups). Silently passes when either package isn't resolvable (web-only
|
|
727
|
+
* projects never reach this plugin anyway).
|
|
728
|
+
*/
|
|
729
|
+
function assertExpoModulesCoreMatchesExpoLinking(projectRoot) {
|
|
730
|
+
if (process.env.ONE_SKIP_EXPO_SDK_CHECK === '1') return
|
|
731
|
+
|
|
732
|
+
const expoModulesCoreVersion = getInstalledPackageVersion(
|
|
733
|
+
'expo-modules-core',
|
|
734
|
+
projectRoot
|
|
735
|
+
)
|
|
736
|
+
const expoLinkingVersion = getInstalledPackageVersion('expo-linking', projectRoot)
|
|
737
|
+
if (!expoModulesCoreVersion || !expoLinkingVersion) return
|
|
738
|
+
|
|
739
|
+
const coreMajor = semverMajor(expoModulesCoreVersion)
|
|
740
|
+
const linkingMajor = semverMajor(expoLinkingVersion)
|
|
741
|
+
if (coreMajor === linkingMajor) return
|
|
742
|
+
|
|
743
|
+
throw new Error(
|
|
744
|
+
`[vxrn/one] Expo SDK mismatch: expo-modules-core@${expoModulesCoreVersion} is installed, ` +
|
|
745
|
+
`but the bundled native modules (expo-linking@${expoLinkingVersion}) are built against ` +
|
|
746
|
+
`expo-modules-core ${linkingMajor}.\n` +
|
|
747
|
+
`The app would build and launch, then crash at startup ` +
|
|
748
|
+
`(NoSuchMethodError in ExpoLinkingModule).\n` +
|
|
749
|
+
`Fix: align your expo SDK with the one this version of one targets (expo-modules-core ${linkingMajor}), ` +
|
|
750
|
+
`or upgrade one to a release targeting your SDK.\n` +
|
|
751
|
+
`To bypass intentionally: set ONE_SKIP_EXPO_SDK_CHECK=1`
|
|
752
|
+
)
|
|
753
|
+
}
|
|
754
|
+
|
|
706
755
|
function semverMajor(version) {
|
|
707
756
|
const match = version.match(/^(\d+)/)
|
|
708
757
|
return match ? Number.parseInt(match[1], 10) : 0
|
|
@@ -851,6 +900,8 @@ function injectExpoUpdatesIosResourcesPatchIntoPodfile(podfile) {
|
|
|
851
900
|
}
|
|
852
901
|
|
|
853
902
|
module.exports = plugin
|
|
903
|
+
module.exports.assertExpoModulesCoreMatchesExpoLinking =
|
|
904
|
+
assertExpoModulesCoreMatchesExpoLinking
|
|
854
905
|
module.exports.addReactNativeScreensFix = addReactNativeScreensFix
|
|
855
906
|
module.exports.addSetCliPathToBundleReactNativeShellScript =
|
|
856
907
|
addSetCliPathToBundleReactNativeShellScript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vxrn",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.3",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -60,16 +60,16 @@
|
|
|
60
60
|
"@expo/config-plugins": "~55.0.6",
|
|
61
61
|
"@hono/node-server": "^1.19.14",
|
|
62
62
|
"@react-native/dev-middleware": "^0.83.4",
|
|
63
|
-
"@vxrn/compiler": "1.21.
|
|
64
|
-
"@vxrn/debug": "1.21.
|
|
65
|
-
"@vxrn/query-string": "1.21.
|
|
66
|
-
"@vxrn/resolve": "1.21.
|
|
67
|
-
"@vxrn/safe-area": "1.21.
|
|
68
|
-
"@vxrn/url-parse": "1.21.
|
|
69
|
-
"@vxrn/utils": "1.21.
|
|
70
|
-
"@vxrn/vendor": "1.21.
|
|
71
|
-
"@vxrn/vite-flow": "1.21.
|
|
72
|
-
"@vxrn/vite-plugin-metro": "1.21.
|
|
63
|
+
"@vxrn/compiler": "1.21.3",
|
|
64
|
+
"@vxrn/debug": "1.21.3",
|
|
65
|
+
"@vxrn/query-string": "1.21.3",
|
|
66
|
+
"@vxrn/resolve": "1.21.3",
|
|
67
|
+
"@vxrn/safe-area": "1.21.3",
|
|
68
|
+
"@vxrn/url-parse": "1.21.3",
|
|
69
|
+
"@vxrn/utils": "1.21.3",
|
|
70
|
+
"@vxrn/vendor": "1.21.3",
|
|
71
|
+
"@vxrn/vite-flow": "1.21.3",
|
|
72
|
+
"@vxrn/vite-plugin-metro": "1.21.3",
|
|
73
73
|
"citty": "^0.1.6",
|
|
74
74
|
"dotenv": "^17.2.1",
|
|
75
75
|
"dotenv-expand": "^12.0.2",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@types/find-node-modules": "^2.1.2",
|
|
100
100
|
"@types/node": "^24.10.0",
|
|
101
101
|
"@types/qrcode-terminal": "^0",
|
|
102
|
-
"@types/ws": "^8.
|
|
102
|
+
"@types/ws": "^8.18.1",
|
|
103
103
|
"depcheck": "^1.4.7",
|
|
104
104
|
"vitest": "^4.1.0"
|
|
105
105
|
},
|
package/src/runtime/hmr-types.ts
CHANGED
|
@@ -54,7 +54,11 @@ export interface DevRuntimeInterface {
|
|
|
54
54
|
createModuleHotContext(moduleId: string): void
|
|
55
55
|
applyUpdates(boundaries: [string, string][]): void
|
|
56
56
|
registerModule(id: string, exportsHolder: DevRuntimeModule['exportsHolder']): void
|
|
57
|
-
loadExports(id: string):
|
|
57
|
+
loadExports(id: string): DevRuntimeModule['exports']
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
declare global {
|
|
61
|
+
var __VXRN_ON_MODULE_UPDATED__: ((moduleId: string) => void) | undefined
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
// the base class is provided by rolldown's devMode runtime
|
|
@@ -72,7 +76,7 @@ class DevRuntime implements DevRuntimeInterface {
|
|
|
72
76
|
registerModule(id: string, exportsHolder: DevRuntimeModule['exportsHolder']): void {
|
|
73
77
|
throw new Error('registerModule should be implemented')
|
|
74
78
|
}
|
|
75
|
-
loadExports(id: string):
|
|
79
|
+
loadExports(id: string): DevRuntimeModule['exports'] {
|
|
76
80
|
throw new Error('loadExports should be implemented')
|
|
77
81
|
}
|
|
78
82
|
}
|
|
@@ -75,20 +75,6 @@ if (typeof globalThis.URLSearchParams === 'undefined') {
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
// suppress HMRClient.setup() error from native side
|
|
79
|
-
// native calls HMRClient.setup() during bundle load but we don't use Metro HMR
|
|
80
|
-
// this intercept catches the error and prevents the red screen
|
|
81
|
-
var __origErrorHandler = globalThis.ErrorUtils && globalThis.ErrorUtils.getGlobalHandler && globalThis.ErrorUtils.getGlobalHandler();
|
|
82
|
-
if (globalThis.ErrorUtils && globalThis.ErrorUtils.setGlobalHandler) {
|
|
83
|
-
globalThis.ErrorUtils.setGlobalHandler(function(error, isFatal) {
|
|
84
|
-
if (error && error.message && error.message.indexOf('HMRClient') !== -1) {
|
|
85
|
-
// suppress HMRClient errors silently
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (__origErrorHandler) __origErrorHandler(error, isFatal);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
78
|
// ensure console exists before any library prelude touches it in release bundles
|
|
93
79
|
globalThis.console = globalThis.console || {};
|
|
94
80
|
var console = globalThis.console;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
import {
|
|
3
|
+
getHermesSWCIncludes,
|
|
3
4
|
getNativeTransformConfig,
|
|
5
|
+
hmrClientNoopPlugin,
|
|
6
|
+
vxrnCompilerPlugin,
|
|
4
7
|
wrapNativeBundleModuleScope,
|
|
5
8
|
} from './createNativeDevEngine'
|
|
6
9
|
|
|
@@ -66,3 +69,109 @@ describe('wrapNativeBundleModuleScope', () => {
|
|
|
66
69
|
expect(wrapNativeBundleModuleScope(input)).toBe(input)
|
|
67
70
|
})
|
|
68
71
|
})
|
|
72
|
+
|
|
73
|
+
describe('getHermesSWCIncludes', () => {
|
|
74
|
+
const CLASS_SET = [
|
|
75
|
+
'transform-classes',
|
|
76
|
+
'transform-class-properties',
|
|
77
|
+
'transform-class-static-block',
|
|
78
|
+
'transform-private-methods',
|
|
79
|
+
'transform-private-property-in-object',
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
it('always includes the full Hermes class-transform set (dev and prod)', () => {
|
|
83
|
+
// regression: transform-classes was missing in dev, leaving a half-transpiled
|
|
84
|
+
// class hierarchy Hermes crashes on at `new Subclass()`
|
|
85
|
+
expect(getHermesSWCIncludes(true)).toEqual(expect.arrayContaining(CLASS_SET))
|
|
86
|
+
expect(getHermesSWCIncludes(false)).toEqual(expect.arrayContaining(CLASS_SET))
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('adds transform-async-to-generator only in production', () => {
|
|
90
|
+
expect(getHermesSWCIncludes(true)).not.toContain('transform-async-to-generator')
|
|
91
|
+
expect(getHermesSWCIncludes(false)).toContain('transform-async-to-generator')
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe('hmrClientNoopPlugin', () => {
|
|
96
|
+
const plugin = hmrClientNoopPlugin()
|
|
97
|
+
const resolveId = plugin.resolveId as unknown as (
|
|
98
|
+
source: string,
|
|
99
|
+
importer?: string
|
|
100
|
+
) => any
|
|
101
|
+
const load = plugin.load as unknown as (id: string) => any
|
|
102
|
+
const VIRTUAL_ID = '\0vxrn-hmr-client-noop'
|
|
103
|
+
|
|
104
|
+
it.each([
|
|
105
|
+
['react-native/Libraries/Utilities/HMRClient', undefined],
|
|
106
|
+
['../Utilities/HMRClient', '/project/node_modules/react-native/Libraries/Core.js'],
|
|
107
|
+
['../../Utilities/HMRClient.js', '/project/react-native/Libraries/Core.js'],
|
|
108
|
+
// native Windows ids use backslashes
|
|
109
|
+
['..\\Utilities\\HMRClient.js', 'C:\\project\\react-native\\Libraries\\Core.js'],
|
|
110
|
+
['../Utilities/HMRClient.ts', '/project/node_modules/react-native/Core.js'],
|
|
111
|
+
['../Utilities/HMRClient.tsx', '/project/node_modules/react-native/Core.js'],
|
|
112
|
+
['../Utilities/HMRClient.cjs', '/project/node_modules/react-native/Core.js'],
|
|
113
|
+
])(
|
|
114
|
+
'aliases RN HMRClient specifier %j to the no-op virtual module',
|
|
115
|
+
(source, importer) => {
|
|
116
|
+
expect(resolveId(source, importer)).toEqual({ id: VIRTUAL_ID, external: false })
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
it.each([
|
|
121
|
+
// trailing letters (no boundary) must not match
|
|
122
|
+
'react-native/Libraries/Utilities/HMRClientRegistry',
|
|
123
|
+
// the Utilities segment must start at a path boundary
|
|
124
|
+
'some/MyUtilities/HMRClient',
|
|
125
|
+
// unrelated RN modules
|
|
126
|
+
'react-native/Libraries/Core/setUpDeveloperTools',
|
|
127
|
+
'react',
|
|
128
|
+
])('does not touch unrelated specifier %j', (source) => {
|
|
129
|
+
expect(resolveId(source)).toBeUndefined()
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('does not alias an app-authored Utilities/HMRClient module', () => {
|
|
133
|
+
expect(resolveId('../Utilities/HMRClient', '/project/src/App.tsx')).toBeUndefined()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('loads a no-op module exposing every HMRClient method RN calls', () => {
|
|
137
|
+
const result = load(VIRTUAL_ID)
|
|
138
|
+
expect(result?.moduleType).toBe('js')
|
|
139
|
+
for (const method of [
|
|
140
|
+
'setup',
|
|
141
|
+
'enable',
|
|
142
|
+
'disable',
|
|
143
|
+
'registerBundle',
|
|
144
|
+
'log',
|
|
145
|
+
'isEnabled',
|
|
146
|
+
]) {
|
|
147
|
+
expect(result!.code).toContain(method)
|
|
148
|
+
}
|
|
149
|
+
expect(result!.code).toContain('export default HMRClient')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('does not load unrelated ids', () => {
|
|
153
|
+
expect(load('\0some-other-virtual')).toBeUndefined()
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe('vxrnCompilerPlugin React Refresh registration', () => {
|
|
158
|
+
it('keeps initial-bundle registrations visible to rolldown', async () => {
|
|
159
|
+
const previousNodeEnv = process.env.NODE_ENV
|
|
160
|
+
process.env.NODE_ENV = 'test'
|
|
161
|
+
try {
|
|
162
|
+
const plugin = vxrnCompilerPlugin('ios', true)
|
|
163
|
+
const transform = plugin.transform as (code: string, id: string) => Promise<any>
|
|
164
|
+
const result = await transform(
|
|
165
|
+
'export const marker = "$RefreshReg$("; export function Probe() { return <div>probe</div> }',
|
|
166
|
+
'/project/src/Probe.tsx'
|
|
167
|
+
)
|
|
168
|
+
const code = result.code as string
|
|
169
|
+
|
|
170
|
+
expect(code).toContain('var __vxrnRefreshReg = globalThis.$RefreshReg$')
|
|
171
|
+
expect(code).toContain('__vxrnRefreshReg(')
|
|
172
|
+
expect(code).toContain('"$RefreshReg$("')
|
|
173
|
+
} finally {
|
|
174
|
+
process.env.NODE_ENV = previousNodeEnv
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
})
|
|
@@ -17,6 +17,29 @@ import { getNativePrelude } from '../runtime/native-prelude'
|
|
|
17
17
|
// files that contain Flow syntax and need stripping
|
|
18
18
|
const FLOW_FILE_PATTERN = /node_modules[\\/](?:react-native|@react-native)[\\/].*\.js$/
|
|
19
19
|
|
|
20
|
+
// Hermes needs the whole class shape lowered *together*. downleveling only the
|
|
21
|
+
// class fields while leaving `class ... extends` as modern ES6 produces a
|
|
22
|
+
// half-transpiled hierarchy Hermes crashes on at `new Subclass()` (TypeError:
|
|
23
|
+
// Cannot read property 'prototype' of undefined). These must stay atomic across
|
|
24
|
+
// both SWC call sites and dev/prod. defining them once makes that a fact, not a
|
|
25
|
+
// convention (the original bug was `transform-classes` missing from one of two
|
|
26
|
+
// hand-copied include lists).
|
|
27
|
+
const HERMES_CLASS_TRANSFORMS = [
|
|
28
|
+
'transform-classes',
|
|
29
|
+
'transform-class-properties',
|
|
30
|
+
'transform-class-static-block',
|
|
31
|
+
'transform-private-methods',
|
|
32
|
+
'transform-private-property-in-object',
|
|
33
|
+
] as const
|
|
34
|
+
|
|
35
|
+
// prod-only: needed for hermesc bytecode AOT compilation, not the dev interpreter
|
|
36
|
+
const HERMES_PROD_TRANSFORMS = ['transform-async-to-generator'] as const
|
|
37
|
+
|
|
38
|
+
/** SWC `env.include` for Hermes-compatible downleveling; see HERMES_CLASS_TRANSFORMS. */
|
|
39
|
+
export function getHermesSWCIncludes(dev: boolean): string[] {
|
|
40
|
+
return [...HERMES_CLASS_TRANSFORMS, ...(dev ? [] : HERMES_PROD_TRANSFORMS)]
|
|
41
|
+
}
|
|
42
|
+
|
|
20
43
|
interface NativeDevEngineOptions {
|
|
21
44
|
root: string
|
|
22
45
|
port: number
|
|
@@ -191,19 +214,28 @@ function getNativePlugins(
|
|
|
191
214
|
serverFileExclusionPlugin(),
|
|
192
215
|
// guard server-only / client-only / web-only / native-only imports
|
|
193
216
|
environmentGuardPlugin(),
|
|
217
|
+
// alias RN's Metro HMR client to a no-op; vxrn drives HMR itself (the
|
|
218
|
+
// rolldown-runtime WebSocket); RN's client otherwise opens a /hot socket and
|
|
219
|
+
// red-boxes "unknown-message [object Object]" on every edit (new arch)
|
|
220
|
+
hmrClientNoopPlugin(),
|
|
194
221
|
// stub CSS imports — native doesn't support CSS and rolldown removed CSS bundling
|
|
195
222
|
cssStubPlugin(),
|
|
196
223
|
// handle import.meta.glob (used by One's route system)
|
|
197
224
|
viteImportGlobPlugin({ root }),
|
|
198
|
-
//
|
|
225
|
+
// @vxrn/compiler babel transforms: reanimated worklets, async generators,
|
|
226
|
+
// react-native codegen, react compiler, same pipeline as metro. runs before
|
|
227
|
+
// flowStripPlugin so react-native's Flow `.js` specs reach codegen with their
|
|
228
|
+
// type argument intact. stripping Flow first would erase it (which is why the
|
|
229
|
+
// codegen "didn't run for <Component>" warning fired).
|
|
230
|
+
vxrnCompilerPlugin(platform, dev),
|
|
231
|
+
// strip Flow from any react-native / @react-native `.js` the compiler didn't
|
|
232
|
+
// handle, the guaranteed safety net before rolldown's oxc core parse (which
|
|
233
|
+
// can't parse Flow). now downstream of the compiler, so codegen sees the types.
|
|
199
234
|
flowStripPlugin(),
|
|
200
235
|
// guard undefined native methods in NativeAnimatedHelper
|
|
201
236
|
nativeAnimatedGuardPlugin(),
|
|
202
237
|
// handle asset imports (.png, .jpg, .ttf, etc.)
|
|
203
238
|
assetPlugin({ root, platform, assetsDest }),
|
|
204
|
-
// @vxrn/compiler babel transforms: reanimated worklets, async generators,
|
|
205
|
-
// react-native codegen, react compiler — same pipeline as metro
|
|
206
|
-
vxrnCompilerPlugin(platform, dev),
|
|
207
239
|
// hermes compat: transform class properties and private fields
|
|
208
240
|
hermesCompatSWCPlugin(dev),
|
|
209
241
|
]
|
|
@@ -333,12 +365,8 @@ async function downlevelClassFieldsInBundle(code: string): Promise<string> {
|
|
|
333
365
|
isModule: false,
|
|
334
366
|
env: {
|
|
335
367
|
targets: { node: 9999 },
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
'transform-class-static-block',
|
|
339
|
-
'transform-private-methods',
|
|
340
|
-
'transform-private-property-in-object',
|
|
341
|
-
],
|
|
368
|
+
// dev-only runtime prelude: the class set only, no prod bytecode transforms
|
|
369
|
+
include: [...HERMES_CLASS_TRANSFORMS],
|
|
342
370
|
},
|
|
343
371
|
jsc: {
|
|
344
372
|
parser: { syntax: 'ecmascript' },
|
|
@@ -470,14 +498,6 @@ try {
|
|
|
470
498
|
// skipped by the per-file SWC plugin) so old Hermes can parse them
|
|
471
499
|
code = await downlevelClassFieldsInBundle(code)
|
|
472
500
|
|
|
473
|
-
// register a no-op HMRClient so RN's native side doesn't error when calling HMRClient.setup()
|
|
474
|
-
// our actual HMR is handled via the outro WebSocket connection
|
|
475
|
-
const hmrClientStub = `registerCallableModule("HMRClient",{setup:function(){},enable:function(){},disable:function(){},registerBundle:function(){},log:function(){}})`
|
|
476
|
-
code = code.replace(
|
|
477
|
-
/registerCallableModule\s*\(\s*["']AppRegistry["']/,
|
|
478
|
-
(match) => hmrClientStub + ',' + match
|
|
479
|
-
)
|
|
480
|
-
|
|
481
501
|
// wrap module code in a function scope so top-level `var`s (e.g. RN
|
|
482
502
|
// fetch.js's `Headers`/`Request`) don't leak as non-configurable
|
|
483
503
|
// globals and break RN's polyfillGlobal (dev-only redbox). see fn doc.
|
|
@@ -505,7 +525,6 @@ try {
|
|
|
505
525
|
return
|
|
506
526
|
}
|
|
507
527
|
const updates = (result as any).updates || []
|
|
508
|
-
|
|
509
528
|
for (const item of updates) {
|
|
510
529
|
const update = item.update || item
|
|
511
530
|
if (update.type === 'Patch' && update.code) {
|
|
@@ -792,6 +811,49 @@ function environmentGuardPlugin(): Plugin {
|
|
|
792
811
|
}
|
|
793
812
|
}
|
|
794
813
|
|
|
814
|
+
/**
|
|
815
|
+
* alias react-native's Metro HMR client (`Libraries/Utilities/HMRClient`) to a
|
|
816
|
+
* no-op module.
|
|
817
|
+
*
|
|
818
|
+
* vxrn drives Fast Refresh itself over the rolldown-runtime WebSocket and never
|
|
819
|
+
* speaks Metro's `/hot` protocol. On the new architecture, react-native
|
|
820
|
+
* `registerCallableModule('HMRClient', require('./HMRClient'))`s its real client
|
|
821
|
+
* eagerly at startup before vxrn's late override runs, and `emplace` keeps
|
|
822
|
+
* that first registration. RN's client then opens a `MetroHMRClient` socket that
|
|
823
|
+
* receives vxrn's `hmr:*` frames it can't parse and red-boxes
|
|
824
|
+
* `unknown-message [object Object]` on every edit.
|
|
825
|
+
*
|
|
826
|
+
* neutralizing the module at its source means RN registers *this* no-op as the
|
|
827
|
+
* one-and-only `HMRClient` (working with `emplace`, so it's arch-agnostic) and
|
|
828
|
+
* the stray socket is never opened. The class-shaped surface
|
|
829
|
+
* (`setup`/`enable`/`disable`/`registerBundle`/`log`/`isEnabled`) mirrors the
|
|
830
|
+
* methods RN calls on it.
|
|
831
|
+
*/
|
|
832
|
+
export function hmrClientNoopPlugin(): Plugin {
|
|
833
|
+
// match RN's HMRClient by module path, tolerating either separator (native
|
|
834
|
+
// Windows ids use `\`) and an optional js/ts extension
|
|
835
|
+
const RN_HMR_CLIENT_RE = /(^|[\\/])Utilities[\\/]HMRClient(\.[cm]?[jt]sx?)?$/
|
|
836
|
+
return {
|
|
837
|
+
name: 'vxrn:hmr-client-noop',
|
|
838
|
+
resolveId(source, importer) {
|
|
839
|
+
const fromReactNative =
|
|
840
|
+
source.startsWith('react-native/') ||
|
|
841
|
+
(importer != null && /(^|[\\/])react-native[\\/]/.test(importer))
|
|
842
|
+
if (fromReactNative && RN_HMR_CLIENT_RE.test(source)) {
|
|
843
|
+
return { id: '\0vxrn-hmr-client-noop', external: false }
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
load(id) {
|
|
847
|
+
if (id === '\0vxrn-hmr-client-noop') {
|
|
848
|
+
return {
|
|
849
|
+
code: `const HMRClient = { setup() {}, enable() {}, disable() {}, registerBundle() {}, log() {}, isEnabled() { return false } }\nexport default HMRClient`,
|
|
850
|
+
moduleType: 'js',
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
},
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
795
857
|
/**
|
|
796
858
|
* Stub CSS imports for native builds.
|
|
797
859
|
* Native doesn't support CSS and rolldown removed CSS bundling support.
|
|
@@ -816,7 +878,7 @@ function cssStubPlugin(): Plugin {
|
|
|
816
878
|
* react-native codegen, react compiler, and react-refresh (dev only) —
|
|
817
879
|
* same pipeline as metro, single babel pass per file.
|
|
818
880
|
*/
|
|
819
|
-
function vxrnCompilerPlugin(platform: string, dev: boolean): Plugin {
|
|
881
|
+
export function vxrnCompilerPlugin(platform: string, dev: boolean): Plugin {
|
|
820
882
|
let compiler: typeof import('@vxrn/compiler') | null = null
|
|
821
883
|
|
|
822
884
|
// whether a file is a user file that should get react-refresh wiring
|
|
@@ -852,7 +914,17 @@ function vxrnCompilerPlugin(platform: string, dev: boolean): Plugin {
|
|
|
852
914
|
const existingPlugins = babelOptions?.plugins || []
|
|
853
915
|
babelOptions = {
|
|
854
916
|
...babelOptions,
|
|
855
|
-
plugins: [
|
|
917
|
+
plugins: [
|
|
918
|
+
...existingPlugins,
|
|
919
|
+
[
|
|
920
|
+
'react-refresh/babel',
|
|
921
|
+
{
|
|
922
|
+
skipEnvCheck: true,
|
|
923
|
+
refreshReg: '__vxrnRefreshReg',
|
|
924
|
+
refreshSig: '__vxrnRefreshSig',
|
|
925
|
+
},
|
|
926
|
+
],
|
|
927
|
+
],
|
|
856
928
|
}
|
|
857
929
|
}
|
|
858
930
|
|
|
@@ -876,6 +948,9 @@ if (globalThis.__ReactRefresh) {
|
|
|
876
948
|
};
|
|
877
949
|
globalThis.$RefreshSig$ = globalThis.__ReactRefresh.createSignatureFunctionForTransform;
|
|
878
950
|
}
|
|
951
|
+
// keep registration calls local so rolldown retains them in the initial bundle.
|
|
952
|
+
var __vxrnRefreshReg = globalThis.$RefreshReg$;
|
|
953
|
+
var __vxrnRefreshSig = globalThis.$RefreshSig$;
|
|
879
954
|
|
|
880
955
|
${out}
|
|
881
956
|
|
|
@@ -1024,15 +1099,9 @@ function hermesCompatSWCPlugin(dev: boolean): Plugin {
|
|
|
1024
1099
|
try {
|
|
1025
1100
|
if (!swc) swc = await import('@swc/core')
|
|
1026
1101
|
|
|
1027
|
-
//
|
|
1028
|
-
//
|
|
1029
|
-
const envIncludes =
|
|
1030
|
-
'transform-class-properties',
|
|
1031
|
-
'transform-class-static-block',
|
|
1032
|
-
'transform-private-methods',
|
|
1033
|
-
'transform-private-property-in-object',
|
|
1034
|
-
...(!dev ? ['transform-classes', 'transform-async-to-generator'] : []),
|
|
1035
|
-
]
|
|
1102
|
+
// app modules: the Hermes class set (unconditional), plus async-to-generator
|
|
1103
|
+
// in prod only (see HERMES_CLASS_TRANSFORMS / HERMES_PROD_TRANSFORMS)
|
|
1104
|
+
const envIncludes = getHermesSWCIncludes(dev)
|
|
1036
1105
|
|
|
1037
1106
|
const result = await swc.transform(code, {
|
|
1038
1107
|
filename: id,
|
|
@@ -1131,6 +1200,16 @@ class ReactNativeDevRuntime extends BaseDevRuntime {
|
|
|
1131
1200
|
ctx.acceptCallbacks[j].fn(this.modules[moduleId].exports);
|
|
1132
1201
|
}
|
|
1133
1202
|
}
|
|
1203
|
+
// surface each committed module id to the framework hot-update hook (if one
|
|
1204
|
+
// is registered). RN's React Refresh can't repaint frameworks that re-wrap
|
|
1205
|
+
// route components away from the edited module's Refresh family (e.g. One),
|
|
1206
|
+
// and the web route-update event has no equivalent on the native /hot
|
|
1207
|
+
// socket, so this generic vxrn global is the bridge.
|
|
1208
|
+
try {
|
|
1209
|
+
if (globalThis.__VXRN_ON_MODULE_UPDATED__ && moduleId) globalThis.__VXRN_ON_MODULE_UPDATED__(moduleId);
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
console.error('[vxrn HMR]: module update hook failed', error);
|
|
1212
|
+
}
|
|
1134
1213
|
}
|
|
1135
1214
|
}
|
|
1136
1215
|
|
|
@@ -58,7 +58,10 @@ export interface DevRuntimeInterface {
|
|
|
58
58
|
createModuleHotContext(moduleId: string): void;
|
|
59
59
|
applyUpdates(boundaries: [string, string][]): void;
|
|
60
60
|
registerModule(id: string, exportsHolder: DevRuntimeModule['exportsHolder']): void;
|
|
61
|
-
loadExports(id: string):
|
|
61
|
+
loadExports(id: string): DevRuntimeModule['exports'];
|
|
62
|
+
}
|
|
63
|
+
declare global {
|
|
64
|
+
var __VXRN_ON_MODULE_UPDATED__: ((moduleId: string) => void) | undefined;
|
|
62
65
|
}
|
|
63
66
|
declare class DevRuntime implements DevRuntimeInterface {
|
|
64
67
|
clientId: string;
|
|
@@ -67,7 +70,7 @@ declare class DevRuntime implements DevRuntimeInterface {
|
|
|
67
70
|
createModuleHotContext(moduleId: string): void;
|
|
68
71
|
applyUpdates(boundaries: [string, string][]): void;
|
|
69
72
|
registerModule(id: string, exportsHolder: DevRuntimeModule['exportsHolder']): void;
|
|
70
|
-
loadExports(id: string):
|
|
73
|
+
loadExports(id: string): DevRuntimeModule['exports'];
|
|
71
74
|
}
|
|
72
75
|
export type { DevRuntime };
|
|
73
76
|
export interface DevRuntimeMessenger {
|