@multiplatform.one/forms 8.6.0 → 9.0.1
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/README.md +20 -0
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +53 -5
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/expoCameraLoader.cjs +54 -0
- package/dist/cjs/fields/Barcode/expoCameraLoader.native.js +58 -0
- package/dist/cjs/fields/Barcode/expoCameraLoader.native.js.map +1 -0
- package/dist/cjs/fields/ColorPicker/expoLinearGradientLoader.cjs +53 -0
- package/dist/cjs/fields/ColorPicker/expoLinearGradientLoader.native.js +57 -0
- package/dist/cjs/fields/ColorPicker/expoLinearGradientLoader.native.js.map +1 -0
- package/dist/cjs/fields/ColorPicker/gradients.native.js +5 -5
- package/dist/cjs/fields/ColorPicker/gradients.native.js.map +1 -1
- package/dist/cjs/fields/ColorPicker/nativeLinearGradient.cjs +93 -0
- package/dist/cjs/fields/ColorPicker/nativeLinearGradient.native.js +101 -0
- package/dist/cjs/fields/ColorPicker/nativeLinearGradient.native.js.map +1 -0
- package/dist/cjs/shared/expoNativeModule.cjs +37 -0
- package/dist/cjs/shared/expoNativeModule.native.js +40 -0
- package/dist/cjs/shared/expoNativeModule.native.js.map +1 -0
- package/dist/cjs/shared/i18n.cjs +1 -1
- package/dist/cjs/shared/i18n.native.js +1 -1
- package/dist/cjs/shared/i18n.native.js.map +1 -1
- package/dist/esm/fields/Barcode/BarcodeScanner.native.js +51 -3
- package/dist/esm/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/esm/fields/Barcode/expoCameraLoader.mjs +28 -0
- package/dist/esm/fields/Barcode/expoCameraLoader.mjs.map +1 -0
- package/dist/esm/fields/Barcode/expoCameraLoader.native.js +29 -0
- package/dist/esm/fields/Barcode/expoCameraLoader.native.js.map +1 -0
- package/dist/esm/fields/ColorPicker/expoLinearGradientLoader.mjs +27 -0
- package/dist/esm/fields/ColorPicker/expoLinearGradientLoader.mjs.map +1 -0
- package/dist/esm/fields/ColorPicker/expoLinearGradientLoader.native.js +28 -0
- package/dist/esm/fields/ColorPicker/expoLinearGradientLoader.native.js.map +1 -0
- package/dist/esm/fields/ColorPicker/gradients.native.js +1 -1
- package/dist/esm/fields/ColorPicker/nativeLinearGradient.mjs +65 -0
- package/dist/esm/fields/ColorPicker/nativeLinearGradient.mjs.map +1 -0
- package/dist/esm/fields/ColorPicker/nativeLinearGradient.native.js +70 -0
- package/dist/esm/fields/ColorPicker/nativeLinearGradient.native.js.map +1 -0
- package/dist/esm/shared/expoNativeModule.mjs +12 -0
- package/dist/esm/shared/expoNativeModule.mjs.map +1 -0
- package/dist/esm/shared/expoNativeModule.native.js +12 -0
- package/dist/esm/shared/expoNativeModule.native.js.map +1 -0
- package/dist/esm/shared/i18n.mjs +1 -1
- package/dist/esm/shared/i18n.mjs.map +1 -1
- package/dist/esm/shared/i18n.native.js +1 -1
- package/dist/esm/shared/i18n.native.js.map +1 -1
- package/dist/jsx/fields/Barcode/BarcodeScanner.native.js +53 -5
- package/dist/jsx/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/jsx/fields/Barcode/expoCameraLoader.mjs +28 -0
- package/dist/jsx/fields/Barcode/expoCameraLoader.mjs.map +1 -0
- package/dist/jsx/fields/Barcode/expoCameraLoader.native.js +58 -0
- package/dist/jsx/fields/Barcode/expoCameraLoader.native.js.map +1 -0
- package/dist/jsx/fields/ColorPicker/expoLinearGradientLoader.mjs +27 -0
- package/dist/jsx/fields/ColorPicker/expoLinearGradientLoader.mjs.map +1 -0
- package/dist/jsx/fields/ColorPicker/expoLinearGradientLoader.native.js +57 -0
- package/dist/jsx/fields/ColorPicker/expoLinearGradientLoader.native.js.map +1 -0
- package/dist/jsx/fields/ColorPicker/gradients.native.js +5 -5
- package/dist/jsx/fields/ColorPicker/gradients.native.js.map +1 -1
- package/dist/jsx/fields/ColorPicker/nativeLinearGradient.mjs +65 -0
- package/dist/jsx/fields/ColorPicker/nativeLinearGradient.mjs.map +1 -0
- package/dist/jsx/fields/ColorPicker/nativeLinearGradient.native.js +101 -0
- package/dist/jsx/fields/ColorPicker/nativeLinearGradient.native.js.map +1 -0
- package/dist/jsx/shared/expoNativeModule.mjs +12 -0
- package/dist/jsx/shared/expoNativeModule.mjs.map +1 -0
- package/dist/jsx/shared/expoNativeModule.native.js +40 -0
- package/dist/jsx/shared/expoNativeModule.native.js.map +1 -0
- package/dist/jsx/shared/i18n.mjs +1 -1
- package/dist/jsx/shared/i18n.mjs.map +1 -1
- package/dist/jsx/shared/i18n.native.js +1 -1
- package/dist/jsx/shared/i18n.native.js.map +1 -1
- package/metro/index.js +92 -0
- package/metro/phosphor-icon-base.js +129 -0
- package/package.json +24 -20
- package/src/fields/Barcode/BarcodeScanner.native.spec.tsx +59 -0
- package/src/fields/Barcode/BarcodeScanner.native.tsx +49 -4
- package/src/fields/Barcode/expoCameraLoader.spec.ts +45 -0
- package/src/fields/Barcode/expoCameraLoader.ts +46 -0
- package/src/fields/ColorPicker/ColorPicker.spec.tsx +18 -16
- package/src/fields/ColorPicker/expoLinearGradientLoader.ts +44 -0
- package/src/fields/ColorPicker/gradients.native.tsx +1 -1
- package/src/fields/ColorPicker/nativeLinearGradient.spec.tsx +109 -0
- package/src/fields/ColorPicker/nativeLinearGradient.tsx +84 -0
- package/src/metroPreset.spec.ts +132 -0
- package/src/shared/expoNativeModule.ts +20 -0
- package/src/shared/i18n.ts +7 -10
- package/src/shared/i18nPeers.spec.ts +49 -0
- package/types/fields/Barcode/BarcodeScanner.native.d.ts +1 -14
- package/types/fields/Barcode/BarcodeScanner.native.d.ts.map +1 -1
- package/types/fields/Barcode/expoCameraLoader.d.ts +15 -0
- package/types/fields/Barcode/expoCameraLoader.d.ts.map +1 -0
- package/types/fields/ColorPicker/expoLinearGradientLoader.d.ts +14 -0
- package/types/fields/ColorPicker/expoLinearGradientLoader.d.ts.map +1 -0
- package/types/fields/ColorPicker/nativeLinearGradient.d.ts +36 -0
- package/types/fields/ColorPicker/nativeLinearGradient.d.ts.map +1 -0
- package/types/shared/expoNativeModule.d.ts +10 -0
- package/types/shared/expoNativeModule.d.ts.map +1 -0
- package/types/shared/i18n.d.ts +6 -8
- package/types/shared/i18n.d.ts.map +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { hasExpoNativeModule } from "../../shared/expoNativeModule";
|
|
2
|
+
|
|
3
|
+
export type ExpoLinearGradientModule = typeof import("expo-linear-gradient");
|
|
4
|
+
|
|
5
|
+
export interface ExpoLinearGradientSources {
|
|
6
|
+
hasNativeModule(name: string): boolean;
|
|
7
|
+
linearGradient(): unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const appSources: ExpoLinearGradientSources = {
|
|
11
|
+
hasNativeModule: hasExpoNativeModule,
|
|
12
|
+
linearGradient: () => {
|
|
13
|
+
try {
|
|
14
|
+
return require("expo-linear-gradient");
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `expo-linear-gradient` is an optional peer: the host app installs it at the
|
|
23
|
+
* version its Expo SDK ships. Returns null when the package is not installed
|
|
24
|
+
* or the binary lacks the native view, and the ColorPicker then draws its
|
|
25
|
+
* gradients with react-native-svg (FZ-10637).
|
|
26
|
+
*/
|
|
27
|
+
export function resolveExpoLinearGradient(
|
|
28
|
+
sources: ExpoLinearGradientSources = appSources,
|
|
29
|
+
): ExpoLinearGradientModule | null {
|
|
30
|
+
try {
|
|
31
|
+
if (!sources.hasNativeModule("ExpoLinearGradient")) return null;
|
|
32
|
+
const mod = sources.linearGradient() as Partial<ExpoLinearGradientModule> | null | undefined;
|
|
33
|
+
return mod?.LinearGradient ? (mod as ExpoLinearGradientModule) : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let cached: ExpoLinearGradientModule | null | undefined;
|
|
40
|
+
|
|
41
|
+
export function loadExpoLinearGradient(): ExpoLinearGradientModule | null {
|
|
42
|
+
if (cached === undefined) cached = resolveExpoLinearGradient();
|
|
43
|
+
return cached;
|
|
44
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { resolveExpoLinearGradient } from "./expoLinearGradientLoader";
|
|
6
|
+
import { gradientStop, pickLinearGradient, SvgLinearGradient } from "./nativeLinearGradient";
|
|
7
|
+
|
|
8
|
+
vi.mock("./expoLinearGradientLoader", async (importOriginal) => ({
|
|
9
|
+
...(await importOriginal<typeof import("./expoLinearGradientLoader")>()),
|
|
10
|
+
loadExpoLinearGradient: () => null,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const ExpoGradient = () => null;
|
|
14
|
+
|
|
15
|
+
// FZ-10637. expo-linear-gradient is an optional peer the host installs at its
|
|
16
|
+
// own SDK version, so the ColorPicker has to draw without it.
|
|
17
|
+
describe("resolveExpoLinearGradient", () => {
|
|
18
|
+
it("returns the module when the package and the native view are both present", () => {
|
|
19
|
+
const mod = { LinearGradient: ExpoGradient };
|
|
20
|
+
expect(
|
|
21
|
+
resolveExpoLinearGradient({ hasNativeModule: () => true, linearGradient: () => mod }),
|
|
22
|
+
).toBe(mod);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns null when the app does not install expo-linear-gradient", () => {
|
|
26
|
+
const missing = () => {
|
|
27
|
+
throw new Error("Cannot find module 'expo-linear-gradient'");
|
|
28
|
+
};
|
|
29
|
+
expect(
|
|
30
|
+
resolveExpoLinearGradient({ hasNativeModule: () => true, linearGradient: missing }),
|
|
31
|
+
).toBeNull();
|
|
32
|
+
expect(
|
|
33
|
+
resolveExpoLinearGradient({ hasNativeModule: () => true, linearGradient: () => null }),
|
|
34
|
+
).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns null without loading the package when the binary lacks the native view", () => {
|
|
38
|
+
const load = vi.fn(() => ({ LinearGradient: ExpoGradient }));
|
|
39
|
+
expect(
|
|
40
|
+
resolveExpoLinearGradient({ hasNativeModule: () => false, linearGradient: load }),
|
|
41
|
+
).toBeNull();
|
|
42
|
+
expect(load).not.toHaveBeenCalled();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("loads expo-linear-gradient from inside a try block, never a static import, so Metro bundles without it", () => {
|
|
46
|
+
const loader = readFileSync(join(__dirname, "expoLinearGradientLoader.ts"), "utf8");
|
|
47
|
+
expect(loader).toMatch(/try \{\s*return require\("expo-linear-gradient"\);/);
|
|
48
|
+
const gradients = readFileSync(join(__dirname, "gradients.native.tsx"), "utf8");
|
|
49
|
+
expect(gradients).not.toMatch(/from "expo-linear-gradient"/);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("the ColorPicker gradient without expo-linear-gradient", () => {
|
|
54
|
+
it("uses expo-linear-gradient when the app has it and react-native-svg when it does not", () => {
|
|
55
|
+
expect(pickLinearGradient({ LinearGradient: ExpoGradient })).toBe(ExpoGradient);
|
|
56
|
+
expect(pickLinearGradient(null)).toBe(SvgLinearGradient);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("draws the same stops and axis with react-native-svg", () => {
|
|
60
|
+
const { container } = render(
|
|
61
|
+
<SvgLinearGradient
|
|
62
|
+
colors={["#FF0000", "#00FF00", "#0000FF"]}
|
|
63
|
+
start={{ x: 0, y: 0.5 }}
|
|
64
|
+
end={{ x: 1, y: 0.5 }}
|
|
65
|
+
/>,
|
|
66
|
+
);
|
|
67
|
+
const gradient = container.querySelector("linearGradient");
|
|
68
|
+
expect(gradient?.getAttribute("x1")).toBe("0");
|
|
69
|
+
expect(gradient?.getAttribute("y1")).toBe("0.5");
|
|
70
|
+
expect(gradient?.getAttribute("x2")).toBe("1");
|
|
71
|
+
expect(gradient?.getAttribute("y2")).toBe("0.5");
|
|
72
|
+
const stops = [...container.querySelectorAll("stop")];
|
|
73
|
+
expect(stops.map((stop) => stop.getAttribute("offset"))).toEqual(["0", "0.5", "1"]);
|
|
74
|
+
expect(stops.map((stop) => stop.getAttribute("stop-color"))).toEqual([
|
|
75
|
+
"#FF0000",
|
|
76
|
+
"#00FF00",
|
|
77
|
+
"#0000FF",
|
|
78
|
+
]);
|
|
79
|
+
const rect = container.querySelector("rect");
|
|
80
|
+
expect(rect?.getAttribute("fill")).toBe(`url(#${gradient?.getAttribute("id")})`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("keeps explicit stop locations", () => {
|
|
84
|
+
const { container } = render(
|
|
85
|
+
<SvgLinearGradient colors={["#FF0000", "#FFFF00", "#FF0000"]} locations={[0, 0.17, 1]} />,
|
|
86
|
+
);
|
|
87
|
+
expect(
|
|
88
|
+
[...container.querySelectorAll("stop")].map((stop) => stop.getAttribute("offset")),
|
|
89
|
+
).toEqual(["0", "0.17", "1"]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("splits an rgba stop into a colour and an opacity, which is how svg takes alpha", () => {
|
|
93
|
+
expect(gradientStop("rgba(0,0,0,0)")).toEqual({ stopColor: "rgb(0,0,0)", stopOpacity: 0 });
|
|
94
|
+
expect(gradientStop("#00FF00")).toEqual({ stopColor: "#00FF00", stopOpacity: 1 });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("the native gradient backgrounds render through the fallback", async () => {
|
|
98
|
+
const { AlphaBackground, HueBackground, SaturationBackground } =
|
|
99
|
+
await import("./gradients.native");
|
|
100
|
+
const { container } = render(
|
|
101
|
+
<>
|
|
102
|
+
<SaturationBackground hueColor="#FF0000" width={100} height={100} />
|
|
103
|
+
<HueBackground width={100} height={14} />
|
|
104
|
+
<AlphaBackground hex="#00FF00" width={100} height={14} />
|
|
105
|
+
</>,
|
|
106
|
+
);
|
|
107
|
+
expect(container.querySelectorAll("linearGradient").length).toBe(4);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
import type { StyleProp, ViewStyle } from "react-native";
|
|
3
|
+
import { Defs, LinearGradient as SvgGradient, Rect, Stop, Svg } from "react-native-svg";
|
|
4
|
+
import { loadExpoLinearGradient } from "./expoLinearGradientLoader";
|
|
5
|
+
|
|
6
|
+
interface GradientPoint {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface LinearGradientProps {
|
|
12
|
+
colors: readonly string[];
|
|
13
|
+
locations?: readonly number[];
|
|
14
|
+
start?: GradientPoint;
|
|
15
|
+
end?: GradientPoint;
|
|
16
|
+
style?: StyleProp<ViewStyle>;
|
|
17
|
+
pointerEvents?: "none" | "auto" | "box-none" | "box-only";
|
|
18
|
+
testID?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const rgbaPattern = /^rgba\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* react-native-svg takes a stop's alpha as `stopOpacity`, not inside the
|
|
25
|
+
* colour, so an rgba() stop is split into the two.
|
|
26
|
+
*/
|
|
27
|
+
export function gradientStop(color: string): { stopColor: string; stopOpacity: number } {
|
|
28
|
+
const rgba = rgbaPattern.exec(color);
|
|
29
|
+
if (!rgba) return { stopColor: color, stopOpacity: 1 };
|
|
30
|
+
return { stopColor: `rgb(${rgba[1]},${rgba[2]},${rgba[3]})`, stopOpacity: Number(rgba[4]) };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let gradientIds = 0;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The same fill as expo-linear-gradient, drawn with react-native-svg, which
|
|
37
|
+
* every native MPO host already carries. `start` and `end` are fractions of
|
|
38
|
+
* the box, as in expo-linear-gradient, and stops without `locations` are
|
|
39
|
+
* spread evenly.
|
|
40
|
+
*/
|
|
41
|
+
export function SvgLinearGradient({
|
|
42
|
+
colors,
|
|
43
|
+
locations,
|
|
44
|
+
start = { x: 0.5, y: 0 },
|
|
45
|
+
end = { x: 0.5, y: 1 },
|
|
46
|
+
style,
|
|
47
|
+
pointerEvents,
|
|
48
|
+
testID,
|
|
49
|
+
}: LinearGradientProps) {
|
|
50
|
+
const id = `mpo-linear-gradient-${(gradientIds += 1)}`;
|
|
51
|
+
const last = Math.max(colors.length - 1, 1);
|
|
52
|
+
return (
|
|
53
|
+
<Svg
|
|
54
|
+
style={style}
|
|
55
|
+
pointerEvents={pointerEvents}
|
|
56
|
+
testID={testID ?? "svg-linear-gradient"}
|
|
57
|
+
preserveAspectRatio="none"
|
|
58
|
+
>
|
|
59
|
+
<Defs>
|
|
60
|
+
<SvgGradient id={id} x1={start.x} y1={start.y} x2={end.x} y2={end.y}>
|
|
61
|
+
{colors.map((color, index) => (
|
|
62
|
+
<Stop
|
|
63
|
+
key={`${index}-${color}`}
|
|
64
|
+
offset={locations?.[index] ?? index / last}
|
|
65
|
+
{...gradientStop(color)}
|
|
66
|
+
/>
|
|
67
|
+
))}
|
|
68
|
+
</SvgGradient>
|
|
69
|
+
</Defs>
|
|
70
|
+
<Rect x="0" y="0" width="100%" height="100%" fill={`url(#${id})`} />
|
|
71
|
+
</Svg>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function pickLinearGradient(
|
|
76
|
+
expo: { LinearGradient?: unknown } | null,
|
|
77
|
+
): ComponentType<LinearGradientProps> {
|
|
78
|
+
return (
|
|
79
|
+
(expo?.LinearGradient as ComponentType<LinearGradientProps> | undefined) ?? SvgLinearGradient
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const LinearGradient: ComponentType<LinearGradientProps> =
|
|
84
|
+
pickLinearGradient(loadExpoLinearGradient());
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
const packageDir = join(import.meta.dirname, "..");
|
|
7
|
+
const requireFromPackage = createRequire(join(packageDir, "package.json"));
|
|
8
|
+
const { phosphorIconBase, withMultiplatformOne } = requireFromPackage("./metro/index.js") as {
|
|
9
|
+
phosphorIconBase: string;
|
|
10
|
+
withMultiplatformOne: <T extends object>(config: T, options?: { router?: string }) => T;
|
|
11
|
+
};
|
|
12
|
+
const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8")) as {
|
|
13
|
+
exports: Record<string, unknown>;
|
|
14
|
+
files: string[];
|
|
15
|
+
dependencies: Record<string, string>;
|
|
16
|
+
peerDependencies: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type Resolution = { type: string; filePath: string };
|
|
20
|
+
type ResolveRequest = (
|
|
21
|
+
context: MetroContext,
|
|
22
|
+
moduleName: string,
|
|
23
|
+
platform: string | null,
|
|
24
|
+
) => Resolution;
|
|
25
|
+
interface MetroContext {
|
|
26
|
+
originModulePath: string;
|
|
27
|
+
resolveRequest: ResolveRequest;
|
|
28
|
+
}
|
|
29
|
+
interface MetroConfig {
|
|
30
|
+
resolver: { resolveRequest?: ResolveRequest; sourceExts?: string[] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const phosphorOrigin = "/app/node_modules/@phosphor-icons/react/dist/csr/Check.es.js";
|
|
34
|
+
const routerOrigin = "/app/node_modules/@multiplatform.one/router/dist/esm/router/index.mjs";
|
|
35
|
+
|
|
36
|
+
function context(originModulePath: string): MetroContext {
|
|
37
|
+
return {
|
|
38
|
+
originModulePath,
|
|
39
|
+
resolveRequest: vi.fn((_context, moduleName) => ({
|
|
40
|
+
type: "sourceFile",
|
|
41
|
+
filePath: `/stock/${moduleName}`,
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolverOf(config: MetroConfig): ResolveRequest {
|
|
47
|
+
const resolveRequest = config.resolver.resolveRequest;
|
|
48
|
+
if (!resolveRequest) throw new Error("the preset set no resolveRequest");
|
|
49
|
+
return resolveRequest;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// FZ-10629, FZ-10634. The host bindings ship with forms, so an Expo app needs
|
|
53
|
+
// no shim of its own.
|
|
54
|
+
describe("@multiplatform.one/forms/metro", () => {
|
|
55
|
+
it("ships in the package: the ./metro export, the files entry and the shim", () => {
|
|
56
|
+
expect(manifest.exports["./metro"]).toBe("./metro/index.js");
|
|
57
|
+
expect(manifest.files).toContain("metro");
|
|
58
|
+
expect(existsSync(phosphorIconBase)).toBe(true);
|
|
59
|
+
expect(phosphorIconBase).toBe(join(packageDir, "metro", "phosphor-icon-base.js"));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("the shim loads only packages forms declares, so it resolves from forms in any install layout", () => {
|
|
63
|
+
const declared = { ...manifest.dependencies, ...manifest.peerDependencies };
|
|
64
|
+
const required = [...readFileSync(phosphorIconBase, "utf8").matchAll(/require\("([^"]+)"\)/g)];
|
|
65
|
+
expect(required.map((match) => match[1]).sort()).toEqual([
|
|
66
|
+
"@tamagui/web",
|
|
67
|
+
"react",
|
|
68
|
+
"react-native-svg",
|
|
69
|
+
]);
|
|
70
|
+
for (const [, name] of required) expect(declared).toHaveProperty(name);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("swaps phosphor's IconBase for the react-native-svg one on a phone", () => {
|
|
74
|
+
const resolve = resolverOf(withMultiplatformOne<MetroConfig>({ resolver: {} }));
|
|
75
|
+
for (const platform of ["ios", "android"]) {
|
|
76
|
+
expect(resolve(context(phosphorOrigin), "../lib/IconBase.es.js", platform)).toEqual({
|
|
77
|
+
type: "sourceFile",
|
|
78
|
+
filePath: phosphorIconBase,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("leaves phosphor's own IconBase on web, and every other request alone", () => {
|
|
84
|
+
const resolve = resolverOf(withMultiplatformOne<MetroConfig>({ resolver: {} }));
|
|
85
|
+
expect(resolve(context(phosphorOrigin), "../lib/IconBase.es.js", "web").filePath).toBe(
|
|
86
|
+
"/stock/../lib/IconBase.es.js",
|
|
87
|
+
);
|
|
88
|
+
expect(resolve(context("/app/src/IconBase.tsx"), "./IconBase", "ios").filePath).toBe(
|
|
89
|
+
"/stock/./IconBase",
|
|
90
|
+
);
|
|
91
|
+
expect(resolve(context(phosphorOrigin), "react", "ios").filePath).toBe("/stock/react");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("chains the resolveRequest the app already set and keeps the rest of its config", () => {
|
|
95
|
+
const appResolver = vi.fn<ResolveRequest>(() => ({ type: "sourceFile", filePath: "/app/own" }));
|
|
96
|
+
const config = withMultiplatformOne<MetroConfig>({
|
|
97
|
+
resolver: { resolveRequest: appResolver, sourceExts: ["tsx"] },
|
|
98
|
+
});
|
|
99
|
+
expect(config.resolver.sourceExts).toEqual(["tsx"]);
|
|
100
|
+
const ctx = context("/app/src/index.tsx");
|
|
101
|
+
expect(resolverOf(config)(ctx, "./x", "ios").filePath).toBe("/app/own");
|
|
102
|
+
expect(appResolver).toHaveBeenCalledWith(ctx, "./x", "ios");
|
|
103
|
+
expect(ctx.resolveRequest).not.toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("applies once", () => {
|
|
107
|
+
const once = withMultiplatformOne<MetroConfig>({ resolver: {} });
|
|
108
|
+
expect(withMultiplatformOne(once)).toBe(once);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("with router: expo-router, a web bundle resolves the router's one to the Expo Router binding", () => {
|
|
112
|
+
const resolve = resolverOf(
|
|
113
|
+
withMultiplatformOne<MetroConfig>({ resolver: {} }, { router: "expo-router" }),
|
|
114
|
+
);
|
|
115
|
+
expect(resolve(context(routerOrigin), "one", "web").filePath).toBe(
|
|
116
|
+
"/stock/@multiplatform.one/router/expo-router",
|
|
117
|
+
);
|
|
118
|
+
expect(resolve(context(routerOrigin), "one", "ios").filePath).toBe("/stock/one");
|
|
119
|
+
expect(resolve(context("/app/src/screen.tsx"), "one", "web").filePath).toBe("/stock/one");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("leaves one alone on web without the option, as a One app needs", () => {
|
|
123
|
+
const resolve = resolverOf(withMultiplatformOne<MetroConfig>({ resolver: {} }));
|
|
124
|
+
expect(resolve(context(routerOrigin), "one", "web").filePath).toBe("/stock/one");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("refuses a router it has no binding for", () => {
|
|
128
|
+
expect(() => withMultiplatformOne({ resolver: {} }, { router: "react-navigation" })).toThrow(
|
|
129
|
+
/unknown router "react-navigation"\. Known: expo-router/,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this binary carries an Expo native module (LC-19 preflight). An OTA
|
|
3
|
+
* can ship a package's JS to a binary built without it, and requiring such a
|
|
4
|
+
* package runs a module factory that throws, which Metro reports as a fatal
|
|
5
|
+
* LogBox in dev even when the caller catches it. Asking expo-modules-core
|
|
6
|
+
* first keeps that factory from running. A host without expo-modules-core
|
|
7
|
+
* answers true, so the caller's own guarded require decides.
|
|
8
|
+
*/
|
|
9
|
+
export function hasExpoNativeModule(name: string): boolean {
|
|
10
|
+
try {
|
|
11
|
+
const core = require("expo-modules-core") as {
|
|
12
|
+
requireOptionalNativeModule?: (name: string) => unknown;
|
|
13
|
+
};
|
|
14
|
+
if (!core.requireOptionalNativeModule) return true;
|
|
15
|
+
const nativeModule = core.requireOptionalNativeModule(name);
|
|
16
|
+
return nativeModule !== null && nativeModule !== undefined;
|
|
17
|
+
} catch {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/shared/i18n.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import * as ReactI18next from "react-i18next";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* react-i18next is
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* naming the package and the fix — the moment an i18n-using field actually
|
|
11
|
-
* renders.
|
|
4
|
+
* react-i18next is a required peer. When an app leaves it out anyway, the
|
|
5
|
+
* bundler can substitute an empty stub for the package, and direct named
|
|
6
|
+
* imports would then surface as cryptic MISSING_EXPORT failures far from the
|
|
7
|
+
* cause. Route every usage through this namespace access instead, so the
|
|
8
|
+
* omission fails loudly, naming the package and the fix, the moment an
|
|
9
|
+
* i18n-using field actually renders.
|
|
12
10
|
*/
|
|
13
11
|
export const useTranslation = ((...args: unknown[]) => {
|
|
14
12
|
const impl = (ReactI18next as { useTranslation?: (...args: unknown[]) => unknown })
|
|
@@ -17,8 +15,7 @@ export const useTranslation = ((...args: unknown[]) => {
|
|
|
17
15
|
throw new Error(
|
|
18
16
|
"[@multiplatform.one/forms] react-i18next is not installed, but a component that " +
|
|
19
17
|
"translates its built-in copy was rendered. Add react-i18next and i18next to your " +
|
|
20
|
-
"app's dependencies
|
|
21
|
-
"components can omit them).",
|
|
18
|
+
"app's dependencies.",
|
|
22
19
|
);
|
|
23
20
|
}
|
|
24
21
|
return impl(...args);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
const manifest = JSON.parse(
|
|
7
|
+
readFileSync(join(import.meta.dirname, "..", "..", "package.json"), "utf8"),
|
|
8
|
+
) as {
|
|
9
|
+
dependencies?: Record<string, string>;
|
|
10
|
+
peerDependencies?: Record<string, string>;
|
|
11
|
+
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// FZ-10637, FZ-1854. forms translates its built-in copy through react-i18next on
|
|
15
|
+
// every render, so the i18n pair is a required peer. Listed as optional it
|
|
16
|
+
// left a host to find out from a failed bundle.
|
|
17
|
+
describe("the i18n peers forms translates through", () => {
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
vi.doUnmock("react-i18next");
|
|
20
|
+
vi.resetModules();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("declares i18next and react-i18next as required peers, never as dependencies", () => {
|
|
24
|
+
for (const name of ["i18next", "react-i18next"]) {
|
|
25
|
+
expect(manifest.dependencies ?? {}).not.toHaveProperty(name);
|
|
26
|
+
expect(manifest.peerDependencies).toHaveProperty(name);
|
|
27
|
+
expect(manifest.peerDependenciesMeta?.[name]?.optional).not.toBe(true);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("accepts the i18next majors react-i18next 16 and 17 run on", () => {
|
|
32
|
+
expect(manifest.peerDependencies?.i18next).toBe(">=25.0.0");
|
|
33
|
+
expect(manifest.peerDependencies?.["react-i18next"]).toBe(">=14");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("translates built-in copy with the host's react-i18next", async () => {
|
|
37
|
+
const { useTranslation } = await import("./i18n");
|
|
38
|
+
const { result } = renderHook(() => useTranslation());
|
|
39
|
+
expect(result.current.t("Close")).toBe("Close");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("names the packages to install when an app leaves them out", async () => {
|
|
43
|
+
vi.doMock("react-i18next", () => ({ useTranslation: undefined }));
|
|
44
|
+
const { useTranslation } = await import("./i18n");
|
|
45
|
+
expect(() => useTranslation()).toThrow(
|
|
46
|
+
/react-i18next is not installed.*Add react-i18next and i18next to your app's dependencies\./s,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -8,18 +8,5 @@ import type { BarcodeScannerProps, CameraPreflightResult } from "./scannerTypes"
|
|
|
8
8
|
* stable scanner surface, never as a present-fail-dismiss flash.
|
|
9
9
|
*/
|
|
10
10
|
export declare function preflightCamera(): Promise<CameraPreflightResult>;
|
|
11
|
-
|
|
12
|
-
* Native barcode scanner — `expo-camera`'s `CameraView` with built-in barcode
|
|
13
|
-
* scanning (bundled in Expo Go SDK 55). Replaces the web `html5-qrcode`, so no
|
|
14
|
-
* `getUserMedia` / DOM browser code reaches the Hermes bundle.
|
|
15
|
-
*
|
|
16
|
-
* LC-19 CAPABILITY-PREFLIGHT: only mounts after a passing `preflightCamera()`.
|
|
17
|
-
* While the permission prompt is pending the surface shows a stable
|
|
18
|
-
* "Requesting camera…" state; a denial renders the error INSIDE the still-open
|
|
19
|
-
* surface with Retry + Close — it never self-dismisses on error.
|
|
20
|
-
*
|
|
21
|
-
* Note: the iOS simulator has no camera, so live scanning can only be verified
|
|
22
|
-
* on a physical device; in the simulator the camera surface renders empty.
|
|
23
|
-
*/
|
|
24
|
-
export declare function BarcodeScanner({ active, onScan, onError, onClose, knobProps, formats, }: BarcodeScannerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export declare function BarcodeScanner(props: BarcodeScannerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
25
12
|
//# sourceMappingURL=BarcodeScanner.native.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarcodeScanner.native.d.ts","sourceRoot":"","sources":["../../../src/fields/Barcode/BarcodeScanner.native.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGjF;;;;;;;GAOG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"BarcodeScanner.native.d.ts","sourceRoot":"","sources":["../../../src/fields/Barcode/BarcodeScanner.native.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGjF;;;;;;;GAOG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAiBtE;AA6ID,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,kDAIxD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type ExpoCameraModule = typeof import("expo-camera");
|
|
2
|
+
export interface ExpoCameraSources {
|
|
3
|
+
hasNativeModule(name: string): boolean;
|
|
4
|
+
camera(): unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* `expo-camera` is an optional peer: the host app installs it at the version
|
|
8
|
+
* its Expo SDK ships, and an app that never scans a barcode leaves it out.
|
|
9
|
+
* Returns null when the package is not installed or the binary lacks the
|
|
10
|
+
* native module, and the Barcode field then reports the camera as unavailable
|
|
11
|
+
* instead of presenting the scanner (FZ-10637).
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveExpoCamera(sources?: ExpoCameraSources): ExpoCameraModule | null;
|
|
14
|
+
export declare function loadExpoCamera(): ExpoCameraModule | null;
|
|
15
|
+
//# sourceMappingURL=expoCameraLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoCameraLoader.d.ts","sourceRoot":"","sources":["../../../src/fields/Barcode/expoCameraLoader.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,cAAc,aAAa,CAAC,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,MAAM,IAAI,OAAO,CAAC;CACnB;AAaD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,iBAA8B,GACtC,gBAAgB,GAAG,IAAI,CASzB;AAID,wBAAgB,cAAc,IAAI,gBAAgB,GAAG,IAAI,CAGxD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ExpoLinearGradientModule = typeof import("expo-linear-gradient");
|
|
2
|
+
export interface ExpoLinearGradientSources {
|
|
3
|
+
hasNativeModule(name: string): boolean;
|
|
4
|
+
linearGradient(): unknown;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* `expo-linear-gradient` is an optional peer: the host app installs it at the
|
|
8
|
+
* version its Expo SDK ships. Returns null when the package is not installed
|
|
9
|
+
* or the binary lacks the native view, and the ColorPicker then draws its
|
|
10
|
+
* gradients with react-native-svg (FZ-10637).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveExpoLinearGradient(sources?: ExpoLinearGradientSources): ExpoLinearGradientModule | null;
|
|
13
|
+
export declare function loadExpoLinearGradient(): ExpoLinearGradientModule | null;
|
|
14
|
+
//# sourceMappingURL=expoLinearGradientLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoLinearGradientLoader.d.ts","sourceRoot":"","sources":["../../../src/fields/ColorPicker/expoLinearGradientLoader.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,wBAAwB,GAAG,cAAc,sBAAsB,CAAC,CAAC;AAE7E,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,cAAc,IAAI,OAAO,CAAC;CAC3B;AAaD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,yBAAsC,GAC9C,wBAAwB,GAAG,IAAI,CAQjC;AAID,wBAAgB,sBAAsB,IAAI,wBAAwB,GAAG,IAAI,CAGxE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
import type { StyleProp, ViewStyle } from "react-native";
|
|
3
|
+
interface GradientPoint {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
export interface LinearGradientProps {
|
|
8
|
+
colors: readonly string[];
|
|
9
|
+
locations?: readonly number[];
|
|
10
|
+
start?: GradientPoint;
|
|
11
|
+
end?: GradientPoint;
|
|
12
|
+
style?: StyleProp<ViewStyle>;
|
|
13
|
+
pointerEvents?: "none" | "auto" | "box-none" | "box-only";
|
|
14
|
+
testID?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* react-native-svg takes a stop's alpha as `stopOpacity`, not inside the
|
|
18
|
+
* colour, so an rgba() stop is split into the two.
|
|
19
|
+
*/
|
|
20
|
+
export declare function gradientStop(color: string): {
|
|
21
|
+
stopColor: string;
|
|
22
|
+
stopOpacity: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The same fill as expo-linear-gradient, drawn with react-native-svg, which
|
|
26
|
+
* every native MPO host already carries. `start` and `end` are fractions of
|
|
27
|
+
* the box, as in expo-linear-gradient, and stops without `locations` are
|
|
28
|
+
* spread evenly.
|
|
29
|
+
*/
|
|
30
|
+
export declare function SvgLinearGradient({ colors, locations, start, end, style, pointerEvents, testID, }: LinearGradientProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export declare function pickLinearGradient(expo: {
|
|
32
|
+
LinearGradient?: unknown;
|
|
33
|
+
} | null): ComponentType<LinearGradientProps>;
|
|
34
|
+
export declare const LinearGradient: ComponentType<LinearGradientProps>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=nativeLinearGradient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nativeLinearGradient.d.ts","sourceRoot":"","sources":["../../../src/fields/ColorPicker/nativeLinearGradient.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,UAAU,aAAa;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAItF;AAID;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,SAAS,EACT,KAAwB,EACxB,GAAsB,EACtB,KAAK,EACL,aAAa,EACb,MAAM,GACP,EAAE,mBAAmB,2CAwBrB;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GACxC,aAAa,CAAC,mBAAmB,CAAC,CAIpC;AAED,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,mBAAmB,CAChB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this binary carries an Expo native module (LC-19 preflight). An OTA
|
|
3
|
+
* can ship a package's JS to a binary built without it, and requiring such a
|
|
4
|
+
* package runs a module factory that throws, which Metro reports as a fatal
|
|
5
|
+
* LogBox in dev even when the caller catches it. Asking expo-modules-core
|
|
6
|
+
* first keeps that factory from running. A host without expo-modules-core
|
|
7
|
+
* answers true, so the caller's own guarded require decides.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasExpoNativeModule(name: string): boolean;
|
|
10
|
+
//# sourceMappingURL=expoNativeModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expoNativeModule.d.ts","sourceRoot":"","sources":["../../src/shared/expoNativeModule.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAWzD"}
|
package/types/shared/i18n.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import * as ReactI18next from "react-i18next";
|
|
2
2
|
/**
|
|
3
|
-
* react-i18next is
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* naming the package and the fix — the moment an i18n-using field actually
|
|
10
|
-
* renders.
|
|
3
|
+
* react-i18next is a required peer. When an app leaves it out anyway, the
|
|
4
|
+
* bundler can substitute an empty stub for the package, and direct named
|
|
5
|
+
* imports would then surface as cryptic MISSING_EXPORT failures far from the
|
|
6
|
+
* cause. Route every usage through this namespace access instead, so the
|
|
7
|
+
* omission fails loudly, naming the package and the fix, the moment an
|
|
8
|
+
* i18n-using field actually renders.
|
|
11
9
|
*/
|
|
12
10
|
export declare const useTranslation: typeof ReactI18next.useTranslation;
|
|
13
11
|
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/shared/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,eAAe,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/shared/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,eAAe,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,EAWrB,OAAO,YAAY,CAAC,cAAc,CAAC"}
|