@cfp-studio/iframe 18.0.0

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.
@@ -0,0 +1,112 @@
1
+ import type { RendererLoader } from "../../src";
2
+ import { getState } from "./state";
3
+
4
+ export async function runRenderer({
5
+ wrapperModule,
6
+ wrapperName,
7
+ previewableModule,
8
+ previewableName,
9
+ renderId,
10
+ shouldAbortRender,
11
+ loadRenderer,
12
+ }: {
13
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
14
+ wrapperModule: any;
15
+ wrapperName: string;
16
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
17
+ previewableModule: any;
18
+ previewableName: string;
19
+ renderId: number;
20
+ shouldAbortRender: () => boolean;
21
+ loadRenderer: RendererLoader;
22
+ }) {
23
+ const currentState = getState();
24
+ if (!currentState || shouldAbortRender()) {
25
+ return;
26
+ }
27
+ window.__PREVIEWJS_IFRAME__.reportEvent({
28
+ kind: "before-render",
29
+ });
30
+ const { render, jsxFactory } = await loadRenderer({
31
+ wrapperModule,
32
+ wrapperName,
33
+ previewableModule,
34
+ previewableName,
35
+ renderId,
36
+ shouldAbortRender,
37
+ });
38
+ if (shouldAbortRender()) {
39
+ return;
40
+ }
41
+ const { autogenCallbackProps, properties } =
42
+ await previewableModule.PreviewJsEval(
43
+ `
44
+ let ${currentState.autogenCallbackPropsSource};
45
+ let ${currentState.propsAssignmentSource};
46
+ return { autogenCallbackProps, properties };
47
+ `,
48
+ {
49
+ __jsxFactory__: jsxFactory,
50
+ }
51
+ );
52
+ if (shouldAbortRender()) {
53
+ return;
54
+ }
55
+ await render(({ presetProps, presetGlobalProps }) => ({
56
+ ...transformFunctions(autogenCallbackProps, []),
57
+ ...transformFunctions(presetGlobalProps, []),
58
+ ...transformFunctions(properties || presetProps, []),
59
+ }));
60
+ if (shouldAbortRender()) {
61
+ return;
62
+ }
63
+ window.__PREVIEWJS_IFRAME__.reportEvent({
64
+ kind: "rendered",
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Ensures that any call to a function within objects and arrays is automatically intercepted
70
+ * and shown to the user.
71
+ */
72
+
73
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
74
+ function transformFunctions(value: any, path: string[]): any {
75
+ if (value && typeof value === "object") {
76
+ if (Array.isArray(value)) {
77
+ // Array.
78
+ return value.map((v, i) =>
79
+ transformFunctions(v, [...path, i.toString(10)])
80
+ );
81
+ }
82
+ if (value.constructor === Object) {
83
+ // Plain object (i.e. not Set or Map or any class instance).
84
+ if (value.$$typeof) {
85
+ // This is likely a React component, e.g. instantiated in a JSX expression.
86
+ // We don't want to show an action invoked when the JSX function is invoked,
87
+ // so skip it.
88
+ return value;
89
+ }
90
+ if (value.__v_isVNode) {
91
+ // This is likely a Vue component (at least Vue 3).
92
+ return value;
93
+ }
94
+ return Object.fromEntries(
95
+ Object.entries(value).map(([k, v]) => [
96
+ k,
97
+ typeof v === "function"
98
+ ? (...args: unknown[]) => {
99
+ window.__PREVIEWJS_IFRAME__.reportEvent({
100
+ kind: "action",
101
+ type: "fn",
102
+ path: [...path, k].join("."),
103
+ });
104
+ return v(...args);
105
+ }
106
+ : transformFunctions(v, [...path, k]),
107
+ ])
108
+ );
109
+ }
110
+ }
111
+ return value;
112
+ }
@@ -0,0 +1,11 @@
1
+ import type { RenderOptions } from "../../src";
2
+
3
+ let state: RenderOptions | null = null;
4
+
5
+ export function getState() {
6
+ return state;
7
+ }
8
+
9
+ export function setState(newState: RenderOptions) {
10
+ state = newState;
11
+ }
@@ -0,0 +1,74 @@
1
+ /// <reference types="vite/client" />
2
+ import type { ErrorPayload, UpdatePayload } from "vite/types/hmrPayload";
3
+ import { generateMessageFromError } from "./error-message";
4
+
5
+ const IGNORED_UPDATE_PATH = "/__previewjs_internal__/preview.js?";
6
+
7
+ export function setupViteHmrListener() {
8
+ const hmr = import.meta.hot;
9
+ if (!hmr) {
10
+ return;
11
+ }
12
+ let error: ErrorPayload | null = null;
13
+ let isFirstUpdate = true;
14
+ hmr.on("vite:beforeFullReload", () => {
15
+ window.__PREVIEWJS_IFRAME__.reportEvent({
16
+ kind: "vite-before-reload",
17
+ });
18
+ });
19
+ hmr.on("vite:error", (payload: ErrorPayload) => {
20
+ error = payload;
21
+ if (typeof payload.err?.message !== "string") {
22
+ // This error doesn't match the expected payload.
23
+ // For example, this can happen with invalid CSS with the
24
+ // vite-plugin-vue2 plugin.
25
+ // Block this to prevent crashes down the track.
26
+ return;
27
+ }
28
+ window.__PREVIEWJS_IFRAME__.reportEvent({
29
+ kind: "error",
30
+ source: "vite",
31
+ message: generateMessageFromError(payload.err.message, payload.err.stack),
32
+ });
33
+ });
34
+ hmr.on("vite:beforeUpdate", (payload: UpdatePayload) => {
35
+ if (payload.updates.every((u) => u.path.startsWith(IGNORED_UPDATE_PATH))) {
36
+ // Ignore.
37
+ return;
38
+ }
39
+ // This is a copy of Vite logic that is disabled when the error overlay is off:
40
+ // https://github.com/vitejs/vite/blob/f3d15f106f378c3850b62fbebd69fc8f7c7f944b/packages/vite/src/client/client.ts#L61
41
+ if (error && isFirstUpdate) {
42
+ window.location.reload();
43
+ } else {
44
+ error = null;
45
+ isFirstUpdate = false;
46
+ }
47
+ if (window.__PREVIEWJS_IFRAME__.lastRenderFailed) {
48
+ window.location.reload();
49
+ } else {
50
+ window.__PREVIEWJS_IFRAME__.reportEvent({
51
+ kind: "vite-before-update",
52
+ payload,
53
+ });
54
+ }
55
+ });
56
+ hmr.on("vite:afterUpdate", (payload: UpdatePayload) => {
57
+ if (payload.updates.every((u) => u.path.startsWith(IGNORED_UPDATE_PATH))) {
58
+ // Ignore.
59
+ return;
60
+ }
61
+ window.__PREVIEWJS_IFRAME__.reportEvent({
62
+ kind: "vite-after-update",
63
+ payload,
64
+ });
65
+ });
66
+ hmr.on("vite:invalidate", () => {
67
+ window.__PREVIEWJS_IFRAME__.reportEvent({
68
+ kind: "vite-invalidate",
69
+ });
70
+ window.__PREVIEWJS_IFRAME__.refresh({
71
+ refetchPreviewableModule: true,
72
+ });
73
+ });
74
+ }
@@ -0,0 +1,58 @@
1
+ export function setupVSCodeControlListener() {
2
+ console.log('setupVSCodeControlListener');
3
+ window.addEventListener('message', (event) => {
4
+ parseMessageFromChannel(event.data, (eventName, args) => {
5
+ console.log(`收到vscode:${eventName}消息: ${args}`);
6
+ if (eventName === 'ready') {
7
+ const { message } = JSON.parse(args) as { message: string };
8
+ console.log(`处理消息体: ${message}`);
9
+ const payload = JSON.parse(message);
10
+ window.__PREVIEWJS_IFRAME__.reportEvent({
11
+ kind: "toggle-inspect",
12
+ enabled: payload.data.isInspectorOpen,
13
+ });
14
+ return true;
15
+ }
16
+ return false
17
+ })
18
+ })
19
+ }
20
+
21
+ export function parseMessageFromChannel(
22
+ message: string,
23
+ emit: (eventName: WebviewEvent, args: string) => boolean): boolean {
24
+ for (const e of webviewEventNames) {
25
+ if (message.substr(0, e.length) === e && message[e.length] === ':') {
26
+ emit(e, message.substr(e.length + 1));
27
+ return true;
28
+ }
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ export type WebviewEvent = 'getState' | 'getUrl' | 'openInEditor' | 'cssMirrorContent' | 'ready' | 'setState' | 'telemetry' | 'websocket'
35
+ | 'getVscodeSettings' | 'copyText' | 'focusEditor' | 'focusEditorGroup' | 'openUrl' | 'toggleScreencast' | 'toggleInspect' | 'replayConsoleMessages'
36
+ | 'devtoolsConnection' | 'toggleCSSMirrorContent' | 'writeToClipboard' | 'readClipboard';
37
+ export const webviewEventNames: WebviewEvent[] = [
38
+ 'getState',
39
+ 'getUrl',
40
+ 'openInEditor',
41
+ 'cssMirrorContent',
42
+ 'ready',
43
+ 'setState',
44
+ 'telemetry',
45
+ 'websocket',
46
+ 'getVscodeSettings',
47
+ 'copyText',
48
+ 'focusEditor',
49
+ 'focusEditorGroup',
50
+ 'openUrl',
51
+ 'toggleScreencast',
52
+ 'toggleInspect',
53
+ 'replayConsoleMessages',
54
+ 'devtoolsConnection',
55
+ 'toggleCSSMirrorContent',
56
+ 'writeToClipboard',
57
+ 'readClipboard',
58
+ ];
@@ -0,0 +1,71 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <link rel="icon" href="/logo.png" />
5
+ <script>
6
+ // 将全局对象 globalThis 赋值给 window.global,确保在不同环境下全局对象的一致性
7
+ window.global = globalThis;
8
+ // 将 window.module 设置为 undefined,避免模块冲突
9
+ window.module = undefined;
10
+
11
+ // 定义一个全局对象 __PREVIEWJS_IFRAME__,用于管理预览框架的状态和事件
12
+ window.__PREVIEWJS_IFRAME__ = {
13
+ // 初始化 lastRenderFailed 为 false,表示初始渲染未失败
14
+ lastRenderFailed: false,
15
+ toggleInspect:false,
16
+ /**
17
+ * 报告预览事件
18
+ * @param {Object} event - 事件对象
19
+ * @param {string} event.kind - 事件类型,如 "rendered" 或 "error"
20
+ * @param {string} [event.source] - 事件源,如 "renderer"
21
+ */
22
+ reportEvent: (event) => {
23
+ // 如果事件类型为 "rendered",则将 lastRenderFailed 设为 false
24
+ if (event.kind === "rendered" ) {
25
+ window.__PREVIEWJS_IFRAME__.lastRenderFailed = false;
26
+ }
27
+ // 如果事件类型为 "error" 且事件源为 "renderer",则将 lastRenderFailed 设为 true
28
+ else if (event.kind === "error" && event.source === "renderer") {
29
+ window.__PREVIEWJS_IFRAME__.lastRenderFailed = true;
30
+ }
31
+ // 调用控制器的 onPreviewEvent 方法处理事件
32
+ (
33
+ // 注意: 与 core/src/html-error.ts 中的代码保持同步.
34
+ window.__PREVIEWJS_CONTROLLER__ || window.parent.__PREVIEWJS_CONTROLLER__
35
+ ).onPreviewEvent(event);
36
+ },
37
+ /**
38
+ * 刷新预览
39
+ * 目前为空函数,可根据需求实现具体逻辑
40
+ */
41
+ refresh: () => {}
42
+ };
43
+ // 报告启动事件,表明预览框架正在初始化
44
+ window.__PREVIEWJS_IFRAME__.reportEvent({
45
+ kind: "bootstrapping",
46
+ });
47
+ </script>
48
+ <!-- %OPTIONAL_HEAD_CONTENT% -->
49
+ <style>
50
+ body {
51
+ padding: 1rem;
52
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
53
+ "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
54
+ "Helvetica Neue", sans-serif;
55
+ -webkit-font-smoothing: antialiased;
56
+ -moz-osx-font-smoothing: grayscale;
57
+ }
58
+
59
+ code,
60
+ .code {
61
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
62
+ monospace;
63
+ }
64
+ </style>
65
+ <meta charset="utf-8" />
66
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
67
+ </head>
68
+ <body>
69
+ <div id="root"></div>
70
+ </body>
71
+ </html>