miaoda-expo-devkit 0.1.1-beta.45 → 0.1.1-beta.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/metro.d.mts CHANGED
@@ -339,22 +339,46 @@ declare function withEsbuildMinify(config: MetroConfig): MetroConfig;
339
339
  declare function withWasmSupport(config: MetroConfig): MetroConfig;
340
340
 
341
341
  /**
342
- * withTransformLogger — Metro 单文件 transform 耗时日志
342
+ * withTransformLogger — Metro bundle 整体耗时日志 + 单文件 transform 耗时日志
343
343
  *
344
- * 通过监听 metro-core Logger 的 'log' 事件,将每个文件的变换耗时写入 JSONL 文件。
344
+ * 写入两类 JSONL 条目:
345
+ *
346
+ * 1. BUNDLING_REQUEST — 每次完整 bundle 请求写一条(通过 unstable_perfLoggerFactory)
347
+ * {
348
+ * "type": "BUNDLING_REQUEST",
349
+ * "status": "SUCCESS", // SUCCESS | FAIL | CANCEL
350
+ * "duration_ms": 1234,
351
+ * "bundle_url": "index.bundle",
352
+ * "initial_build": true, // true=首次构建 false=增量更新
353
+ * "graph_node_count": 220,
354
+ * "bundle_length": 512000,
355
+ * "points": {
356
+ * "resolvingAndTransformingDependencies_start": 5,
357
+ * "resolvingAndTransformingDependencies_end": 980,
358
+ * "serializingBundle_start": 981,
359
+ * "serializingBundle_end": 1200
360
+ * },
361
+ * "t": 1234567890
362
+ * }
363
+ *
364
+ * 2. TRANSFORM_FILE — 每个 cache miss 的文件写一条(通过 metro-core Logger)
365
+ * {
366
+ * "type": "TRANSFORM_FILE",
367
+ * "file": "src/components/Button.tsx",
368
+ * "duration_ms": 45,
369
+ * "t": 1234567890
370
+ * }
371
+ * 注意:缓存命中的文件不会触发此条目。
345
372
  *
346
373
  * 激活条件(两者同时满足):
347
374
  * 1. 环境变量 METRO_TRANSFORM_LOG 设置为目标文件路径
348
375
  * 2. 当前处于开发模式(__DEV__ === true),expo export 生产构建时不记录
349
- *
350
- * 每行格式:
351
- * {"phase":"start","file":"...","duration_ms":null,"t":1234567890}
352
- * {"phase":"end","file":"...","duration_ms":42,"t":1234567890}
353
376
  */
354
377
 
355
378
  /**
356
- * 在开发模式下,若 METRO_TRANSFORM_LOG 已设置,则安装 transform 耗时日志监听器。
357
- * 该函数调用时即生效(挂监听器),Metro config 本身无需修改,返回原对象。
379
+ * 在开发模式下,若 METRO_TRANSFORM_LOG 已设置,则注入:
380
+ * - unstable_perfLoggerFactory(整体 bundle 耗时)
381
+ * - metro-core Logger 监听器(单文件 transform 耗时,仅 cache miss)
358
382
  */
359
383
  declare function withTransformLogger(config: MetroConfig): MetroConfig;
360
384
 
package/dist/metro.d.ts CHANGED
@@ -339,22 +339,46 @@ declare function withEsbuildMinify(config: MetroConfig): MetroConfig;
339
339
  declare function withWasmSupport(config: MetroConfig): MetroConfig;
340
340
 
341
341
  /**
342
- * withTransformLogger — Metro 单文件 transform 耗时日志
342
+ * withTransformLogger — Metro bundle 整体耗时日志 + 单文件 transform 耗时日志
343
343
  *
344
- * 通过监听 metro-core Logger 的 'log' 事件,将每个文件的变换耗时写入 JSONL 文件。
344
+ * 写入两类 JSONL 条目:
345
+ *
346
+ * 1. BUNDLING_REQUEST — 每次完整 bundle 请求写一条(通过 unstable_perfLoggerFactory)
347
+ * {
348
+ * "type": "BUNDLING_REQUEST",
349
+ * "status": "SUCCESS", // SUCCESS | FAIL | CANCEL
350
+ * "duration_ms": 1234,
351
+ * "bundle_url": "index.bundle",
352
+ * "initial_build": true, // true=首次构建 false=增量更新
353
+ * "graph_node_count": 220,
354
+ * "bundle_length": 512000,
355
+ * "points": {
356
+ * "resolvingAndTransformingDependencies_start": 5,
357
+ * "resolvingAndTransformingDependencies_end": 980,
358
+ * "serializingBundle_start": 981,
359
+ * "serializingBundle_end": 1200
360
+ * },
361
+ * "t": 1234567890
362
+ * }
363
+ *
364
+ * 2. TRANSFORM_FILE — 每个 cache miss 的文件写一条(通过 metro-core Logger)
365
+ * {
366
+ * "type": "TRANSFORM_FILE",
367
+ * "file": "src/components/Button.tsx",
368
+ * "duration_ms": 45,
369
+ * "t": 1234567890
370
+ * }
371
+ * 注意:缓存命中的文件不会触发此条目。
345
372
  *
346
373
  * 激活条件(两者同时满足):
347
374
  * 1. 环境变量 METRO_TRANSFORM_LOG 设置为目标文件路径
348
375
  * 2. 当前处于开发模式(__DEV__ === true),expo export 生产构建时不记录
349
- *
350
- * 每行格式:
351
- * {"phase":"start","file":"...","duration_ms":null,"t":1234567890}
352
- * {"phase":"end","file":"...","duration_ms":42,"t":1234567890}
353
376
  */
