@uniflowed/react-native 0.0.0-alpha.40

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/index.js ADDED
@@ -0,0 +1,21 @@
1
+ // @flow
2
+ //
3
+ // `@uniflowed/react-native`: React Native, re-exported.
4
+ //
5
+ // This is the real `react-native` package, not a declaration of it. uf owns the
6
+ // Flow-first compiler, router, lints and target selection around React Native;
7
+ // the host components, Platform object, StyleSheet runtime and native module
8
+ // bridge stay with React Native itself.
9
+ //
10
+ // `export *` rather than a list of names: React Native's public JavaScript
11
+ // surface moves with its release, and a hand-maintained copy here would turn
12
+ // every upstream addition into a uf runtime gap.
13
+ //
14
+ // React Native is a peer dependency, so the application chooses the exact
15
+ // version. uf's own release does not decide when an app moves its native host.
16
+
17
+ export * from "react-native";
18
+
19
+ import * as ReactNative from "react-native";
20
+
21
+ export { ReactNative };
@@ -0,0 +1,333 @@
1
+ // @noflow
2
+ //
3
+ // `@uniflowed/react-native/metro-transformer.cjs`: the Babel transformer Metro
4
+ // runs for every module in a native bundle.
5
+ //
6
+ // Metro loads `transformer.babelTransformerPath` with `require`, in its own
7
+ // workers, whatever the app's config is written in — so this is CommonJS, and
8
+ // plain JavaScript for the reason `metro.cjs` is: no process that loads it has
9
+ // uf's hooks.
10
+ //
11
+ // For a module uf owns (`isFlowModule`), it runs `uf transform` — Flow's own
12
+ // lowering, the React Compiler, the StyleX policy — and hands the result to the
13
+ // transformer the project's Metro config already had, so Expo's preset or React
14
+ // Native's still does everything it did before: the module system, React
15
+ // Refresh, platform inlining. Anything else goes to that transformer untouched.
16
+
17
+ "use strict";
18
+
19
+ const fs = require("node:fs");
20
+ const os = require("node:os");
21
+ const path = require("node:path");
22
+
23
+ /**
24
+ * An upstream transformer a person names on purpose. It wins over everything.
25
+ */
26
+ const UPSTREAM_OVERRIDE_ENV = "UF_METRO_UPSTREAM_TRANSFORMER";
27
+
28
+ /**
29
+ * The upstream transformer `withUniflowedMetro()` found in the config it
30
+ * composed.
31
+ *
32
+ * # Why the environment
33
+ *
34
+ * Metro calls a Babel transformer with `{ src, filename, options, plugins }`
35
+ * and nothing from the config it was loaded from — `getBabelTransformArgs` in
36
+ * `metro-transform-worker` builds `options` from the bundle request and five
37
+ * named config values — so there is no key on `transformer` this module could
38
+ * read the upstream from. The environment is the one channel that reaches it:
39
+ * the config is evaluated before Metro starts its workers, and a worker, child
40
+ * process or thread, starts with a copy of the environment it was created from.
41
+ *
42
+ * A name of its own, rather than reusing `UF_METRO_UPSTREAM_TRANSFORMER`,
43
+ * because the two say different things. That one is a person overriding the
44
+ * upstream, and it wins; this one is what the project's config already said,
45
+ * and it is written again every time a config is composed.
46
+ */
47
+ const CONFIGURED_UPSTREAM_ENV = "UF_METRO_CONFIGURED_UPSTREAM";
48
+
49
+ let host = null;
50
+ let upstream = null;
51
+
52
+ async function transform(args) {
53
+ const { src, filename, options } = args;
54
+ const projectRoot = projectRootOf(options);
55
+ const flow = loadHost();
56
+ const absolute = path.isAbsolute(filename) ? filename : path.join(projectRoot, filename);
57
+ let next = src;
58
+
59
+ if (flow.isFlowModule(absolute)) {
60
+ findUfBinary(flow);
61
+ const out = await flow.transformFlow(src, absolute, {
62
+ root: projectRoot,
63
+ development: options?.dev === true,
64
+ // Off, because the upstream transformer registers components for React
65
+ // Refresh itself — Expo's preset and React Native's both add
66
+ // `react-refresh/babel` to a development bundle — and a component
67
+ // registered twice is two families to the runtime, and a refresh that
68
+ // remounts instead of preserving state.
69
+ refresh: false,
70
+ sourceMap: true,
71
+ });
72
+ if (out != null) {
73
+ if (out.css != null && out.css !== "") {
74
+ throw new Error(
75
+ `@uniflowed/react-native/metro: ${filename} produced StyleX CSS, but the ` +
76
+ "native Metro contract cannot attach CSS to a React Native bundle yet. " +
77
+ "Use native style props or keep that StyleX module out of the native route.",
78
+ );
79
+ }
80
+ next = out.code;
81
+ }
82
+ }
83
+
84
+ // Everything Metro passed, and not a chosen subset of it. `plugins` carries
85
+ // Metro's `functionMapBabelPlugin` and `importLocationsPlugin`, and
86
+ // forwarding `{ src, filename, options }` alone dropped both for every Flow
87
+ // module in the bundle — function names in stack traces among them.
88
+ return loadUpstream(projectRoot).transformer.transform({
89
+ ...args,
90
+ src: next,
91
+ // RN's Babel transformer still gates React Refresh on `hot`, although
92
+ // recent Metro versions omit it. Preserve an explicit caller decision.
93
+ options: { ...options, hot: options?.hot ?? options?.dev === true },
94
+ });
95
+ }
96
+
97
+ /**
98
+ * The cache key Metro files every transformed module under.
99
+ *
100
+ * Four identities, because four things decide what a module compiles to: this
101
+ * file, the JavaScript side of `uf transform`, the `uf` binary that does the
102
+ * compiling, and the upstream transformer with its own key. The binary is the
103
+ * one that was missing. A rebuilt or upgraded `uf` changes the output of every
104
+ * Flow module without touching any file this key used to hash, so Metro went on
105
+ * serving the old transforms until somebody thought to pass `--reset-cache` —
106
+ * the defect `@uniflowed/host`'s own cache keys on `ufBinaryIdentity()` to
107
+ * avoid.
108
+ */
109
+ function getCacheKey(options) {
110
+ const projectRoot = projectRootOf(options);
111
+ const flow = loadHost();
112
+ const binary = findUfBinary(flow);
113
+ const loaded = loadUpstream(projectRoot);
114
+ const parts = [
115
+ "uniflowed-react-native-metro-transformer-v2",
116
+ fileIdentity(__filename),
117
+ fileIdentity(require.resolve("@uniflowed/host/transform")),
118
+ flow.ufBinaryIdentity(binary) ?? binary,
119
+ loaded.path,
120
+ ];
121
+ if (typeof loaded.transformer.getCacheKey === "function") {
122
+ parts.push(String(loaded.transformer.getCacheKey(options)));
123
+ }
124
+ return parts.join("\0");
125
+ }
126
+
127
+ /**
128
+ * `@uniflowed/host/transform`, loaded synchronously.
129
+ *
130
+ * It is an ES module and this file is not, and `getCacheKey` has to answer
131
+ * synchronously: Metro asks it once, on its main thread, before the first
132
+ * transform. `require` of an ES module is what Node 20.19 and 22.12 added, and
133
+ * on an older Node the error says that rather than failing on a name.
134
+ */
135
+ function loadHost() {
136
+ if (host != null) return host;
137
+ try {
138
+ host = require("@uniflowed/host/transform");
139
+ } catch (error) {
140
+ if (error?.code === "ERR_REQUIRE_ESM") {
141
+ throw new Error(
142
+ `@uniflowed/react-native/metro: Node ${process.version} cannot require @uniflowed/host, ` +
143
+ "which is an ES module. Run Metro on Node 20.19, 22.12 or later.",
144
+ );
145
+ }
146
+ throw error;
147
+ }
148
+ return host;
149
+ }
150
+
151
+ /**
152
+ * The `uf` that compiles this bundle, found the way its installer arranged and
153
+ * handed to `@uniflowed/host` through `UF_BINARY`.
154
+ *
155
+ * In order: `UF_BINARY`, which `uf dev` sets to itself so a bundle compiles
156
+ * with exactly the `uf` that started it; `uf` on `PATH`; and the installer's
157
+ * own directory, `UF_BIN_DIR` or `~/.local/bin`. The last is there for the
158
+ * processes that bundle an app without a person's shell in front of them —
159
+ * Xcode's "Bundle React Native code and images" phase runs with a minimal
160
+ * `PATH`, and a `uf` the installer put in `~/.local/bin` is invisible to it.
161
+ *
162
+ * Found once and written back to `UF_BINARY`, so that the binary this key
163
+ * identifies and the binary `@uniflowed/host` spawns cannot be two different
164
+ * files.
165
+ */
166
+ function findUfBinary(flow) {
167
+ const named = process.env.UF_BINARY;
168
+ if (named != null && named !== "") return named;
169
+ if (flow.ufBinaryIdentity("uf") != null) return "uf";
170
+ const directory = process.env.UF_BIN_DIR || path.join(os.homedir(), ".local", "bin");
171
+ const installed = path.join(directory, "uf");
172
+ if (flow.ufBinaryIdentity(installed) != null) {
173
+ process.env.UF_BINARY = installed;
174
+ return installed;
175
+ }
176
+ throw new Error(
177
+ "@uniflowed/react-native/metro: cannot find the `uf` binary that compiles Flow for this bundle. " +
178
+ `UF_BINARY is not set, \`uf\` is not on PATH (${process.env.PATH ?? ""}), ` +
179
+ `and there is no executable at ${installed}. ` +
180
+ "Install uf with `curl -fsSL https://setup.uniflowed.dev | sh`, or set UF_BINARY to its path — " +
181
+ "for Xcode's bundle phase, export it from ios/.xcode.env.local.",
182
+ );
183
+ }
184
+
185
+ /**
186
+ * The Babel transformer that runs after uf's, loaded once per worker.
187
+ *
188
+ * An upstream named by a person or by the composed config is the only
189
+ * candidate: if it does not load, that is an error, not an invitation to
190
+ * guess. Only a config that never went through `withUniflowedMetro()` falls
191
+ * back to looking for the transformer the project's own Metro config would
192
+ * have named.
193
+ */
194
+ function loadUpstream(projectRoot) {
195
+ if (upstream != null) return upstream;
196
+ const override = process.env[UPSTREAM_OVERRIDE_ENV];
197
+ const configured = process.env[CONFIGURED_UPSTREAM_ENV];
198
+ let candidates;
199
+ if (override != null && override !== "") {
200
+ candidates = [{ request: override, from: projectRoot, why: UPSTREAM_OVERRIDE_ENV }];
201
+ } else if (configured != null && configured !== "") {
202
+ candidates = [
203
+ {
204
+ request: configured,
205
+ from: projectRoot,
206
+ why: "transformer.babelTransformerPath, composed by withUniflowedMetro()",
207
+ },
208
+ ];
209
+ } else {
210
+ candidates = installedCandidates(projectRoot);
211
+ }
212
+
213
+ const tried = [];
214
+ for (const candidate of candidates) {
215
+ let resolved;
216
+ try {
217
+ resolved = require.resolve(candidate.request, { paths: [candidate.from] });
218
+ } catch {
219
+ tried.push(`${candidate.request} (${candidate.why}): not found from ${candidate.from}`);
220
+ continue;
221
+ }
222
+ const loaded = require(resolved);
223
+ const transformer =
224
+ loaded?.__esModule === true && loaded.default != null ? loaded.default : loaded;
225
+ if (typeof transformer?.transform !== "function") {
226
+ tried.push(`${resolved} (${candidate.why}): does not export transform()`);
227
+ continue;
228
+ }
229
+ if (transformer.isUniflowedMetroTransformer === true) {
230
+ throw new Error(
231
+ `@uniflowed/react-native/metro: ${resolved} is uf's own Metro transformer, named as the ` +
232
+ "transformer to run after uf's. Two copies of @uniflowed/react-native have composed the " +
233
+ "same config; install one.",
234
+ );
235
+ }
236
+ upstream = { transformer, path: resolved };
237
+ return upstream;
238
+ }
239
+
240
+ throw new Error(
241
+ "@uniflowed/react-native/metro: there is no React Native Babel transformer to run after uf's. " +
242
+ "Compose the project's Metro config so the transformer it already has keeps running — " +
243
+ "`module.exports = withUniflowedMetro(getDefaultConfig(__dirname))`, with getDefaultConfig " +
244
+ "from expo/metro-config or @react-native/metro-config — or set UF_METRO_UPSTREAM_TRANSFORMER " +
245
+ "to a transformer's path.\n" +
246
+ tried.map((line) => ` ${line}`).join("\n"),
247
+ );
248
+ }
249
+
250
+ /**
251
+ * The transformers a project's own Metro config would have named, most likely
252
+ * first.
253
+ *
254
+ * Looked up from the project, not from this package. A package manager is free
255
+ * to nest `@expo/metro-config` inside `expo` — npm does, for the SDK 57
256
+ * template — and a `require` from here sees only what was hoisted, which is why
257
+ * this list could not find Expo's transformer in a freshly created Expo app.
258
+ *
259
+ * Expo's first when the project has Expo, because in an Expo project it is the
260
+ * one `getDefaultConfig()` names and React Native's would bundle without
261
+ * `babel-preset-expo`. `metro-babel-transformer`, which this list used to end
262
+ * with, is gone from it: it applies no React Native preset, so reaching it meant
263
+ * React Native's own Flow source arriving at Babel with nothing to strip it — a
264
+ * failure much further from its cause than this refusal.
265
+ */
266
+ function installedCandidates(projectRoot) {
267
+ const candidates = [];
268
+ const expo = packageDirectory("expo", projectRoot);
269
+ if (expo != null) {
270
+ candidates.push({
271
+ request: "@expo/metro-config/babel-transformer",
272
+ from: expo,
273
+ why: "Expo's transformer, beside the installed expo",
274
+ });
275
+ }
276
+ candidates.push({
277
+ request: "@react-native/metro-babel-transformer",
278
+ from: projectRoot,
279
+ why: "React Native's transformer",
280
+ });
281
+ const reactNative = packageDirectory("react-native", projectRoot);
282
+ if (reactNative != null) {
283
+ candidates.push({
284
+ request: "@react-native/metro-babel-transformer",
285
+ from: reactNative,
286
+ why: "React Native's transformer, beside the installed react-native",
287
+ });
288
+ }
289
+ candidates.push({
290
+ request: "metro-react-native-babel-transformer",
291
+ from: projectRoot,
292
+ why: "React Native's transformer before 0.73",
293
+ });
294
+ return candidates;
295
+ }
296
+
297
+ /**
298
+ * The directory `name` is installed in, looked up the way Node looks up a
299
+ * package — `node_modules` in the starting directory and then in each parent.
300
+ *
301
+ * Walked rather than resolved, because `require.resolve("expo/package.json")`
302
+ * depends on the package's `exports` allowing it, and the question here is only
303
+ * whether the package is installed.
304
+ */
305
+ function packageDirectory(name, from) {
306
+ let directory = from;
307
+ for (;;) {
308
+ const candidate = path.join(directory, "node_modules", name);
309
+ if (fs.existsSync(path.join(candidate, "package.json"))) return candidate;
310
+ const parent = path.dirname(directory);
311
+ if (parent === directory) return null;
312
+ directory = parent;
313
+ }
314
+ }
315
+
316
+ function projectRootOf(options) {
317
+ return typeof options?.projectRoot === "string" ? options.projectRoot : process.cwd();
318
+ }
319
+
320
+ function fileIdentity(file) {
321
+ const stat = fs.statSync(file);
322
+ return `${file}:${stat.size}:${stat.mtimeMs}`;
323
+ }
324
+
325
+ module.exports = {
326
+ transform,
327
+ getCacheKey,
328
+ // Read by `loadUpstream`, so a config composed by two copies of this package
329
+ // is refused instead of recursing into itself.
330
+ isUniflowedMetroTransformer: true,
331
+ // The name `metro.cjs` writes the composed upstream under, kept in one place.
332
+ configuredUpstreamEnv: CONFIGURED_UPSTREAM_ENV,
333
+ };
package/metro.cjs ADDED
@@ -0,0 +1,205 @@
1
+ // @noflow
2
+ //
3
+ // `@uniflowed/react-native/metro`: the Metro half of uf's native target.
4
+ //
5
+ // # Why this module is CommonJS rather than Flow
6
+ //
7
+ // Every other module in `@uniflowed/*` is Flow, because `uf` is the process
8
+ // that loads it and `uf` installs the hooks that make Flow run. This one is
9
+ // loaded by somebody else's process. Metro evaluates `metro.config.js` in
10
+ // plain Node — under `expo start`, `react-native start`, the bundle phases
11
+ // Xcode and Gradle run, and on EAS Build's machines — and none of those has
12
+ // heard of uf's loader. Until ubugeeei-prod/uf#988 this file was Flow, and
13
+ // outside `uf test` importing it was `SyntaxError: Unexpected token 'export'`.
14
+ // The suite never saw that, because the suite is the one place the hooks are
15
+ // always installed.
16
+ //
17
+ // CommonJS rather than an ES module, because every template that writes a
18
+ // Metro config writes CommonJS — `require("expo/metro-config")`,
19
+ // `require("@react-native/metro-config")` — and an ES module config can still
20
+ // `import { withUniflowedMetro } from "@uniflowed/react-native/metro"`: Node
21
+ // reads a CommonJS module's named exports. The other direction, `require` of
22
+ // an ES module, needs Node 20.19 or 22.12.
23
+ //
24
+ // `uf build --target native` writes the same extension list and pipeline into
25
+ // the build manifest's target contract, so what a finished build reports and
26
+ // what a project's Metro config does are one answer rather than two.
27
+
28
+ "use strict";
29
+
30
+ const path = require("node:path");
31
+
32
+ const { configuredUpstreamEnv } = require("./metro-transformer.cjs");
33
+
34
+ /** Source extensions uf's native target needs, added to the project's own. */
35
+ const metroSourceExts = Object.freeze(["js", "jsx", "mjs", "cjs"]);
36
+
37
+ /** The `package.json` fields Metro reads for an entry point, in order. */
38
+ const metroResolverMainFields = Object.freeze(["react-native", "browser", "main"]);
39
+
40
+ /** What a module goes through, in order, on its way into a native bundle. */
41
+ const metroTransformPipeline = Object.freeze([
42
+ "flow",
43
+ "react-compiler",
44
+ "stylex-css-refusal",
45
+ "metro-babel",
46
+ ]);
47
+
48
+ /** The Babel transformer Metro runs, as the absolute path Metro wants. */
49
+ const metroTransformerPath = path.join(__dirname, "metro-transformer.cjs");
50
+
51
+ const nativePackages = Object.freeze([
52
+ "brand",
53
+ "cell",
54
+ "core",
55
+ "effect",
56
+ "fetch",
57
+ "form",
58
+ "graphql",
59
+ "hooks",
60
+ "i18n",
61
+ "immer",
62
+ "query",
63
+ "react",
64
+ "react-native",
65
+ "relay",
66
+ "state",
67
+ "temporal",
68
+ "validator",
69
+ ]);
70
+ const domPackages = new Set([
71
+ "ui",
72
+ "web",
73
+ "browser",
74
+ "react-testing",
75
+ "story",
76
+ "vrt",
77
+ "pwa",
78
+ "motion",
79
+ ]);
80
+
81
+ function nativeResolution(previous) {
82
+ return (context, moduleName, platform) => {
83
+ const name = moduleName.startsWith("@uniflowed/") ? moduleName.split("/")[1] : null;
84
+ if (
85
+ domPackages.has(name) ||
86
+ (name === "router" &&
87
+ moduleName !== "@uniflowed/router/native" &&
88
+ moduleName !== "@uniflowed/router/routing")
89
+ ) {
90
+ throw new Error(
91
+ `${moduleName} needs a browser DOM and cannot be bundled for ${platform}. ` +
92
+ `Native packages: ${nativePackages.map((item) => `@uniflowed/${item}`).join(", ")}, ` +
93
+ "@uniflowed/router/native and @uniflowed/router/routing. Keep DOM components in $page.web.js.",
94
+ );
95
+ }
96
+ return (previous ?? context.resolveRequest)(context, moduleName, platform);
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Add uf's source extensions, resolver fields and transformer to a Metro config.
102
+ *
103
+ * Metro already owns platform suffix resolution (`.ios.js`, `.android.js`,
104
+ * `.native.js`), and uf's route scanner uses the same suffixes, so nothing
105
+ * about that is declared again here.
106
+ *
107
+ * # Composing with the transformer that is already there
108
+ *
109
+ * Metro accepts one `babelTransformerPath`, and every real config already has
110
+ * one: `getDefaultConfig()` names Expo's in `expo/metro-config` and React
111
+ * Native's in `@react-native/metro-config`. This function used to refuse such a
112
+ * config, which left a hand-written object as the only input it accepted —
113
+ * `withUniflowedMetro(getDefaultConfig(__dirname))` threw in every project that
114
+ * had ever been generated.
115
+ *
116
+ * Replacing that transformer would be worse than refusing it. Expo's is how
117
+ * `babel-preset-expo`, Expo Router and React Refresh reach a module, and a
118
+ * custom one — an SVG transformer, say — is a decision the project made. So the
119
+ * transformer that was there keeps running, after uf's and on uf's output, and
120
+ * uf's takes its place in the config.
121
+ *
122
+ * Metro gives a Babel transformer nothing from the config it came from, so the
123
+ * one being composed travels to Metro's workers in the environment; see
124
+ * `configuredUpstreamEnv` in `metro-transformer.cjs` for why that is the only
125
+ * channel there is. A transformer the config names by package is resolved here,
126
+ * from the project root, where Metro would have resolved it — so a transformer
127
+ * that is not installed is an error while the config loads, not in a worker
128
+ * halfway through the first bundle.
129
+ *
130
+ * Composing twice is composing once: a config whose transformer is already
131
+ * uf's is returned with nothing recorded, so wrapping a config a library has
132
+ * already wrapped cannot make uf's transformer its own upstream.
133
+ *
134
+ * @param {object} [config] a Metro config — usually `getDefaultConfig(__dirname)`
135
+ * @returns {object} the same config, with uf's extensions, fields and transformer
136
+ */
137
+ function withUniflowedMetro(config = {}) {
138
+ const resolver = config.resolver ?? {};
139
+ const transformer = config.transformer ?? {};
140
+ const existing = transformer.babelTransformerPath;
141
+ if (existing != null && existing !== metroTransformerPath) {
142
+ process.env[configuredUpstreamEnv] = resolveTransformer(existing, config.projectRoot);
143
+ }
144
+ return {
145
+ ...config,
146
+ resolver: {
147
+ ...resolver,
148
+ sourceExts: mergeUnique(resolver.sourceExts, metroSourceExts),
149
+ resolverMainFields: mergeUnique(resolver.resolverMainFields, metroResolverMainFields),
150
+ resolveRequest: nativeResolution(resolver.resolveRequest),
151
+ },
152
+ transformer: {
153
+ ...transformer,
154
+ babelTransformerPath: metroTransformerPath,
155
+ },
156
+ };
157
+ }
158
+
159
+ /**
160
+ * The absolute path of the transformer a config names.
161
+ *
162
+ * An absolute path is taken as given, because that is what Metro does with it.
163
+ * A package name or a relative path is resolved from the project root, which is
164
+ * what `getDefaultConfig(projectRoot)` records on the config, and from the
165
+ * working directory for a config that does not say.
166
+ */
167
+ function resolveTransformer(specifier, projectRoot) {
168
+ if (typeof specifier !== "string" || specifier === "") {
169
+ throw new Error(
170
+ `@uniflowed/react-native/metro: transformer.babelTransformerPath is ${JSON.stringify(specifier)}, ` +
171
+ "and the path of a Babel transformer module was expected.",
172
+ );
173
+ }
174
+ if (path.isAbsolute(specifier)) return specifier;
175
+ const from = typeof projectRoot === "string" ? projectRoot : process.cwd();
176
+ try {
177
+ return require.resolve(specifier, { paths: [from] });
178
+ } catch (error) {
179
+ throw new Error(
180
+ `@uniflowed/react-native/metro: transformer.babelTransformerPath names ${specifier}, ` +
181
+ `which does not resolve from ${from}: ${String(error.message).split("\n")[0]}. ` +
182
+ "Install it, or name it by its absolute path.",
183
+ );
184
+ }
185
+ }
186
+
187
+ function mergeUnique(existing, required) {
188
+ const merged = [];
189
+ const seen = new Set();
190
+ for (const value of [...(existing ?? []), ...required]) {
191
+ if (seen.has(value)) continue;
192
+ seen.add(value);
193
+ merged.push(value);
194
+ }
195
+ return merged;
196
+ }
197
+
198
+ module.exports = {
199
+ metroResolverMainFields,
200
+ metroSourceExts,
201
+ metroTransformPipeline,
202
+ metroTransformerPath,
203
+ nativePackages,
204
+ withUniflowedMetro,
205
+ };
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@uniflowed/react-native",
3
+ "version": "0.0.0-alpha.40",
4
+ "description": "React Native, re-exported for the Unified Toolchain for Flow.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ubugeeei-prod/uf.git",
11
+ "directory": "packages/react-native"
12
+ },
13
+ "exports": {
14
+ ".": "./index.js",
15
+ "./metro": "./metro.cjs",
16
+ "./metro-transformer.cjs": "./metro-transformer.cjs"
17
+ },
18
+ "files": [
19
+ "index.js",
20
+ "metro.cjs",
21
+ "metro-transformer.cjs",
22
+ "!*.test.js"
23
+ ],
24
+ "dependencies": {
25
+ "@uniflowed/host": "0.0.0-alpha.40"
26
+ },
27
+ "peerDependencies": {
28
+ "react": ">=19.2.3",
29
+ "react-native": ">=0.86"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "react-native": {
33
+ "optional": true
34
+ }
35
+ }
36
+ }