@cfp-studio/iframe 18.4.3 → 18.4.4

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/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.19.2_jiti@2.7.0_less@4.2.2_lightningcss@1.30.2_sass@1.85.1_terser@5.40.0_tsx@4.19.3_yaml@2.7.0/node_modules/vite/types/hmrPayload.d.ts
1
+ //#region ../../../../node_modules/.pnpm/vite@6.4.5_@types+node@24.1_13c54df9746541450cb8a36e10ae9cdd/node_modules/vite/types/hmrPayload.d.ts
2
2
  interface UpdatePayload {
3
3
  type: 'update';
4
4
  updates: Update[];
@@ -33,6 +33,14 @@ declare global {
33
33
  */
34
34
  lastRenderFailed: boolean;
35
35
  toggleInspect: boolean;
36
+ /**
37
+ * 编译期错误兜底开关(`index.html` 的内联脚本用)。
38
+ *
39
+ * `true` 时由 `index.html` 上报那些「模块还没加载就崩」的错误
40
+ * (Vite 解析 `.less` 失败、`react-dom` 缺失等);`logs.ts` 接管后置 `false`。
41
+ * 见 `docs/todo/39`。
42
+ */
43
+ startupErrorGuard: boolean;
36
44
  /**
37
45
  * 处理 HMR 更新
38
46
  * @param payload - HMR 更新的负载
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfp-studio/iframe",
3
- "version": "18.4.3",
3
+ "version": "18.4.4",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "dist",
@@ -45,6 +45,10 @@ export function setUpLogInterception() {
45
45
  console.info = makeLogger("info", console.info);
46
46
  console.warn = makeLogger("warn", console.warn);
47
47
  console.error = makeLogger("error", console.error);
48
+ // 接管 `index.html` 的编译期兜底(见 `docs/todo/39`):
49
+ // 本模块靠模块加载才能执行,所以更早的失败只能由 `index.html` 的内联脚本接住。
50
+ // 现在轮到我们了,关掉那个兜底,避免同一个错误上报两次。
51
+ window.__PREVIEWJS_IFRAME__.startupErrorGuard = false;
48
52
  window.onerror = (message, source, lineno, colno, error) => {
49
53
  window.__PREVIEWJS_IFRAME__.reportEvent({
50
54
  kind: "error",
@@ -642,11 +642,30 @@ function collectRuntimeSnapshot(): RuntimeSnapshot {
642
642
  };
643
643
  }
644
644
 
645
+ /**
646
+ * 渲染错误面板的标记属性(`docs/todo/39`)。
647
+ *
648
+ * 与 `@cfp-studio/analyzer-api` 的 `ERROR_PANEL_ATTRIBUTE` 保持一致。
649
+ * 面板是**预览工具自己画的内容**,不是被预览组件的一部分 ——
650
+ * 不排除的话它会作为普通 DOM 出现在 Runtime Tree 里,还会参与点击选框命中。
651
+ */
652
+ const ERROR_PANEL_ATTRIBUTE = "data-preview-error-panel";
653
+
654
+ /** 该元素是否是预览工具自己的错误面板。 */
655
+ function isErrorPanel(element: Element): boolean {
656
+ return element.hasAttribute?.(ERROR_PANEL_ATTRIBUTE) ?? false;
657
+ }
658
+
645
659
  function walk(element: Element | null, parentId: string | undefined, path: string, nodes: RuntimeNode[]): boolean {
646
660
  if (!element || nodes.length >= MAX_NODES) {
647
661
  return nodes.length >= MAX_NODES;
648
662
  }
649
663
 
664
+ if (isErrorPanel(element)) {
665
+ // 跳过面板自身与它的子树,但不计为截断。
666
+ return false;
667
+ }
668
+
650
669
  const fiber = getElementFiberUpward(element);
651
670
  const source = getSourceLocation(fiber);
652
671
  // Path-based ID: stable across snapshots as long as the DOM structure is
@@ -13,6 +13,8 @@
13
13
  // 初始化 lastRenderFailed 为 false,表示初始渲染未失败
14
14
  lastRenderFailed: false,
15
15
  toggleInspect: false,
16
+ /** 编译期兜底上报开关:`logs.ts` 接管后置 false(见 `docs/todo/39`)。 */
17
+ startupErrorGuard: true,
16
18
  /**
17
19
  * 报告预览事件
18
20
  * @param {Object} event - 事件对象
@@ -56,6 +58,47 @@
56
58
  window.__PREVIEWJS_IFRAME__.reportEvent({
57
59
  kind: "bootstrapping",
58
60
  });
61
+
62
+ /**
63
+ * 编译期错误的兜底上报(`docs/todo/39`)。
64
+ *
65
+ * **为什么必须放在这里**:Vite 的模块图加载失败(例如 `.less` 缺 `less` 包、
66
+ * `react-dom` 未安装)发生在**任何业务 JS 执行之前** —— `__previewjs_internal__/logs.ts`
67
+ * 里那套 `window.onerror` 自己也靠模块加载才能注册,救不了更早的失败。
68
+ * 结果就是「白屏 + 零报错」,用户无从下手。
69
+ *
70
+ * 这里是内联脚本,一定先于模块执行,所以能接住那些“还没进入应用就崩”的错误。
71
+ * 模块加载成功后,`logs.ts` 会把这两个钩子**覆盖**成更完整的版本(带格式化与
72
+ * `vite:error` 分流),这属于预期叠加:本处只要求“不静默”。
73
+ */
74
+ const reportStartupError = (raw) => {
75
+ // `logs.ts` 加载成功后会把这个开关关掉(它有更完整的格式化与 vite:error 分流),
76
+ // 避免同一个错误被上报两次。
77
+ if (!window.__PREVIEWJS_IFRAME__.startupErrorGuard) {
78
+ return;
79
+ }
80
+ const text = raw instanceof Error ? raw.stack || raw.message : String(raw);
81
+ window.__PREVIEWJS_IFRAME__.reportEvent({
82
+ kind: "error",
83
+ source: "vite",
84
+ message: text,
85
+ });
86
+ };
87
+
88
+ window.addEventListener("error", (event) => {
89
+ // 只接模块脚本 / 资源加载失败,其它错误交给 logs.ts 的版本。
90
+ if (event.target && event.target !== window && event.target.tagName) {
91
+ reportStartupError(`Failed to load ${event.target.tagName.toLowerCase()}: ${event.target.src || event.target.href || ""}`);
92
+ return;
93
+ }
94
+ if (event.error || event.message) {
95
+ reportStartupError(event.error || event.message);
96
+ }
97
+ }, true);
98
+
99
+ window.addEventListener("unhandledrejection", (event) => {
100
+ reportStartupError(event.reason);
101
+ });
59
102
  </script>
60
103
  <!-- %OPTIONAL_HEAD_CONTENT% -->
61
104
  <style>