|
1 |
| -// This file is copied from https://github.com/source-academy/sa-vscode/blob/main/src/utils/messages.ts |
| 1 | +// This file is originally created in https://github.com/source-academy/sa-vscode/blob/main/src/utils/messages.ts |
| 2 | +// It also needs to be copied to source-academy/frontend:src/features/vscode/messages.ts |
| 3 | +// Ideally it is split into multiple files, but for ease of copying, it is kept as one file. |
| 4 | + |
| 5 | +// ================================================================================ |
| 6 | +// Message type definitions |
| 7 | +// ================================================================================ |
| 8 | +const Messages = createMessages({ |
| 9 | + /** Sent from the iframe to the extension */ |
| 10 | + ExtensionPing: () => ({}), |
| 11 | + /** Sent from the extension to the iframe */ |
| 12 | + ExtensionPong: (token: string | null) => ({ token }), |
| 13 | + IsVsc: () => ({}), |
| 14 | + NewEditor: (assessmentName: string, questionId: number, code: string) => ({ |
| 15 | + assessmentName, |
| 16 | + questionId, |
| 17 | + code, |
| 18 | + }), |
| 19 | + Text: (code: string) => ({ code }), |
| 20 | +}); |
| 21 | + |
| 22 | +export default Messages; |
| 23 | + |
| 24 | +// ================================================================================ |
| 25 | +// Code for type generation |
| 26 | +// ================================================================================ |
| 27 | + |
| 28 | +// Define BaseMessage to be the base type for all messages, such that all messages have a type field |
2 | 29 | type BaseMessage<T extends string, P extends object> = {
|
3 | 30 | type: T;
|
4 | 31 | } & P;
|
5 | 32 |
|
| 33 | +// A helper function to create messages dynamically from schema (hoisted!) |
6 | 34 | function createMessages<T extends Record<string, (...args: any[]) => object>>(
|
7 |
| - creators: T |
| 35 | + creators: T, |
8 | 36 | ): {
|
9 |
| - [K in Extract<keyof T, string>]: (...args: Parameters<T[K]>) => BaseMessage<K, ReturnType<T[K]>>; |
| 37 | + [K in Extract<keyof T, string>]: ( |
| 38 | + ...args: Parameters<T[K]> |
| 39 | + ) => BaseMessage<K, ReturnType<T[K]>>; |
10 | 40 | } {
|
11 | 41 | return Object.fromEntries(
|
12 | 42 | Object.entries(creators).map(([key, creator]) => [
|
13 | 43 | key,
|
14 | 44 | (...args: any[]) => ({
|
15 | 45 | type: key,
|
16 |
| - ...creator(...args) |
17 |
| - }) |
18 |
| - ]) |
| 46 | + ...creator(...args), |
| 47 | + }), |
| 48 | + ]), |
19 | 49 | ) as any;
|
20 | 50 | }
|
21 | 51 |
|
22 |
| -const Messages = createMessages({ |
23 |
| - WebviewStarted: () => ({}), |
24 |
| - IsVsc: () => ({}), |
25 |
| - NewEditor: (assessmentName: string, questionId: number, code: string) => ({ |
26 |
| - assessmentName, |
27 |
| - questionId, |
28 |
| - code |
29 |
| - }), |
30 |
| - Text: (code: string) => ({ code }) |
31 |
| -}); |
32 |
| - |
33 |
| -export default Messages; |
34 |
| - |
35 |
| -// Define MessageTypes to map each key in Messages to its specific message type |
36 |
| -export type MessageTypes = { |
| 52 | +// Define MessageTypes as a map of each key in Messages to its specific message type |
| 53 | +type MessageTypes = { |
37 | 54 | [K in keyof typeof Messages]: ReturnType<(typeof Messages)[K]>;
|
38 | 55 | };
|
39 | 56 |
|
40 | 57 | // Define MessageType as a union of all message types
|
41 | 58 | export type MessageType = MessageTypes[keyof MessageTypes];
|
42 | 59 |
|
43 |
| -export const FRONTEND_ELEMENT_ID = 'frontend'; |
| 60 | +// Also define MessageTypeNames as an "enum" to avoid hardcoding strings |
| 61 | +export const MessageTypeNames = (() => |
| 62 | + ({ |
| 63 | + ...Object.keys(Messages) |
| 64 | + .filter((k) => isNaN(Number(k))) |
| 65 | + .reduce( |
| 66 | + (acc, cur) => ({ |
| 67 | + ...acc, |
| 68 | + [cur]: cur, |
| 69 | + }), |
| 70 | + {}, |
| 71 | + ), |
| 72 | + }) as { |
| 73 | + [k in keyof typeof Messages]: k; |
| 74 | + })(); |
| 75 | + |
| 76 | +// ================================================================================ |
| 77 | +// Wrapper functions |
| 78 | +// ================================================================================ |
| 79 | + |
| 80 | +export const FRONTEND_ELEMENT_ID = "frontend"; |
44 | 81 |
|
45 | 82 | export function sendToWebview(message: MessageType) {
|
46 |
| - window.parent.postMessage(message, '*'); |
| 83 | + window.parent.postMessage(message, "*"); |
47 | 84 | }
|
48 | 85 | export function sendToFrontend(document: Document, message: MessageType) {
|
49 | 86 | const iframe: HTMLIFrameElement = document.getElementById(
|
50 |
| - FRONTEND_ELEMENT_ID |
| 87 | + FRONTEND_ELEMENT_ID, |
51 | 88 | ) as HTMLIFrameElement;
|
52 | 89 | const contentWindow = iframe.contentWindow;
|
53 | 90 | if (!contentWindow) {
|
54 | 91 | return;
|
55 | 92 | }
|
56 | 93 | // TODO: Don't hardcode this!
|
57 |
| - contentWindow.postMessage(message, 'http://localhost:8000'); |
| 94 | + contentWindow.postMessage(message, "http://localhost:8000"); |
58 | 95 | }
|
0 commit comments