feat(plugin-infra): support esm bundler (#3460)

This commit is contained in:
Alex Yang 2023-07-29 12:07:32 -07:00 committed by GitHub
parent 6388a798c9
commit 0b66e911b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 191 additions and 103 deletions

View File

@ -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'),
},
],
}),
],

View File

@ -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,
};
};

View File

@ -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 => {

View File

@ -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": {

View File

@ -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],
},

View File

@ -1,5 +1,6 @@
{
"name": "@affine/bookmark-plugin",
"type": "module",
"version": "0.8.0-canary.1",
"description": "Bookmark Plugin",
"affinePlugin": {

View File

@ -1,5 +1,6 @@
{
"name": "@affine/copilot-plugin",
"type": "module",
"private": true,
"description": "Copilot plugin",
"affinePlugin": {

View File

@ -1,5 +1,6 @@
{
"name": "@affine/hello-world-plugin",
"type": "module",
"private": true,
"description": "Hello world plugin",
"version": "0.8.0-canary.1",

View File

@ -1,5 +1,6 @@
{
"name": "@affine/image-preview-plugin",
"type": "module",
"version": "0.8.0-canary.1",
"description": "Image preview plugin",
"affinePlugin": {

View File

@ -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