@linhey/react-debug-inspector 1.2.1 → 1.2.3

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
@@ -48,23 +48,33 @@ npm install @linhey/react-debug-inspector --save-dev
48
48
 
49
49
  ### 1. 配置 Vite (vite.config.ts)
50
50
 
51
+ 推荐在 Vite 8+ 使用官方导出的插件工厂,确保 `data-debug` 注入稳定:
52
+
51
53
  ```typescript
52
54
  import { defineConfig } from 'vite';
53
55
  import react from '@vitejs/plugin-react';
54
- import debugInspector from '@linhey/react-debug-inspector';
56
+ import { createViteDebugInspectorPlugin } from '@linhey/react-debug-inspector';
55
57
 
56
58
  export default defineConfig({
57
59
  plugins: [
58
- react({
59
- babel: {
60
- // 仅在开发环境下启用注入
61
- plugins: process.env.NODE_ENV === 'development' ? [debugInspector] : []
62
- }
63
- }),
60
+ createViteDebugInspectorPlugin(),
61
+ react(),
64
62
  ],
65
63
  });
66
64
  ```
67
65
 
66
+ 如果你仍在旧链路中使用 Babel 直接注入,也可以保留:
67
+
68
+ ```typescript
69
+ import debugInspector from '@linhey/react-debug-inspector';
70
+
71
+ react({
72
+ babel: {
73
+ plugins: [debugInspector],
74
+ }
75
+ })
76
+ ```
77
+
68
78
  ### 2. 初始化交互界面 (main.tsx)
69
79
 
