vxrn 1.25.16 → 1.26.0
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/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +2 -0
- package/dist/index.native.js.map +1 -1
- package/dist/patches/builtInDepPatches.mjs +21 -0
- package/dist/patches/builtInDepPatches.mjs.map +1 -1
- package/dist/patches/builtInDepPatches.native.js +21 -0
- package/dist/patches/builtInDepPatches.native.js.map +1 -1
- package/dist/plugins/reactNativeDevServer.mjs +5 -0
- package/dist/plugins/reactNativeDevServer.mjs.map +1 -1
- package/dist/plugins/reactNativeDevServer.native.js +5 -0
- package/dist/plugins/reactNativeDevServer.native.js.map +1 -1
- package/dist/plugins/rnCodegenPlugin.mjs +16 -0
- package/dist/plugins/rnCodegenPlugin.mjs.map +1 -0
- package/dist/plugins/rnCodegenPlugin.native.js +16 -0
- package/dist/plugins/rnCodegenPlugin.native.js.map +1 -0
- package/dist/plugins/rnCodegenPlugin.test.mjs +239 -0
- package/dist/plugins/rnCodegenPlugin.test.mjs.map +1 -0
- package/dist/plugins/rnCodegenPlugin.test.native.js +242 -0
- package/dist/plugins/rnCodegenPlugin.test.native.js.map +1 -0
- package/dist/plugins/workletImportsPlugin.mjs +160 -0
- package/dist/plugins/workletImportsPlugin.mjs.map +1 -0
- package/dist/plugins/workletImportsPlugin.native.js +230 -0
- package/dist/plugins/workletImportsPlugin.native.js.map +1 -0
- package/dist/utils/createNativeDevEngine.mjs +198 -92
- package/dist/utils/createNativeDevEngine.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.native.js +252 -99
- package/dist/utils/createNativeDevEngine.native.js.map +1 -1
- package/dist/utils/createNativeDevEngine.test.mjs +775 -4
- package/dist/utils/createNativeDevEngine.test.mjs.map +1 -1
- package/dist/utils/createNativeDevEngine.test.native.js +836 -4
- package/dist/utils/createNativeDevEngine.test.native.js.map +1 -1
- package/dist/utils/patches.mjs +12 -5
- package/dist/utils/patches.mjs.map +1 -1
- package/dist/utils/patches.native.js +13 -5
- package/dist/utils/patches.native.js.map +1 -1
- package/dist/utils/patches.test.mjs +28 -0
- package/dist/utils/patches.test.mjs.map +1 -1
- package/dist/utils/patches.test.native.js +47 -0
- package/dist/utils/patches.test.native.js.map +1 -1
- package/dist/utils/scanDepsToOptimize.mjs +5 -1
- package/dist/utils/scanDepsToOptimize.mjs.map +1 -1
- package/dist/utils/scanDepsToOptimize.native.js +5 -1
- package/dist/utils/scanDepsToOptimize.native.js.map +1 -1
- package/package.json +19 -12
- package/src/index.ts +2 -0
- package/src/patches/builtInDepPatches.ts +25 -0
- package/src/plugins/reactNativeDevServer.ts +12 -0
- package/src/plugins/rnCodegenPlugin.test.ts +313 -0
- package/src/plugins/rnCodegenPlugin.ts +20 -0
- package/src/plugins/workletImportsPlugin.ts +226 -0
- package/src/utils/createNativeDevEngine.test.ts +877 -3
- package/src/utils/createNativeDevEngine.ts +259 -107
- package/src/utils/patches.test.ts +33 -0
- package/src/utils/patches.ts +17 -6
- package/src/utils/scanDepsToOptimize.ts +9 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
- package/types/patches/builtInDepPatches.d.ts.map +1 -1
- package/types/plugins/reactNativeDevServer.d.ts.map +1 -1
- package/types/plugins/rnCodegenPlugin.d.ts +6 -0
- package/types/plugins/rnCodegenPlugin.d.ts.map +1 -0
- package/types/plugins/rnCodegenPlugin.test.d.ts +2 -0
- package/types/plugins/rnCodegenPlugin.test.d.ts.map +1 -0
- package/types/plugins/workletImportsPlugin.d.ts +8 -0
- package/types/plugins/workletImportsPlugin.d.ts.map +1 -0
- package/types/utils/createNativeDevEngine.d.ts +13 -0
- package/types/utils/createNativeDevEngine.d.ts.map +1 -1
- package/types/utils/patches.d.ts.map +1 -1
- package/types/utils/scanDepsToOptimize.d.ts.map +1 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { rnCodegenPlugin, transformReactNativeCodegen } from "./rnCodegenPlugin.mjs";
|
|
3
|
+
describe("rnCodegenPlugin", () => {
|
|
4
|
+
it("generates view-config replacement for codegenNativeComponent default export", () => {
|
|
5
|
+
const inputCode = `
|
|
6
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
7
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
8
|
+
|
|
9
|
+
export interface NativeProps extends ViewProps {
|
|
10
|
+
color?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
14
|
+
`;
|
|
15
|
+
const result = transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
16
|
+
expect(result).toBeDefined();
|
|
17
|
+
expect(result?.code).not.toContain("codegenNativeComponent<NativeProps>");
|
|
18
|
+
expect(result?.code).toContain("export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);");
|
|
19
|
+
expect(result?.code).toContain('uiViewClassName: "MyCustomView"');
|
|
20
|
+
expect(result?.code).toContain("export const __INTERNAL_VIEW_CONFIG");
|
|
21
|
+
expect(result?.map).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
it("leaves already-generated output alone on a second pass", () => {
|
|
24
|
+
const inputCode = `
|
|
25
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
26
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
27
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
28
|
+
|
|
29
|
+
export interface NativeProps extends ViewProps {
|
|
30
|
+
color?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface NativeCommands {
|
|
34
|
+
reload: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const Commands = codegenNativeCommands<NativeCommands>({
|
|
38
|
+
supportedCommands: ['reload'],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
42
|
+
`;
|
|
43
|
+
const id = "/workspace/src/MyCustomViewNativeComponent.ts";
|
|
44
|
+
const first = transformReactNativeCodegen(inputCode, id);
|
|
45
|
+
expect(first?.code).toContain("export const Commands");
|
|
46
|
+
expect(() => transformReactNativeCodegen(first.code, id)).not.toThrow();
|
|
47
|
+
expect(transformReactNativeCodegen(first.code, id)).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
it("removes export const Commands = codegenNativeCommands(...) and generates runtime commands", () => {
|
|
50
|
+
const inputCode = `
|
|
51
|
+
import * as React from 'react';
|
|
52
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
53
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
54
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
55
|
+
|
|
56
|
+
export interface NativeProps extends ViewProps {
|
|
57
|
+
color?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type ComponentType = HostComponent<NativeProps>;
|
|
61
|
+
|
|
62
|
+
interface NativeCommands {
|
|
63
|
+
flash: (viewRef: React.ElementRef<ComponentType>, enabled: boolean) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
67
|
+
supportedCommands: ['flash'],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
71
|
+
`;
|
|
72
|
+
const result = transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
73
|
+
expect(result).toBeDefined();
|
|
74
|
+
expect(result?.code).not.toContain("codegenNativeCommands<NativeCommands>");
|
|
75
|
+
expect(result?.code).toContain("export const Commands = {");
|
|
76
|
+
expect(result?.code).toContain('dispatchCommand(ref, "flash", [enabled]);');
|
|
77
|
+
expect(result?.code).toContain("export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);");
|
|
78
|
+
});
|
|
79
|
+
it("throws an error if native commands are exported with a name other than Commands", () => {
|
|
80
|
+
const inputCode = `
|
|
81
|
+
import * as React from 'react';
|
|
82
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
83
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
84
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
85
|
+
|
|
86
|
+
export interface NativeProps extends ViewProps {
|
|
87
|
+
color?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type ComponentType = HostComponent<NativeProps>;
|
|
91
|
+
|
|
92
|
+
interface NativeCommands {
|
|
93
|
+
flash: (viewRef: React.ElementRef<ComponentType>, enabled: boolean) => void;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const OtherCommands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
97
|
+
supportedCommands: ['flash'],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
101
|
+
`;
|
|
102
|
+
expect(() => {
|
|
103
|
+
transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
104
|
+
}).toThrow("Native commands must be exported with the name 'Commands'");
|
|
105
|
+
});
|
|
106
|
+
it("throws an error if 'Commands' is exported without codegenNativeCommands", () => {
|
|
107
|
+
const inputCode = `
|
|
108
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
109
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
110
|
+
|
|
111
|
+
export interface NativeProps extends ViewProps {
|
|
112
|
+
color?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const Commands = 123;
|
|
116
|
+
|
|
117
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
118
|
+
`;
|
|
119
|
+
expect(() => {
|
|
120
|
+
transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
121
|
+
}).toThrow("'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.");
|
|
122
|
+
});
|
|
123
|
+
it("throws an error if 'Commands' is exported via export specifier without codegenNativeCommands", () => {
|
|
124
|
+
const inputCode = `
|
|
125
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
126
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
127
|
+
|
|
128
|
+
export interface NativeProps extends ViewProps {
|
|
129
|
+
color?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const Commands = 123;
|
|
133
|
+
export { Commands };
|
|
134
|
+
|
|
135
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
136
|
+
`;
|
|
137
|
+
expect(() => {
|
|
138
|
+
transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
139
|
+
}).toThrow("'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.");
|
|
140
|
+
});
|
|
141
|
+
it("ignores files that do not match the codegen pattern", () => {
|
|
142
|
+
const inputCode = `
|
|
143
|
+
export default function Button() {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
147
|
+
const result = transformReactNativeCodegen(inputCode, "/workspace/src/Button.tsx");
|
|
148
|
+
expect(result).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
it("ignores matching files without codegenNativeComponent", () => {
|
|
151
|
+
const inputCode = `
|
|
152
|
+
export default function MyNativeComponent() {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
const result = transformReactNativeCodegen(inputCode, "/workspace/src/MyNativeComponent.tsx");
|
|
157
|
+
expect(result).toBeUndefined();
|
|
158
|
+
});
|
|
159
|
+
it("supports files inside specs/ directory", () => {
|
|
160
|
+
const inputCode = `
|
|
161
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
162
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
163
|
+
|
|
164
|
+
export interface NativeProps extends ViewProps {
|
|
165
|
+
color?: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default codegenNativeComponent<NativeProps>('SpecView') as HostComponent<NativeProps>;
|
|
169
|
+
`;
|
|
170
|
+
const result = transformReactNativeCodegen(inputCode, "/workspace/specs/SpecView.ts");
|
|
171
|
+
expect(result).toBeDefined();
|
|
172
|
+
expect(result?.code).toContain('uiViewClassName: "SpecView"');
|
|
173
|
+
});
|
|
174
|
+
it("works through the Vite plugin interface", () => {
|
|
175
|
+
const plugin = rnCodegenPlugin();
|
|
176
|
+
expect(plugin.name).toBe("vxrn:rn-codegen");
|
|
177
|
+
expect(plugin.enforce).toBe("pre");
|
|
178
|
+
const inputCode = `
|
|
179
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
180
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
181
|
+
|
|
182
|
+
export interface NativeProps extends ViewProps {
|
|
183
|
+
color?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default codegenNativeComponent<NativeProps>('PluginView') as HostComponent<NativeProps>;
|
|
187
|
+
`;
|
|
188
|
+
const transformFn = typeof plugin.transform === "function" ? plugin.transform : plugin.transform?.handler;
|
|
189
|
+
const result = transformFn.call({}, inputCode, "/workspace/src/PluginViewNativeComponent.ts");
|
|
190
|
+
expect(result).toBeDefined();
|
|
191
|
+
expect(result?.code).toContain('uiViewClassName: "PluginView"');
|
|
192
|
+
expect(result?.map).toBeDefined();
|
|
193
|
+
});
|
|
194
|
+
it("removes export const Commands with sequence expression (e.g. coverage instrumentation)", () => {
|
|
195
|
+
const inputCode = `
|
|
196
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
197
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
198
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
199
|
+
|
|
200
|
+
export interface NativeProps extends ViewProps {
|
|
201
|
+
color?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const Commands = (0, codegenNativeCommands({
|
|
205
|
+
supportedCommands: [],
|
|
206
|
+
}));
|
|
207
|
+
|
|
208
|
+
export default codegenNativeComponent<NativeProps>('SequenceView') as HostComponent<NativeProps>;
|
|
209
|
+
`;
|
|
210
|
+
const result = transformReactNativeCodegen(inputCode, "/workspace/src/SequenceViewNativeComponent.ts");
|
|
211
|
+
expect(result).toBeDefined();
|
|
212
|
+
expect(result?.code).not.toContain("export const Commands");
|
|
213
|
+
expect(result?.code).toContain('uiViewClassName: "SequenceView"');
|
|
214
|
+
expect(result?.code).toContain("export default NativeComponentRegistry.get");
|
|
215
|
+
});
|
|
216
|
+
it("transforms Flow component specs correctly", () => {
|
|
217
|
+
const flowCode = `/**
|
|
218
|
+
* @flow strict-local
|
|
219
|
+
* @format
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
|
|
223
|
+
import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
|
|
224
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
225
|
+
|
|
226
|
+
type FlowViewProps = $ReadOnly<{
|
|
227
|
+
...ViewProps,
|
|
228
|
+
title?: string,
|
|
229
|
+
}>;
|
|
230
|
+
|
|
231
|
+
export default codegenNativeComponent<FlowViewProps>('FlowView') as HostComponent<FlowViewProps>;
|
|
232
|
+
`;
|
|
233
|
+
const result = transformReactNativeCodegen(flowCode, "/workspace/src/FlowViewNativeComponent.js");
|
|
234
|
+
expect(result).toBeDefined();
|
|
235
|
+
expect(result?.code).toContain('uiViewClassName: "FlowView"');
|
|
236
|
+
expect(result?.code).toContain("export default NativeComponentRegistry.get");
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
//# sourceMappingURL=rnCodegenPlugin.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["describe","expect","it","rnCodegenPlugin","transformReactNativeCodegen","inputCode","result","toBeDefined","code","not","toContain","map","id","first","toThrow","toBeUndefined","plugin","name","toBe","enforce","transformFn","transform","handler","call","flowCode"],"sources":["../../src/plugins/rnCodegenPlugin.test.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { rnCodegenPlugin, transformReactNativeCodegen } from "./rnCodegenPlugin.native.js";
|
|
3
|
+
describe("rnCodegenPlugin", function () {
|
|
4
|
+
it("generates view-config replacement for codegenNativeComponent default export", function () {
|
|
5
|
+
var inputCode = `
|
|
6
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
7
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
8
|
+
|
|
9
|
+
export interface NativeProps extends ViewProps {
|
|
10
|
+
color?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
14
|
+
`;
|
|
15
|
+
var result = transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
16
|
+
expect(result).toBeDefined();
|
|
17
|
+
expect(result === null || result === void 0 ? void 0 : result.code).not.toContain("codegenNativeComponent<NativeProps>");
|
|
18
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain("export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);");
|
|
19
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain('uiViewClassName: "MyCustomView"');
|
|
20
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain("export const __INTERNAL_VIEW_CONFIG");
|
|
21
|
+
expect(result === null || result === void 0 ? void 0 : result.map).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
it("leaves already-generated output alone on a second pass", function () {
|
|
24
|
+
var inputCode = `
|
|
25
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
26
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
27
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
28
|
+
|
|
29
|
+
export interface NativeProps extends ViewProps {
|
|
30
|
+
color?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface NativeCommands {
|
|
34
|
+
reload: (viewRef: React.ElementRef<HostComponent<NativeProps>>) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const Commands = codegenNativeCommands<NativeCommands>({
|
|
38
|
+
supportedCommands: ['reload'],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
42
|
+
`;
|
|
43
|
+
var id = "/workspace/src/MyCustomViewNativeComponent.ts";
|
|
44
|
+
var first = transformReactNativeCodegen(inputCode, id);
|
|
45
|
+
expect(first === null || first === void 0 ? void 0 : first.code).toContain("export const Commands");
|
|
46
|
+
expect(function () {
|
|
47
|
+
return transformReactNativeCodegen(first.code, id);
|
|
48
|
+
}).not.toThrow();
|
|
49
|
+
expect(transformReactNativeCodegen(first.code, id)).toBeUndefined();
|
|
50
|
+
});
|
|
51
|
+
it("removes export const Commands = codegenNativeCommands(...) and generates runtime commands", function () {
|
|
52
|
+
var inputCode = `
|
|
53
|
+
import * as React from 'react';
|
|
54
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
55
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
56
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
57
|
+
|
|
58
|
+
export interface NativeProps extends ViewProps {
|
|
59
|
+
color?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ComponentType = HostComponent<NativeProps>;
|
|
63
|
+
|
|
64
|
+
interface NativeCommands {
|
|
65
|
+
flash: (viewRef: React.ElementRef<ComponentType>, enabled: boolean) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
69
|
+
supportedCommands: ['flash'],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
73
|
+
`;
|
|
74
|
+
var result = transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
75
|
+
expect(result).toBeDefined();
|
|
76
|
+
expect(result === null || result === void 0 ? void 0 : result.code).not.toContain("codegenNativeCommands<NativeCommands>");
|
|
77
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain("export const Commands = {");
|
|
78
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain('dispatchCommand(ref, "flash", [enabled]);');
|
|
79
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain("export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);");
|
|
80
|
+
});
|
|
81
|
+
it("throws an error if native commands are exported with a name other than Commands", function () {
|
|
82
|
+
var inputCode = `
|
|
83
|
+
import * as React from 'react';
|
|
84
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
85
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
86
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
87
|
+
|
|
88
|
+
export interface NativeProps extends ViewProps {
|
|
89
|
+
color?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type ComponentType = HostComponent<NativeProps>;
|
|
93
|
+
|
|
94
|
+
interface NativeCommands {
|
|
95
|
+
flash: (viewRef: React.ElementRef<ComponentType>, enabled: boolean) => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const OtherCommands: NativeCommands = codegenNativeCommands<NativeCommands>({
|
|
99
|
+
supportedCommands: ['flash'],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
103
|
+
`;
|
|
104
|
+
expect(function () {
|
|
105
|
+
transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
106
|
+
}).toThrow("Native commands must be exported with the name 'Commands'");
|
|
107
|
+
});
|
|
108
|
+
it("throws an error if 'Commands' is exported without codegenNativeCommands", function () {
|
|
109
|
+
var inputCode = `
|
|
110
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
111
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
112
|
+
|
|
113
|
+
export interface NativeProps extends ViewProps {
|
|
114
|
+
color?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const Commands = 123;
|
|
118
|
+
|
|
119
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
120
|
+
`;
|
|
121
|
+
expect(function () {
|
|
122
|
+
transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
123
|
+
}).toThrow("'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.");
|
|
124
|
+
});
|
|
125
|
+
it("throws an error if 'Commands' is exported via export specifier without codegenNativeCommands", function () {
|
|
126
|
+
var inputCode = `
|
|
127
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
128
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
129
|
+
|
|
130
|
+
export interface NativeProps extends ViewProps {
|
|
131
|
+
color?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const Commands = 123;
|
|
135
|
+
export { Commands };
|
|
136
|
+
|
|
137
|
+
export default codegenNativeComponent<NativeProps>('MyCustomView') as HostComponent<NativeProps>;
|
|
138
|
+
`;
|
|
139
|
+
expect(function () {
|
|
140
|
+
transformReactNativeCodegen(inputCode, "/workspace/src/MyCustomViewNativeComponent.ts");
|
|
141
|
+
}).toThrow("'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.");
|
|
142
|
+
});
|
|
143
|
+
it("ignores files that do not match the codegen pattern", function () {
|
|
144
|
+
var inputCode = `
|
|
145
|
+
export default function Button() {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
`;
|
|
149
|
+
var result = transformReactNativeCodegen(inputCode, "/workspace/src/Button.tsx");
|
|
150
|
+
expect(result).toBeUndefined();
|
|
151
|
+
});
|
|
152
|
+
it("ignores matching files without codegenNativeComponent", function () {
|
|
153
|
+
var inputCode = `
|
|
154
|
+
export default function MyNativeComponent() {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
var result = transformReactNativeCodegen(inputCode, "/workspace/src/MyNativeComponent.tsx");
|
|
159
|
+
expect(result).toBeUndefined();
|
|
160
|
+
});
|
|
161
|
+
it("supports files inside specs/ directory", function () {
|
|
162
|
+
var inputCode = `
|
|
163
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
164
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
165
|
+
|
|
166
|
+
export interface NativeProps extends ViewProps {
|
|
167
|
+
color?: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default codegenNativeComponent<NativeProps>('SpecView') as HostComponent<NativeProps>;
|
|
171
|
+
`;
|
|
172
|
+
var result = transformReactNativeCodegen(inputCode, "/workspace/specs/SpecView.ts");
|
|
173
|
+
expect(result).toBeDefined();
|
|
174
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain('uiViewClassName: "SpecView"');
|
|
175
|
+
});
|
|
176
|
+
it("works through the Vite plugin interface", function () {
|
|
177
|
+
var _plugin_transform;
|
|
178
|
+
var plugin = rnCodegenPlugin();
|
|
179
|
+
expect(plugin.name).toBe("vxrn:rn-codegen");
|
|
180
|
+
expect(plugin.enforce).toBe("pre");
|
|
181
|
+
var inputCode = `
|
|
182
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
183
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
184
|
+
|
|
185
|
+
export interface NativeProps extends ViewProps {
|
|
186
|
+
color?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export default codegenNativeComponent<NativeProps>('PluginView') as HostComponent<NativeProps>;
|
|
190
|
+
`;
|
|
191
|
+
var transformFn = typeof plugin.transform === "function" ? plugin.transform : (_plugin_transform = plugin.transform) === null || _plugin_transform === void 0 ? void 0 : _plugin_transform.handler;
|
|
192
|
+
var result = transformFn.call({}, inputCode, "/workspace/src/PluginViewNativeComponent.ts");
|
|
193
|
+
expect(result).toBeDefined();
|
|
194
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain('uiViewClassName: "PluginView"');
|
|
195
|
+
expect(result === null || result === void 0 ? void 0 : result.map).toBeDefined();
|
|
196
|
+
});
|
|
197
|
+
it("removes export const Commands with sequence expression (e.g. coverage instrumentation)", function () {
|
|
198
|
+
var inputCode = `
|
|
199
|
+
import type { ViewProps, HostComponent } from 'react-native';
|
|
200
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
201
|
+
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
|
|
202
|
+
|
|
203
|
+
export interface NativeProps extends ViewProps {
|
|
204
|
+
color?: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const Commands = (0, codegenNativeCommands({
|
|
208
|
+
supportedCommands: [],
|
|
209
|
+
}));
|
|
210
|
+
|
|
211
|
+
export default codegenNativeComponent<NativeProps>('SequenceView') as HostComponent<NativeProps>;
|
|
212
|
+
`;
|
|
213
|
+
var result = transformReactNativeCodegen(inputCode, "/workspace/src/SequenceViewNativeComponent.ts");
|
|
214
|
+
expect(result).toBeDefined();
|
|
215
|
+
expect(result === null || result === void 0 ? void 0 : result.code).not.toContain("export const Commands");
|
|
216
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain('uiViewClassName: "SequenceView"');
|
|
217
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain("export default NativeComponentRegistry.get");
|
|
218
|
+
});
|
|
219
|
+
it("transforms Flow component specs correctly", function () {
|
|
220
|
+
var flowCode = `/**
|
|
221
|
+
* @flow strict-local
|
|
222
|
+
* @format
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
|
|
226
|
+
import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes';
|
|
227
|
+
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
|
|
228
|
+
|
|
229
|
+
type FlowViewProps = $ReadOnly<{
|
|
230
|
+
...ViewProps,
|
|
231
|
+
title?: string,
|
|
232
|
+
}>;
|
|
233
|
+
|
|
234
|
+
export default codegenNativeComponent<FlowViewProps>('FlowView') as HostComponent<FlowViewProps>;
|
|
235
|
+
`;
|
|
236
|
+
var result = transformReactNativeCodegen(flowCode, "/workspace/src/FlowViewNativeComponent.js");
|
|
237
|
+
expect(result).toBeDefined();
|
|
238
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain('uiViewClassName: "FlowView"');
|
|
239
|
+
expect(result === null || result === void 0 ? void 0 : result.code).toContain("export default NativeComponentRegistry.get");
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
//# sourceMappingURL=rnCodegenPlugin.test.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["describe","expect","it","rnCodegenPlugin","transformReactNativeCodegen","inputCode","result","toBeDefined","code","not","toContain","map","id","first","toThrow","toBeUndefined","_plugin_transform","plugin","name","toBe","enforce","transformFn","transform","handler","call","flowCode"],"sources":["../../src/plugins/rnCodegenPlugin.test.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { isBuiltin } from "node:module";
|
|
3
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
4
|
+
import { statSync } from "node:fs";
|
|
5
|
+
import { rolldown } from "rolldown";
|
|
6
|
+
import { parseSync } from "oxc-parser";
|
|
7
|
+
import { getClosureVariables, JS_GLOBALS, transformHermesLoops } from "@vxrn/compiler";
|
|
8
|
+
import { hermesCompatSWCPlugin, hermesLoopsPlugin } from "../utils/createNativeDevEngine.mjs";
|
|
9
|
+
function workletImportsPlugin(imports) {
|
|
10
|
+
const selections = new Map(Object.entries(imports).map(([source, names]) => [source, [...new Set(names)]]));
|
|
11
|
+
const rejectRuntimeDependency = source => {
|
|
12
|
+
const normalized = source.replace(/\\/g, "/");
|
|
13
|
+
if (isBuiltin(source) || /(?:^|\/node_modules\/)(?:react|react-native|react-native-worklets|react-native-reanimated|expo-modules-core)(?:\/|$)/.test(normalized) || /(?:^|\/node_modules\/)@react-native\//.test(normalized)) {
|
|
14
|
+
throw new Error(`[worklet imports] runtime dependency ${source} is not pure JavaScript`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
name: "vxrn:worklet-imports",
|
|
19
|
+
options(nativeOptions) {
|
|
20
|
+
const modules = /* @__PURE__ */new Map();
|
|
21
|
+
const instance = {
|
|
22
|
+
name: "vxrn:worklet-imports:bundle",
|
|
23
|
+
async resolveId(source, importer, options) {
|
|
24
|
+
const names = selections.get(source);
|
|
25
|
+
if (!names) return;
|
|
26
|
+
if (!names.length) throw new Error(`[worklet imports] no exports selected for ${source}`);
|
|
27
|
+
if (options.kind !== "import-statement") {
|
|
28
|
+
throw new Error(`[worklet imports] ${source} requires a static ESM import`);
|
|
29
|
+
}
|
|
30
|
+
rejectRuntimeDependency(source);
|
|
31
|
+
const resolved = await this.resolve(source, importer, {
|
|
32
|
+
skipSelf: true
|
|
33
|
+
});
|
|
34
|
+
if (!resolved || resolved.external || !isAbsolute(resolved.id) || !statSync(resolved.id, {
|
|
35
|
+
throwIfNoEntry: false
|
|
36
|
+
})?.isFile()) {
|
|
37
|
+
throw new Error(`[worklet imports] ${source} must resolve to a JavaScript file`);
|
|
38
|
+
}
|
|
39
|
+
rejectRuntimeDependency(resolved.id);
|
|
40
|
+
const key = createHash("sha256").update(JSON.stringify([resolved.id, names])).digest("hex").slice(0, 16);
|
|
41
|
+
const id = join(dirname(resolved.id), `__one_worklet_imports_${key}.js`);
|
|
42
|
+
modules.set(id, {
|
|
43
|
+
entry: resolved.id,
|
|
44
|
+
names,
|
|
45
|
+
key
|
|
46
|
+
});
|
|
47
|
+
return id;
|
|
48
|
+
},
|
|
49
|
+
moduleParsed(info) {
|
|
50
|
+
if (!info.code || ![...selections.keys()].some(source => info.code.includes(source))) return;
|
|
51
|
+
const {
|
|
52
|
+
program,
|
|
53
|
+
errors
|
|
54
|
+
} = parseSync(info.id, info.code, {
|
|
55
|
+
lang: /\.[cm]?ts$/.test(info.id) ? "ts" : info.id.endsWith(".tsx") ? "tsx" : "jsx"
|
|
56
|
+
});
|
|
57
|
+
if (errors.length) throw new Error(errors[0].message);
|
|
58
|
+
for (const node of program.body) {
|
|
59
|
+
if (node.type !== "ImportDeclaration" && node.type !== "ExportNamedDeclaration") continue;
|
|
60
|
+
const source = node.source?.value;
|
|
61
|
+
if (!source) continue;
|
|
62
|
+
const names = selections.get(source);
|
|
63
|
+
if (!names) continue;
|
|
64
|
+
for (const specifier of node.specifiers) {
|
|
65
|
+
const imported = specifier.type === "ImportDefaultSpecifier" ? "default" : specifier.type === "ImportSpecifier" ? specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value : specifier.type === "ExportSpecifier" ? specifier.local.type === "Identifier" ? specifier.local.name : specifier.local.value : void 0;
|
|
66
|
+
if (imported !== void 0 && !names.includes(imported)) {
|
|
67
|
+
throw new Error(`[worklet imports] ${info.id} imports unselected export ${imported} from ${source}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
async load(id) {
|
|
73
|
+
const selected = modules.get(id);
|
|
74
|
+
if (!selected) return;
|
|
75
|
+
const outerContext = this;
|
|
76
|
+
const innerEntry = "\0one-worklet-import-entry";
|
|
77
|
+
const bundle = await rolldown({
|
|
78
|
+
cwd: nativeOptions.cwd,
|
|
79
|
+
input: innerEntry,
|
|
80
|
+
platform: "neutral",
|
|
81
|
+
resolve: nativeOptions.resolve,
|
|
82
|
+
transform: nativeOptions.transform,
|
|
83
|
+
shimMissingExports: false,
|
|
84
|
+
plugins: [{
|
|
85
|
+
name: "vxrn:pure-worklet-module",
|
|
86
|
+
resolveId(source, _importer, options) {
|
|
87
|
+
if (source === innerEntry) return source;
|
|
88
|
+
if (options.kind === "dynamic-import") {
|
|
89
|
+
throw new Error(`[worklet imports] dynamic imports are unsupported in ${selected.entry}`);
|
|
90
|
+
}
|
|
91
|
+
rejectRuntimeDependency(source);
|
|
92
|
+
},
|
|
93
|
+
load(source) {
|
|
94
|
+
if (source === innerEntry) {
|
|
95
|
+
return `export { ${selected.names.map((name, index) => `${JSON.stringify(name)} as __export${index}`).join(", ")} } from ${JSON.stringify(selected.entry)};`;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
moduleParsed(info) {
|
|
99
|
+
rejectRuntimeDependency(info.id);
|
|
100
|
+
if (isAbsolute(info.id)) outerContext.addWatchFile(info.id);
|
|
101
|
+
if (info.packageJsonPath) outerContext.addWatchFile(info.packageJsonPath);
|
|
102
|
+
}
|
|
103
|
+
}, hermesCompatSWCPlugin(false), hermesLoopsPlugin()]
|
|
104
|
+
});
|
|
105
|
+
let code;
|
|
106
|
+
try {
|
|
107
|
+
const output = await bundle.generate({
|
|
108
|
+
format: "iife",
|
|
109
|
+
name: "__oneWorkletBundle"
|
|
110
|
+
});
|
|
111
|
+
const chunk = output.output[0];
|
|
112
|
+
if (output.output.length !== 1 || chunk.type !== "chunk" || chunk.imports.length || chunk.dynamicImports.length) {
|
|
113
|
+
throw new Error(`[worklet imports] ${selected.entry} must produce one self-contained JavaScript module`);
|
|
114
|
+
}
|
|
115
|
+
code = transformHermesLoops(chunk.code, id)?.code ?? chunk.code;
|
|
116
|
+
for (const file of await bundle.watchFiles) {
|
|
117
|
+
if (isAbsolute(file)) outerContext.addWatchFile(file);
|
|
118
|
+
}
|
|
119
|
+
} finally {
|
|
120
|
+
await bundle.close();
|
|
121
|
+
}
|
|
122
|
+
const loader = `__oneLoadWorkletModule_${selected.key}`;
|
|
123
|
+
const loaderCode = `function ${loader}() {
|
|
124
|
+
'worklet';
|
|
125
|
+
const cache = globalThis.__oneWorkletImportCache || (globalThis.__oneWorkletImportCache = new WeakMap());
|
|
126
|
+
let namespace = cache.get(${loader});
|
|
127
|
+
if (namespace === undefined) {
|
|
128
|
+
${code}
|
|
129
|
+
namespace = __oneWorkletBundle;
|
|
130
|
+
cache.set(${loader}, namespace);
|
|
131
|
+
}
|
|
132
|
+
return namespace;
|
|
133
|
+
}`;
|
|
134
|
+
const parsed = parseSync(id, loaderCode, {
|
|
135
|
+
lang: "js"
|
|
136
|
+
});
|
|
137
|
+
if (parsed.errors.length) throw new Error(parsed.errors[0].message);
|
|
138
|
+
const captures = getClosureVariables(parsed.program.body[0], /* @__PURE__ */new Set([...JS_GLOBALS, "arguments"]));
|
|
139
|
+
if (captures.length) {
|
|
140
|
+
throw new Error(`[worklet imports] ${selected.entry} requires unsupported runtime globals: ${captures.join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
return `${loaderCode}
|
|
143
|
+
${selected.names.map((name, index) => `
|
|
144
|
+
function __oneExport${index}(...args) {
|
|
145
|
+
'worklet';
|
|
146
|
+
return (0, ${loader}().__export${index})(...args);
|
|
147
|
+
}
|
|
148
|
+
export { __oneExport${index} as ${JSON.stringify(name)} };
|
|
149
|
+
`).join("\n")}`;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
...nativeOptions,
|
|
154
|
+
plugins: [instance, nativeOptions.plugins]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export { workletImportsPlugin };
|
|
160
|
+
//# sourceMappingURL=workletImportsPlugin.mjs.map
|