miaoda-expo-devkit 0.1.1-beta.38 → 0.1.1-beta.39

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 CHANGED
@@ -9,6 +9,7 @@ Expo / React Native 开发环境工具集,通过 Metro 构建层注入以下
9
9
  - **LogBox 屏蔽** — web 平台禁用 Expo 全屏错误遮罩
10
10
  - **expo-notifications stub** — Expo Go(Android)中提供 no-op 实现,核心 API 调用时弹出带参数校验的调试 Alert,Dev Build 透传真实模块
11
11
  - **expo-media-library stub** — Expo Go / Web 中提供 no-op 实现,`saveToLibraryAsync`、`createAssetAsync`、权限请求等 API 调用时弹出 Alert 提示,Dev Build 原生环境透传真实模块
12
+ - **expo-calendar stub** — Expo Go / Web 中提供 no-op 实现,`getEventsAsync`、`createEventAsync`、`getCalendarsAsync`、权限请求等核心 API 调用时弹出 Alert 提示并校验参数,Dev Build 原生环境透传真实模块
12
13
 
13
14
  ## 安装
14
15
 
@@ -39,11 +40,11 @@ const { withDevkit } = require('miaoda-expo-devkit/metro');
39
40
  module.exports = withDevkit(getDefaultConfig(__dirname));
40
41
  ```
41
42
 
42
- `withDevkit` 已内置所有 wrapper(含 expo-notifications、expo-media-library stub),无需手动叠加。也可单独使用各 wrapper:
43
+ `withDevkit` 已内置所有 wrapper(含 expo-notifications、expo-media-library、expo-calendar stub),无需手动叠加。也可单独使用各 wrapper:
43
44
 
44
45
  ```js
