Skip to content

Commit

Permalink
refactor: switch to JS API exposed on Window instead of postMessage (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
fwouts committed Aug 21, 2023
1 parent b0ca983 commit f146172
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 67 deletions.
2 changes: 1 addition & 1 deletion app/client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async function onUrlChanged() {
props,
analyzeResponse.types
);
iframeController.load({
iframeController.render({
previewableId,
propsAssignmentSource: transpile(
await generatePropsAssignmentSource(
Expand Down
8 changes: 4 additions & 4 deletions chromeless/client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { LoadPreviewOptions, PreviewEvent } from "@previewjs/iframe";
import type { PreviewEvent, RenderOptions } from "@previewjs/iframe";
import { createController } from "@previewjs/iframe";

declare global {
interface Window {
loadIframePreview(options: LoadPreviewOptions): void;
loadIframePreview(options: RenderOptions): void;
onIframeEvent?(event: PreviewEvent): void;
}
}
Expand All @@ -19,7 +19,7 @@ const controller = createController({

window.onload = () => {
controller.start();
window.loadIframePreview = (options: LoadPreviewOptions) => {
controller.load(options);
window.loadIframePreview = (options: RenderOptions) => {
controller.render(options);
};
};
31 changes: 12 additions & 19 deletions iframe/preview/__previewjs_internal__/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AppToPreviewMessage, RenderMessage } from "../../src/messages";
import type { RenderOptions } from "../../src";
import { overrideCopyCutPaste } from "./copy-cut-paste";
import { setUpLinkInterception } from "./links";
import { setUpLogInterception } from "./logs";
Expand Down Expand Up @@ -30,10 +30,10 @@ export function initPreview({
}) {
let renderId = 0;

async function load({
async function render({
autogenCallbackPropsSource,
propsAssignmentSource,
}: RenderMessage) {
}: RenderOptions) {
try {
renderId += 1;
setState({
Expand Down Expand Up @@ -63,20 +63,13 @@ export function initPreview({
throw new Error(`Unable to find #root!`);
}

let lastRenderMessage: RenderMessage | null = null;
window.addEventListener(
"message",
(event: MessageEvent<AppToPreviewMessage>) => {
const data = event.data;
switch (data.kind) {
case "render":
lastRenderMessage = data;
// eslint-disable-next-line no-console
load(data).catch(console.error);
break;
}
}
);
let lastRenderOptions: RenderOptions | null = null;
window.__PREVIEWJS__ = {
render: async (data) => {
lastRenderOptions = data;
await render(data);
},
};

sendMessageFromPreview({
kind: "bootstrapped",
Expand All @@ -85,9 +78,9 @@ export function initPreview({
return (updatedPreviewableModule: any, updatedWrapperModule: any) => {
previewableModule = updatedPreviewableModule;
wrapperModule = updatedWrapperModule;
if (lastRenderMessage) {
if (lastRenderOptions) {
// eslint-disable-next-line no-console
load(lastRenderMessage).catch(console.error);
render(lastRenderOptions).catch(console.error);
}
};
}
69 changes: 35 additions & 34 deletions iframe/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import type { ErrorPayload, UpdatePayload } from "vite/types/hmrPayload";
import type { AppToPreviewMessage, PreviewToAppMessage } from "./messages";
import type { PreviewToAppMessage } from "./messages";

declare global {
interface Window {
__PREVIEWJS__: {
render(options: RenderOptions): Promise<void>;
};
}
}

export interface RenderOptions {
previewableId: string;
autogenCallbackPropsSource: string;
propsAssignmentSource: string;
}

export function createController(options: {
getIframe: () => HTMLIFrameElement | null;
Expand All @@ -11,20 +25,14 @@ export function createController(options: {
export interface PreviewIframeController {
start(): void;
stop(): void;
load(options: LoadPreviewOptions): void;
render(options: RenderOptions): void;
resetIframe(id: string): void;
}

export interface LoadPreviewOptions {
previewableId: string;
propsAssignmentSource: string;
autogenCallbackPropsSource: string;
}

class PreviewIframeControllerImpl implements PreviewIframeController {
private idBootstrapped: string | null = null;
private pendingPreviewableIdBootstrap: string | null = null;
private lastMessage: AppToPreviewMessage | null = null;
private lastRenderOptions: RenderOptions | null = null;
private expectRenderTimeout?: any;

constructor(
Expand All @@ -42,37 +50,29 @@ class PreviewIframeControllerImpl implements PreviewIframeController {
window.removeEventListener("message", this.onWindowMessage);
}

load(options: LoadPreviewOptions) {
this.send({
kind: "render",
...options,
});
}

private send(message: AppToPreviewMessage) {
this.lastMessage = message;
async render(options: RenderOptions) {
this.lastRenderOptions = options;
if (
this.idBootstrapped !== message.previewableId &&
this.pendingPreviewableIdBootstrap !== message.previewableId
this.idBootstrapped !== options.previewableId &&
this.pendingPreviewableIdBootstrap !== options.previewableId
) {
this.resetIframe(message.previewableId);
this.resetIframe(options.previewableId);
return;
}
const iframeWindow = this.options.getIframe()?.contentWindow;
if (!iframeWindow) {
return;
}
iframeWindow.postMessage(message, document.location.href);
if (message.kind === "render") {
this.clearExpectRenderTimeout();
this.expectRenderTimeout = setTimeout(() => {
// eslint-disable-next-line no-console
console.warn(
"Expected render did not occur after 5 seconds. Reloading iframe..."
);
this.resetIframe(message.previewableId);
}, 5000);
}
const renderPromise = iframeWindow.__PREVIEWJS__.render(options);
this.clearExpectRenderTimeout();
this.expectRenderTimeout = setTimeout(() => {
// eslint-disable-next-line no-console
console.warn(
"Expected render did not occur after 5 seconds. Reloading iframe..."
);
this.resetIframe(options.previewableId);
}, 5000);
await renderPromise;
}

resetIframe(id: string) {
Expand Down Expand Up @@ -143,8 +143,9 @@ class PreviewIframeControllerImpl implements PreviewIframeController {
private onBootstrapped() {
this.idBootstrapped = this.pendingPreviewableIdBootstrap;
this.pendingPreviewableIdBootstrap = null;
if (this.lastMessage) {
this.send(this.lastMessage);
if (this.lastRenderOptions) {
// eslint-disable-next-line no-console
this.render(this.lastRenderOptions).catch(console.error);
}
this.options.listener({
kind: "bootstrapped",
Expand Down
9 changes: 0 additions & 9 deletions iframe/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,3 @@ export interface ViteBeforeUpdateMessage {
kind: "vite-before-update";
payload: UpdatePayload;
}

export type AppToPreviewMessage = RenderMessage;

export interface RenderMessage {
kind: "render";
previewableId: string;
autogenCallbackPropsSource: string;
propsAssignmentSource: string;
}

0 comments on commit f146172

Please sign in to comment.