import type { FontSpec, MeasureWidthImpl } from './types'; /** * 默认测量实现(成熟方案):react-native-text-size * * 注意: * - 该库的 `measure` 是 Promise 异步接口,本模块以 async 方式对齐。 * - 为避免在测试/Node 环境直接 require `react-native` 造成崩溃,这里使用“延迟加载 + 捕获异常”。 */ type TextSizeMeasureParams = { text: string; width?: number; fontFamily?: string; fontSize?: number; fontWeight?: string; allowFontScaling?: boolean; usePreciseWidth?: boolean; }; type TextSizeMeasureResult = { width: number }; function loadReactNativeTextSize(): { measure: (params: TextSizeMeasureParams) => Promise; } { // 使用 require 确保 Metro 能解析并打包该原生模块(动态 import 在某些环境下无法被正确解析) // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-explicit-any const mod: any = require('react-native-text-size'); return mod?.default ?? mod; } function toTextSizeFontSpecs(fontSpec: FontSpec): Pick { return { fontFamily: fontSpec.fontFamily, fontSize: fontSpec.fontSize, fontWeight: fontSpec.fontWeight, }; } /** * 默认测量函数(可替换/可注入)。 * - width 约束设为极大值,避免自动换行影响“单行宽度”测量 * - usePreciseWidth=true,取更精确的宽度(开销更大,但对本算法更稳定) */ export const defaultMeasureWidthImpl: MeasureWidthImpl = async ({ text, fontSpec }) => { const TextSize = loadReactNativeTextSize(); if (!TextSize || typeof TextSize.measure !== 'function') { // 典型原因:在 Expo Go 中运行,或没有使用包含该原生模块的 Development Build。 // 这里抛出更明确的错误,方便上层捕获并在 meta.reason=MEASURE_FAILED 时看到根因。 throw new Error( [ 'react-native-text-size 原生模块不可用:TextSize.measure 不是函数。', '请确认你不是在 Expo Go 里运行;需要使用包含该原生模块的 Development Build(expo-dev-client / expo run:ios / EAS dev build)。', ].join(' '), ); } const res = await TextSize.measure({ text, width: 1_000_000_000, usePreciseWidth: true, allowFontScaling: true, ...toTextSizeFontSpecs(fontSpec), }); return res.width; };