Skip to content

Commit

Permalink
feat: Refactor vscode bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
dineug committed Oct 19, 2024
1 parent 5152787 commit 48bfac5
Show file tree
Hide file tree
Showing 18 changed files with 356 additions and 350 deletions.
5 changes: 4 additions & 1 deletion packages/erd-editor-schema/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ export default defineConfig({
'@': join(__dirname, 'src'),
},
},
plugins: [dts(), typescript({ noEmitOnError: true })],
plugins: [
dts({ compilerOptions: { declarationMap: true } }),
typescript({ noEmitOnError: true }),
],
});
86 changes: 45 additions & 41 deletions packages/intellij-webview/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ import {
} from '@dineug/erd-editor';
import {
AnyAction,
Emitter,
Bridge,
hostExportFileCommand,
hostInitialCommand,
hostSaveReplicationCommand,
hostSaveThemeCommand,
hostSaveValueCommand,
ThemeOptions,
vscodeExportFileAction,
vscodeInitialAction,
vscodeSaveReplicationAction,
vscodeSaveThemeAction,
webviewReplicationAction,
webviewImportFileCommand,
webviewInitialValueCommand,
webviewReplicationCommand,
webviewUpdateReadonlyCommand,
webviewUpdateThemeCommand,
} from '@dineug/erd-editor-vscode-bridge';
import { encode } from 'base64-arraybuffer';

