From be9f44fc4f69e11533dda5c1a2957c5d1024217d Mon Sep 17 00:00:00 2001 From: doodlewind <7312949+doodlewind@users.noreply.github.com> Date: Fri, 14 Mar 2025 05:26:57 +0000 Subject: [PATCH] fix(editor): worker loading in webpack env (#10832) ### TL;DR Created dedicated worker entry points to avoid dynamic imports. ### What changed? - Painters are provided during worker initialization - Removed `ParagraphPaintConfigExtension` and the associated configuration system - Created dedicated worker entry points in both the integration test and frontend packages - Modified `ViewportLayoutPainter` to accept painters in its constructor - Updated the `TurboRendererConfig` interface to require a `painterWorkerEntry` function ### Why make this change? Webpack support. Extension objects in main thread are not available to be passed into workers. Dynamic painter path import is hard to support in webpack environment. With the [webpack-ignore](https://webpack.js.org/api/module-methods/#webpackignore) rule, there are still build errors in webpack. --- .../blocks/block-paragraph/src/index.ts | 2 +- .../src/turbo/paragraph-painter-config.ts | 16 ------- .../src/turbo/paragraph-painter.worker.ts | 21 ++++++--- .../affine/gfx/turbo-renderer/src/index.ts | 1 + .../src/painter/painter.worker.ts | 43 +++++++++---------- .../gfx/turbo-renderer/src/turbo-renderer.ts | 34 ++++++--------- .../affine/gfx/turbo-renderer/src/types.ts | 15 +------ .../edgeless/viewport-renderer.spec.ts | 12 +++++- .../src/__tests__/utils/renderer-entry.ts | 13 ++---- .../src/__tests__/utils/setup.ts | 10 +++++ .../utils/turbo-painter-entry.worker.ts | 6 +++ .../extensions/turbo-painter-entry.worker.ts | 6 +++ .../blocksuite/extensions/turbo-renderer.ts | 20 ++++++--- 13 files changed, 100 insertions(+), 99 deletions(-) delete mode 100644 blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter-config.ts create mode 100644 blocksuite/integration-test/src/__tests__/utils/turbo-painter-entry.worker.ts create mode 100644 packages/frontend/core/src/blocksuite/extensions/turbo-painter-entry.worker.ts diff --git a/blocksuite/affine/blocks/block-paragraph/src/index.ts b/blocksuite/affine/blocks/block-paragraph/src/index.ts index 86bba19416..800f253ef9 100644 --- a/blocksuite/affine/blocks/block-paragraph/src/index.ts +++ b/blocksuite/affine/blocks/block-paragraph/src/index.ts @@ -4,4 +4,4 @@ export * from './paragraph-block.js'; export * from './paragraph-service.js'; export * from './paragraph-spec.js'; export * from './turbo/paragraph-layout-provider.js'; -export * from './turbo/paragraph-painter-config.js'; +export * from './turbo/paragraph-painter.worker.js'; diff --git a/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter-config.ts b/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter-config.ts deleted file mode 100644 index 9e2bd43c75..0000000000 --- a/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter-config.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BlockPainterConfigIdentifier } from '@blocksuite/affine-gfx-turbo-renderer'; -import type { Container } from '@blocksuite/global/di'; -import { Extension } from '@blocksuite/store'; - -export class ParagraphPaintConfigExtension extends Extension { - static override setup(di: Container) { - const config = { - type: 'affine:paragraph', - path: new URL( - '@blocksuite/affine-block-paragraph/turbo-painter', - import.meta.url - ).href, - }; - di.addImpl(BlockPainterConfigIdentifier, config); - } -} diff --git a/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter.worker.ts b/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter.worker.ts index 3256b92043..4bed352a06 100644 --- a/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter.worker.ts +++ b/blocksuite/affine/blocks/block-paragraph/src/turbo/paragraph-painter.worker.ts @@ -39,16 +39,23 @@ function isParagraphLayout(layout: BlockLayout): layout is ParagraphLayout { return layout.type === 'affine:paragraph'; } -export default class ParagraphLayoutPainter implements BlockLayoutPainter { - static readonly font = new FontFace( - 'Inter', - `url(https://fonts.gstatic.com/s/inter/v18/UcCo3FwrK3iLTcviYwYZ8UA3.woff2)` - ); +export class ParagraphLayoutPainter implements BlockLayoutPainter { + private static readonly supportFontFace = + typeof FontFace !== 'undefined' && + typeof self !== 'undefined' && + 'fonts' in self; - static fontLoaded = false; + static readonly font = ParagraphLayoutPainter.supportFontFace + ? new FontFace( + 'Inter', + `url(https://fonts.gstatic.com/s/inter/v18/UcCo3FwrK3iLTcviYwYZ8UA3.woff2)` + ) + : null; + + static fontLoaded = !ParagraphLayoutPainter.supportFontFace; static { - if (typeof self !== 'undefined' && 'fonts' in self) { + if (ParagraphLayoutPainter.supportFontFace && ParagraphLayoutPainter.font) { // @ts-expect-error worker fonts API self.fonts.add(ParagraphLayoutPainter.font); diff --git a/blocksuite/affine/gfx/turbo-renderer/src/index.ts b/blocksuite/affine/gfx/turbo-renderer/src/index.ts index 2d5533a045..5d088a41de 100644 --- a/blocksuite/affine/gfx/turbo-renderer/src/index.ts +++ b/blocksuite/affine/gfx/turbo-renderer/src/index.ts @@ -1,4 +1,5 @@ export * from './layout/block-layout-provider'; +export * from './painter/painter.worker'; export * from './text-utils'; export * from './turbo-renderer'; export * from './types'; diff --git a/blocksuite/affine/gfx/turbo-renderer/src/painter/painter.worker.ts b/blocksuite/affine/gfx/turbo-renderer/src/painter/painter.worker.ts index b26daaee21..f3a8cd18bd 100644 --- a/blocksuite/affine/gfx/turbo-renderer/src/painter/painter.worker.ts +++ b/blocksuite/affine/gfx/turbo-renderer/src/painter/painter.worker.ts @@ -17,12 +17,19 @@ class BlockPainterRegistry { } } -class ViewportLayoutPainter { +export class ViewportLayoutPainter { private readonly canvas: OffscreenCanvas = new OffscreenCanvas(0, 0); private ctx: OffscreenCanvasRenderingContext2D | null = null; private zoom = 1; public readonly registry = new BlockPainterRegistry(); + constructor(painters: Record) { + Object.entries(painters).forEach(([type, painter]) => { + this.registry.register(type, painter); + }); + self.onmessage = this.handler; + } + setSize(layoutRectW: number, layoutRectH: number, dpr: number, zoom: number) { const width = layoutRectW * dpr * zoom; const height = layoutRectH * dpr * zoom; @@ -66,26 +73,16 @@ class ViewportLayoutPainter { }; self.postMessage(message, { transfer: [bitmap] }); } + + handler = async (e: MessageEvent) => { + const { type, data } = e.data; + switch (type) { + case 'paintLayout': { + const { layout, width, height, dpr, zoom, version } = data; + this.setSize(width, height, dpr, zoom); + this.paint(layout, version); + break; + } + } + }; } - -const painter = new ViewportLayoutPainter(); - -self.onmessage = async (e: MessageEvent) => { - const { type, data } = e.data; - - switch (type) { - case 'paintLayout': { - const { layout, width, height, dpr, zoom, version } = data; - painter.setSize(width, height, dpr, zoom); - painter.paint(layout, version); - break; - } - case 'registerPainter': { - const { painterConfigs } = data; - painterConfigs.forEach(async ({ type, path }) => { - painter.registry.register(type, new (await import(path)).default()); - }); - break; - } - } -}; diff --git a/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts b/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts index 7f043c4b7c..9e8ea357d4 100644 --- a/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts +++ b/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts @@ -6,7 +6,6 @@ import { type GfxViewportElement, } from '@blocksuite/block-std/gfx'; import type { Container } from '@blocksuite/global/di'; -import { createIdentifier } from '@blocksuite/global/di'; import { DisposableGroup } from '@blocksuite/global/disposable'; import debounce from 'lodash-es/debounce'; @@ -18,9 +17,7 @@ import { syncCanvasSize, } from './renderer-utils'; import type { - BlockPainterConfig, MessagePaint, - MessageRegisterPainter, RendererOptions, RenderingState, TurboRendererConfig, @@ -29,16 +26,12 @@ import type { } from './types'; const debug = false; // Toggle for debug logs -const workerUrl = new URL('./painter/painter.worker.ts', import.meta.url); const defaultOptions: RendererOptions = { zoomThreshold: 1, // With high enough zoom, fallback to DOM rendering debounceTime: 1000, // During this period, fallback to DOM }; -export const BlockPainterConfigIdentifier = - createIdentifier('block-painter-config'); - export const TurboRendererConfigFactory = ConfigExtensionFactory('viewport-turbo-renderer'); @@ -47,13 +40,24 @@ export class ViewportTurboRendererExtension extends GfxExtension { public state: RenderingState = 'inactive'; public readonly canvas: HTMLCanvasElement = document.createElement('canvas'); - private readonly worker: Worker = new Worker(workerUrl, { type: 'module' }); + private readonly worker: Worker; private readonly disposables = new DisposableGroup(); private layoutCacheData: ViewportLayout | null = null; private layoutVersion = 0; private bitmap: ImageBitmap | null = null; private viewportElement: GfxViewportElement | null = null; + constructor(gfx: GfxController) { + super(gfx); + + const id = TurboRendererConfigFactory.identifier; + const config = this.std.getOptional(id); + if (!config) { + throw new Error('TurboRendererConfig not found'); + } + this.worker = config.painterWorkerEntry(); + } + static override extendGfx(gfx: GfxController) { Object.defineProperty(gfx, 'turboRenderer', { get() { @@ -75,13 +79,6 @@ export class ViewportTurboRendererExtension extends GfxExtension { }; } - get painterConfigs() { - const painterConfigMap = this.std.provider.getAll( - BlockPainterConfigIdentifier - ); - return Array.from(painterConfigMap.values()); - } - override mounted() { const mountPoint = document.querySelector('.affine-edgeless-viewport'); if (mountPoint) { @@ -123,13 +120,6 @@ export class ViewportTurboRendererExtension extends GfxExtension { this.disposables.add( this.std.store.slots.blockUpdated.subscribe(() => this.invalidate()) ); - - const painterConfigs = this.painterConfigs; - const message: MessageRegisterPainter = { - type: 'registerPainter', - data: { painterConfigs }, - }; - this.worker.postMessage(message); } override unmounted() { diff --git a/blocksuite/affine/gfx/turbo-renderer/src/types.ts b/blocksuite/affine/gfx/turbo-renderer/src/types.ts index 15c873897a..146efc3e54 100644 --- a/blocksuite/affine/gfx/turbo-renderer/src/types.ts +++ b/blocksuite/affine/gfx/turbo-renderer/src/types.ts @@ -83,20 +83,9 @@ export interface RendererOptions { debounceTime: number; } -export interface BlockPainterConfig { - type: string; - path: string; -} - export interface TurboRendererConfig { options?: Partial; + painterWorkerEntry: () => Worker; } -export type MessageRegisterPainter = { - type: 'registerPainter'; - data: { - painterConfigs: BlockPainterConfig[]; - }; -}; - -export type HostToWorkerMessage = MessagePaint | MessageRegisterPainter; +export type HostToWorkerMessage = MessagePaint; diff --git a/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts b/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts index 2d3d35e6f7..52613603b6 100644 --- a/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts +++ b/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts @@ -1,13 +1,21 @@ -import { ViewportTurboRendererExtension } from '@blocksuite/affine-gfx-turbo-renderer'; +import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph'; +import { + TurboRendererConfigFactory, + ViewportTurboRendererExtension, +} from '@blocksuite/affine-gfx-turbo-renderer'; import { beforeEach, describe, expect, test } from 'vitest'; import { wait } from '../utils/common.js'; import { addSampleNotes } from '../utils/doc-generator.js'; -import { setupEditor } from '../utils/setup.js'; +import { createPainterWorker, setupEditor } from '../utils/setup.js'; describe('viewport turbo renderer', () => { beforeEach(async () => { const cleanup = await setupEditor('edgeless', [ + ParagraphLayoutHandlerExtension, + TurboRendererConfigFactory({ + painterWorkerEntry: createPainterWorker, + }), ViewportTurboRendererExtension, ]); return cleanup; diff --git a/blocksuite/integration-test/src/__tests__/utils/renderer-entry.ts b/blocksuite/integration-test/src/__tests__/utils/renderer-entry.ts index 9fe51b3719..182cc55be2 100644 --- a/blocksuite/integration-test/src/__tests__/utils/renderer-entry.ts +++ b/blocksuite/integration-test/src/__tests__/utils/renderer-entry.ts @@ -1,7 +1,4 @@ -import { - ParagraphLayoutHandlerExtension, - ParagraphPaintConfigExtension, -} from '@blocksuite/affine/blocks/paragraph'; +import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph'; import { TurboRendererConfigFactory, ViewportTurboRendererExtension, @@ -9,17 +6,13 @@ import { } from '@blocksuite/affine/gfx/turbo-renderer'; import { addSampleNotes } from './doc-generator.js'; -import { setupEditor } from './setup.js'; +import { createPainterWorker, setupEditor } from './setup.js'; async function init() { setupEditor('edgeless', [ ParagraphLayoutHandlerExtension, - ParagraphPaintConfigExtension, TurboRendererConfigFactory({ - options: { - zoomThreshold: 1, - debounceTime: 1000, - }, + painterWorkerEntry: createPainterWorker, }), ViewportTurboRendererExtension, ]); diff --git a/blocksuite/integration-test/src/__tests__/utils/setup.ts b/blocksuite/integration-test/src/__tests__/utils/setup.ts index fac26f4c2b..3ab759dc68 100644 --- a/blocksuite/integration-test/src/__tests__/utils/setup.ts +++ b/blocksuite/integration-test/src/__tests__/utils/setup.ts @@ -99,6 +99,16 @@ async function createEditor( return app; } +export function createPainterWorker() { + const worker = new Worker( + new URL('./turbo-painter-entry.worker.ts', import.meta.url), + { + type: 'module', + } + ); + return worker; +} + export async function setupEditor( mode: DocMode = 'page', extensions: ExtensionType[] = [] diff --git a/blocksuite/integration-test/src/__tests__/utils/turbo-painter-entry.worker.ts b/blocksuite/integration-test/src/__tests__/utils/turbo-painter-entry.worker.ts new file mode 100644 index 0000000000..93dcba993d --- /dev/null +++ b/blocksuite/integration-test/src/__tests__/utils/turbo-painter-entry.worker.ts @@ -0,0 +1,6 @@ +import { ParagraphLayoutPainter } from '@blocksuite/affine-block-paragraph/turbo-painter'; +import { ViewportLayoutPainter } from '@blocksuite/affine-gfx-turbo-renderer/painter'; + +new ViewportLayoutPainter({ + 'affine:paragraph': new ParagraphLayoutPainter(), +}); diff --git a/packages/frontend/core/src/blocksuite/extensions/turbo-painter-entry.worker.ts b/packages/frontend/core/src/blocksuite/extensions/turbo-painter-entry.worker.ts new file mode 100644 index 0000000000..f160f66087 --- /dev/null +++ b/packages/frontend/core/src/blocksuite/extensions/turbo-painter-entry.worker.ts @@ -0,0 +1,6 @@ +import { ParagraphLayoutPainter } from '@blocksuite/affine/blocks/paragraph'; +import { ViewportLayoutPainter } from '@blocksuite/affine/gfx/turbo-renderer'; + +new ViewportLayoutPainter({ + 'affine:paragraph': new ParagraphLayoutPainter(), +}); diff --git a/packages/frontend/core/src/blocksuite/extensions/turbo-renderer.ts b/packages/frontend/core/src/blocksuite/extensions/turbo-renderer.ts index 82dc789204..c8cc85805d 100644 --- a/packages/frontend/core/src/blocksuite/extensions/turbo-renderer.ts +++ b/packages/frontend/core/src/blocksuite/extensions/turbo-renderer.ts @@ -1,21 +1,31 @@ -import { - ParagraphLayoutHandlerExtension, - ParagraphPaintConfigExtension, -} from '@blocksuite/affine/blocks/paragraph'; +import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph'; import { TurboRendererConfigFactory, ViewportTurboRendererExtension, } from '@blocksuite/affine/gfx/turbo-renderer'; +function createPainterWorker() { + const worker = new Worker( + /* webpackChunkName: "turbo-painter-entry" */ new URL( + './turbo-painter-entry.worker.ts', + import.meta.url + ), + { + type: 'module', + } + ); + return worker; +} + export function patchTurboRendererExtension() { return [ ParagraphLayoutHandlerExtension, - ParagraphPaintConfigExtension, TurboRendererConfigFactory({ options: { zoomThreshold: 1, debounceTime: 1000, }, + painterWorkerEntry: createPainterWorker, }), ViewportTurboRendererExtension, ];