45
- const { withDevStubs, withEntryInjection, withExpoMediaLibraryStub } = require('miaoda-expo-devkit/metro');
46
- module.exports = withExpoMediaLibraryStub(withEntryInjection(withDevStubs(config)));
46
+ const { withDevStubs, withEntryInjection, withExpoMediaLibraryStub, withExpoCalendarStub } = require('miaoda-expo-devkit/metro');
47
+ module.exports = withExpoCalendarStub(withExpoMediaLibraryStub(withEntryInjection(withDevStubs(config))));
47
48
  ```
48
49
 
49
50
  ### Sentry 初始化
@@ -243,6 +244,11 @@ metro.config.js
243
244
  │ ├─ Expo Go / Web:no-op + Alert 提示(不崩溃)
244
245
  │ └─ Dev Build(原生):透传真实 expo-media-library
245
246
 
247
+ ├─ withExpoCalendarStub → resolver.resolveRequest
248
+ │ └─ expo-calendar → dist/stubs/expo-calendar-stub.js (全平台)
249
+ │ ├─ Expo Go / Web:no-op + Alert 提示 + 参数校验(不崩溃)
250
+ │ └─ Dev Build(原生):透传真实 expo-calendar
251
+
246
252
  └─ withEntryInjection → resolver.resolveRequest
247
253
  └─ expo-router/entry-classic → dist/stubs/expo-router-entry-stub.js
248
254
  ├─ require('./entry-inject') ← 注入脚本(bundle 首部执行)
@@ -274,13 +280,14 @@ sentry-react-native-stub.js
274
280
  | 子路径 | 文件 | 内容 |
275
281
  |---|---|---|
276
282
  | `.` | `dist/index.js` | `SentryCapture`、`MetroSymbolicator`、全部类型 |
277
- | `./metro` | `dist/metro.js` | `withDevkit`、`withDevStubs`、`withEntryInjection`、`withExpoNotificationsStub`、`withExpoMediaLibraryStub` 等 |
283
+ | `./metro` | `dist/metro.js` | `withDevkit`、`withDevStubs`、`withEntryInjection`、`withExpoNotificationsStub`、`withExpoMediaLibraryStub`、`withExpoCalendarStub` 等 |
278
284
  | `./babel-plugin-jsx-source` | `dist/babel/plugin-jsx-source.js` | Babel 插件:为 JSX 注入 source 信息 |
279
285
  | `./babel-preset` | `dist/babel/preset.js` | Babel Preset:集成 jsx-source 和 lucide 插件 |
280
286
  | `./sentry-react-native-stub` | `dist/stubs/sentry-react-native-stub.js` | `@sentry/react-native` 模块替换 stub |
281
287
  | `./no-op-logbox` | `dist/stubs/no-op-logbox.js` | LogBox no-op stub |
282
288
  | `./expo-notifications-stub` | `dist/stubs/expo-notifications-stub.js` | `expo-notifications` Expo Go Android stub |
283
289
  | `./expo-media-library-stub` | `dist/stubs/expo-media-library-stub.js` | `expo-media-library` Expo Go / Web stub |
290
+ | `./expo-calendar-stub` | `dist/stubs/expo-calendar-stub.js` | `expo-calendar` Expo Go / Web stub |
284
291
 
285
292
  ---
286
293
 
@@ -445,6 +452,49 @@ Expo Go 扫码预览不支持访问手机相册
445
452
 
446
453
  ---
447
454
 
455
+ ### expo-calendar-stub.js
456
+
457
+ `expo-calendar` 模块替换 stub,由 `withExpoCalendarStub()` 在 Metro 层注入(**全平台**)。
458
+
459
+ **背景:** `expo-calendar` 依赖原生系统日历 API,在 Expo Go 和 Web 环境中不可用,调用 `getEventsAsync` 等 API 会直接崩溃。
460
+
461
+ **运行时行为:**
462
+ - **Expo Go / Web**:提供 no-op 实现,以下 API 调用时弹出 Alert / Dialog 提示(不崩溃):
463
+ - `useCalendarPermissions()` / `useRemindersPermissions()` — 初始返回 `undetermined`,`requestPermission()` 弹 Alert
464
+ - `requestCalendarPermissionsAsync()` / `requestRemindersPermissionsAsync()` — 弹 Alert 提示,返回 denied
465
+ - `getCalendarPermissionsAsync()` / `getRemindersPermissionsAsync()` — 静默返回 denied
466
+ - `getCalendarsAsync(entityType?)` — 弹 Alert 显示查询类型,返回空数组
467
+ - `createCalendarAsync(details)` — 弹 Alert 含名称和颜色,校验 `details.title` 非空
468
+ - `updateCalendarAsync(id, details)` / `deleteCalendarAsync(id)` — 弹 Alert 含日历 ID,校验 ID 格式
469
+ - `getEventsAsync(calendarIds, startDate, endDate)` — 弹 Alert 含时间范围,校验 calendarIds 非空及日期合法性,返回空数组
470
+ - `createEventAsync(calendarId, eventData)` — 弹 Alert 含标题和时间,Android 下校验 startDate/endDate
471
+ - `updateEventAsync(id, details)` / `deleteEventAsync(id)` — 弹 Alert 含事件 ID,校验 ID 格式
472
+ - 其他未知 API — Proxy 兜底,静默返回 `undefined`;以 `PermissionsAsync` 结尾的 API 返回 denied 结构
473
+ - **Development Build(原生)**:透传真实 `expo-calendar`,功能完全正常
474
+
475
+ **枚举常量**(stub & Dev Build 均可用):
476
+ `EntityTypes`、`Frequency`、`Availability`、`CalendarType`、`EventStatus`、`SourceType`、
477
+ `AttendeeRole`、`AttendeeStatus`、`AttendeeType`、`AlarmMethod`、`EventAccessLevel`、
478
+ `CalendarAccessLevel`、`ReminderStatus`、`DayOfTheWeek`、`MonthOfTheYear`
479
+
480
+ **Alert 消息格式(合规示例):**
481
+ ```
482
+ 创建日历事件
483
+ 秒哒扫码预览不支持访问手机日历
484
+
485
+ 日历 ID: cal1
486
+ 标题: 团队会议
487
+ 开始: 2025/1/1 10:00:00
488
+ 结束: 2025/1/1 11:00:00
489
+
490
+ ✅ 参数合规
491
+ 发布为正式 App 后可正常使用
492
+ ```
493
+
494
+ **手动验证:** 在 `devkit-e2e` App 中扫码进入「Calendar Stub 验证」页面,逐按钮触发并对照期望结果。
495
+
496
+ ---
497
+
448
498
  ## Babel 插件:jsx-source
449
499
 
450
500
  `babel-plugin-jsx-source` 为 JSX 元素注入 source 属性(文件路径、行列号),用于开发调试。通过 `dataSet` 对象注入,这是 React Web 可识别的数据通道。
package/dist/metro.d.mts CHANGED
@@ -118,6 +118,7 @@ declare function withRouteEndpoint(config: MetroConfig, options: RouteEndpointOp
118
118
  * withLucideResolver — 消除 lucide 子路径未在 exports 声明时的 warning
119
119
  * withExpoNotificationsStub — Android:expo-notifications → stub(Expo Go no-op,Dev Build 透传)
120
120
  * withExpoMediaLibraryStub — Web / Expo Go:expo-media-library → stub(弹 Alert 提示,Dev Build 透传)
121
+ * withExpoCalendarStub — Web / Expo Go:expo-calendar → stub(弹 Alert 提示,Dev Build 透传)
121
122
  * withEntryInjection — 在 expo-router 启动前注入脚本(仅 __DEV__)
122
123
  * withDevStubs — 替换 Sentry DSN / 屏蔽 LogBox(仅 __DEV__)
123
124
  * withRouteEndpoint — 添加 /__routes 端点(仅 __DEV__)
@@ -277,6 +278,23 @@ declare function withExpoNotificationsStub(config: MetroConfig): MetroConfig;
277
278
  */
278
279
  declare function withExpoMediaLibraryStub(config: MetroConfig): MetroConfig;
279
280
 
281
+ /**
282
+ * withExpoCalendarStub — Web / Expo Go 将 expo-calendar 替换为 stub
283
+ *
284
+ * expo-calendar 依赖 native module,在 Web 和 Expo Go 中不可用。
285
+ * stub 在运行时通过 Platform.OS 和 isRunningInExpoGo() 判断:
286
+ * - Web / Expo Go:no-op(核心 API 弹 Alert 提示,不崩溃)
287
+ * - Development Build(原生):透传真实 expo-calendar,功能完全正常
288
+ *
289
+ * 此 wrapper 需要在开发和生产构建中均生效,因为 native module 缺失是运行时问题。
290
+ */
291
+
292
+ /**
293
+ * @param config Metro config 对象
294
+ * @returns 注入 resolveRequest 后的新 Metro config
295
+ */
296
+ declare function withExpoCalendarStub(config: MetroConfig): MetroConfig;
297
+
280
298
  /**
281
299
  * 将 Metro transformer 的 minifier 切换为 esbuild。
282
300
  *
@@ -302,4 +320,4 @@ declare function withEsbuildMinify(config: MetroConfig): MetroConfig;
302
320
 
303
321
  declare function withWasmSupport(config: MetroConfig): MetroConfig;
304
322
 
305
- export { type DevkitOptions, type InjectOptions, type PatchNativeWindCacheOptions, type RouteEndpointOptions, patchNativeWindCachePath, withCssInterop, withDevStubs, withDevkit, withEntryInjection, withEsbuildMinify, withExpoMediaLibraryStub, withExpoNotificationsStub, withLucideResolver, withNativeWind, withRouteEndpoint, withWasmSupport, withWorkspaceNodeModules };
323
+ export { type DevkitOptions, type InjectOptions, type PatchNativeWindCacheOptions, type RouteEndpointOptions, patchNativeWindCachePath, withCssInterop, withDevStubs, withDevkit, withEntryInjection, withEsbuildMinify, withExpoCalendarStub, withExpoMediaLibraryStub, withExpoNotificationsStub, withLucideResolver, withNativeWind, withRouteEndpoint, withWasmSupport, withWorkspaceNodeModules };
package/dist/metro.d.ts CHANGED
@@ -118,6 +118,7 @@ declare function withRouteEndpoint(config: MetroConfig, options: RouteEndpointOp
118
118
  * withLucideResolver — 消除 lucide 子路径未在 exports 声明时的 warning
119
119
  * withExpoNotificationsStub — Android:expo-notifications → stub(Expo Go no-op,Dev Build 透传)
120
120
  * withExpoMediaLibraryStub — Web / Expo Go:expo-media-library → stub(弹 Alert 提示,Dev Build 透传)
121
+ * withExpoCalendarStub — Web / Expo Go:expo-calendar → stub(弹 Alert 提示,Dev Build 透传)
121
122
  * withEntryInjection — 在 expo-router 启动前注入脚本(仅 __DEV__)
122
123
  * withDevStubs — 替换 Sentry DSN / 屏蔽 LogBox(仅 __DEV__)
123
124
  * withRouteEndpoint — 添加 /__routes 端点(仅 __DEV__)
@@ -277,6 +278,23 @@ declare function withExpoNotificationsStub(config: MetroConfig): MetroConfig;
277
278
  */
278
279
  declare function withExpoMediaLibraryStub(config: MetroConfig): MetroConfig;
279
280
 
281
+ /**
282
+ * withExpoCalendarStub — Web / Expo Go 将 expo-calendar 替换为 stub
283
+ *
284
+ * expo-calendar 依赖 native module,在 Web 和 Expo Go 中不可用。
285
+ * stub 在运行时通过 Platform.OS 和 isRunningInExpoGo() 判断:
286
+ * - Web / Expo Go:no-op(核心 API 弹 Alert 提示,不崩溃)
287
+ * - Development Build(原生):透传真实 expo-calendar,功能完全正常
288
+ *
289
+ * 此 wrapper 需要在开发和生产构建中均生效,因为 native module 缺失是运行时问题。
290
+ */
291
+
292
+ /**
293
+ * @param config Metro config 对象
294
+ * @returns 注入 resolveRequest 后的新 Metro config
295
+ */
296
+ declare function withExpoCalendarStub(config: MetroConfig): MetroConfig;
297
+
280
298
  /**
281
299
  * 将 Metro transformer 的 minifier 切换为 esbuild。
282
300
  *
@@ -302,4 +320,4 @@ declare function withEsbuildMinify(config: MetroConfig): MetroConfig;
302
320
 
303
321
  declare function withWasmSupport(config: MetroConfig): MetroConfig;
304
322
 
305
- export { type DevkitOptions, type InjectOptions, type PatchNativeWindCacheOptions, type RouteEndpointOptions, patchNativeWindCachePath, withCssInterop, withDevStubs, withDevkit, withEntryInjection, withEsbuildMinify, withExpoMediaLibraryStub, withExpoNotificationsStub, withLucideResolver, withNativeWind, withRouteEndpoint, withWasmSupport, withWorkspaceNodeModules };
323
+ export { type DevkitOptions, type InjectOptions, type PatchNativeWindCacheOptions, type RouteEndpointOptions, patchNativeWindCachePath, withCssInterop, withDevStubs, withDevkit, withEntryInjection, withEsbuildMinify, withExpoCalendarStub, withExpoMediaLibraryStub, withExpoNotificationsStub, withLucideResolver, withNativeWind, withRouteEndpoint, withWasmSupport, withWorkspaceNodeModules };
package/dist/metro.js CHANGED
@@ -36,6 +36,7 @@ __export(metro_exports, {
36
36
  withDevkit: () => withDevkit,
37
37
  withEntryInjection: () => withEntryInjection,
38
38
  withEsbuildMinify: () => withEsbuildMinify,
39
+ withExpoCalendarStub: () => withExpoCalendarStub,
39
40
  withExpoMediaLibraryStub: () => withExpoMediaLibraryStub,
40
41
  withExpoNotificationsStub: () => withExpoNotificationsStub,
41
42
  withLucideResolver: () => withLucideResolver,
@@ -285,7 +286,7 @@ function withCssInterop(config) {
285
286
  }
286
287
 
287
288
  // src/metro/withDevkit.ts
288
- var import_path10 = __toESM(require("path"));
289
+ var import_path11 = __toESM(require("path"));
289
290
 
290
291
  // src/metro/withEsbuildMinify.ts
291
292
  function withEsbuildMinify(config) {
@@ -384,6 +385,26 @@ function withExpoMediaLibraryStub(config) {
384
385
  return { ...config, resolver: { ...config.resolver, resolveRequest } };
385
386
  }
386
387
 
388
+ // src/metro/withExpoCalendarStub.ts
389
+ var import_path10 = __toESM(require("path"));
390
+ var EXPO_CALENDAR_STUB_FILENAME = "expo-calendar-stub.js";
391
+ var EXPO_CALENDAR_STUB_PATH = import_path10.default.resolve(
392
+ __dirname,
393
+ "stubs",
394
+ EXPO_CALENDAR_STUB_FILENAME
395
+ );
396
+ function withExpoCalendarStub(config) {
397
+ const upstream = config.resolver?.resolveRequest ?? null;
398
+ const resolveRequest = (context, moduleName, platform) => {
399
+ if (moduleName === "expo-calendar" && !context.originModulePath.includes(EXPO_CALENDAR_STUB_FILENAME)) {
400
+ return { filePath: EXPO_CALENDAR_STUB_PATH, type: "sourceFile" };
401
+ }
402
+ if (upstream) return upstream(context, moduleName, platform);
403
+ return context.resolveRequest(context, moduleName, platform);
404
+ };
405
+ return { ...config, resolver: { ...config.resolver, resolveRequest } };
406
+ }
407
+
387
408
  // src/metro/withWasmSupport.ts
388
409
  function withWasmSupport(config) {
389
410
  const existing = config.resolver?.assetExts ?? [];
@@ -408,10 +429,11 @@ function withDevkit(config, options = {}) {
408
429
  config = withLucideResolver(config);
409
430
  config = withExpoNotificationsStub(config);
410
431
  config = withExpoMediaLibraryStub(config);
432
+ config = withExpoCalendarStub(config);
411
433
  if (typeof __DEV__ !== "undefined" && __DEV__) {
412
434
  config = withEntryInjection(config);
413
435
  config = withDevStubs(config);
414
- config = withRouteEndpoint(config, { appDir: import_path10.default.join(projectRoot, "src", "app") });
436
+ config = withRouteEndpoint(config, { appDir: import_path11.default.join(projectRoot, "src", "app") });
415
437
  }
416
438
  return withNativeWind(config, { input, inlineRem: 16 });
417
439
  }
@@ -431,6 +453,7 @@ try {
431
453
  withDevkit,
432
454
  withEntryInjection,
433
455
  withEsbuildMinify,
456
+ withExpoCalendarStub,
434
457
  withExpoMediaLibraryStub,
435
458
  withExpoNotificationsStub,
436
459
  withLucideResolver,
package/dist/metro.mjs CHANGED
@@ -244,7 +244,7 @@ function withCssInterop(config) {
244
244
  }
245
245
 
246
246
  // src/metro/withDevkit.ts
247
- import path10 from "path";
247
+ import path11 from "path";
248
248
 
249
249
  // src/metro/withEsbuildMinify.ts
250
250
  function withEsbuildMinify(config) {
@@ -343,6 +343,26 @@ function withExpoMediaLibraryStub(config) {
343
343
  return { ...config, resolver: { ...config.resolver, resolveRequest } };
344
344
  }
345
345
 
346
+ // src/metro/withExpoCalendarStub.ts
347
+ import path10 from "path";
348
+ var EXPO_CALENDAR_STUB_FILENAME = "expo-calendar-stub.js";
349
+ var EXPO_CALENDAR_STUB_PATH = path10.resolve(
350
+ __dirname,
351
+ "stubs",
352
+ EXPO_CALENDAR_STUB_FILENAME
353
+ );
354
+ function withExpoCalendarStub(config) {
355
+ const upstream = config.resolver?.resolveRequest ?? null;
356
+ const resolveRequest = (context, moduleName, platform) => {
357
+ if (moduleName === "expo-calendar" && !context.originModulePath.includes(EXPO_CALENDAR_STUB_FILENAME)) {
358
+ return { filePath: EXPO_CALENDAR_STUB_PATH, type: "sourceFile" };
359
+ }
360
+ if (upstream) return upstream(context, moduleName, platform);
361
+ return context.resolveRequest(context, moduleName, platform);
362
+ };
363
+ return { ...config, resolver: { ...config.resolver, resolveRequest } };
364
+ }
365
+
346
366
  // src/metro/withWasmSupport.ts
347
367
  function withWasmSupport(config) {
348
368
  const existing = config.resolver?.assetExts ?? [];
@@ -367,10 +387,11 @@ function withDevkit(config, options = {}) {
367
387
  config = withLucideResolver(config);
368
388
  config = withExpoNotificationsStub(config);
369
389
  config = withExpoMediaLibraryStub(config);
390
+ config = withExpoCalendarStub(config);
370
391
  if (typeof __DEV__ !== "undefined" && __DEV__) {
371
392
  config = withEntryInjection(config);
372
393
  config = withDevStubs(config);
373
- config = withRouteEndpoint(config, { appDir: path10.join(projectRoot, "src", "app") });
394
+ config = withRouteEndpoint(config, { appDir: path11.join(projectRoot, "src", "app") });
374
395
  }
375
396
  return withNativeWind(config, { input, inlineRem: 16 });
376
397
  }
@@ -389,6 +410,7 @@ export {
389
410
  withDevkit,
390
411
  withEntryInjection,
391
412
  withEsbuildMinify,
413
+ withExpoCalendarStub,
392
414
  withExpoMediaLibraryStub,
393
415
  withExpoNotificationsStub,
394
416
  withLucideResolver,
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/rules/no-pressable-without-on-press.ts
21
+ var no_pressable_without_on_press_exports = {};
22
+ __export(no_pressable_without_on_press_exports, {
23
+ default: () => no_pressable_without_on_press_default
24
+ });
25
+ module.exports = __toCommonJS(no_pressable_without_on_press_exports);
26
+ var PRESSABLE_COMPONENTS = /* @__PURE__ */ new Set([
27
+ "Pressable",
28
+ "TouchableOpacity",
29
+ "TouchableHighlight",
30
+ "TouchableNativeFeedback",
31
+ "TouchableWithoutFeedback"
32
+ ]);
33
+ function hasOnPress(attrs) {
34
+ return attrs.some((attr) => {
35
+ if (attr.type !== "JSXAttribute") return false;
36
+ const n = attr.name;
37
+ return n.type === "JSXIdentifier" && n.name === "onPress";
38
+ });
39
+ }
40
+ var noPressableWithoutOnPressRule = {
41
+ meta: {
42
+ type: "problem",
43
+ docs: {
44
+ description: "Pressable-family components must have an onPress handler."
45
+ },
46
+ schema: [],
47
+ messages: {
48
+ missingOnPress: "<{{name}}> is missing onPress. Add: onPress={() => { /* handler */ }}"
49
+ }
50
+ },
51
+ create(context) {
52
+ function check(node) {
53
+ const nameNode = node.name;
54
+ if (nameNode.type !== "JSXIdentifier") return;
55
+ const componentName = nameNode.name;
56
+ if (!componentName || !PRESSABLE_COMPONENTS.has(componentName)) return;
57
+ const attrs = node.attributes;
58
+ if (!hasOnPress(attrs)) {
59
+ context.report({
60
+ node,
61
+ messageId: "missingOnPress",
62
+ data: { name: componentName }
63
+ });
64
+ }
65
+ }
66
+ return {
67
+ JSXOpeningElement: check,
68
+ JSXSelfClosingElement: check
69
+ };
70
+ }
71
+ };
72
+ var plugin = {
73
+ meta: { name: "rn-pressable" },
74
+ rules: { "no-pressable-without-on-press": noPressableWithoutOnPressRule }
75
+ };
76
+ var no_pressable_without_on_press_default = plugin;
77
+ module.exports = module.exports.default;
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ var import_react_native = require("react-native");
3
+ var import_expo = require("expo");
4
+ var import_web_stub_dialog = require("./web-stub-dialog");
5
+ if (import_react_native.Platform.OS !== "web" && !(0, import_expo.isRunningInExpoGo)()) {
6
+ module.exports = require("expo-calendar");
7
+ } else {
8
+ let showCalendarAlert = function(title, lines, isValid, errors) {
9
+ if (import_react_native.Platform.OS === "web") {
10
+ (0, import_web_stub_dialog.showWebStubDialog)({
11
+ title,
12
+ details: lines,
13
+ errors: !isValid && errors && errors.length > 0 ? errors : void 0
14
+ });
15
+ return;
16
+ }
17
+ const statusLine = isValid ? "\u2705 \u53C2\u6570\u5408\u89C4" : "\u274C \u53C2\u6570\u6709\u8BEF";
18
+ const parts = ["\u79D2\u54D2\u626B\u7801\u9884\u89C8\u4E0D\u652F\u6301\u8BBF\u95EE\u624B\u673A\u65E5\u5386", "", ...lines, "", statusLine];
19
+ if (!isValid && errors && errors.length > 0) {
20
+ parts.push(`\u95EE\u9898: ${errors.join(" / ")}`);
21
+ parts.push("");
22
+ parts.push("\u8BF7\u622A\u56FE\u53D1\u7ED9\u79D2\u54D2 Agent \u4FEE\u590D");
23
+ } else {
24
+ parts.push("");
25
+ parts.push("\u53D1\u5E03\u4E3A\u6B63\u5F0F App \u540E\u53EF\u6B63\u5E38\u4F7F\u7528");
26
+ }
27
+ import_react_native.Alert.alert(title, parts.join("\n"), [{ text: "\u77E5\u9053\u4E86" }]);
28
+ }, validateId = function(id, label = "id") {
29
+ const errors = [];
30
+ if (typeof id !== "string" || id.trim().length === 0) {
31
+ errors.push(`${label} \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
32
+ }
33
+ return { ok: errors.length === 0, errors };
34
+ }, validateDate = function(value, label) {
35
+ const errors = [];
36
+ if (value == null) {
37
+ errors.push(`${label} \u4E0D\u80FD\u4E3A\u7A7A`);
38
+ } else if (!(value instanceof Date) && typeof value !== "string") {
39
+ errors.push(`${label} \u5FC5\u987B\u662F Date \u6216\u65E5\u671F\u5B57\u7B26\u4E32`);
40
+ } else if (value instanceof Date && isNaN(value.getTime())) {
41
+ errors.push(`${label} \u662F\u65E0\u6548\u7684 Date \u5BF9\u8C61`);
42
+ }
43
+ return errors;
44
+ }, formatDateArg = function(value) {
45
+ if (value instanceof Date) return value.toLocaleString();
46
+ if (typeof value === "string") return value;
47
+ return String(value);
48
+ }, useCalendarPermissions = function() {
49
+ const [permission, setPermission] = useState(UNDETERMINED_PERMISSION);
50
+ const requestPermission = useCallback(async () => {
51
+ showCalendarAlert(
52
+ "\u8BF7\u6C42\u65E5\u5386\u6743\u9650",
53
+ ["\u64CD\u4F5C: \u8BF7\u6C42\u8BBF\u95EE\u624B\u673A\u65E5\u5386\u6743\u9650", "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u4E0B\u6A21\u62DF\u8FD4\u56DE\u672A\u6388\u6743", "\u53D1\u5E03\u6B63\u5F0F App \u540E\u4F1A\u5F39\u51FA\u7CFB\u7EDF\u6388\u6743\u5F39\u7A97"],
54
+ true
55
+ );
56
+ setPermission(DENIED_PERMISSION);
57
+ return DENIED_PERMISSION;
58
+ }, []);
59
+ const getPermission = useCallback(async () => permission, [permission]);
60
+ return [permission, requestPermission, getPermission];
61
+ }, useRemindersPermissions = function() {
62
+ const [permission, setPermission] = useState(UNDETERMINED_PERMISSION);
63
+ const requestPermission = useCallback(async () => {
64
+ showCalendarAlert(
65
+ "\u8BF7\u6C42\u63D0\u9192\u4E8B\u9879\u6743\u9650",
66
+ ["\u64CD\u4F5C: \u8BF7\u6C42\u8BBF\u95EE\u624B\u673A\u63D0\u9192\u4E8B\u9879\u6743\u9650\uFF08iOS \u4E13\u5C5E\uFF09", "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u4E0B\u6A21\u62DF\u8FD4\u56DE\u672A\u6388\u6743", "\u53D1\u5E03\u6B63\u5F0F App \u540E\u4F1A\u5F39\u51FA\u7CFB\u7EDF\u6388\u6743\u5F39\u7A97"],
67
+ true
68
+ );
69
+ setPermission(DENIED_PERMISSION);
70
+ return DENIED_PERMISSION;
71
+ }, []);
72
+ const getPermission = useCallback(async () => permission, [permission]);
73
+ return [permission, requestPermission, getPermission];
74
+ };
75
+ var showCalendarAlert2 = showCalendarAlert, validateId2 = validateId, validateDate2 = validateDate, formatDateArg2 = formatDateArg, useCalendarPermissions2 = useCalendarPermissions, useRemindersPermissions2 = useRemindersPermissions;
76
+ const UNDETERMINED_PERMISSION = {
77
+ status: "undetermined",
78
+ granted: false,
79
+ canAskAgain: true,
80
+ expires: "never"
81
+ };
82
+ const DENIED_PERMISSION = {
83
+ status: "denied",
84
+ granted: false,
85
+ canAskAgain: false,
86
+ expires: "never"
87
+ };
88
+ const { useState, useCallback } = require("react");
89
+ const requestCalendarPermissionsAsync = async () => {
90
+ showCalendarAlert(
91
+ "\u8BF7\u6C42\u65E5\u5386\u6743\u9650",
92
+ ["\u64CD\u4F5C: \u8BF7\u6C42\u8BBF\u95EE\u624B\u673A\u65E5\u5386\u6743\u9650", "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u4E0B\u6A21\u62DF\u8FD4\u56DE\u672A\u6388\u6743", "\u53D1\u5E03\u6B63\u5F0F App \u540E\u4F1A\u5F39\u51FA\u7CFB\u7EDF\u6388\u6743\u5F39\u7A97"],
93
+ true
94
+ );
95
+ return DENIED_PERMISSION;
96
+ };
97
+ const getCalendarPermissionsAsync = async () => DENIED_PERMISSION;
98
+ const requestRemindersPermissionsAsync = async () => {
99
+ showCalendarAlert(
100
+ "\u8BF7\u6C42\u63D0\u9192\u4E8B\u9879\u6743\u9650",
101
+ ["\u64CD\u4F5C: \u8BF7\u6C42\u8BBF\u95EE\u624B\u673A\u63D0\u9192\u4E8B\u9879\u6743\u9650\uFF08iOS \u4E13\u5C5E\uFF09", "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u4E0B\u6A21\u62DF\u8FD4\u56DE\u672A\u6388\u6743"],
102
+ true
103
+ );
104
+ return DENIED_PERMISSION;
105
+ };
106
+ const getRemindersPermissionsAsync = async () => DENIED_PERMISSION;
107
+ const requestPermissionsAsync = requestCalendarPermissionsAsync;
108
+ const getPermissionsAsync = getCalendarPermissionsAsync;
109
+ const isAvailableAsync = async () => false;
110
+ const getCalendarsAsync = async (entityType) => {
111
+ const typeStr = entityType != null ? String(entityType) : "\u5168\u90E8";
112
+ showCalendarAlert("\u83B7\u53D6\u65E5\u5386\u5217\u8868", [`\u7C7B\u578B: ${typeStr}`, "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u8FD4\u56DE\u7A7A\u5217\u8868"], true);
113
+ return [];
114
+ };
115
+ const getDefaultCalendarAsync = async () => {
116
+ showCalendarAlert("\u83B7\u53D6\u9ED8\u8BA4\u65E5\u5386", ["\u64CD\u4F5C: \u83B7\u53D6\u7CFB\u7EDF\u9ED8\u8BA4\u65E5\u5386", "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u4E0D\u652F\u6301"], false, ["\u65E0\u6CD5\u8BBF\u95EE\u7CFB\u7EDF\u65E5\u5386"]);
117
+ return void 0;
118
+ };
119
+ const createCalendarAsync = async (details) => {
120
+ const errors = [];
121
+ const d = details && typeof details === "object" ? details : {};
122
+ const titleVal = d["title"];
123
+ if (typeof titleVal !== "string" || titleVal.trim().length === 0) {
124
+ errors.push("details.title \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
125
+ }
126
+ const isValid = errors.length === 0;
127
+ const titleDisplay = typeof titleVal === "string" ? titleVal : "(\u672A\u586B\u5199)";
128
+ showCalendarAlert(
129
+ "\u521B\u5EFA\u65E5\u5386",
130
+ [`\u540D\u79F0: ${titleDisplay}`, `\u989C\u8272: ${d["color"] ?? "(\u672A\u8BBE\u7F6E)"}`],
131
+ isValid,
132
+ isValid ? void 0 : errors
133
+ );
134
+ return "stub";
135
+ };
136
+ const updateCalendarAsync = async (id, details) => {
137
+ const { ok: idOk, errors: idErrors } = validateId(id, "calendarId");
138
+ const d = details && typeof details === "object" ? details : {};
139
+ showCalendarAlert(
140
+ "\u66F4\u65B0\u65E5\u5386",
141
+ [
142
+ `\u65E5\u5386 ID: ${typeof id === "string" ? id : String(id)}`,
143
+ `\u66F4\u65B0\u5B57\u6BB5: ${Object.keys(d).join(", ") || "(\u65E0)"}`
144
+ ],
145
+ idOk,
146
+ idOk ? void 0 : idErrors
147
+ );
148
+ return typeof id === "string" ? id : "stub";
149
+ };
150
+ const deleteCalendarAsync = async (id) => {
151
+ const { ok, errors } = validateId(id, "calendarId");
152
+ showCalendarAlert(
153
+ "\u5220\u9664\u65E5\u5386",
154
+ [`\u65E5\u5386 ID: ${typeof id === "string" ? id : String(id)}`],
155
+ ok,
156
+ ok ? void 0 : errors
157
+ );
158
+ };
159
+ const getEventsAsync = async (calendarIds, startDate, endDate) => {
160
+ const errors = [];
161
+ if (!Array.isArray(calendarIds) || calendarIds.length === 0) {
162
+ errors.push("calendarIds \u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4");
163
+ }
164
+ errors.push(...validateDate(startDate, "startDate"));
165
+ errors.push(...validateDate(endDate, "endDate"));
166
+ const isValid = errors.length === 0;
167
+ const idsStr = Array.isArray(calendarIds) ? `[${calendarIds.slice(0, 3).join(", ")}${calendarIds.length > 3 ? "\u2026" : ""}]` : String(calendarIds);
168
+ showCalendarAlert(
169
+ "\u67E5\u8BE2\u65E5\u5386\u4E8B\u4EF6",
170
+ [
171
+ `\u65E5\u5386 IDs: ${idsStr}`,
172
+ `\u5F00\u59CB\u65F6\u95F4: ${formatDateArg(startDate)}`,
173
+ `\u7ED3\u675F\u65F6\u95F4: ${formatDateArg(endDate)}`,
174
+ "\u79D2\u54D2\u9884\u89C8\u6A21\u5F0F\u8FD4\u56DE\u7A7A\u5217\u8868"
175
+ ],
176
+ isValid,
177
+ isValid ? void 0 : errors
178
+ );
179
+ return [];
180
+ };
181
+ const getEventAsync = async (id) => {
182
+ const { ok, errors } = validateId(id, "eventId");
183
+ showCalendarAlert(
184
+ "\u83B7\u53D6\u65E5\u5386\u4E8B\u4EF6",
185
+ [`\u4E8B\u4EF6 ID: ${typeof id === "string" ? id : String(id)}`],
186
+ ok,
187
+ ok ? void 0 : errors
188
+ );
189
+ return void 0;
190
+ };
191
+ const createEventAsync = async (calendarId, eventData) => {
192
+ const errors = [];
193
+ const { errors: idErrors } = validateId(calendarId, "calendarId");
194
+ errors.push(...idErrors);
195
+ const data = eventData && typeof eventData === "object" ? eventData : {};
196
+ if (import_react_native.Platform.OS === "android") {
197
+ errors.push(...validateDate(data["startDate"], "eventData.startDate"));
198
+ errors.push(...validateDate(data["endDate"], "eventData.endDate"));
199
+ }
200
+ const titleVal = data["title"];
201
+ const titleDisplay = typeof titleVal === "string" ? titleVal : "(\u672A\u586B\u5199)";
202
+ const startDisplay = data["startDate"] != null ? formatDateArg(data["startDate"]) : "(\u672A\u8BBE\u7F6E)";
203
+ const endDisplay = data["endDate"] != null ? formatDateArg(data["endDate"]) : "(\u672A\u8BBE\u7F6E)";
204
+ const isValid = errors.length === 0;
205
+ showCalendarAlert(
206
+ "\u521B\u5EFA\u65E5\u5386\u4E8B\u4EF6",
207
+ [
208
+ `\u65E5\u5386 ID: ${typeof calendarId === "string" ? calendarId : String(calendarId)}`,
209
+ `\u6807\u9898: ${titleDisplay}`,
210
+ `\u5F00\u59CB: ${startDisplay}`,
211
+ `\u7ED3\u675F: ${endDisplay}`
212
+ ],
213
+ isValid,
214
+ isValid ? void 0 : errors
215
+ );
216
+ return "stub";
217
+ };
218
+ const updateEventAsync = async (id, details) => {
219
+ const { ok, errors } = validateId(id, "eventId");
220
+ const d = details && typeof details === "object" ? details : {};
221
+ showCalendarAlert(
222
+ "\u66F4\u65B0\u65E5\u5386\u4E8B\u4EF6",
223
+ [
224
+ `\u4E8B\u4EF6 ID: ${typeof id === "string" ? id : String(id)}`,
225
+ `\u66F4\u65B0\u5B57\u6BB5: ${Object.keys(d).join(", ") || "(\u65E0)"}`
226
+ ],
227
+ ok,
228
+ ok ? void 0 : errors
229
+ );
230
+ return typeof id === "string" ? id : "stub";
231
+ };
232
+ const deleteEventAsync = async (id) => {
233
+ const { ok, errors } = validateId(id, "eventId");
234
+ showCalendarAlert(
235
+ "\u5220\u9664\u65E5\u5386\u4E8B\u4EF6",
236
+ [`\u4E8B\u4EF6 ID: ${typeof id === "string" ? id : String(id)}`],
237
+ ok,
238
+ ok ? void 0 : errors
239
+ );
240
+ };
241
+ const enums = {
242
+ EntityTypes: { EVENT: "event", REMINDER: "reminder" },
243
+ Frequency: { DAILY: "daily", WEEKLY: "weekly", MONTHLY: "monthly", YEARLY: "yearly" },
244
+ Availability: {
245
+ NOT_SUPPORTED: "notSupported",
246
+ BUSY: "busy",
247
+ FREE: "free",
248
+ TENTATIVE: "tentative",
249
+ UNAVAILABLE: "unavailable"
250
+ },
251
+ CalendarType: {
252
+ LOCAL: "local",
253
+ CALDAV: "caldav",
254
+ EXCHANGE: "exchange",
255
+ SUBSCRIBED: "subscribed",
256
+ BIRTHDAYS: "birthdays",
257
+ UNKNOWN: "unknown"
258
+ },
259
+ EventStatus: { NONE: "none", CONFIRMED: "confirmed", TENTATIVE: "tentative", CANCELED: "canceled" },
260
+ SourceType: {
261
+ LOCAL: "local",
262
+ EXCHANGE: "exchange",
263
+ CALDAV: "caldav",
264
+ MOBILEME: "mobileme",
265
+ SUBSCRIBED: "subscribed",
266
+ BIRTHDAYS: "birthdays"
267
+ },
268
+ AttendeeRole: {
269
+ UNKNOWN: "unknown",
270
+ REQUIRED: "required",
271
+ OPTIONAL: "optional",
272
+ CHAIR: "chair",
273
+ NON_PARTICIPANT: "nonParticipant",
274
+ ATTENDEE: "attendee",
275
+ ORGANIZER: "organizer",
276
+ PERFORMER: "performer",
277
+ SPEAKER: "speaker",
278
+ NONE: "none"
279
+ },
280
+ AttendeeStatus: {
281
+ UNKNOWN: "unknown",
282
+ PENDING: "pending",
283
+ ACCEPTED: "accepted",
284
+ DECLINED: "declined",
285
+ TENTATIVE: "tentative",
286
+ DELEGATED: "delegated",
287
+ COMPLETED: "completed",
288
+ IN_PROCESS: "inProcess",
289
+ INVITED: "invited",
290
+ NONE: "none"
291
+ },
292
+ AttendeeType: {
293
+ UNKNOWN: "unknown",
294
+ PERSON: "person",
295
+ ROOM: "room",
296
+ GROUP: "group",
297
+ RESOURCE: "resource",
298
+ OPTIONAL: "optional",
299
+ REQUIRED: "required",
300
+ NONE: "none"
301
+ },
302
+ AlarmMethod: { ALARM: "alarm", ALERT: "alert", EMAIL: "email", SMS: "sms", DEFAULT: "default" },
303
+ EventAccessLevel: { CONFIDENTIAL: "confidential", PRIVATE: "private", PUBLIC: "public", DEFAULT: "default" },
304
+ CalendarAccessLevel: {
305
+ CONTRIBUTOR: "contributor",
306
+ EDITOR: "editor",
307
+ FREEBUSY: "freebusy",
308
+ OVERRIDE: "override",
309
+ OWNER: "owner",
310
+ READ: "read",
311
+ RESPOND: "respond",
312
+ ROOT: "root",
313
+ NONE: "none"
314
+ },
315
+ ReminderStatus: { COMPLETED: "completed", INCOMPLETE: "incomplete" },
316
+ PermissionStatus: { GRANTED: "granted", DENIED: "denied", UNDETERMINED: "undetermined" },
317
+ DayOfTheWeek: { SU: 1, MO: 2, TU: 3, WE: 4, TH: 5, FR: 6, SA: 7 },
318
+ MonthOfTheYear: { JAN: 1, FEB: 2, MAR: 3, APR: 4, MAY: 5, JUN: 6, JUL: 7, AUG: 8, SEP: 9, OCT: 10, NOV: 11, DEC: 12 },
319
+ CalendarDialogResultActions: { SAVED: "saved", CANCELED: "canceled", DELETED: "deleted" }
320
+ };
321
+ const coreHandlers = {
322
+ isAvailableAsync,
323
+ useCalendarPermissions,
324
+ useRemindersPermissions,
325
+ requestCalendarPermissionsAsync,
326
+ getCalendarPermissionsAsync,
327
+ requestRemindersPermissionsAsync,
328
+ getRemindersPermissionsAsync,
329
+ requestPermissionsAsync,
330
+ getPermissionsAsync,
331
+ getCalendarsAsync,
332
+ getDefaultCalendarAsync,
333
+ createCalendarAsync,
334
+ updateCalendarAsync,
335
+ deleteCalendarAsync,
336
+ getEventsAsync,
337
+ getEventAsync,
338
+ createEventAsync,
339
+ updateEventAsync,
340
+ deleteEventAsync
341
+ };
342
+ const noopPermission = async () => DENIED_PERMISSION;
343
+ const noop = async () => void 0;
344
+ module.exports = new Proxy(enums, {
345
+ get(target, key) {
346
+ if (key in target) return target[key];
347
+ if (key in coreHandlers) return coreHandlers[key];
348
+ if (key.endsWith("PermissionsAsync")) return noopPermission;
349
+ return noop;
350
+ }
351
+ });
352
+ }
353
+ //# sourceMappingURL=expo-calendar-stub.js.map
@@ -8,7 +8,8 @@
8
8
  { "name": "expo-router-url", "specifier": "miaoda-expo-devkit/rules/no-duplicate-expo-router-url" },
