diff --git a/.prettierignore b/.prettierignore index 3540c38c4..d65a1b317 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,3 +12,5 @@ tests/affine-legacy/0.7.0-canary.18/static .github/helm _next storybook-static +web-static +public diff --git a/apps/core/.webpack/config.ts b/apps/core/.webpack/config.ts index 754f06d0c..db2a00d46 100644 --- a/apps/core/.webpack/config.ts +++ b/apps/core/.webpack/config.ts @@ -276,12 +276,13 @@ export const createConfiguration: ( devServer: { hot: 'only', - liveReload: false, + liveReload: true, client: undefined, historyApiFallback: true, static: { directory: resolve(rootPath, 'public'), publicPath: '/', + watch: true, }, } as DevServerConfiguration, } satisfies webpack.Configuration; diff --git a/apps/core/.webpack/template.html b/apps/core/.webpack/template.html index b7045d454..bb59e2420 100644 --- a/apps/core/.webpack/template.html +++ b/apps/core/.webpack/template.html @@ -38,6 +38,7 @@ rel="shortcut icon" href="https://affine.pro/favicon.ico" /> +
diff --git a/apps/core/package.json b/apps/core/package.json index 45ac234e1..1c24af31f 100644 --- a/apps/core/package.json +++ b/apps/core/package.json @@ -10,7 +10,6 @@ }, "dependencies": { "@affine-test/fixtures": "workspace:*", - "@affine/bookmark-block": "workspace:*", "@affine/component": "workspace:*", "@affine/copilot": "workspace:*", "@affine/debug": "workspace:*", @@ -53,6 +52,7 @@ "react-resizable-panels": "^0.0.53", "react-router-dom": "^6.14.1", "rxjs": "^7.8.1", + "ses": "^0.18.5", "swr": "^2.1.5", "y-protocols": "^1.0.5", "yjs": "^13.6.6", diff --git a/apps/core/project.json b/apps/core/project.json index e87a65e40..cc82bd236 100644 --- a/apps/core/project.json +++ b/apps/core/project.json @@ -45,7 +45,7 @@ "options": { "script": "build" }, - "outputs": ["{projectRoot}/dist"] + "outputs": ["{projectRoot}/dist", "{projectRoot}/public/plugins"] } } } diff --git a/apps/core/public/.gitignore b/apps/core/public/.gitignore index 69a24fb74..2e65c91a6 100644 --- a/apps/core/public/.gitignore +++ b/apps/core/public/.gitignore @@ -1,2 +1,4 @@ *.js -*.map \ No newline at end of file +*.map + +plugins diff --git a/apps/core/src/app.tsx b/apps/core/src/app.tsx index bd692d17c..bd3f72a97 100644 --- a/apps/core/src/app.tsx +++ b/apps/core/src/app.tsx @@ -48,8 +48,6 @@ currentWorkspaceIdAtom.onMount = set => { if (value) { set(value); localStorage.setItem('last_workspace_id', value); - } else { - set(null); } }; callback(router.state); @@ -65,8 +63,6 @@ currentPageIdAtom.onMount = set => { const value = state.location.pathname.split('/')[3]; if (value) { set(value); - } else { - set(null); } }; callback(router.state); diff --git a/apps/core/src/atoms/layout.ts b/apps/core/src/atoms/layout.ts deleted file mode 100644 index 0237ef5cd..000000000 --- a/apps/core/src/atoms/layout.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { ExpectedLayout } from '@toeverything/plugin-infra/type'; -import { atom } from 'jotai'; - -export const contentLayoutBaseAtom = atom('editor'); - -type SetStateAction = Value | ((prev: Value) => Value); -export const contentLayoutAtom = atom< - ExpectedLayout, - [SetStateAction], - void ->( - get => get(contentLayoutBaseAtom), - (get, set, layout) => { - set(contentLayoutBaseAtom, prev => { - let setV: (prev: ExpectedLayout) => ExpectedLayout; - if (typeof layout !== 'function') { - setV = () => layout; - } else { - setV = layout; - } - const nextValue = setV(prev); - if (nextValue === 'editor') { - return nextValue; - } - if (nextValue.first !== 'editor') { - throw new Error('The first element of the layout should be editor.'); - } - if (nextValue.splitPercentage && nextValue.splitPercentage < 70) { - throw new Error('The split percentage should be greater than 70.'); - } - return nextValue; - }); - } -); diff --git a/apps/core/src/bootstrap/register-plugins.ts b/apps/core/src/bootstrap/register-plugins.ts index 15d6eec7e..117d92218 100644 --- a/apps/core/src/bootstrap/register-plugins.ts +++ b/apps/core/src/bootstrap/register-plugins.ts @@ -1,6 +1,181 @@ -import('@affine/bookmark-block'); -if (runtimeConfig.enablePlugin) { - import('@affine/copilot'); -} +/// +import 'ses'; + +import * as AFFiNEComponent from '@affine/component'; +import * as BlockSuiteBlocksStd from '@blocksuite/blocks/std'; +import { DisposableGroup } from '@blocksuite/global/utils'; +import * as BlockSuiteGlobalUtils from '@blocksuite/global/utils'; +import * as Icons from '@blocksuite/icons'; +import type { + CallbackMap, + PluginContext, +} from '@toeverything/plugin-infra/entry'; +import * as Manager from '@toeverything/plugin-infra/manager'; +import { + editorItemsAtom, + headerItemsAtom, + registeredPluginAtom, + rootStore, + windowItemsAtom, +} from '@toeverything/plugin-infra/manager'; +import * as Jotai from 'jotai'; +import { Provider } from 'jotai/react'; +import * as JotaiUtils from 'jotai/utils'; +import type { PropsWithChildren } from 'react'; +import * as React from 'react'; +import * as ReactJSXRuntime from 'react/jsx-runtime'; +import * as ReactDom from 'react-dom'; +import * as ReactDomClient from 'react-dom/client'; + +const PluginProvider = ({ children }: PropsWithChildren) => + React.createElement( + Provider, + { + store: rootStore, + }, + children + ); + +console.log('JotaiUtils', JotaiUtils); + +const customRequire = (id: string) => { + if (id === '@toeverything/plugin-infra/manager') { + return harden(Manager); + } + if (id === 'react') { + return React; + } + if (id === 'react/jsx-runtime') { + return ReactJSXRuntime; + } + if (id === 'react-dom') { + return ReactDom; + } + if (id === 'react-dom/client') { + return ReactDomClient; + } + if (id === '@blocksuite/icons') { + return harden(Icons); + } + if (id === '@affine/component') { + return harden(AFFiNEComponent); + } + if (id === '@blocksuite/blocks/std') { + return harden(BlockSuiteBlocksStd); + } + if (id === '@blocksuite/global/utils') { + return harden(BlockSuiteGlobalUtils); + } + if (id === 'jotai') { + return harden(Jotai); + } + if (id === 'jotai/utils') { + return harden(JotaiUtils); + } + if (id === '../plugin.js') { + return entryCompartment.evaluate('exports'); + } + throw new Error(`Cannot find module '${id}'`); +}; + +const createGlobalThis = () => { + return { + process: harden({ + env: { + NODE_ENV: process.env.NODE_ENV, + }, + }), + // UNSAFE: React will read `window` and `document` + window, + document, + navigator, + userAgent: navigator.userAgent, + + // fixme: use our own db api + indexedDB: globalThis.indexedDB, + IDBRequest: globalThis.IDBRequest, + IDBDatabase: globalThis.IDBDatabase, + IDBCursorWithValue: globalThis.IDBCursorWithValue, + IDBFactory: globalThis.IDBFactory, + IDBKeyRange: globalThis.IDBKeyRange, + IDBOpenDBRequest: globalThis.IDBOpenDBRequest, + IDBTransaction: globalThis.IDBTransaction, + IDBObjectStore: globalThis.IDBObjectStore, + IDBIndex: globalThis.IDBIndex, + IDBCursor: globalThis.IDBCursor, + IDBVersionChangeEvent: globalThis.IDBVersionChangeEvent, + + exports: {}, + console: globalThis.console, + require: customRequire, + }; +}; + +const group = new DisposableGroup(); +const pluginList = await ( + await fetch(new URL(`./plugins/plugin-list.json`, window.location.origin)) +).json(); +const builtInPlugins: string[] = pluginList.map((plugin: any) => plugin.name); +const pluginGlobalThis = createGlobalThis(); +const pluginEntry = await fetch('/plugins/plugin.js').then(res => res.text()); +const entryCompartment = new Compartment(pluginGlobalThis, {}); +entryCompartment.evaluate(pluginEntry, { + __evadeHtmlCommentTest__: true, +}); +await Promise.all( + builtInPlugins.map(plugin => { + const pluginCompartment = new Compartment(createGlobalThis(), {}); + const pluginGlobalThis = pluginCompartment.globalThis; + const baseURL = new URL(`./plugins/${plugin}/`, window.location.origin); + const packageJsonURL = new URL('package.json', baseURL); + return fetch(packageJsonURL).then(async res => { + const packageJson = await res.json(); + const pluginConfig = packageJson['affinePlugin']; + if ( + pluginConfig.release === false && + process.env.NODE_ENV !== 'development' + ) { + return; + } + rootStore.set(registeredPluginAtom, prev => [...prev, plugin]); + const coreEntry = new URL(pluginConfig.entry.core, baseURL.toString()); + const codeText = await fetch(coreEntry).then(res => res.text()); + pluginCompartment.evaluate(codeText); + pluginGlobalThis.__INTERNAL__ENTRY = { + register: (part, callback) => { + if (part === 'headerItem') { + rootStore.set(headerItemsAtom, items => ({ + ...items, + [plugin]: callback as CallbackMap['headerItem'], + })); + } else if (part === 'editor') { + rootStore.set(editorItemsAtom, items => ({ + ...items, + [plugin]: callback as CallbackMap['editor'], + })); + } else if (part === 'window') { + rootStore.set(windowItemsAtom, items => ({ + ...items, + [plugin]: callback as CallbackMap['window'], + })); + } else { + throw new Error(`Unknown part: ${part}`); + } + }, + utils: { + PluginProvider, + }, + } satisfies PluginContext; + const dispose = pluginCompartment.evaluate( + 'exports.entry(__INTERNAL__ENTRY)' + ); + if (typeof dispose !== 'function') { + throw new Error('Plugin entry must return a function'); + } + pluginGlobalThis.__INTERNAL__ENTRY = undefined; + group.add(dispose); + }); + }) +); console.log('register plugins finished'); diff --git a/apps/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx b/apps/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx index 5cf89174d..4ecacf836 100644 --- a/apps/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx +++ b/apps/core/src/components/affine/setting-modal/general-setting/plugins/index.tsx @@ -1,22 +1,25 @@ -import { SettingHeader } from '@affine/component/setting-components'; +import { + SettingHeader, + SettingWrapper, +} from '@affine/component/setting-components'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { affinePluginsAtom } from '@toeverything/plugin-infra/manager'; +import { registeredPluginAtom } from '@toeverything/plugin-infra/manager'; import { useAtomValue } from 'jotai'; export const Plugins = () => { const t = useAFFiNEI18N(); - const plugins = useAtomValue(affinePluginsAtom); + const allowedPlugins = useAtomValue(registeredPluginAtom); + console.log('allowedPlugins', allowedPlugins); return ( <> - {Object.values(plugins).map(({ definition, uiAdapter }) => { - const Content = uiAdapter.debugContent; - return
{Content && }
; - })} + {allowedPlugins.map(plugin => ( + + ))} ); }; diff --git a/apps/core/src/components/blocksuite/workspace-header/header.tsx b/apps/core/src/components/blocksuite/workspace-header/header.tsx index df76cb7b2..716272f24 100644 --- a/apps/core/src/components/blocksuite/workspace-header/header.tsx +++ b/apps/core/src/components/blocksuite/workspace-header/header.tsx @@ -7,20 +7,18 @@ import { SidebarSwitch } from '@affine/component/app-sidebar/sidebar-header'; import { isDesktop } from '@affine/env/constant'; import { CloseIcon, MinusIcon, RoundedRectangleIcon } from '@blocksuite/icons'; import type { Page } from '@blocksuite/store'; -import { affinePluginsAtom } from '@toeverything/plugin-infra/manager'; -import type { PluginUIAdapter } from '@toeverything/plugin-infra/type'; +import { headerItemsAtom } from '@toeverything/plugin-infra/manager'; import { useAtomValue } from 'jotai'; import type { FC, HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; import { forwardRef, - memo, useCallback, useEffect, useMemo, + useRef, useState, } from 'react'; -import { contentLayoutAtom } from '../../../atoms/layout'; import { currentModeAtom } from '../../../atoms/mode'; import type { AffineOfficialWorkspace } from '../../../shared'; import DownloadClientTip from './download-tips'; @@ -124,41 +122,37 @@ const HeaderRightItems: Record = { export type HeaderProps = BaseHeaderProps; -const PluginHeaderItemAdapter = memo<{ - headerItem: PluginUIAdapter['headerItem']; -}>(function PluginHeaderItemAdapter({ headerItem }) { - return ( -
- {headerItem({ - contentLayoutAtom, - })} -
- ); -}); - const PluginHeader = () => { - const affinePluginsMap = useAtomValue(affinePluginsAtom); - const plugins = useMemo( - () => Object.values(affinePluginsMap), - [affinePluginsMap] - ); + const rootRef = useRef(null); + const headerItems = useAtomValue(headerItemsAtom); + useEffect(() => { + const root = rootRef.current; + if (!root) { + return; + } + let disposes: (() => void)[] = []; + const renderTimeout = setTimeout(() => { + disposes = Object.entries(headerItems).map(([id, headerItem]) => { + const div = document.createElement('div'); + div.setAttribute('plugin-id', id); + const cleanup = headerItem(div); + root.appendChild(div); + return () => { + cleanup(); + root.removeChild(div); + }; + }); + }); - return ( -
- {plugins - .filter(plugin => plugin.uiAdapter.headerItem != null) - .map(plugin => { - const headerItem = plugin.uiAdapter - .headerItem as PluginUIAdapter['headerItem']; - return ( - - ); - })} -
- ); + return () => { + clearTimeout(renderTimeout); + setTimeout(() => { + disposes.forEach(dispose => dispose()); + }); + }; + }, [headerItems]); + + return
; }; export const Header = forwardRef< diff --git a/apps/core/src/components/blocksuite/workspace-header/styles.css.ts b/apps/core/src/components/blocksuite/workspace-header/styles.css.ts index 28a70e34d..38c026e9d 100644 --- a/apps/core/src/components/blocksuite/workspace-header/styles.css.ts +++ b/apps/core/src/components/blocksuite/workspace-header/styles.css.ts @@ -238,3 +238,10 @@ export const windowAppControl = style({ }, }, } as ComplexStyleRule); + +export const pluginHeaderItems = style({ + display: 'flex', + gap: '12px', + alignItems: 'center', + height: '100%', +}); diff --git a/apps/core/src/components/page-detail-editor.tsx b/apps/core/src/components/page-detail-editor.tsx index 70935fe7d..9da3611b7 100644 --- a/apps/core/src/components/page-detail-editor.tsx +++ b/apps/core/src/components/page-detail-editor.tsx @@ -7,21 +7,22 @@ import { assertExists } from '@blocksuite/global/utils'; import type { Page, Workspace } from '@blocksuite/store'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page'; -import { affinePluginsAtom } from '@toeverything/plugin-infra/manager'; -import type { - AffinePlugin, - LayoutNode, - PluginUIAdapter, -} from '@toeverything/plugin-infra/type'; -import type { PluginBlockSuiteAdapter } from '@toeverything/plugin-infra/type'; +import type { CallbackMap } from '@toeverything/plugin-infra/entry'; +import { + affinePluginsAtom, + contentLayoutAtom, + editorItemsAtom, + rootStore, + windowItemsAtom, +} from '@toeverything/plugin-infra/manager'; +import type { AffinePlugin, LayoutNode } from '@toeverything/plugin-infra/type'; import clsx from 'clsx'; import { useAtomValue, useSetAtom } from 'jotai'; import type { CSSProperties, FC, ReactElement } from 'react'; -import { memo, Suspense, useCallback, useMemo } from 'react'; +import { memo, Suspense, useCallback, useEffect, useMemo, useRef } from 'react'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { pageSettingFamily } from '../atoms'; -import { contentLayoutAtom } from '../atoms/layout'; import { fontStyleOptions, useAppSetting } from '../atoms/settings'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; import * as styles from './page-detail-editor.css'; @@ -42,11 +43,6 @@ const EditorWrapper = memo(function EditorWrapper({ onLoad, isPublic, }: PageDetailEditorProps) { - const affinePluginsMap = useAtomValue(affinePluginsAtom); - const plugins = useMemo( - () => Object.values(affinePluginsMap), - [affinePluginsMap] - ); const page = useBlockSuiteWorkspacePage(workspace, pageId); if (!page) { throw new PageNotFoundError(workspace, pageId); @@ -100,33 +96,65 @@ const EditorWrapper = memo(function EditorWrapper({ if (onLoad) { dispose = onLoad(page, editor); } - const uiDecorators = plugins - .map(plugin => plugin.blockSuiteAdapter.uiDecorator) - .filter((ui): ui is PluginBlockSuiteAdapter['uiDecorator'] => - Boolean(ui) - ); - const disposes = uiDecorators.map(ui => ui(editor)); + const editorItems = rootStore.get(editorItemsAtom); + let disposes: (() => void)[] = []; + const renderTimeout = setTimeout(() => { + disposes = Object.entries(editorItems).map(([id, editorItem]) => { + const div = document.createElement('div'); + div.setAttribute('plugin-id', id); + const cleanup = editorItem(div, editor); + assertExists(parent); + document.body.appendChild(div); + return () => { + cleanup(); + document.body.removeChild(div); + }; + }); + }); + return () => { - disposes.forEach(fn => fn()); dispose(); + clearTimeout(renderTimeout); + setTimeout(() => { + disposes.forEach(dispose => dispose()); + }); }; }, - [plugins, onLoad] + [onLoad] )} /> ); }); const PluginContentAdapter = memo<{ - detailContent: PluginUIAdapter['detailContent']; -}>(function PluginContentAdapter({ detailContent }) { - return ( -
- {detailContent({ - contentLayoutAtom, - })} -
- ); + windowItem: CallbackMap['window']; +}>(function PluginContentAdapter({ windowItem }) { + const ref = useRef(null); + useEffect(() => { + const root = ref.current; + if (!root) { + return; + } + let cleanup: () => void = () => {}; + let childDiv: HTMLDivElement | null = null; + const renderTimeout = setTimeout(() => { + const div = document.createElement('div'); + cleanup = windowItem(div); + root.appendChild(div); + childDiv = div; + }); + + return () => { + clearTimeout(renderTimeout); + setTimeout(() => { + cleanup(); + if (childDiv) { + root.removeChild(childDiv); + } + }); + }; + }, [windowItem]); + return
; }); type LayoutPanelProps = { @@ -139,16 +167,13 @@ const LayoutPanel = memo(function LayoutPanel( props: LayoutPanelProps ): ReactElement { const node = props.node; + const windowItems = useAtomValue(windowItemsAtom); if (typeof node === 'string') { if (node === 'editor') { return ; } else { - const plugin = props.plugins.find( - plugin => plugin.definition.id === node - ); - const Content = plugin?.uiAdapter.detailContent; - assertExists(Content); - return ; + const windowItem = windowItems[node]; + return ; } } else { return ( diff --git a/apps/core/src/pages/workspace/detail-page.tsx b/apps/core/src/pages/workspace/detail-page.tsx index 4b9de35cd..35aa2e1e4 100644 --- a/apps/core/src/pages/workspace/detail-page.tsx +++ b/apps/core/src/pages/workspace/detail-page.tsx @@ -8,7 +8,7 @@ import type { EditorContainer } from '@blocksuite/editor'; import { assertExists } from '@blocksuite/global/utils'; import type { Page } from '@blocksuite/store'; import { currentPageIdAtom } from '@toeverything/plugin-infra/manager'; -import { useAtomValue } from 'jotai/index'; +import { useAtomValue } from 'jotai'; import { useAtom } from 'jotai/react'; import { type ReactElement, useCallback, useEffect } from 'react'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; diff --git a/apps/core/tsconfig.json b/apps/core/tsconfig.json index 1df4ba0cb..b0fa6678f 100644 --- a/apps/core/tsconfig.json +++ b/apps/core/tsconfig.json @@ -46,9 +46,6 @@ "path": "../../packages/workspace" }, - { - "path": "../../plugins/bookmark-block" - }, { "path": "../../plugins/copilot" }, diff --git a/package.json b/package.json index 9bad30018..67e53537f 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,14 @@ }, "scripts": { "dev": "dev-core", + "dev:plugin": "vite build --watch", "dev:electron": "yarn workspace @affine/electron dev:app", "dev:plugins": "./apps/electron/scripts/plugins/dev-plugins.mjs", "build": "yarn nx build @affine/core", "build:electron": "yarn nx build @affine/electron", "build:storage": "yarn nx run-many -t build -p @affine/storage", "build:infra": "yarn nx run-many -t build -p plugin-infra infra", - "build:plugins": "yarn nx run-many -t build -p @affine/bookmark-block", + "build:plugins": "yarn workspace @affine/bookmark-block build && yarn vite build", "build:storybook": "yarn nx build @affine/storybook", "start:web-static": "yarn workspace @affine/core static-server", "start:storybook": "yarn exec serve apps/storybook/storybook-static -l 6006", @@ -76,7 +77,7 @@ "@typescript-eslint/parser": "^5.60.1", "@vanilla-extract/vite-plugin": "^3.8.2", "@vanilla-extract/webpack-plugin": "^2.2.0", - "@vitejs/plugin-react": "^4.0.1", + "@vitejs/plugin-react-swc": "^3.3.2", "@vitest/coverage-istanbul": "^0.32.2", "@vitest/ui": "^0.32.2", "eslint": "^8.44.0", @@ -106,6 +107,7 @@ "typescript": "^5.1.6", "vite": "^4.3.9", "vite-plugin-istanbul": "^4.1.0", + "vite-plugin-static-copy": "^0.17.0", "vite-tsconfig-paths": "^4.2.0", "vitest": "^0.32.2", "vitest-fetch-mock": "^0.2.2", diff --git a/packages/cli/src/bin/build-core.ts b/packages/cli/src/bin/build-core.ts index a51c7260d..131b9a877 100644 --- a/packages/cli/src/bin/build-core.ts +++ b/packages/cli/src/bin/build-core.ts @@ -43,6 +43,12 @@ const flags = { coverage: process.env.COVERAGE === 'true', } satisfies BuildFlags; +spawn('vite', ['build'], { + cwd: projectRoot, + stdio: 'inherit', + shell: true, +}); + spawn( 'node', [ diff --git a/packages/cli/src/bin/dev-core.ts b/packages/cli/src/bin/dev-core.ts index 9eaf1d55f..a3a53934e 100644 --- a/packages/cli/src/bin/dev-core.ts +++ b/packages/cli/src/bin/dev-core.ts @@ -91,6 +91,12 @@ flags.mode = buildFlags.mode as any; flags.channel = buildFlags.channel as any; flags.coverage = buildFlags.coverage; +spawn('vite', ['build', '--watch'], { + cwd: projectRoot, + stdio: 'inherit', + shell: true, +}); + spawn( 'node', [ diff --git a/packages/plugin-infra/package.json b/packages/plugin-infra/package.json index 9fe2ddb16..f09721729 100644 --- a/packages/plugin-infra/package.json +++ b/packages/plugin-infra/package.json @@ -9,6 +9,11 @@ "dist" ], "exports": { + "./entry": { + "type": "./dist/entry.d.ts", + "import": "./dist/entry.js", + "require": "./dist/entry.cjs" + }, "./manager": { "type": "./dist/manager.d.ts", "import": "./dist/manager.js", diff --git a/packages/plugin-infra/src/entry.ts b/packages/plugin-infra/src/entry.ts new file mode 100644 index 000000000..c7454925d --- /dev/null +++ b/packages/plugin-infra/src/entry.ts @@ -0,0 +1,17 @@ +import type { EditorContainer } from '@blocksuite/editor'; +import type { FC } from 'react'; + +export type Part = 'headerItem' | 'editor' | 'window'; + +export type CallbackMap = { + headerItem: (root: HTMLElement) => () => void; + window: (root: HTMLElement) => () => void; + editor: (root: HTMLElement, editor: EditorContainer) => () => void; +}; + +export interface PluginContext { + register: (part: T, callback: CallbackMap[T]) => void; + utils: { + PluginProvider: FC; + }; +} diff --git a/packages/plugin-infra/src/manager.ts b/packages/plugin-infra/src/manager.ts index 82db915b9..6077b47d3 100644 --- a/packages/plugin-infra/src/manager.ts +++ b/packages/plugin-infra/src/manager.ts @@ -3,17 +3,30 @@ import type { Page, Workspace } from '@blocksuite/store'; import { atom, createStore } from 'jotai/vanilla'; import { getWorkspace, waitForWorkspace } from './__internal__/workspace'; -import type { AffinePlugin, Definition, ServerAdapter } from './type'; +import type { CallbackMap } from './entry'; +import type { + AffinePlugin, + Definition, + ExpectedLayout, + ServerAdapter, +} from './type'; import type { Loader, PluginUIAdapter } from './type'; import type { PluginBlockSuiteAdapter } from './type'; -const isServer = typeof window === 'undefined'; -const isClient = typeof window !== 'undefined'; - // global store export const rootStore = createStore(); -// todo: for now every plugin is enabled by default +// id -> HTML element +export const headerItemsAtom = atom>( + {} +); +export const editorItemsAtom = atom>({}); +export const registeredPluginAtom = atom([]); +export const windowItemsAtom = atom>({}); + +/** + * @deprecated + */ export const affinePluginsAtom = atom>>({}); export const currentWorkspaceIdAtom = atom(null); export const currentPageIdAtom = atom(null); @@ -39,6 +52,38 @@ export const currentPageAtom = atom>(async get => { return page; }); +const contentLayoutBaseAtom = atom('editor'); + +type SetStateAction = Value | ((prev: Value) => Value); +export const contentLayoutAtom = atom< + ExpectedLayout, + [SetStateAction], + void +>( + get => get(contentLayoutBaseAtom), + (get, set, layout) => { + set(contentLayoutBaseAtom, prev => { + let setV: (prev: ExpectedLayout) => ExpectedLayout; + if (typeof layout !== 'function') { + setV = () => layout; + } else { + setV = layout; + } + const nextValue = setV(prev); + if (nextValue === 'editor') { + return nextValue; + } + if (nextValue.first !== 'editor') { + throw new Error('The first element of the layout should be editor.'); + } + if (nextValue.splitPercentage && nextValue.splitPercentage < 70) { + throw new Error('The split percentage should be greater than 70.'); + } + return nextValue; + }); + } +); + export function definePlugin( definition: Definition, uiAdapterLoader?: Loader>, @@ -47,8 +92,8 @@ export function definePlugin( ) { const basePlugin = { definition, - uiAdapter: {}, - blockSuiteAdapter: {}, + uiAdapter: undefined, + blockSuiteAdapter: undefined, }; rootStore.set(affinePluginsAtom, plugins => ({ @@ -56,76 +101,21 @@ export function definePlugin( [definition.id]: basePlugin, })); - if (isServer) { - if (serverAdapter) { - console.log('register server adapter'); - serverAdapter - .load() - .then(({ default: adapter }) => { - rootStore.set(affinePluginsAtom, plugins => ({ - ...plugins, - [definition.id]: { - ...basePlugin, - serverAdapter: adapter, - }, - })); - }) - .catch(err => { - console.error(err); - }); - } - } else if (isClient) { - if (blockSuiteAdapter) { - const updateAdapter = (adapter: Partial) => { + if (serverAdapter) { + console.log('register server adapter'); + serverAdapter + .load() + .then(({ default: adapter }) => { rootStore.set(affinePluginsAtom, plugins => ({ ...plugins, [definition.id]: { ...basePlugin, - blockSuiteAdapter: adapter, + serverAdapter: adapter, }, })); - }; - - blockSuiteAdapter - .load() - .then(({ default: adapter }) => updateAdapter(adapter)) - .catch(err => { - console.error('[definePlugin] blockSuiteAdapter error', err); - }); - - if (import.meta.webpackHot) { - blockSuiteAdapter.hotModuleReload(async _ => { - const adapter = (await _).default; - updateAdapter(adapter); - console.info('[HMR] Plugin', definition.id, 'hot reloaded.'); - }); - } - } - if (uiAdapterLoader) { - const updateAdapter = (adapter: Partial) => { - rootStore.set(affinePluginsAtom, plugins => ({ - ...plugins, - [definition.id]: { - ...basePlugin, - uiAdapter: adapter, - }, - })); - }; - - uiAdapterLoader - .load() - .then(({ default: adapter }) => updateAdapter(adapter)) - .catch(err => { - console.error('[definePlugin] blockSuiteAdapter error', err); - }); - - if (import.meta.webpackHot) { - uiAdapterLoader.hotModuleReload(async _ => { - const adapter = (await _).default; - updateAdapter(adapter); - console.info('[HMR] Plugin', definition.id, 'hot reloaded.'); - }); - } - } + }) + .catch(err => { + console.error(err); + }); } } diff --git a/packages/plugin-infra/src/type.ts b/packages/plugin-infra/src/type.ts index 22daf1169..a18962622 100644 --- a/packages/plugin-infra/src/type.ts +++ b/packages/plugin-infra/src/type.ts @@ -185,7 +185,7 @@ export type ServerAdapter = (affine: AFFiNEServer) => () => void; export type AffinePlugin = { definition: Definition; - uiAdapter: Partial; - blockSuiteAdapter: Partial; + uiAdapter: undefined; + blockSuiteAdapter: undefined; serverAdapter?: ServerAdapter; }; diff --git a/packages/plugin-infra/vite.config.ts b/packages/plugin-infra/vite.config.ts index 44ae68c50..549830ebf 100644 --- a/packages/plugin-infra/vite.config.ts +++ b/packages/plugin-infra/vite.config.ts @@ -11,6 +11,7 @@ export default defineConfig({ minify: false, lib: { entry: { + entry: resolve(root, 'src/entry.ts'), type: resolve(root, 'src/type.ts'), manager: resolve(root, 'src/manager.ts'), '__internal__/workspace': resolve( diff --git a/plugins/bookmark-block/README.md b/plugins/bookmark-block/README.md index e29a477b4..09faa5378 100644 --- a/plugins/bookmark-block/README.md +++ b/plugins/bookmark-block/README.md @@ -1,5 +1,7 @@ # `@affine/bookmark-block` +> Moved to [@affine/bookmark-plugin](../bookmark) +> > A block for bookmarking a website ![preview](assets/preview.png) diff --git a/plugins/bookmark-block/package.json b/plugins/bookmark-block/package.json index e7be9fa4f..db24fbe3b 100644 --- a/plugins/bookmark-block/package.json +++ b/plugins/bookmark-block/package.json @@ -12,18 +12,8 @@ "dev": "node ./scripts/dev.mjs" }, "dependencies": { - "@affine/component": "workspace:*", "@toeverything/plugin-infra": "workspace:*", - "foxact": "^0.2.11", "link-preview-js": "^3.0.4" }, - "devDependencies": { - "react": "18.2.0", - "react-dom": "18.2.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - }, "version": "0.7.0-canary.47" } diff --git a/plugins/bookmark-block/src/blocksuite/index.tsx b/plugins/bookmark-block/src/blocksuite/index.tsx deleted file mode 100644 index 38c0aaefa..000000000 --- a/plugins/bookmark-block/src/blocksuite/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { PluginBlockSuiteAdapter } from '@toeverything/plugin-infra/type'; -import { noop } from 'foxact/noop'; -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; - -import { BookMarkUI } from './ui'; - -export default { - uiDecorator: editor => { - if ( - editor.parentElement && - editor.page.awarenessStore.getFlag('enable_bookmark_operation') - ) { - const div = document.createElement('div'); - editor.parentElement.appendChild(div); - const root = createRoot(div); - root.render( - - - - ); - return () => { - root.unmount(); - div.remove(); - }; - } else { - return noop; - } - }, -} satisfies Partial; diff --git a/plugins/bookmark-block/src/index.ts b/plugins/bookmark-block/src/index.ts index 3fd8ef053..97faf8cbe 100644 --- a/plugins/bookmark-block/src/index.ts +++ b/plugins/bookmark-block/src/index.ts @@ -22,26 +22,9 @@ definePlugin( commands: ['com.blocksuite.bookmark-block.get-bookmark-data-by-link'], }, undefined, + undefined, { - load: () => import('./blocksuite/index'), - hotModuleReload: onHot => - import.meta.webpackHot && - import.meta.webpackHot.accept('./blocksuite', () => - onHot(import('./blocksuite/index')) - ), - }, - { - load: () => - import( - /* webpackIgnore: true */ - './server' - ), - hotModuleReload: onHot => - onHot( - import( - /* webpackIgnore: true */ - './server' - ) - ), + load: () => import('./server'), + hotModuleReload: onHot => onHot(import('./server')), } ); diff --git a/plugins/bookmark/package.json b/plugins/bookmark/package.json new file mode 100644 index 000000000..358b1352f --- /dev/null +++ b/plugins/bookmark/package.json @@ -0,0 +1,16 @@ +{ + "name": "@affine/bookmark-plugin", + "version": "0.1.0", + "affinePlugin": { + "release": true, + "entry": { + "core": "./src/index.ts" + } + }, + "dependencies": { + "@affine/component": "workspace:*", + "@blocksuite/icons": "^2.1.25", + "@toeverything/plugin-infra": "workspace:*", + "foxact": "^0.2.11" + } +} diff --git a/plugins/bookmark-block/src/blocksuite/ui.tsx b/plugins/bookmark/src/app.tsx similarity index 94% rename from plugins/bookmark-block/src/blocksuite/ui.tsx rename to plugins/bookmark/src/app.tsx index be776ba3e..6e0421bcb 100644 --- a/plugins/bookmark-block/src/blocksuite/ui.tsx +++ b/plugins/bookmark/src/app.tsx @@ -8,7 +8,8 @@ import { } from '@blocksuite/blocks/std'; import { assertExists } from '@blocksuite/global/utils'; import type { Page } from '@blocksuite/store'; -import type { FC } from 'react'; +import type { FC, ReactElement } from 'react'; +import { StrictMode } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; export type BookMarkProps = { @@ -95,7 +96,8 @@ const shouldShowBookmarkMenu = (pastedBlocks: SerializedBlock[]) => { } return !!firstBlock.text[0].attributes?.link; }; -export const BookMarkUI: FC = ({ page }) => { + +const BookMarkUI: FC = ({ page }) => { const [anchor, setAnchor] = useState(null); const [selectedOption, setSelectedOption] = useState( menuOptions[0].id @@ -218,3 +220,15 @@ export const BookMarkUI: FC = ({ page }) => { ) : null; }; + +type AppProps = { + page: Page; +}; + +export const App = (props: AppProps): ReactElement => { + return ( + + + + ); +}; diff --git a/plugins/bookmark/src/index.ts b/plugins/bookmark/src/index.ts new file mode 100644 index 000000000..deae09251 --- /dev/null +++ b/plugins/bookmark/src/index.ts @@ -0,0 +1,21 @@ +import type { PluginContext } from '@toeverything/plugin-infra/entry'; +import { createElement } from 'react'; +import { createRoot } from 'react-dom/client'; + +import { App } from './app'; + +export const entry = (context: PluginContext) => { + console.log('register'); + + context.register('editor', (div, editor) => { + const root = createRoot(div); + root.render(createElement(App, { page: editor.page })); + return () => { + root.unmount(); + }; + }); + + return () => { + console.log('unregister'); + }; +}; diff --git a/plugins/bookmark/tsconfig.json b/plugins/bookmark/tsconfig.json new file mode 100644 index 000000000..f2db0bc49 --- /dev/null +++ b/plugins/bookmark/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "include": ["./src"], + "compilerOptions": { + "noEmit": false, + "outDir": "lib", + "jsx": "preserve" + }, + "references": [ + { + "path": "../../packages/plugin-infra" + }, + { + "path": "../../packages/component" + } + ] +} diff --git a/plugins/copilot/package.json b/plugins/copilot/package.json index a85f5b400..7fc452acc 100644 --- a/plugins/copilot/package.json +++ b/plugins/copilot/package.json @@ -1,28 +1,27 @@ { "name": "@affine/copilot", "private": true, - "main": "./src/index.ts", - "module": "./src/index.ts", - "exports": { - ".": "./src/index.ts" + "affinePlugin": { + "release": false, + "entry": { + "core": "./src/index.ts" + } }, "dependencies": { "@affine/component": "workspace:*", "@toeverything/plugin-infra": "workspace:*", + "idb": "^7.1.1", "langchain": "^0.0.107", "marked": "^5.1.0", "marked-gfm-heading-id": "^3.0.4", - "marked-mangle": "^1.1.0" + "marked-mangle": "^1.1.0", + "zod": "^3.21.4" }, "devDependencies": { "@types/marked": "^5.0.0", - "@types/react": "^18.2.14", - "@types/react-dom": "^18.2.6", - "idb": "^7.1.1", "jotai": "^2.2.2", "react": "18.2.0", - "react-dom": "18.2.0", - "zod": "^3.21.4" + "react-dom": "18.2.0" }, "peerDependencies": { "react": "*", diff --git a/plugins/copilot/src/UI/detail-content.tsx b/plugins/copilot/src/UI/detail-content.tsx index e762cef51..f28f4007f 100644 --- a/plugins/copilot/src/UI/detail-content.tsx +++ b/plugins/copilot/src/UI/detail-content.tsx @@ -1,11 +1,9 @@ import { IconButton } from '@affine/component'; import { SendIcon } from '@blocksuite/icons'; -import { rootStore } from '@toeverything/plugin-infra/manager'; -import type { PluginUIAdapter } from '@toeverything/plugin-infra/type'; -import { Provider, useAtomValue, useSetAtom } from 'jotai'; +import { contentLayoutAtom } from '@toeverything/plugin-infra/manager'; +import { useAtomValue, useSetAtom } from 'jotai'; import type { ReactElement } from 'react'; -import { StrictMode, Suspense, useCallback, useState } from 'react'; -import { createRoot } from 'react-dom/client'; +import { Suspense, useCallback, useState } from 'react'; import { ConversationList } from '../core/components/conversation-list'; import { FollowingUp } from '../core/components/following-up'; @@ -17,51 +15,6 @@ import { textareaStyle, } from './index.css'; -if (typeof window === 'undefined') { - import('@blocksuite/blocks') - .then(({ FormatQuickBar }) => { - FormatQuickBar.customElements.push((_page, getSelection) => { - const div = document.createElement('div'); - const root = createRoot(div); - - const AskAI = (): ReactElement => { - const { conversationAtom } = useChatAtoms(); - const call = useSetAtom(conversationAtom); - const onClickAskAI = useCallback(() => { - const selection = getSelection(); - if (selection != null) { - const text = selection.models - .map(model => { - return model.text?.toString(); - }) - .filter((v): v is string => Boolean(v)) - .join('\n'); - console.log('selected text:', text); - call( - `I selected some text from the document: \n"${text}."` - ).catch(err => { - console.error(err); - }); - } - }, [call]); - - return
Ask AI
; - }; - root.render( - - - - - - ); - return div; - }); - }) - .catch(error => { - console.error(error); - }); -} - const Actions = () => { const { conversationAtom, followingUpAtoms } = useChatAtoms(); const call = useSetAtom(conversationAtom); @@ -108,12 +61,10 @@ const DetailContentImpl = () => { ); }; -export const DetailContent: PluginUIAdapter['detailContent'] = ({ - contentLayoutAtom, -}): ReactElement => { +export const DetailContent = (): ReactElement => { const layout = useAtomValue(contentLayoutAtom); const key = useAtomValue(openAIApiKeyAtom); - if (layout === 'editor' || layout.second !== 'com.affine.copilot') { + if (layout === 'editor' || layout.second !== 'copilot') { return <>; } if (!key) { diff --git a/plugins/copilot/src/UI/header-item.tsx b/plugins/copilot/src/UI/header-item.tsx index c6f32b7ee..925867af2 100644 --- a/plugins/copilot/src/UI/header-item.tsx +++ b/plugins/copilot/src/UI/header-item.tsx @@ -1,12 +1,10 @@ import { IconButton, Tooltip } from '@affine/component'; -import type { PluginUIAdapter } from '@toeverything/plugin-infra/type'; +import { contentLayoutAtom } from '@toeverything/plugin-infra/manager'; import { useSetAtom } from 'jotai'; import type { ReactElement } from 'react'; import { useCallback } from 'react'; -export const HeaderItem: PluginUIAdapter['headerItem'] = ({ - contentLayoutAtom, -}): ReactElement => { +export const HeaderItem = (): ReactElement => { const setLayout = useSetAtom(contentLayoutAtom); return ( @@ -18,7 +16,7 @@ export const HeaderItem: PluginUIAdapter['headerItem'] = ({ return { direction: 'horizontal', first: 'editor', - second: 'com.affine.copilot', + second: 'copilot', splitPercentage: 70, }; } else { diff --git a/plugins/copilot/src/core/components/conversation/index.tsx b/plugins/copilot/src/core/components/conversation/index.tsx index 36f4b599c..770e4192c 100644 --- a/plugins/copilot/src/core/components/conversation/index.tsx +++ b/plugins/copilot/src/core/components/conversation/index.tsx @@ -1,5 +1,4 @@ import { Button } from '@affine/component'; -import { WorkspaceAvatar } from '@affine/component/workspace-avatar'; import { PlusIcon, ResetIcon } from '@blocksuite/icons'; import { clsx } from 'clsx'; import type { MessageType } from 'langchain/schema'; @@ -31,7 +30,6 @@ export const Conversation = (props: ConversationProps): ReactElement => { [styles.avatarRightStyle]: props.type === 'human', })} > -
import('./UI/index'), - hotModuleReload: onHot => - import.meta.webpackHot && - import.meta.webpackHot.accept('./UI', () => onHot(import('./UI/index'))), - } -); +import { DetailContent } from './UI/detail-content'; +import { HeaderItem } from './UI/header-item'; + +export const entry = (context: PluginContext) => { + context.register('headerItem', div => { + const root = createRoot(div); + root.render( + createElement(context.utils.PluginProvider, {}, createElement(HeaderItem)) + ); + return () => { + root.unmount(); + }; + }); + + context.register('window', div => { + const root = createRoot(div); + root.render( + createElement( + context.utils.PluginProvider, + {}, + createElement(DetailContent) + ) + ); + return () => { + root.unmount(); + }; + }); + return () => {}; +}; diff --git a/plugins/hello-world/package.json b/plugins/hello-world/package.json new file mode 100644 index 000000000..583fd4475 --- /dev/null +++ b/plugins/hello-world/package.json @@ -0,0 +1,16 @@ +{ + "name": "@affine/hello-world-plugin", + "private": true, + "version": "0.0.1", + "affinePlugin": { + "release": false, + "entry": { + "core": "./src/index.ts" + } + }, + "dependencies": { + "@affine/component": "workspace:*", + "@blocksuite/icons": "^2.1.25", + "@toeverything/plugin-infra": "workspace:*" + } +} diff --git a/plugins/hello-world/src/app.tsx b/plugins/hello-world/src/app.tsx new file mode 100644 index 000000000..a809e3b49 --- /dev/null +++ b/plugins/hello-world/src/app.tsx @@ -0,0 +1,17 @@ +import { IconButton, Tooltip } from '@affine/component'; +import { AffineLogoSBlue2_1Icon } from '@blocksuite/icons'; +import { useCallback } from 'react'; + +export const HeaderItem = () => { + return ( + + { + console.log('clicked hello world!'); + }, [])} + > + + + + ); +}; diff --git a/plugins/hello-world/src/index.ts b/plugins/hello-world/src/index.ts new file mode 100644 index 000000000..b94b13eb7 --- /dev/null +++ b/plugins/hello-world/src/index.ts @@ -0,0 +1,26 @@ +import type { PluginContext } from '@toeverything/plugin-infra/entry'; +import { + currentWorkspaceIdAtom, + rootStore, +} from '@toeverything/plugin-infra/manager'; +import { createElement } from 'react'; +import { createRoot } from 'react-dom/client'; + +import { HeaderItem } from './app'; + +export const entry = (context: PluginContext) => { + console.log('register'); + console.log('hello, world!'); + console.log(rootStore.get(currentWorkspaceIdAtom)); + context.register('headerItem', div => { + const root = createRoot(div); + root.render(createElement(HeaderItem)); + return () => { + root.unmount(); + }; + }); + + return () => { + console.log('unregister'); + }; +}; diff --git a/plugins/hello-world/tsconfig.json b/plugins/hello-world/tsconfig.json new file mode 100644 index 000000000..f2db0bc49 --- /dev/null +++ b/plugins/hello-world/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "include": ["./src"], + "compilerOptions": { + "noEmit": false, + "outDir": "lib", + "jsx": "preserve" + }, + "references": [ + { + "path": "../../packages/plugin-infra" + }, + { + "path": "../../packages/component" + } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json index 54d05f6a0..c8909a512 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -8,6 +8,7 @@ "outDir": "lib" }, "include": [ + "vite.config.ts", "vitest.config.ts", "scripts", "apps/core/.webpack/runtime-config.ts" diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 000000000..0e66984e7 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,92 @@ +import { createRequire } from 'node:module'; +import { resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; +import react from '@vitejs/plugin-react-swc'; +import { defineConfig } from 'vite'; + +const root = fileURLToPath(new URL('.', import.meta.url)); + +const require = createRequire(import.meta.url); + +const builtInPlugins = ['hello-world', 'bookmark', 'copilot']; + +const outputJson: [pluginName: string, output: string][] = []; + +const entry = builtInPlugins.reduce( + (acc, plugin) => { + const packageJson = require(resolve( + root, + 'plugins', + plugin, + 'package.json' + )); + const entry = packageJson.affinePlugin.entry.core; + acc[`${plugin}/index`] = resolve(root, 'plugins', plugin, entry); + packageJson.affinePlugin.entry.core = './index.js'; + outputJson.push([plugin, JSON.stringify(packageJson, null, 2)]); + return acc; + }, + {} as Record +); + +export default defineConfig({ + build: { + outDir: resolve(root, 'apps', 'core', 'public', 'plugins'), + emptyOutDir: true, + minify: false, + lib: { + entry, + formats: ['cjs'], + }, + rollupOptions: { + output: { + manualChunks: () => 'plugin', + entryFileNames: '[name].js', + chunkFileNames: '[name].js', + }, + external: [ + // built-in packages + /^@affine/, + /^@blocksuite/, + /^@toeverything/, + + // react + /^react/, + /^react-dom/, + + // store + /^jotai/, + + // css + /^@vanilla-extract/, + ], + plugins: [ + vanillaExtractPlugin(), + { + name: 'generate-manifest', + generateBundle() { + this.emitFile({ + type: 'asset', + fileName: `plugin-list.json`, + source: JSON.stringify( + builtInPlugins.map(plugin => ({ + name: plugin, + })) + ), + }); + outputJson.forEach(([name, json]) => { + this.emitFile({ + type: 'asset', + fileName: `${name}/package.json`, + source: json, + }); + }); + }, + }, + ], + }, + }, + plugins: [react()], +}); diff --git a/vitest.config.ts b/vitest.config.ts index c6b955a0b..0df34bf95 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,7 +2,7 @@ import path, { resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; -import react from '@vitejs/plugin-react'; +import react from '@vitejs/plugin-react-swc'; import { defineConfig } from 'vitest/config'; const rootDir = fileURLToPath(new URL('.', import.meta.url)); diff --git a/yarn.lock b/yarn.lock index 71913de8e..f6eda9f01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -64,19 +64,23 @@ __metadata: languageName: unknown linkType: soft -"@affine/bookmark-block@workspace:*, @affine/bookmark-block@workspace:plugins/bookmark-block": +"@affine/bookmark-block@workspace:plugins/bookmark-block": version: 0.0.0-use.local resolution: "@affine/bookmark-block@workspace:plugins/bookmark-block" + dependencies: + "@toeverything/plugin-infra": "workspace:*" + link-preview-js: ^3.0.4 + languageName: unknown + linkType: soft + +"@affine/bookmark-plugin@workspace:plugins/bookmark": + version: 0.0.0-use.local + resolution: "@affine/bookmark-plugin@workspace:plugins/bookmark" dependencies: "@affine/component": "workspace:*" + "@blocksuite/icons": ^2.1.25 "@toeverything/plugin-infra": "workspace:*" foxact: ^0.2.11 - link-preview-js: ^3.0.4 - react: 18.2.0 - react-dom: 18.2.0 - peerDependencies: - react: "*" - react-dom: "*" languageName: unknown linkType: soft @@ -165,8 +169,6 @@ __metadata: "@affine/component": "workspace:*" "@toeverything/plugin-infra": "workspace:*" "@types/marked": ^5.0.0 - "@types/react": ^18.2.14 - "@types/react-dom": ^18.2.6 idb: ^7.1.1 jotai: ^2.2.2 langchain: ^0.0.107 @@ -187,7 +189,6 @@ __metadata: resolution: "@affine/core@workspace:apps/core" dependencies: "@affine-test/fixtures": "workspace:*" - "@affine/bookmark-block": "workspace:*" "@affine/component": "workspace:*" "@affine/copilot": "workspace:*" "@affine/debug": "workspace:*" @@ -241,6 +242,7 @@ __metadata: react-resizable-panels: ^0.0.53 react-router-dom: ^6.14.1 rxjs: ^7.8.1 + ses: ^0.18.5 style-loader: ^3.3.3 swc-loader: ^0.2.3 swc-plugin-coverage-instrument: ^0.0.19 @@ -373,6 +375,16 @@ __metadata: languageName: unknown linkType: soft +"@affine/hello-world-plugin@workspace:plugins/hello-world": + version: 0.0.0-use.local + resolution: "@affine/hello-world-plugin@workspace:plugins/hello-world" + dependencies: + "@affine/component": "workspace:*" + "@blocksuite/icons": ^2.1.25 + "@toeverything/plugin-infra": "workspace:*" + languageName: unknown + linkType: soft + "@affine/i18n@workspace:*, @affine/i18n@workspace:packages/i18n": version: 0.0.0-use.local resolution: "@affine/i18n@workspace:packages/i18n" @@ -434,7 +446,7 @@ __metadata: "@typescript-eslint/parser": ^5.60.1 "@vanilla-extract/vite-plugin": ^3.8.2 "@vanilla-extract/webpack-plugin": ^2.2.0 - "@vitejs/plugin-react": ^4.0.1 + "@vitejs/plugin-react-swc": ^3.3.2 "@vitest/coverage-istanbul": ^0.32.2 "@vitest/ui": ^0.32.2 eslint: ^8.44.0 @@ -464,6 +476,7 @@ __metadata: typescript: ^5.1.6 vite: ^4.3.9 vite-plugin-istanbul: ^4.1.0 + vite-plugin-static-copy: ^0.17.0 vite-tsconfig-paths: ^4.2.0 vitest: ^0.32.2 vitest-fetch-mock: ^0.2.2 @@ -4657,6 +4670,13 @@ __metadata: languageName: node linkType: hard +"@endo/env-options@npm:^0.1.1": + version: 0.1.1 + resolution: "@endo/env-options@npm:0.1.1" + checksum: c721db3e2d3f9a52a5c391dc8e6d473ffc44cf2bb252fad3048551e35aac52484d612753211116ff9a7157a0768d7c61352c494f491e3463e78fef438beb1d42 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm64@npm:0.17.19" @@ -11385,7 +11405,7 @@ __metadata: languageName: node linkType: hard -"@swc/core@npm:^1.3.70": +"@swc/core@npm:^1.3.61, @swc/core@npm:^1.3.70": version: 1.3.70 resolution: "@swc/core@npm:1.3.70" dependencies: @@ -12841,6 +12861,17 @@ __metadata: languageName: node linkType: hard +"@vitejs/plugin-react-swc@npm:^3.3.2": + version: 3.3.2 + resolution: "@vitejs/plugin-react-swc@npm:3.3.2" + dependencies: + "@swc/core": ^1.3.61 + peerDependencies: + vite: ^4 + checksum: 1e44b1b7c50b6ae381e2ded9a7971af42286144bf257d7ed1a9dbf8acec0c4af78fe46982941566d31b80b7886649d9b1c06a873571d228e0b8fce68742b9ff4 + languageName: node + linkType: hard + "@vitejs/plugin-react@npm:^3.0.1": version: 3.1.0 resolution: "@vitejs/plugin-react@npm:3.1.0" @@ -19460,8 +19491,8 @@ __metadata: linkType: hard "foxact@npm:^0.2.11": - version: 0.2.11 - resolution: "foxact@npm:0.2.11" + version: 0.2.17 + resolution: "foxact@npm:0.2.17" dependencies: client-only: ^0.0.1 server-only: ^0.0.1 @@ -19470,7 +19501,7 @@ __metadata: peerDependenciesMeta: react: optional: true - checksum: 711a5d1e287c45eb258a9003910d13fc2c82f3c6cd0e4e9c673dbe64c6a7d95da7cc983b5f143270bc6304b9ceb0de2d25c01588e066e01e8eb0dbb55e55eaa6 + checksum: 9229ac42032dbce52c95f5583601ba5dbfe8e4bd40b6ec3b372d91793b7b621e01eb45bb953dfe077e65a2ecd4dcdaa42c8f1c60efbf43048a3cd0038a754b60 languageName: node linkType: hard @@ -29218,6 +29249,15 @@ __metadata: languageName: node linkType: hard +"ses@npm:^0.18.5": + version: 0.18.5 + resolution: "ses@npm:0.18.5" + dependencies: + "@endo/env-options": ^0.1.1 + checksum: b41faaf28efc97c09b0322de5c12603e015238e4b2cf5195c8fc3f5fb9bc66169dd394c63bcdb30d8fa36a55d03eeda3df64857ab724b2297fb6b60c4f5dc0e2 + languageName: node + linkType: hard + "set-blocking@npm:^2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" @@ -31924,6 +31964,20 @@ __metadata: languageName: node linkType: hard +"vite-plugin-static-copy@npm:^0.17.0": + version: 0.17.0 + resolution: "vite-plugin-static-copy@npm:0.17.0" + dependencies: + chokidar: ^3.5.3 + fast-glob: ^3.2.11 + fs-extra: ^11.1.0 + picocolors: ^1.0.0 + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + checksum: b73f001f080517d7c2342ad5b0779c1e5ec8b5820e85a57280e8f979d095946a32ab717a59eaae60e3f715784a138785208619aa6d936cd9a80e6a03e364e4fc + languageName: node + linkType: hard + "vite-tsconfig-paths@npm:^4.2.0": version: 4.2.0 resolution: "vite-tsconfig-paths@npm:4.2.0"