diff --git a/apps/core/.webpack/config.ts b/apps/core/.webpack/config.ts
index 0519258db1..ca9a758adb 100644
--- a/apps/core/.webpack/config.ts
+++ b/apps/core/.webpack/config.ts
@@ -1,7 +1,6 @@
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
-import HTMLPlugin from 'html-webpack-plugin';
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
import { PerfseePlugin } from '@perfsee/webpack';
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
@@ -253,14 +252,6 @@ export const createConfiguration: (
ignoreOrder: true,
}),
]),
- new HTMLPlugin({
- template: join(rootPath, '.webpack', 'template.html'),
- inject: 'body',
- scriptLoading: 'module',
- minify: false,
- chunks: ['index', 'plugin', 'polyfill-ses'],
- filename: 'index.html',
- }),
new VanillaExtractPlugin(),
new webpack.DefinePlugin({
'process.env': JSON.stringify({}),
diff --git a/apps/core/.webpack/webpack.config.ts b/apps/core/.webpack/webpack.config.ts
index ddf671e983..a08b054513 100644
--- a/apps/core/.webpack/webpack.config.ts
+++ b/apps/core/.webpack/webpack.config.ts
@@ -1,8 +1,9 @@
import { createConfiguration, rootPath } from './config.js';
import { merge } from 'webpack-merge';
-import { resolve } from 'node:path';
+import { join, resolve } from 'node:path';
import type { BuildFlags } from '@affine/cli/config';
import { getRuntimeConfig } from './runtime-config.js';
+import HTMLPlugin from 'html-webpack-plugin';
export default async function (cli_env: any, _: any) {
const flags: BuildFlags = JSON.parse(
@@ -28,6 +29,29 @@ export default async function (cli_env: any, _: any) {
dependOn: ['polyfill-ses', 'plugin'],
import: resolve(rootPath, 'src/index.tsx'),
},
+ '_plugin/index.test': {
+ asyncChunks: false,
+ dependOn: ['polyfill-ses', 'plugin'],
+ import: resolve(rootPath, 'src/_plugin/index.test.tsx'),
+ },
},
+ plugins: [
+ new HTMLPlugin({
+ template: join(rootPath, '.webpack', 'template.html'),
+ inject: 'body',
+ scriptLoading: 'module',
+ minify: false,
+ chunks: ['index', 'plugin', 'polyfill-ses'],
+ filename: 'index.html',
+ }),
+ new HTMLPlugin({
+ template: join(rootPath, '.webpack', 'template.html'),
+ inject: 'body',
+ scriptLoading: 'module',
+ minify: false,
+ chunks: ['_plugin/index.test', 'plugin', 'polyfill-ses'],
+ filename: '_plugin/index.html',
+ }),
+ ],
});
}
diff --git a/apps/core/src/_plugin/index.test.tsx b/apps/core/src/_plugin/index.test.tsx
new file mode 100644
index 0000000000..dd045064b5
--- /dev/null
+++ b/apps/core/src/_plugin/index.test.tsx
@@ -0,0 +1,42 @@
+import { assertExists } from '@blocksuite/global/utils';
+import {
+ registeredPluginAtom,
+ rootStore,
+} from '@toeverything/plugin-infra/atom';
+import { use } from 'foxact/use';
+import { useAtomValue } from 'jotai';
+import { Provider } from 'jotai/react';
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+
+import { _pluginNestedImportsMap } from '../bootstrap/plugins/setup';
+import { pluginRegisterPromise } from '../bootstrap/register-plugins';
+
+const root = document.getElementById('app');
+assertExists(root);
+
+const App = () => {
+ use(pluginRegisterPromise);
+ const plugins = useAtomValue(registeredPluginAtom);
+ _pluginNestedImportsMap.forEach(value => {
+ const exports = value.get('index.js');
+ assertExists(exports);
+ assertExists(exports?.get('entry'));
+ });
+ return (
+
+
Successfully loaded plugins:
+ {plugins.map(plugin => {
+ return
{plugin}
;
+ })}
+
+ );
+};
+
+createRoot(root).render(
+
+
+
+
+
+);
diff --git a/apps/core/src/bootstrap/plugins/setup.ts b/apps/core/src/bootstrap/plugins/setup.ts
index 26df4123a0..b2b6cdb6cf 100644
--- a/apps/core/src/bootstrap/plugins/setup.ts
+++ b/apps/core/src/bootstrap/plugins/setup.ts
@@ -37,43 +37,46 @@ const permissionLogger = new DebugLogger('plugins:permission');
const importLogger = new DebugLogger('plugins:import');
const setupRootImportsMap = () => {
- rootImportsMap.set('react', new Map(Object.entries(React)));
- rootImportsMap.set(
+ _rootImportsMap.set('react', new Map(Object.entries(React)));
+ _rootImportsMap.set(
'react/jsx-runtime',
new Map(Object.entries(ReactJSXRuntime))
);
- rootImportsMap.set('react-dom', new Map(Object.entries(ReactDom)));
- rootImportsMap.set(
+ _rootImportsMap.set('react-dom', new Map(Object.entries(ReactDom)));
+ _rootImportsMap.set(
'react-dom/client',
new Map(Object.entries(ReactDomClient))
);
- rootImportsMap.set('@blocksuite/icons', new Map(Object.entries(Icons)));
- rootImportsMap.set(
+ _rootImportsMap.set('@blocksuite/icons', new Map(Object.entries(Icons)));
+ _rootImportsMap.set(
'@affine/component',
new Map(Object.entries(AFFiNEComponent))
);
- rootImportsMap.set(
+ _rootImportsMap.set(
'@blocksuite/blocks/std',
new Map(Object.entries(BlockSuiteBlocksStd))
);
- rootImportsMap.set(
+ _rootImportsMap.set(
'@blocksuite/global/utils',
new Map(Object.entries(BlockSuiteGlobalUtils))
);
- rootImportsMap.set('jotai', new Map(Object.entries(Jotai)));
- rootImportsMap.set('jotai/utils', new Map(Object.entries(JotaiUtils)));
- rootImportsMap.set(
+ _rootImportsMap.set('jotai', new Map(Object.entries(Jotai)));
+ _rootImportsMap.set('jotai/utils', new Map(Object.entries(JotaiUtils)));
+ _rootImportsMap.set(
'@toeverything/plugin-infra/atom',
new Map(Object.entries(Atom))
);
- rootImportsMap.set('swr', new Map(Object.entries(SWR)));
+ _rootImportsMap.set('swr', new Map(Object.entries(SWR)));
};
// module -> importName -> updater[]
-const rootImportsMap = new Map>();
+export const _rootImportsMap = new Map>();
setupRootImportsMap();
// pluginName -> module -> importName -> updater[]
-const pluginNestedImportsMap = new Map>>();
+export const _pluginNestedImportsMap = new Map<
+ string,
+ Map>
+>();
const pluginImportsFunctionMap = new Map void>();
export const createImports = (pluginName: string) => {
@@ -85,12 +88,12 @@ export const createImports = (pluginName: string) => {
newUpdaters: [string, [string, ((val: any) => void)[]][]][]
) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const currentImportMap = pluginNestedImportsMap.get(pluginName)!;
- console.log('currentImportMap', pluginName, currentImportMap);
+ const currentImportMap = _pluginNestedImportsMap.get(pluginName)!;
+ importLogger.debug('currentImportMap', pluginName, currentImportMap);
for (const [module, moduleUpdaters] of newUpdaters) {
- console.log('imports module', module, moduleUpdaters);
- let moduleImports = rootImportsMap.get(module);
+ importLogger.debug('imports module', module, moduleUpdaters);
+ let moduleImports = _rootImportsMap.get(module);
if (!moduleImports) {
moduleImports = currentImportMap.get(module);
}
@@ -107,11 +110,11 @@ export const createImports = (pluginName: string) => {
}
}
} else {
- console.log(
+ console.error(
'cannot find module in plugin import map',
module,
currentImportMap,
- pluginNestedImportsMap
+ _pluginNestedImportsMap
);
}
}
@@ -299,13 +302,13 @@ export const setupPluginCode = async (
pluginName: string,
filename: string
) => {
- if (!pluginNestedImportsMap.has(pluginName)) {
- pluginNestedImportsMap.set(pluginName, new Map());
+ if (!_pluginNestedImportsMap.has(pluginName)) {
+ _pluginNestedImportsMap.set(pluginName, new Map());
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const currentImportMap = pluginNestedImportsMap.get(pluginName)!;
+ const currentImportMap = _pluginNestedImportsMap.get(pluginName)!;
const isMissingPackage = (name: string) =>
- rootImportsMap.has(name) && !currentImportMap.has(name);
+ _rootImportsMap.has(name) && !currentImportMap.has(name);
const bundleAnalysis = await fetch(`${baseUrl}/${filename}.json`).then(res =>
res.json()
@@ -321,7 +324,7 @@ export const setupPluginCode = async (
if (isMissingPackage(name)) {
return Promise.resolve();
} else {
- console.log('missing package', name);
+ importLogger.debug('missing package', name);
return setupPluginCode(baseUrl, pluginName, name);
}
})
@@ -329,7 +332,7 @@ export const setupPluginCode = async (
const code = await fetch(`${baseUrl}/${filename.replace(/^\.\//, '')}`).then(
res => res.text()
);
- console.log('evaluating', filename);
+ importLogger.debug('evaluating', filename);
const moduleCompartment = new Compartment(
createOrGetGlobalThis(
pluginName,
@@ -358,7 +361,6 @@ export const setupPluginCode = async (
onceVar: setVarProxy,
});
- console.log('module exports alias', moduleExports);
for (const [newExport, [originalExport]] of Object.entries(moduleExports)) {
if (newExport === originalExport) continue;
const value = moduleExportsMap.get(originalExport);
@@ -366,7 +368,6 @@ export const setupPluginCode = async (
moduleExportsMap.delete(originalExport);
}
- console.log('module re-exports', moduleReexports);
for (const [name, reexports] of Object.entries(moduleReexports)) {
const targetExports = currentImportMap.get(filename);
const moduleExports = currentImportMap.get(name);
@@ -374,7 +375,6 @@ export const setupPluginCode = async (
assertExists(moduleExports);
for (const [exportedName, localName] of reexports) {
const exportedValue: any = moduleExports.get(exportedName);
- console.log('re-export', name, localName, exportedName, exportedValue);
assertExists(exportedValue);
targetExports.set(localName, exportedValue);
}
@@ -395,7 +395,7 @@ const entryLogger = new DebugLogger('plugin:entry');
export const evaluatePluginEntry = (pluginName: string) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const currentImportMap = pluginNestedImportsMap.get(pluginName)!;
+ const currentImportMap = _pluginNestedImportsMap.get(pluginName)!;
const pluginExports = currentImportMap.get('index.js');
assertExists(pluginExports);
const entryFunction = pluginExports.get('entry');
diff --git a/apps/core/src/bootstrap/register-plugins.ts b/apps/core/src/bootstrap/register-plugins.ts
index 2679c0e9e1..afd62506b9 100644
--- a/apps/core/src/bootstrap/register-plugins.ts
+++ b/apps/core/src/bootstrap/register-plugins.ts
@@ -22,7 +22,7 @@ declare global {
globalThis.__pluginPackageJson__ = [];
-Promise.all(
+export const pluginRegisterPromise = Promise.all(
[...builtinPluginUrl].map(url => {
return fetch(`${url}/package.json`)
.then(async res => {
diff --git a/tests/affine-plugin/e2e/basic.spec.ts b/tests/affine-plugin/e2e/basic.spec.ts
index d299b50d9c..1597bd5184 100644
--- a/tests/affine-plugin/e2e/basic.spec.ts
+++ b/tests/affine-plugin/e2e/basic.spec.ts
@@ -1,8 +1,13 @@
import { test } from '@affine-test/kit/playwright';
-import { openHomePage } from '@affine-test/kit/utils/load-page';
+import { openHomePage, openPluginPage } from '@affine-test/kit/utils/load-page';
import { waitEditorLoad } from '@affine-test/kit/utils/page-logic';
import { expect } from '@playwright/test';
+test('plugin map should valid', async ({ page }) => {
+ await openPluginPage(page);
+ await page.waitForSelector('[data-plugins-load-status="success"]');
+});
+
test('plugin should exist', async ({ page }) => {
await openHomePage(page);
await waitEditorLoad(page);
diff --git a/tests/kit/utils/load-page.ts b/tests/kit/utils/load-page.ts
index 87e8c7730c..f50c192c99 100644
--- a/tests/kit/utils/load-page.ts
+++ b/tests/kit/utils/load-page.ts
@@ -5,3 +5,7 @@ export const webUrl = 'http://localhost:8080';
export async function openHomePage(page: Page) {
await page.goto(webUrl);
}
+
+export async function openPluginPage(page: Page) {
+ await page.goto(`${webUrl}/_plugin/index.html`);
+}
diff --git a/vitest.config.ts b/vitest.config.ts
index f96aed4cc1..6974d34f28 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -26,8 +26,8 @@ export default defineConfig({
include: [
resolve(rootDir, 'packages/**/*.spec.ts'),
resolve(rootDir, 'packages/**/*.spec.tsx'),
- resolve(rootDir, 'apps/web/**/*.spec.ts'),
- resolve(rootDir, 'apps/web/**/*.spec.tsx'),
+ resolve(rootDir, 'apps/core/**/*.spec.ts'),
+ resolve(rootDir, 'apps/core/**/*.spec.tsx'),
resolve(rootDir, 'tests/unit/**/*.spec.ts'),
resolve(rootDir, 'tests/unit/**/*.spec.tsx'),
],