9
9
  { "name": "css-import", "specifier": "miaoda-expo-devkit/rules/no-missing-css-import" },
10
10
  { "name": "expo-router-layout", "specifier": "miaoda-expo-devkit/rules/no-invalid-tabs-screen" },
11
- { "name": "expo-router-dynamic-tab", "specifier": "miaoda-expo-devkit/rules/no-unregistered-dynamic-tab-route" }
11
+ { "name": "expo-router-dynamic-tab", "specifier": "miaoda-expo-devkit/rules/no-unregistered-dynamic-tab-route" },
12
+ { "name": "rn-pressable", "specifier": "miaoda-expo-devkit/rules/no-pressable-without-on-press" }
12
13
  ],
13
14
 
14
15
  "categories": {
@@ -23,6 +24,7 @@
23
24
  "css-import/no-missing-css-import": "error",
24
25
  "expo-router-layout/no-invalid-tabs-screen": "error",
25
26
  "expo-router-dynamic-tab/no-unregistered-dynamic-tab-route": "error",
27
+ "rn-pressable/no-pressable-without-on-press": "error",
26
28
 
27
29
  "expo/no-dynamic-env-var": "error",
28
30
  "expo/no-env-var-destructuring": "error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miaoda-expo-devkit",
3
- "version": "0.1.1-beta.38",
3
+ "version": "0.1.1-beta.39",
4
4
  "description": "Expo 应用开发工具集:Sentry DSN 替换 stub、错误/网络捕获、Metro 符号化",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -46,6 +46,7 @@
46
46
  "./no-op-logbox": "./dist/stubs/no-op-logbox.js",
47
47
  "./expo-notifications-stub": "./dist/stubs/expo-notifications-stub.js",
48
48
  "./expo-media-library-stub": "./dist/stubs/expo-media-library-stub.js",
49
+ "./expo-calendar-stub": "./dist/stubs/expo-calendar-stub.js",
49
50
  "./rules/no-undeclared-expo-plugin": "./dist/rules/no-undeclared-expo-plugin.js",
50
51
  "./rules/no-unused-expo-plugin": "./dist/rules/no-unused-expo-plugin.js",
51
52
  "./rules/no-unstable-expo-router": "./dist/rules/no-unstable-expo-router.js",