354
377
 
355
378
  /**
356
- * 在开发模式下,若 METRO_TRANSFORM_LOG 已设置,则安装 transform 耗时日志监听器。
357
- * 该函数调用时即生效(挂监听器),Metro config 本身无需修改,返回原对象。
379
+ * 在开发模式下,若 METRO_TRANSFORM_LOG 已设置,则注入:
380
+ * - unstable_perfLoggerFactory(整体 bundle 耗时)
381
+ * - metro-core Logger 监听器(单文件 transform 耗时,仅 cache miss)
358
382
  */
359
383
  declare function withTransformLogger(config: MetroConfig): MetroConfig;
360
384
 
package/dist/metro.js CHANGED
@@ -443,22 +443,68 @@ function withWasmSupport(config) {
443
443
  // src/metro/withTransformLogger.ts
444
444
  var import_node_fs = __toESM(require("fs"));
445
445
  var import_node_path = __toESM(require("path"));
446
- function installLogger(logFile) {
447
- const { Logger } = require("metro-core");
446
+ var import_metro_core = require("metro-core");
447
+ function installPerfLogger(logFile, existingFactory) {
448
448
  const dir = import_node_path.default.dirname(logFile);
449
449
  if (!import_node_fs.default.existsSync(dir)) {
450
450
  import_node_fs.default.mkdirSync(dir, { recursive: true });
451
451
  }
452
452
  import_node_fs.default.writeFileSync(logFile, "");
453
- Logger.on("log", (entry) => {
454
- if (entry["action_name"] !== "Transforming file") return;
455
- const line = JSON.stringify({
456
- phase: entry["action_phase"],
457
- file: entry["file_name"],
458
- duration_ms: entry["duration_ms"] ?? null,
459
- t: Date.now()
460
- });
461
- import_node_fs.default.appendFileSync(logFile, line + "\n");
453
+ return (type, opts) => {
454
+ const upstream = existingFactory?.(type, opts) ?? null;
455
+ const startTime = process.hrtime.bigint();
456
+ const points = {};
457
+ const annotations = {};
458
+ const logger = {
459
+ start() {
460
+ upstream?.start?.();
461
+ },
462
+ end(status) {
463
+ const duration_ms = Number(process.hrtime.bigint() - startTime) / 1e6;
464
+ const line = JSON.stringify({
465
+ type,
466
+ status,
467
+ duration_ms: Math.round(duration_ms),
468
+ ...annotations,
469
+ points,
470
+ t: Date.now()
471
+ });
472
+ import_node_fs.default.appendFileSync(logFile, line + "\n");
473
+ upstream?.end?.(status);
474
+ },
475
+ annotate(ann) {
476
+ for (const [, kv] of Object.entries(ann)) {
477
+ if (kv && typeof kv === "object") {
478
+ Object.assign(annotations, kv);
479
+ }
480
+ }
481
+ upstream?.annotate?.(ann);
482
+ },
483
+ point(name) {
484
+ points[name] = Math.round(Number(process.hrtime.bigint() - startTime) / 1e6);
485
+ upstream?.point?.(name);
486
+ },
487
+ subSpan(label) {
488
+ return upstream?.subSpan?.(label) ?? logger;
489
+ }
490
+ };
491
+ return logger;
492
+ };
493
+ }
494
+ var fileLoggerInstalled = false;
495
+ function installFileLogger(logFile) {
496
+ if (fileLoggerInstalled) return;
497
+ fileLoggerInstalled = true;
498
+ import_metro_core.Logger.on("log", (entry) => {
499
+ if (entry.action_name === "Transforming file" && entry.action_phase === "end") {
500
+ const line = JSON.stringify({
501
+ type: "TRANSFORM_FILE",
502
+ file: entry.file_name,
503
+ duration_ms: entry.duration_ms,
504
+ t: Date.now()
505
+ });
506
+ import_node_fs.default.appendFileSync(logFile, line + "\n");
507
+ }
462
508
  });
463
509
  }
464
510
  function withTransformLogger(config) {
@@ -466,8 +512,11 @@ function withTransformLogger(config) {
466
512
  if (!logFile) return config;
467
513
  const isDev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env["NODE_ENV"] !== "production";
468
514
  if (!isDev) return config;
469
- installLogger(logFile);
470
- return config;
515
+ installFileLogger(logFile);
516
+ return {
517
+ ...config,
518
+ unstable_perfLoggerFactory: installPerfLogger(logFile, config.unstable_perfLoggerFactory)
519
+ };
471
520
  }
472
521
 
473
522
  // src/metro/withDevkit.ts
package/dist/metro.mjs CHANGED
@@ -399,22 +399,68 @@ function withWasmSupport(config) {
399
399
  // src/metro/withTransformLogger.ts
400
400
  import fs4 from "fs";
401
401
  import path12 from "path";
402
- function installLogger(logFile) {
403
- const { Logger } = __require("metro-core");
402
+ import { Logger } from "metro-core";
403
+ function installPerfLogger(logFile, existingFactory) {
404
404
  const dir = path12.dirname(logFile);
405
405
  if (!fs4.existsSync(dir)) {
406
406
  fs4.mkdirSync(dir, { recursive: true });
407
407
  }
408
408
  fs4.writeFileSync(logFile, "");
409
+ return (type, opts) => {
410
+ const upstream = existingFactory?.(type, opts) ?? null;
411
+ const startTime = process.hrtime.bigint();
412
+ const points = {};
413
+ const annotations = {};
414
+ const logger = {
415
+ start() {
416
+ upstream?.start?.();
417
+ },
418
+ end(status) {
419
+ const duration_ms = Number(process.hrtime.bigint() - startTime) / 1e6;
420
+ const line = JSON.stringify({
421
+ type,
422
+ status,
423
+ duration_ms: Math.round(duration_ms),
424
+ ...annotations,
425
+ points,
426
+ t: Date.now()
427
+ });
428
+ fs4.appendFileSync(logFile, line + "\n");
429
+ upstream?.end?.(status);
430
+ },
431
+ annotate(ann) {
432
+ for (const [, kv] of Object.entries(ann)) {
433
+ if (kv && typeof kv === "object") {
434
+ Object.assign(annotations, kv);
435
+ }
436
+ }
437
+ upstream?.annotate?.(ann);
438
+ },
439
+ point(name) {
440
+ points[name] = Math.round(Number(process.hrtime.bigint() - startTime) / 1e6);
441
+ upstream?.point?.(name);
442
+ },
443
+ subSpan(label) {
444
+ return upstream?.subSpan?.(label) ?? logger;
445
+ }
446
+ };
447
+ return logger;
448
+ };
449
+ }
450
+ var fileLoggerInstalled = false;
451
+ function installFileLogger(logFile) {
452
+ if (fileLoggerInstalled) return;
453
+ fileLoggerInstalled = true;
409
454
  Logger.on("log", (entry) => {
410
- if (entry["action_name"] !== "Transforming file") return;
411
- const line = JSON.stringify({
412
- phase: entry["action_phase"],
413
- file: entry["file_name"],
414
- duration_ms: entry["duration_ms"] ?? null,
415
- t: Date.now()
416
- });
417
- fs4.appendFileSync(logFile, line + "\n");
455
+ if (entry.action_name === "Transforming file" && entry.action_phase === "end") {
456
+ const line = JSON.stringify({
457
+ type: "TRANSFORM_FILE",
458
+ file: entry.file_name,
459
+ duration_ms: entry.duration_ms,
460
+ t: Date.now()
461
+ });
462
+ fs4.appendFileSync(logFile, line + "\n");
463
+ }
418
464
  });
419
465
  }
420
466
  function withTransformLogger(config) {
@@ -422,8 +468,11 @@ function withTransformLogger(config) {
422
468
  if (!logFile) return config;
423
469
  const isDev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env["NODE_ENV"] !== "production";
424
470
  if (!isDev) return config;
425
- installLogger(logFile);
426
- return config;
471
+ installFileLogger(logFile);
472
+ return {
473
+ ...config,
474
+ unstable_perfLoggerFactory: installPerfLogger(logFile, config.unstable_perfLoggerFactory)
475
+ };
427
476
  }
428
477
 
429
478
  // src/metro/withDevkit.ts
@@ -0,0 +1,56 @@
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-inline-box-shadow-string.ts
21
+ var no_inline_box_shadow_string_exports = {};
22
+ __export(no_inline_box_shadow_string_exports, {
23
+ default: () => no_inline_box_shadow_string_default
24
+ });
25
+ module.exports = __toCommonJS(no_inline_box_shadow_string_exports);
26
+ var noInlineBoxShadowStringRule = {
27
+ meta: {
28
+ type: "problem",
29
+ docs: {
30
+ description: "Disallow boxShadow string in inline style objects. NativeWind drops all styles including backgroundColor on Android when a function-style prop contains a boxShadow string."
31
+ },
32
+ schema: [],
33
+ messages: {
34
+ noInlineBoxShadowString: 'boxShadow string in inline style is not supported by NativeWind on Android \u2014 it causes backgroundColor and other styles to be lost. Use the object form instead: boxShadow: [{ offsetX: 0, offsetY: 4, blurRadius: 12, color: "rgba(33,150,243,0.35)" }], or replace with elevation + shadowColor for Android.'
35
+ }
36
+ },
37
+ create(context) {
38
+ return {
39
+ // 匹配所有属性键为 boxShadow 且值为字符串字面量的节点。
40
+ // 覆盖两种写法:
41
+ // style={{ boxShadow: '...' }}
42
+ // style={({ pressed }) => ({ boxShadow: '...' })}
43
+ Property(node) {
44
+ if (node.key.type === "Identifier" && node.key.name === "boxShadow" && node.value.type === "Literal" && typeof node.value.value === "string") {
45
+ context.report({ node, messageId: "noInlineBoxShadowString" });
46
+ }
47
+ }
48
+ };
49
+ }
50
+ };
51
+ var plugin = {
52
+ meta: { name: "no-inline-box-shadow-string" },
53
+ rules: { "no-inline-box-shadow-string": noInlineBoxShadowStringRule }
54
+ };
55
+ var no_inline_box_shadow_string_default = plugin;
56
+ module.exports = module.exports.default;
@@ -10,7 +10,8 @@
10
10
  { "name": "expo-router-layout", "specifier": "miaoda-expo-devkit/rules/no-invalid-tabs-screen" },
11
11
  { "name": "expo-router-dynamic-tab", "specifier": "miaoda-expo-devkit/rules/no-unregistered-dynamic-tab-route" },
12
12
  { "name": "rn-pressable", "specifier": "miaoda-expo-devkit/rules/no-pressable-without-on-press" },
13
- { "name": "expo-video-compat", "specifier": "miaoda-expo-devkit/rules/no-expo-video-compat" }
13
+ { "name": "expo-video-compat", "specifier": "miaoda-expo-devkit/rules/no-expo-video-compat" },
14
+ { "name": "rn-style", "specifier": "miaoda-expo-devkit/rules/no-inline-box-shadow-string" }
14
15
  ],
15
16
 
16
17
  "categories": {
@@ -27,6 +28,7 @@
27
28
  "expo-router-dynamic-tab/no-unregistered-dynamic-tab-route": "error",
28
29
  "rn-pressable/no-pressable-without-on-press": "error",
29
30
  "expo-video-compat/no-expo-video-compat": "warn",
31
+ "rn-style/no-inline-box-shadow-string": "error",
30
32
 
31
33
  "expo/no-dynamic-env-var": "error",
32
34
  "expo/no-env-var-destructuring": "error",
@@ -48,7 +50,8 @@
48
50
  "metro.config.js",
49
51
  "babel.config.js",
50
52
  ".pnpmfile.cjs",
51
- "output/**"
53
+ "output/**",
54
+ "supabase/**"
52
55
  ],
53
56
 
54
57
  "overrides": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miaoda-expo-devkit",
3
- "version": "0.1.1-beta.45",
3
+ "version": "0.1.1-beta.47",
4
4
  "description": "Expo 应用开发工具集:Sentry DSN 替换 stub、错误/网络捕获、Metro 符号化",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -58,6 +58,7 @@
58
58
  "./rules/no-expo-audio-compat": "./dist/rules/no-expo-audio-compat.js",
59
59
  "./rules/no-pressable-without-on-press": "./dist/rules/no-pressable-without-on-press.js",
60
60
  "./rules/no-expo-video-compat": "./dist/rules/no-expo-video-compat.js",
61
+ "./rules/no-inline-box-shadow-string": "./dist/rules/no-inline-box-shadow-string.js",
61
62
  "./biome": "./biome-config.json",
62
63
  "./oxlint": "./oxlint-config.json",
63
64
  "./tsconfig-base": "./tsconfig-base.json"