70
80
  ```typescript
package/dist/index.d.mts CHANGED
@@ -9,4 +9,16 @@ declare function babelPluginDebugLabel(): {
9
9
  */
10
10
  declare function initInspector(): void;
11
11
 
12
- export { babelPluginDebugLabel as default, initInspector };
12
+ type TransformResult = {
13
+ code: string;
14
+ map: object | null;
15
+ };
16
+ type ViteCompatiblePlugin = {
17
+ name: string;
18
+ apply: 'serve';
19
+ enforce: 'pre';
20
+ transform: (code: string, id: string) => Promise<TransformResult | null>;
21
+ };
22
+ declare function createViteDebugInspectorPlugin(): ViteCompatiblePlugin;
23
+
24
+ export { createViteDebugInspectorPlugin, babelPluginDebugLabel as default, initInspector };
package/dist/index.d.ts CHANGED
@@ -9,4 +9,16 @@ declare function babelPluginDebugLabel(): {
9
9
  */
10
10
  declare function initInspector(): void;
11
11
 
12
- export { babelPluginDebugLabel as default, initInspector };
12
+ type TransformResult = {
13
+ code: string;
14
+ map: object | null;
15
+ };
16
+ type ViteCompatiblePlugin = {
17
+ name: string;
18
+ apply: 'serve';
19
+ enforce: 'pre';
20
+ transform: (code: string, id: string) => Promise<TransformResult | null>;
21
+ };
22
+ declare function createViteDebugInspectorPlugin(): ViteCompatiblePlugin;
23
+
24
+ export { createViteDebugInspectorPlugin, babelPluginDebugLabel as default, initInspector };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,11 +17,20 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ createViteDebugInspectorPlugin: () => createViteDebugInspectorPlugin,
23
34
  default: () => index_default,
24
35
  initInspector: () => initInspector
25
36
  });
@@ -102,6 +113,7 @@ function initInspector() {
102
113
  let latestHoverEvent = null;
103
114
  let pendingHoverFrame = false;
104
115
  let anchorUpdatePending = false;
116
+ let selectionLocked = false;
105
117
  const edgeOffset = 24;
106
118
  const successColor = "#10b981";
107
119
  const defaultOverlayBg = "rgba(14, 165, 233, 0.15)";
@@ -271,6 +283,11 @@ function initInspector() {
271
283
  event.stopImmediatePropagation?.();
272
284
  }
273
285
  };
286
+ const isInspectorChromeTarget = (target) => {
287
+ if (!target) return false;
288
+ if (target === toggleBtn || target === overlay || target === tooltip || target === actionMenu) return true;
289
+ return !!target.closest('[data-inspector-ignore="true"]');
290
+ };
274
291
  const extractTextContent = (target) => {
275
292
  const ariaLabel = normalizeText(target.getAttribute("aria-label") || "");
276
293
  if (ariaLabel) return truncateText(ariaLabel);
@@ -410,6 +427,30 @@ function initInspector() {
410
427
  if (!actionMenu) return;
411
428
  actionMenu.style.display = "none";
412
429
  };
430
+ const finalizeSelection = (debugId) => {
431
+ selectionLocked = true;
432
+ copyText(debugId).then(() => {
433
+ showCopyFeedback("Copied!", "success");
434
+ win.setTimeout(() => {
435
+ selectionLocked = false;
436
+ stopInspecting();
437
+ }, 600);
438
+ }).catch(() => {
439
+ selectionLocked = false;
440
+ });
441
+ };
442
+ const selectDebugTarget = (target) => {
443
+ if (selectionLocked) return;
444
+ const debugEl = target?.closest("[data-debug]");
445
+ if (!debugEl) {
446
+ selectionLocked = false;
447
+ stopInspecting();
448
+ return;
449
+ }
450
+ const debugId = debugEl.getAttribute("data-debug");
451
+ if (!debugId) return;
452
+ finalizeSelection(debugId);
453
+ };
413
454
  const copyText = async (value) => {
414
455
  await navigator.clipboard.writeText(value);
415
456
  };
@@ -464,7 +505,7 @@ function initInspector() {
464
505
  if (!isInspecting || !overlay || !tooltip) return;
465
506
  const target = event.target;
466
507
  if (!target) return;
467
- if (target === toggleBtn || target === overlay || target === tooltip || target.closest('[data-inspector-ignore="true"]')) {
508
+ if (isInspectorChromeTarget(target)) {
468
509
  return;
469
510
  }
470
511
  const debugEl = target.closest("[data-debug]");
@@ -498,6 +539,7 @@ function initInspector() {
498
539
  };
499
540
  const stopInspecting = () => {
500
541
  isInspecting = false;
542
+ selectionLocked = false;
501
543
  latestContext = null;
502
544
  lastHoveredDebugEl = null;
503
545
  toggleBtn.style.transform = "scale(1)";
@@ -617,25 +659,32 @@ function initInspector() {
617
659
  inspectByPointer(latestHoverEvent);
618
660
  });
619
661
  };
662
+ const handlePointerSuppression = (event) => {
663
+ if (!isInspecting) return;
664
+ const target = event.target;
665
+ if (!target || isInspectorChromeTarget(target)) return;
666
+ if (selectionLocked) {
667
+ suppressEvent(event, { preventDefault: true, immediate: true });
668
+ return;
669
+ }
670
+ const isTouchSelectionEvent = event.type === "touchend" || event.type === "pointerup" && "pointerType" in event && event.pointerType !== "mouse";
671
+ suppressEvent(event, { preventDefault: true, immediate: true });
672
+ if (isTouchSelectionEvent) {
673
+ selectDebugTarget(target);
674
+ }
675
+ };
620
676
  const handleWindowClick = (event) => {
621
677
  if (!isInspecting) return;
622
678
  const target = event.target;
623
679
  if (!target || target === toggleBtn) return;
624
- if (target.closest('[data-inspector-ignore="true"]')) {
680
+ if (isInspectorChromeTarget(target)) {
625
681
  return;
626
682
  }
627
683
  suppressEvent(event, { preventDefault: true, immediate: true });
628
- const debugEl = target.closest("[data-debug]");
629
- if (!debugEl) {
630
- stopInspecting();
684
+ if (selectionLocked) {
631
685
  return;
632
686
  }
633
- const debugId = debugEl.getAttribute("data-debug");
634
- if (!debugId) return;
635
- copyText(debugId).then(() => {
636
- showCopyFeedback("Copied!", "success");
637
- win.setTimeout(stopInspecting, 600);
638
- });
687
+ selectDebugTarget(target);
639
688
  };
640
689
  const handleKeyDown = (event) => {
641
690
  if (event.key === "Escape" && isInspecting) stopInspecting();
@@ -653,6 +702,12 @@ function initInspector() {
653
702
  win.addEventListener("resize", scheduleAnchorUpdate);
654
703
  win.addEventListener("scroll", scheduleAnchorUpdate, true);
655
704
  doc.addEventListener("mousemove", handleMouseMove);
705
+ win.addEventListener("pointerdown", handlePointerSuppression, { capture: true });
706
+ win.addEventListener("pointerup", handlePointerSuppression, { capture: true });
707
+ win.addEventListener("mousedown", handlePointerSuppression, { capture: true });
708
+ win.addEventListener("mouseup", handlePointerSuppression, { capture: true });
709
+ win.addEventListener("touchstart", handlePointerSuppression, { capture: true });
710
+ win.addEventListener("touchend", handlePointerSuppression, { capture: true });
656
711
  win.addEventListener("click", handleWindowClick, { capture: true });
657
712
  win.addEventListener("keydown", handleKeyDown);
658
713
  updateAnchorForDialogs();
@@ -662,6 +717,12 @@ function initInspector() {
662
717
  win.removeEventListener("resize", scheduleAnchorUpdate);
663
718
  win.removeEventListener("scroll", scheduleAnchorUpdate, true);
664
719
  doc.removeEventListener("mousemove", handleMouseMove);
720
+ win.removeEventListener("pointerdown", handlePointerSuppression, { capture: true });
721
+ win.removeEventListener("pointerup", handlePointerSuppression, { capture: true });
722
+ win.removeEventListener("mousedown", handlePointerSuppression, { capture: true });
723
+ win.removeEventListener("mouseup", handlePointerSuppression, { capture: true });
724
+ win.removeEventListener("touchstart", handlePointerSuppression, { capture: true });
725
+ win.removeEventListener("touchend", handlePointerSuppression, { capture: true });
665
726
  win.removeEventListener("click", handleWindowClick, { capture: true });
666
727
  win.removeEventListener("keydown", handleKeyDown);
667
728
  for (const eventName of shieldedEvents) {
@@ -676,9 +737,39 @@ function initInspector() {
676
737
  };
677
738
  }
678
739
 
740
+ // src/vite-plugin.ts
741
+ function createViteDebugInspectorPlugin() {
742
+ return {
743
+ name: "react-debug-inspector-transform",
744
+ apply: "serve",
745
+ enforce: "pre",
746
+ async transform(code, id) {
747
+ if (id.includes("node_modules")) return null;
748
+ if (!/\.[jt]sx($|\?)/.test(id)) return null;
749
+ const { transformAsync } = await import("@babel/core");
750
+ const result = await transformAsync(code, {
751
+ filename: id,
752
+ babelrc: false,
753
+ configFile: false,
754
+ plugins: [babelPluginDebugLabel()],
755
+ parserOpts: {
756
+ plugins: ["jsx", "typescript"]
757
+ },
758
+ sourceMaps: true
759
+ });
760
+ if (!result?.code) return null;
761
+ return {
762
+ code: result.code,
763
+ map: result.map ?? null
764
+ };
765
+ }
766
+ };
767
+ }
768
+
679
769
  // src/index.ts
680
770
  var index_default = babelPluginDebugLabel;
681
771
  // Annotate the CommonJS export names for ESM import in node:
682
772
  0 && (module.exports = {
773
+ createViteDebugInspectorPlugin,
683
774
  initInspector
684
775
  });
package/dist/index.mjs CHANGED
@@ -75,6 +75,7 @@ function initInspector() {
75
75
  let latestHoverEvent = null;
76
76
  let pendingHoverFrame = false;
77
77
  let anchorUpdatePending = false;
78
+ let selectionLocked = false;
78
79
  const edgeOffset = 24;
79
80
  const successColor = "#10b981";
80
81
  const defaultOverlayBg = "rgba(14, 165, 233, 0.15)";
@@ -244,6 +245,11 @@ function initInspector() {
244
245
  event.stopImmediatePropagation?.();
245
246
  }
246
247
  };
248
+ const isInspectorChromeTarget = (target) => {
249
+ if (!target) return false;
250
+ if (target === toggleBtn || target === overlay || target === tooltip || target === actionMenu) return true;
251
+ return !!target.closest('[data-inspector-ignore="true"]');
252
+ };
247
253
  const extractTextContent = (target) => {
248
254
  const ariaLabel = normalizeText(target.getAttribute("aria-label") || "");
249
255
  if (ariaLabel) return truncateText(ariaLabel);
@@ -383,6 +389,30 @@ function initInspector() {
383
389
  if (!actionMenu) return;
384
390
  actionMenu.style.display = "none";
385
391
  };
392
+ const finalizeSelection = (debugId) => {
393
+ selectionLocked = true;
394
+ copyText(debugId).then(() => {
395
+ showCopyFeedback("Copied!", "success");
396
+ win.setTimeout(() => {
397
+ selectionLocked = false;
398
+ stopInspecting();
399
+ }, 600);
400
+ }).catch(() => {
401
+ selectionLocked = false;
402
+ });
403
+ };
404
+ const selectDebugTarget = (target) => {
405
+ if (selectionLocked) return;
406
+ const debugEl = target?.closest("[data-debug]");
407
+ if (!debugEl) {
408
+ selectionLocked = false;
409
+ stopInspecting();
410
+ return;
411
+ }
412
+ const debugId = debugEl.getAttribute("data-debug");
413
+ if (!debugId) return;
414
+ finalizeSelection(debugId);
415
+ };
386
416
  const copyText = async (value) => {
387
417
  await navigator.clipboard.writeText(value);
388
418
  };
@@ -437,7 +467,7 @@ function initInspector() {
437
467
  if (!isInspecting || !overlay || !tooltip) return;
438
468
  const target = event.target;
439
469
  if (!target) return;
440
- if (target === toggleBtn || target === overlay || target === tooltip || target.closest('[data-inspector-ignore="true"]')) {
470
+ if (isInspectorChromeTarget(target)) {
441
471
  return;
442
472
  }
443
473
  const debugEl = target.closest("[data-debug]");
@@ -471,6 +501,7 @@ function initInspector() {
471
501
  };
472
502
  const stopInspecting = () => {
473
503
  isInspecting = false;
504
+ selectionLocked = false;
474
505
  latestContext = null;
475
506
  lastHoveredDebugEl = null;
476
507
  toggleBtn.style.transform = "scale(1)";
@@ -590,25 +621,32 @@ function initInspector() {
590
621
  inspectByPointer(latestHoverEvent);
591
622
  });
592
623
  };
624
+ const handlePointerSuppression = (event) => {
625
+ if (!isInspecting) return;
626
+ const target = event.target;
627
+ if (!target || isInspectorChromeTarget(target)) return;
628
+ if (selectionLocked) {
629
+ suppressEvent(event, { preventDefault: true, immediate: true });
630
+ return;
631
+ }
632
+ const isTouchSelectionEvent = event.type === "touchend" || event.type === "pointerup" && "pointerType" in event && event.pointerType !== "mouse";
633
+ suppressEvent(event, { preventDefault: true, immediate: true });
634
+ if (isTouchSelectionEvent) {
635
+ selectDebugTarget(target);
636
+ }
637
+ };
593
638
  const handleWindowClick = (event) => {
594
639
  if (!isInspecting) return;
595
640
  const target = event.target;
596
641
  if (!target || target === toggleBtn) return;
597
- if (target.closest('[data-inspector-ignore="true"]')) {
642
+ if (isInspectorChromeTarget(target)) {
598
643
  return;
599
644
  }
600
645
  suppressEvent(event, { preventDefault: true, immediate: true });
601
- const debugEl = target.closest("[data-debug]");
602
- if (!debugEl) {
603
- stopInspecting();
646
+ if (selectionLocked) {
604
647
  return;
605
648
  }
606
- const debugId = debugEl.getAttribute("data-debug");
607
- if (!debugId) return;
608
- copyText(debugId).then(() => {
609
- showCopyFeedback("Copied!", "success");
610
- win.setTimeout(stopInspecting, 600);
611
- });
649
+ selectDebugTarget(target);
612
650
  };
613
651
  const handleKeyDown = (event) => {
614
652
  if (event.key === "Escape" && isInspecting) stopInspecting();
@@ -626,6 +664,12 @@ function initInspector() {
626
664
  win.addEventListener("resize", scheduleAnchorUpdate);
627
665
  win.addEventListener("scroll", scheduleAnchorUpdate, true);
628
666
  doc.addEventListener("mousemove", handleMouseMove);
667
+ win.addEventListener("pointerdown", handlePointerSuppression, { capture: true });
668
+ win.addEventListener("pointerup", handlePointerSuppression, { capture: true });
669
+ win.addEventListener("mousedown", handlePointerSuppression, { capture: true });
670
+ win.addEventListener("mouseup", handlePointerSuppression, { capture: true });
671
+ win.addEventListener("touchstart", handlePointerSuppression, { capture: true });
672
+ win.addEventListener("touchend", handlePointerSuppression, { capture: true });
629
673
  win.addEventListener("click", handleWindowClick, { capture: true });
630
674
  win.addEventListener("keydown", handleKeyDown);
631
675
  updateAnchorForDialogs();
@@ -635,6 +679,12 @@ function initInspector() {
635
679
  win.removeEventListener("resize", scheduleAnchorUpdate);
636
680
  win.removeEventListener("scroll", scheduleAnchorUpdate, true);
637
681
  doc.removeEventListener("mousemove", handleMouseMove);
682
+ win.removeEventListener("pointerdown", handlePointerSuppression, { capture: true });
683
+ win.removeEventListener("pointerup", handlePointerSuppression, { capture: true });
684
+ win.removeEventListener("mousedown", handlePointerSuppression, { capture: true });
685
+ win.removeEventListener("mouseup", handlePointerSuppression, { capture: true });
686
+ win.removeEventListener("touchstart", handlePointerSuppression, { capture: true });
687
+ win.removeEventListener("touchend", handlePointerSuppression, { capture: true });
638
688
  win.removeEventListener("click", handleWindowClick, { capture: true });
639
689
  win.removeEventListener("keydown", handleKeyDown);
640
690
  for (const eventName of shieldedEvents) {
@@ -649,9 +699,39 @@ function initInspector() {
649
699
  };
650
700
  }
651
701
 
702
+ // src/vite-plugin.ts
703
+ function createViteDebugInspectorPlugin() {
704
+ return {
705
+ name: "react-debug-inspector-transform",
706
+ apply: "serve",
707
+ enforce: "pre",
708
+ async transform(code, id) {
709
+ if (id.includes("node_modules")) return null;
710
+ if (!/\.[jt]sx($|\?)/.test(id)) return null;
711
+ const { transformAsync } = await import("@babel/core");
712
+ const result = await transformAsync(code, {
713
+ filename: id,
714
+ babelrc: false,
715
+ configFile: false,
716
+ plugins: [babelPluginDebugLabel()],
717
+ parserOpts: {
718
+ plugins: ["jsx", "typescript"]
719
+ },
720
+ sourceMaps: true
721
+ });
722
+ if (!result?.code) return null;
723
+ return {
724
+ code: result.code,
725
+ map: result.map ?? null
726
+ };
727
+ }
728
+ };
729
+ }
730
+
652
731
  // src/index.ts
653
732
  var index_default = babelPluginDebugLabel;
654
733
  export {
734
+ createViteDebugInspectorPlugin,
655
735
  index_default as default,
656
736
  initInspector
657
737
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linhey/react-debug-inspector",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "A developer tool to inspect React components in browser and jump to source code.",
5
5
  "author": "linhey",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@
17
17
  "scripts": {
18
18
  "dev": "tsup src/index.ts --format cjs,esm --watch --dts",
19
19
  "dev:test-app": "vite --config vite.config.test.ts",
20
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
20
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean --external @babel/core",
21
21
  "test": "vitest run",
22
22
  "test:e2e": "playwright test",
23
23
  "test:e2e:ui": "playwright test --ui",
@@ -25,8 +25,10 @@
25
25
  "test:all": "npm run test && npm run test:e2e",
26
26
  "prepublishOnly": "npm run build"
27
27
  },
28
+ "dependencies": {
29
+ "@babel/core": "^7.24.0"
30
+ },
28
31
  "devDependencies": {
29
- "@babel/core": "^7.24.0",
30
32
  "@playwright/test": "^1.58.2",
31
33
  "@types/node": "^25.3.5",
32
34
  "@types/react": "^19.2.14",