mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-28 11:02:59 +03:00
feat(plugin-infra): support esm bundler (#3460)
This commit is contained in:
parent
6388a798c9
commit
0b66e911b1
@ -32,6 +32,7 @@ const OptimizeOptionOptions: (
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
minify: TerserPlugin.swcMinify,
|
||||
exclude: [/\.min\.js$/, /plugins\/.+\/.+\.mjs$/],
|
||||
parallel: true,
|
||||
extractComments: true,
|
||||
terserOptions: {
|
||||
@ -269,7 +270,10 @@ export const createConfiguration: (
|
||||
}),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{ from: resolve(rootPath, 'public'), to: resolve(rootPath, 'dist') },
|
||||
{
|
||||
from: resolve(rootPath, 'public'),
|
||||
to: resolve(rootPath, 'dist'),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
@ -9,44 +9,36 @@ 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';
|
||||
import * as SWR from 'swr';
|
||||
|
||||
const customRequire = (id: string) => {
|
||||
if (id === '@toeverything/plugin-infra/atom') {
|
||||
return Atom;
|
||||
}
|
||||
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 Icons;
|
||||
}
|
||||
if (id === '@affine/component') {
|
||||
return AFFiNEComponent;
|
||||
}
|
||||
if (id === '@blocksuite/blocks/std') {
|
||||
return BlockSuiteBlocksStd;
|
||||
}
|
||||
if (id === '@blocksuite/global/utils') {
|
||||
return BlockSuiteGlobalUtils;
|
||||
}
|
||||
if (id === 'jotai') {
|
||||
return Jotai;
|
||||
}
|
||||
if (id === 'jotai/utils') {
|
||||
return JotaiUtils;
|
||||
}
|
||||
throw new Error(`Cannot find module '${id}'`);
|
||||
const setupImportsMap = () => {
|
||||
importsMap.set('react', new Map(Object.entries(React)));
|
||||
importsMap.set('react/jsx-runtime', new Map(Object.entries(ReactJSXRuntime)));
|
||||
importsMap.set('react-dom', new Map(Object.entries(ReactDom)));
|
||||
importsMap.set('react-dom/client', new Map(Object.entries(ReactDomClient)));
|
||||
importsMap.set('@blocksuite/icons', new Map(Object.entries(Icons)));
|
||||
importsMap.set('@affine/component', new Map(Object.entries(AFFiNEComponent)));
|
||||
importsMap.set(
|
||||
'@blocksuite/blocks/std',
|
||||
new Map(Object.entries(BlockSuiteBlocksStd))
|
||||
);
|
||||
importsMap.set(
|
||||
'@blocksuite/global/utils',
|
||||
new Map(Object.entries(BlockSuiteGlobalUtils))
|
||||
);
|
||||
importsMap.set('jotai', new Map(Object.entries(Jotai)));
|
||||
importsMap.set('jotai/utils', new Map(Object.entries(JotaiUtils)));
|
||||
importsMap.set(
|
||||
'@toeverything/plugin-infra/atom',
|
||||
new Map(Object.entries(Atom))
|
||||
);
|
||||
importsMap.set('swr', new Map(Object.entries(SWR)));
|
||||
};
|
||||
|
||||
const importsMap = new Map<string, Map<string, any>>();
|
||||
setupImportsMap();
|
||||
export { importsMap };
|
||||
|
||||
export const createGlobalThis = () => {
|
||||
return {
|
||||
process: Object.freeze({
|
||||
@ -69,8 +61,15 @@ export const createGlobalThis = () => {
|
||||
clearTimeout: function (id: number) {
|
||||
return globalThis.clearTimeout(id);
|
||||
},
|
||||
// copilot uses these
|
||||
|
||||
// safe to use for all plugins
|
||||
Error: globalThis.Error,
|
||||
TypeError: globalThis.TypeError,
|
||||
RangeError: globalThis.RangeError,
|
||||
console: globalThis.console,
|
||||
crypto: globalThis.crypto,
|
||||
|
||||
// copilot uses these
|
||||
CustomEvent: globalThis.CustomEvent,
|
||||
Date: globalThis.Date,
|
||||
Math: globalThis.Math,
|
||||
@ -80,8 +79,8 @@ export const createGlobalThis = () => {
|
||||
TextEncoder: globalThis.TextEncoder,
|
||||
TextDecoder: globalThis.TextDecoder,
|
||||
Request: globalThis.Request,
|
||||
Error: globalThis.Error,
|
||||
// bookmark uses these
|
||||
|
||||
// image-preview uses these
|
||||
Blob: globalThis.Blob,
|
||||
ClipboardItem: globalThis.ClipboardItem,
|
||||
|
||||
@ -98,9 +97,5 @@ export const createGlobalThis = () => {
|
||||
IDBIndex: globalThis.IDBIndex,
|
||||
IDBCursor: globalThis.IDBCursor,
|
||||
IDBVersionChangeEvent: globalThis.IDBVersionChangeEvent,
|
||||
|
||||
exports: {},
|
||||
console: globalThis.console,
|
||||
require: customRequire,
|
||||
};
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ import { Provider } from 'jotai/react';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { createGlobalThis } from './plugins/setup';
|
||||
import { createGlobalThis, importsMap } from './plugins/setup';
|
||||
|
||||
if (!process.env.COVERAGE) {
|
||||
lockdown({
|
||||
@ -33,6 +33,29 @@ if (!process.env.COVERAGE) {
|
||||
});
|
||||
}
|
||||
|
||||
const imports = (
|
||||
newUpdaters: [string, [string, ((val: any) => void)[]][]][]
|
||||
) => {
|
||||
for (const [module, moduleUpdaters] of newUpdaters) {
|
||||
const moduleImports = importsMap.get(module);
|
||||
if (moduleImports) {
|
||||
for (const [importName, importUpdaters] of moduleUpdaters) {
|
||||
const updateImport = (value: any) => {
|
||||
for (const importUpdater of importUpdaters) {
|
||||
importUpdater(value);
|
||||
}
|
||||
};
|
||||
if (moduleImports.has(importName)) {
|
||||
const val = moduleImports.get(importName);
|
||||
updateImport(val);
|
||||
} else {
|
||||
console.log('import not found', importName, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const builtinPluginUrl = new Set([
|
||||
'/plugins/bookmark',
|
||||
'/plugins/copilot',
|
||||
@ -79,8 +102,7 @@ await Promise.all(
|
||||
if (!release && process.env.NODE_ENV === 'production') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const pluginCompartment = new Compartment(createGlobalThis(), {});
|
||||
const pluginGlobalThis = pluginCompartment.globalThis;
|
||||
const pluginCompartment = new Compartment(createGlobalThis());
|
||||
const baseURL = url;
|
||||
const entryURL = `${baseURL}/${core}`;
|
||||
rootStore.set(registeredPluginAtom, prev => [...prev, pluginName]);
|
||||
@ -107,58 +129,69 @@ await Promise.all(
|
||||
);
|
||||
}
|
||||
const codeText = await res.text();
|
||||
pluginCompartment.evaluate(codeText, {
|
||||
__evadeHtmlCommentTest__: true,
|
||||
});
|
||||
pluginGlobalThis.__INTERNAL__ENTRY = {
|
||||
register: (part, callback) => {
|
||||
logger.info(`Registering ${pluginName} to ${part}`);
|
||||
if (part === 'headerItem') {
|
||||
rootStore.set(headerItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['headerItem'],
|
||||
}));
|
||||
} else if (part === 'editor') {
|
||||
rootStore.set(editorItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['editor'],
|
||||
}));
|
||||
} else if (part === 'window') {
|
||||
rootStore.set(windowItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['window'],
|
||||
}));
|
||||
} else if (part === 'setting') {
|
||||
rootStore.set(settingItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['setting'],
|
||||
}));
|
||||
} else if (part === 'formatBar') {
|
||||
FormatQuickBar.customElements.push((page, getBlockRange) => {
|
||||
const div = document.createElement('div');
|
||||
(callback as CallbackMap['formatBar'])(
|
||||
div,
|
||||
page,
|
||||
getBlockRange
|
||||
);
|
||||
return div;
|
||||
});
|
||||
} 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');
|
||||
try {
|
||||
const entryPoint = pluginCompartment.evaluate(codeText, {
|
||||
__evadeHtmlCommentTest__: true,
|
||||
});
|
||||
entryPoint({
|
||||
imports,
|
||||
onceVar: {
|
||||
entry: (
|
||||
entryFunction: (context: PluginContext) => () => void
|
||||
) => {
|
||||
const cleanup = entryFunction({
|
||||
register: (part, callback) => {
|
||||
logger.info(`Registering ${pluginName} to ${part}`);
|
||||
if (part === 'headerItem') {
|
||||
rootStore.set(headerItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['headerItem'],
|
||||
}));
|
||||
} else if (part === 'editor') {
|
||||
rootStore.set(editorItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['editor'],
|
||||
}));
|
||||
} else if (part === 'window') {
|
||||
rootStore.set(windowItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['window'],
|
||||
}));
|
||||
} else if (part === 'setting') {
|
||||
rootStore.set(settingItemsAtom, items => ({
|
||||
...items,
|
||||
[pluginName]: callback as CallbackMap['setting'],
|
||||
}));
|
||||
} else if (part === 'formatBar') {
|
||||
FormatQuickBar.customElements.push(
|
||||
(page, getBlockRange) => {
|
||||
const div = document.createElement('div');
|
||||
(callback as CallbackMap['formatBar'])(
|
||||
div,
|
||||
page,
|
||||
getBlockRange
|
||||
);
|
||||
return div;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unknown part: ${part}`);
|
||||
}
|
||||
},
|
||||
utils: {
|
||||
PluginProvider,
|
||||
},
|
||||
});
|
||||
if (typeof cleanup !== 'function') {
|
||||
throw new Error('Plugin entry must return a function');
|
||||
}
|
||||
group.add(cleanup);
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(pluginName, e);
|
||||
}
|
||||
pluginGlobalThis.__INTERNAL__ENTRY = undefined;
|
||||
group.add(dispose);
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -13,6 +13,7 @@
|
||||
"devDependencies": {
|
||||
"@clack/core": "^0.3.2",
|
||||
"@clack/prompts": "^0.6.3",
|
||||
"@endo/static-module-record": "^0.7.20",
|
||||
"ts-node": "^10.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { parseArgs } from 'node:util';
|
||||
|
||||
import { StaticModuleRecord } from '@endo/static-module-record';
|
||||
import {
|
||||
packageJsonInputSchema,
|
||||
packageJsonOutputSchema,
|
||||
@ -52,6 +53,9 @@ const external = [
|
||||
// store
|
||||
/^jotai/,
|
||||
|
||||
// utils
|
||||
'swr',
|
||||
|
||||
// css
|
||||
/^@vanilla-extract/,
|
||||
|
||||
@ -132,7 +136,7 @@ await build({
|
||||
lib: {
|
||||
entry: coreEntry,
|
||||
fileName: 'index',
|
||||
formats: ['cjs'],
|
||||
formats: ['es'],
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
@ -152,6 +156,28 @@ await build({
|
||||
plugins: [
|
||||
vanillaExtractPlugin(),
|
||||
react(),
|
||||
{
|
||||
name: 'parse-bundle',
|
||||
renderChunk(code, chunk) {
|
||||
if (chunk.fileName.endsWith('.mjs')) {
|
||||
const record = new StaticModuleRecord(code, chunk.fileName);
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'analysis.json',
|
||||
source: JSON.stringify(
|
||||
{
|
||||
exports: record.exports,
|
||||
imports: record.imports,
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
});
|
||||
return record.__syncModuleProgram__;
|
||||
}
|
||||
return code;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'generate-package.json',
|
||||
async generateBundle() {
|
||||
@ -162,7 +188,7 @@ await build({
|
||||
affinePlugin: {
|
||||
release: json.affinePlugin.release,
|
||||
entry: {
|
||||
core: 'index.js',
|
||||
core: 'index.mjs',
|
||||
},
|
||||
assets: [...metadata.assets],
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@affine/bookmark-plugin",
|
||||
"type": "module",
|
||||
"version": "0.8.0-canary.1",
|
||||
"description": "Bookmark Plugin",
|
||||
"affinePlugin": {
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@affine/copilot-plugin",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"description": "Copilot plugin",
|
||||
"affinePlugin": {
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@affine/hello-world-plugin",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"description": "Hello world plugin",
|
||||
"version": "0.8.0-canary.1",
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@affine/image-preview-plugin",
|
||||
"type": "module",
|
||||
"version": "0.8.0-canary.1",
|
||||
"description": "Image preview plugin",
|
||||
"affinePlugin": {
|
||||
|
33
yarn.lock
33
yarn.lock
@ -92,6 +92,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@clack/core": ^0.3.2
|
||||
"@clack/prompts": ^0.6.3
|
||||
"@endo/static-module-record": ^0.7.20
|
||||
dotenv: ^16.3.1
|
||||
ts-node: ^10.9.1
|
||||
peerDependencies:
|
||||
@ -664,6 +665,17 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@agoric/babel-generator@npm:^7.17.6":
|
||||
version: 7.17.6
|
||||
resolution: "@agoric/babel-generator@npm:7.17.6"
|
||||
dependencies:
|
||||
"@babel/types": ^7.17.0
|
||||
jsesc: ^2.5.1
|
||||
source-map: ^0.5.0
|
||||
checksum: ffc35c0df5089a7a8fbe57526049931e59c67f658ca37f8c0cca9a57fc61855f582f0223494fc270fe24c012be951f3d952eb983238b74185e144e7310f53d3a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@alloc/quick-lru@npm:^5.2.0":
|
||||
version: 5.2.0
|
||||
resolution: "@alloc/quick-lru@npm:5.2.0"
|
||||
@ -1951,7 +1963,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.3, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7":
|
||||
"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.3, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7":
|
||||
version: 7.22.7
|
||||
resolution: "@babel/parser@npm:7.22.7"
|
||||
bin:
|
||||
@ -3221,7 +3233,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.7.2":
|
||||
"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.17.3, @babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.7.2":
|
||||
version: 7.22.8
|
||||
resolution: "@babel/traverse@npm:7.22.8"
|
||||
dependencies:
|
||||
@ -3239,7 +3251,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.13, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
|
||||
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.8, @babel/types@npm:^7.17.0, @babel/types@npm:^7.18.13, @babel/types@npm:^7.2.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
|
||||
version: 7.22.5
|
||||
resolution: "@babel/types@npm:7.22.5"
|
||||
dependencies:
|
||||
@ -4211,6 +4223,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@endo/static-module-record@npm:^0.7.20":
|
||||
version: 0.7.20
|
||||
resolution: "@endo/static-module-record@npm:0.7.20"
|
||||
dependencies:
|
||||
"@agoric/babel-generator": ^7.17.6
|
||||
"@babel/parser": ^7.17.3
|
||||
"@babel/traverse": ^7.17.3
|
||||
"@babel/types": ^7.17.0
|
||||
ses: ^0.18.5
|
||||
checksum: a60e39aea043f774aef34e14348f972c462e9c8e75688b054abc14b3ea26968bcbbf501c07178bcbdf1c27c44955c6972f53df6a8e59fd75f06c3b5d2564bf8e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@esbuild/android-arm64@npm:0.17.6":
|
||||
version: 0.17.6
|
||||
resolution: "@esbuild/android-arm64@npm:0.17.6"
|
||||
@ -28498,7 +28523,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"source-map@npm:^0.5.7":
|
||||
"source-map@npm:^0.5.0, source-map@npm:^0.5.7":
|
||||
version: 0.5.7
|
||||
resolution: "source-map@npm:0.5.7"
|
||||
checksum: 5dc2043b93d2f194142c7f38f74a24670cd7a0063acdaf4bf01d2964b402257ae843c2a8fa822ad5b71013b5fcafa55af7421383da919752f22ff488bc553f4d
|
||||
|
Loading…
Reference in New Issue
Block a user