const bridge = new Emitter();
const workerBridge = new Emitter();
const bridge = new Bridge();
const workerBridge = new Bridge();
const editor = document.createElement('erd-editor');
const sharedStore = editor.getSharedStore({ mouseTracker: false });
const replicationStoreWorker = new Worker(
Expand Down Expand Up @@ -47,7 +52,7 @@ import('@dineug/erd-editor-shiki-worker').then(({ getShikiService }) => {
setExportFileCallback(async (blob, options) => {
const arrayBuffer = await blob.arrayBuffer();
dispatch(
vscodeExportFileAction({
Bridge.executeCommand(hostExportFileCommand, {
value: encode(arrayBuffer),
fileName: options.fileName,
})
Expand All @@ -56,11 +61,11 @@ setExportFileCallback(async (blob, options) => {

const handleChangePresetTheme = (event: Event) => {
const e = event as CustomEvent<ThemeOptions>;
dispatch(vscodeSaveThemeAction(e.detail));
dispatch(Bridge.executeCommand(hostSaveThemeCommand, e.detail));
};

bridge.on({
webviewImportFile: ({ payload: { type, op, value } }) => {
Bridge.mergeRegister(
bridge.registerCommand(webviewImportFileCommand, ({ type, op, value }) => {
switch (type) {
case 'json':
op === 'set' ? (editor.value = value) : editor.setDiffValue(value);
Expand All @@ -69,48 +74,47 @@ bridge.on({
op === 'set' && editor.setSchemaSQL(value);
break;
}
},
webviewInitialValue: action => {
const {
payload: { value },
} = action;
dispatchWorker(action);
}),
bridge.registerCommand(webviewInitialValueCommand, ({ value }) => {
dispatchWorker(
Bridge.executeCommand(webviewInitialValueCommand, { value })
);

editor.addEventListener('changePresetTheme', handleChangePresetTheme);
editor.setInitialValue(value);
editor.enableThemeBuilder = true;
sharedStore.subscribe(actions => {
dispatchWorker(webviewReplicationAction({ actions }));
dispatch(vscodeSaveReplicationAction({ actions }));
dispatchWorker(
Bridge.executeCommand(webviewReplicationCommand, { actions })
);
dispatch(Bridge.executeCommand(hostSaveReplicationCommand, { actions }));
});
document.body.appendChild(editor);
},
webviewReplication: action => {
const {
payload: { actions },
} = action;
}),
bridge.registerCommand(webviewReplicationCommand, ({ actions }) => {
sharedStore.dispatch(actions);
dispatchWorker(action);
},
webviewUpdateTheme: ({ payload }) => {
dispatchWorker(
Bridge.executeCommand(webviewReplicationCommand, { actions })
);
}),
bridge.registerCommand(webviewUpdateThemeCommand, payload => {
editor.setPresetTheme({
...payload,
appearance: payload.appearance === 'auto' ? 'dark' : payload.appearance,
});
},
webviewUpdateReadonly: ({ payload }) => {
editor.readonly = payload;
},
});
}),
bridge.registerCommand(webviewUpdateReadonlyCommand, readonly => {
editor.readonly = readonly;
}),
workerBridge.registerCommand(hostSaveValueCommand, ({ value }) => {
dispatch(Bridge.executeCommand(hostSaveValueCommand, { value }));
})
);

workerBridge.on({
vscodeSaveValue: action => {
dispatch(action);
},
globalThis.addEventListener('message', event => {
bridge.executeAction(event.data);
});

window.addEventListener('message', event => bridge.emit(event.data));
replicationStoreWorker.addEventListener('message', event => {
workerBridge.emit(event.data);
workerBridge.executeAction(event.data);
});
dispatch(vscodeInitialAction());
dispatch(Bridge.executeCommand(hostInitialCommand, undefined));
26 changes: 15 additions & 11 deletions packages/intellij-webview/src/services/replicationStore.worker.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { createReplicationStore } from '@dineug/erd-editor/engine.js';
import {
AnyAction,
Emitter,
vscodeSaveValueAction,
Bridge,
hostSaveValueCommand,
webviewInitialValueCommand,
webviewReplicationCommand,
} from '@dineug/erd-editor-vscode-bridge';

import { toWidth } from '@/utils/text';

const store = createReplicationStore({ toWidth });
const bridge = new Emitter();
const bridge = new Bridge();

const dispatch = (action: AnyAction) => {
globalThis.postMessage(action);
Expand All @@ -17,20 +19,22 @@ const dispatch = (action: AnyAction) => {
store.on({
change: () => {
dispatch(
vscodeSaveValueAction({
Bridge.executeCommand(hostSaveValueCommand, {
value: store.value,
})
);
},
});

bridge.on({
webviewInitialValue: ({ payload: { value } }) => {
Bridge.mergeRegister(
bridge.registerCommand(webviewInitialValueCommand, ({ value }) => {
store.setInitialValue(value);
},
webviewReplication: ({ payload: { actions } }) => {
}),
bridge.registerCommand(webviewReplicationCommand, ({ actions }) => {
store.dispatch(actions);
},
});
})
);

globalThis.addEventListener('message', event => bridge.emit(event.data));
globalThis.addEventListener('message', event => {
bridge.executeAction(event.data);
});
5 changes: 4 additions & 1 deletion packages/r-html/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export default defineConfig({
'@': join(__dirname, 'src'),
},
},
plugins: [dts(), typescript({ noEmitOnError: true })],
plugins: [
dts({ compilerOptions: { declarationMap: true } }),
typescript({ noEmitOnError: true }),
],
server: {
open: true,
},
Expand Down
5 changes: 4 additions & 1 deletion packages/schema-sql-parser/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ export default defineConfig({
'@': join(__dirname, 'src'),
},
},
plugins: [dts(), typescript({ noEmitOnError: true })],
plugins: [
dts({ compilerOptions: { declarationMap: true } }),
typescript({ noEmitOnError: true }),
],
});
5 changes: 4 additions & 1 deletion packages/shared/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ export default defineConfig({
'@': join(__dirname, 'src'),
},
},
plugins: [dts(), typescript({ noEmitOnError: true })],
plugins: [
dts({ compilerOptions: { declarationMap: true } }),
typescript({ noEmitOnError: true }),
],
});
5 changes: 4 additions & 1 deletion packages/vite-plugin-r-html/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@ export default defineConfig({
'@': join(__dirname, 'src'),
},
},
plugins: [dts(), typescript({ noEmitOnError: true })],
plugins: [
dts({ compilerOptions: { declarationMap: true } }),
typescript({ noEmitOnError: true }),
],
});
68 changes: 68 additions & 0 deletions packages/vscode-bridge/src/bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { isObject, isString, safeCallback } from '@dineug/shared';

export type Command<P> = {
type: string;
};

export type CommandListener<P> = (payload: P) => void;

export type CommandPayload<T extends Command<unknown>> =
T extends Command<infer P> ? P : never;

export type Dispose = () => void;

export type AnyAction<P = any> = {
type: string;
payload: P;
};

export function createCommand<T = void>(type: string): Command<T> {
return { type };
}

export class Bridge {
#commands = new Map<string, Set<CommandListener<any>>>();

static mergeRegister(...disposables: Dispose[]) {
return () => {
disposables.forEach(dispose => dispose());
};
}

static executeCommand<T extends Command<unknown>>(
command: T,
payload: CommandPayload<T>
): AnyAction {
return {
type: command.type,
payload,
};
}

registerCommand<P>(
command: Command<P>,
listener: CommandListener<P>
): Dispose {
const listeners = this.#commands.get(command.type) ?? new Set();
listeners.add(listener);

if (!this.#commands.has(command.type)) {
this.#commands.set(command.type, listeners);
}

return () => {
listeners.delete(listener);
};
}

executeAction(action: AnyAction) {
if (!isAction(action)) return;

const listeners = this.#commands.get(action.type);
listeners?.forEach(listener => safeCallback(listener, action.payload));
}
}

function isAction(action: AnyAction) {
return isObject(action) && isString(action.type);
}
42 changes: 42 additions & 0 deletions packages/vscode-bridge/src/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createCommand } from './bridge';
import { type ThemeOptions } from './theme';

type Base64 = string;

export const hostExportFileCommand = createCommand<{
value: Base64;
fileName: string;
}>('hostExportFileCommand');
export const hostImportFileCommand = createCommand<{
type: 'json' | 'sql';
op: 'set' | 'diff';
accept: string;
}>('hostImportFileCommand');
export const hostInitialCommand = createCommand('hostInitialCommand');
export const hostSaveValueCommand = createCommand<{
value: string;
}>('hostSaveValueCommand');
export const hostSaveReplicationCommand = createCommand<{
actions: any;
}>('hostSaveReplicationCommand');
export const hostSaveThemeCommand = createCommand<ThemeOptions>(
'hostSaveThemeCommand'
);

export const webviewImportFileCommand = createCommand<{
type: 'json' | 'sql';
op: 'set' | 'diff';
value: string;
}>('webviewImportFileCommand');
export const webviewInitialValueCommand = createCommand<{
value: string;
}>('webviewInitialValueCommand');
export const webviewUpdateThemeCommand = createCommand<Partial<ThemeOptions>>(
'webviewUpdateThemeCommand'
);
export const webviewUpdateReadonlyCommand = createCommand<boolean>(
'webviewUpdateReadonlyCommand'
);
export const webviewReplicationCommand = createCommand<{
actions: any;
}>('webviewReplicationCommand');
Loading

0 comments on commit 48bfac5

Please sign in to comment.