feat(plugin): add vue example (#3592)

This commit is contained in:
Alex Yang
2023-08-05 23:59:14 -04:00
committed by GitHub
parent 48350d7654
commit 7bf77b566d
21 changed files with 650 additions and 174 deletions

View File

@@ -25,7 +25,8 @@
"storage", "storage",
"infra", "infra",
"plugin-cli", "plugin-cli",
"sdk" "sdk",
"plugin"
] ]
] ]
} }

View File

@@ -135,7 +135,14 @@ const pluginFetch = createFetch({});
const timer = createTimers(abortController.signal); const timer = createTimers(abortController.signal);
const sharedGlobalThis = Object.assign(Object.create(null), timer, { const sharedGlobalThis = Object.assign(Object.create(null), timer, {
Object: globalThis.Object,
fetch: pluginFetch, fetch: pluginFetch,
Symbol: globalThis.Symbol,
Error: globalThis.Error,
TypeError: globalThis.TypeError,
RangeError: globalThis.RangeError,
console: globalThis.console,
crypto: globalThis.crypto,
}); });
const dynamicImportMap = new Map< const dynamicImportMap = new Map<
@@ -222,6 +229,9 @@ export const createOrGetGlobalThis = (
if (sharedGlobalThis[key]) return sharedGlobalThis[key]; if (sharedGlobalThis[key]) return sharedGlobalThis[key];
const result = Reflect.get(window, key); const result = Reflect.get(window, key);
if (typeof result === 'function') { if (typeof result === 'function') {
if (result === ShadowRoot) {
return result;
}
return function (...args: any[]) { return function (...args: any[]) {
permissionLogger.debug( permissionLogger.debug(
`${pluginName} is calling window`, `${pluginName} is calling window`,
@@ -262,15 +272,11 @@ export const createOrGetGlobalThis = (
userAgent: navigator.userAgent, userAgent: navigator.userAgent,
}, },
// safe to use for all plugins MouseEvent: globalThis.MouseEvent,
Error: globalThis.Error, KeyboardEvent: globalThis.KeyboardEvent,
TypeError: globalThis.TypeError, CustomEvent: globalThis.CustomEvent,
RangeError: globalThis.RangeError,
console: globalThis.console,
crypto: globalThis.crypto,
// copilot uses these // copilot uses these
CustomEvent: globalThis.CustomEvent,
Date: globalThis.Date, Date: globalThis.Date,
Math: globalThis.Math, Math: globalThis.Math,
URL: globalThis.URL, URL: globalThis.URL,
@@ -284,6 +290,10 @@ export const createOrGetGlobalThis = (
Blob: globalThis.Blob, Blob: globalThis.Blob,
ClipboardItem: globalThis.ClipboardItem, ClipboardItem: globalThis.ClipboardItem,
// vue uses these
Element: globalThis.Element,
SVGElement: globalThis.SVGElement,
// fixme: use our own db api // fixme: use our own db api
indexedDB: globalThis.indexedDB, indexedDB: globalThis.indexedDB,
IDBRequest: globalThis.IDBRequest, IDBRequest: globalThis.IDBRequest,
@@ -299,6 +309,7 @@ export const createOrGetGlobalThis = (
IDBVersionChangeEvent: globalThis.IDBVersionChangeEvent, IDBVersionChangeEvent: globalThis.IDBVersionChangeEvent,
} }
); );
pluginGlobalThis.global = pluginGlobalThis;
globalThisMap.set(pluginName, pluginGlobalThis); globalThisMap.set(pluginName, pluginGlobalThis);
return pluginGlobalThis; return pluginGlobalThis;
}; };

View File

@@ -1,5 +1,7 @@
import { DebugLogger } from '@affine/debug'; import { DebugLogger } from '@affine/debug';
import { registeredPluginAtom, rootStore } from '@toeverything/infra/atom'; import { registeredPluginAtom, rootStore } from '@toeverything/infra/atom';
import { packageJsonOutputSchema } from '@toeverything/infra/type';
import type { z } from 'zod';
import { evaluatePluginEntry, setupPluginCode } from './plugins/setup'; import { evaluatePluginEntry, setupPluginCode } from './plugins/setup';
@@ -8,6 +10,7 @@ const builtinPluginUrl = new Set([
'/plugins/copilot', '/plugins/copilot',
'/plugins/hello-world', '/plugins/hello-world',
'/plugins/image-preview', '/plugins/image-preview',
'/plugins/vue-hello-world',
]); ]);
const logger = new DebugLogger('register-plugins'); const logger = new DebugLogger('register-plugins');
@@ -23,7 +26,10 @@ export const pluginRegisterPromise = Promise.all(
[...builtinPluginUrl].map(url => { [...builtinPluginUrl].map(url => {
return fetch(`${url}/package.json`) return fetch(`${url}/package.json`)
.then(async res => { .then(async res => {
const packageJson = await res.json(); const packageJson = (await res.json()) as z.infer<
typeof packageJsonOutputSchema
>;
packageJsonOutputSchema.parse(packageJson);
const { const {
name: pluginName, name: pluginName,
affinePlugin: { affinePlugin: {
@@ -38,6 +44,12 @@ export const pluginRegisterPromise = Promise.all(
if (!release && !runtimeConfig.enablePlugin) { if (!release && !runtimeConfig.enablePlugin) {
return Promise.resolve(); return Promise.resolve();
} }
if (
release === 'development' &&
process.env.NODE_ENV !== 'development'
) {
return Promise.resolve();
}
const baseURL = url; const baseURL = url;
const entryURL = `${baseURL}/${core}`; const entryURL = `${baseURL}/${core}`;
rootStore.set(registeredPluginAtom, prev => [...prev, pluginName]); rootStore.set(registeredPluginAtom, prev => [...prev, pluginName]);

View File

@@ -65,7 +65,7 @@
"@faker-js/faker": "^8.0.2", "@faker-js/faker": "^8.0.2",
"@istanbuljs/schema": "^0.1.3", "@istanbuljs/schema": "^0.1.3",
"@magic-works/i18n-codegen": "^0.5.0", "@magic-works/i18n-codegen": "^0.5.0",
"@nx/vite": "16.5.5", "@nx/vite": "16.6.0",
"@perfsee/sdk": "^1.8.5", "@perfsee/sdk": "^1.8.5",
"@playwright/test": "^1.36.2", "@playwright/test": "^1.36.2",
"@taplo/cli": "^0.5.2", "@taplo/cli": "^0.5.2",
@@ -90,6 +90,7 @@
"eslint-plugin-sonarjs": "^0.19.0", "eslint-plugin-sonarjs": "^0.19.0",
"eslint-plugin-unicorn": "^48.0.0", "eslint-plugin-unicorn": "^48.0.0",
"eslint-plugin-unused-imports": "^3.0.0", "eslint-plugin-unused-imports": "^3.0.0",
"eslint-plugin-vue": "^9.16.1",
"fake-indexeddb": "4.0.2", "fake-indexeddb": "4.0.2",
"happy-dom": "^10.8.0", "happy-dom": "^10.8.0",
"husky": "^8.0.3", "husky": "^8.0.3",
@@ -97,7 +98,7 @@
"madge": "^6.1.0", "madge": "^6.1.0",
"msw": "^1.2.3", "msw": "^1.2.3",
"nanoid": "^4.0.2", "nanoid": "^4.0.2",
"nx": "16.5.5", "nx": "16.6.0",
"nx-cloud": "latest", "nx-cloud": "latest",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"prettier": "^3.0.0", "prettier": "^3.0.0",

View File

@@ -9,7 +9,7 @@ export const packageJsonInputSchema = z.object({
version: z.string(), version: z.string(),
description: z.string(), description: z.string(),
affinePlugin: z.object({ affinePlugin: z.object({
release: z.boolean(), release: z.union([z.boolean(), z.enum(['development'])]),
entry: z.object({ entry: z.object({
core: z.string(), core: z.string(),
server: z.string().optional(), server: z.string().optional(),
@@ -23,7 +23,7 @@ export const packageJsonOutputSchema = z.object({
version: z.string(), version: z.string(),
description: z.string(), description: z.string(),
affinePlugin: z.object({ affinePlugin: z.object({
release: z.boolean(), release: z.union([z.boolean(), z.enum(['development'])]),
entry: z.object({ entry: z.object({
core: z.string(), core: z.string(),
}), }),

View File

@@ -14,6 +14,7 @@
"@swc/core": "^1.3.72", "@swc/core": "^1.3.72",
"@toeverything/infra": "workspace:^", "@toeverything/infra": "workspace:^",
"@vanilla-extract/rollup-plugin": "^1.2.2", "@vanilla-extract/rollup-plugin": "^1.2.2",
"@vitejs/plugin-vue": "^4.2.3",
"rollup": "^3.27.0", "rollup": "^3.27.0",
"rollup-plugin-swc3": "^0.9.1", "rollup-plugin-swc3": "^0.9.1",
"ts-node": "^10.9.1" "ts-node": "^10.9.1"

View File

@@ -11,6 +11,7 @@ import {
} from '@toeverything/infra/type'; } from '@toeverything/infra/type';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import react from '@vitejs/plugin-react-swc'; import react from '@vitejs/plugin-react-swc';
import vue from '@vitejs/plugin-vue';
import { build, type PluginOption } from 'vite'; import { build, type PluginOption } from 'vite';
import type { z } from 'zod'; import type { z } from 'zod';
@@ -30,7 +31,19 @@ if (!plugin) {
const command = result.positionals[0]; const command = result.positionals[0];
const isWatch = command === 'dev'; const isWatch = (() => {
switch (command) {
case 'dev': {
return true;
}
case 'build': {
return false;
}
default: {
throw new Error('invalid command');
}
}
})();
const external = [ const external = [
// built-in packages // built-in packages
@@ -131,6 +144,7 @@ await build({
build: { build: {
watch: isWatch ? {} : undefined, watch: isWatch ? {} : undefined,
minify: false, minify: false,
target: 'es2020',
outDir: coreOutDir, outDir: coreOutDir,
emptyOutDir: true, emptyOutDir: true,
lib: { lib: {
@@ -169,6 +183,7 @@ await build({
}, },
plugins: [ plugins: [
vanillaExtractPlugin(), vanillaExtractPlugin(),
vue(),
react(), react(),
{ {
name: 'parse-bundle', name: 'parse-bundle',

View File

@@ -1,6 +1,13 @@
{ {
"name": "@affine/bookmark-plugin", "name": "@affine/bookmark-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"namedInputs": {
"default": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/plugin-cli/**/*",
"sharedGlobals"
]
},
"targets": { "targets": {
"build": { "build": {
"executor": "nx:run-script", "executor": "nx:run-script",
@@ -8,7 +15,7 @@
"script": "build" "script": "build"
}, },
"dependsOn": ["^build"], "dependsOn": ["^build"],
"inputs": ["{projectRoot}/**/*"], "inputs": ["default"],
"outputs": [ "outputs": [
"{workspaceRoot}/apps/core/public/plugins/bookmark", "{workspaceRoot}/apps/core/public/plugins/bookmark",
"{workspaceRoot}/apps/electron/dist/plugins/bookmark" "{workspaceRoot}/apps/electron/dist/plugins/bookmark"

View File

@@ -1,7 +1,13 @@
{ {
"name": "@affine/copilot-plugin", "name": "@affine/copilot-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"tags": ["plugin"], "namedInputs": {
"default": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/plugin-cli/src/**/*",
"sharedGlobals"
]
},
"targets": { "targets": {
"build": { "build": {
"executor": "nx:run-script", "executor": "nx:run-script",
@@ -9,11 +15,12 @@
"script": "build" "script": "build"
}, },
"dependsOn": ["^build"], "dependsOn": ["^build"],
"inputs": ["{projectRoot}/**/*"], "inputs": ["default"],
"outputs": [ "outputs": [
"{workspaceRoot}/apps/core/public/plugins/copilot", "{workspaceRoot}/apps/core/public/plugins/copilot",
"{workspaceRoot}/apps/electron/dist/plugins/copilot" "{workspaceRoot}/apps/electron/dist/plugins/copilot"
] ]
} }
} },
"tags": ["plugin"]
} }

View File

@@ -1,7 +1,13 @@
{ {
"name": "@affine/hello-world-plugin", "name": "@affine/hello-world-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"tags": ["plugin"], "namedInputs": {
"default": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/plugin-cli/src/**/*",
"sharedGlobals"
]
},
"targets": { "targets": {
"build": { "build": {
"executor": "nx:run-script", "executor": "nx:run-script",
@@ -9,11 +15,12 @@
"script": "build" "script": "build"
}, },
"dependsOn": ["^build"], "dependsOn": ["^build"],
"inputs": ["{projectRoot}/**/*"], "inputs": ["default"],
"outputs": [ "outputs": [
"{workspaceRoot}/apps/core/public/plugins/hello-world", "{workspaceRoot}/apps/core/public/plugins/hello-world",
"{workspaceRoot}/apps/electron/dist/plugins/hello-world" "{workspaceRoot}/apps/electron/dist/plugins/hello-world"
] ]
} }
} },
"tags": ["plugin"]
} }

View File

@@ -1,7 +1,13 @@
{ {
"name": "@affine/image-preview-plugin", "name": "@affine/image-preview-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"tags": ["plugin"], "namedInputs": {
"default": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/plugin-cli/src/**/*",
"sharedGlobals"
]
},
"targets": { "targets": {
"build": { "build": {
"executor": "nx:run-script", "executor": "nx:run-script",
@@ -9,11 +15,12 @@
"script": "build" "script": "build"
}, },
"dependsOn": ["^build"], "dependsOn": ["^build"],
"inputs": ["{projectRoot}/**/*"], "inputs": ["default"],
"outputs": [ "outputs": [
"{workspaceRoot}/apps/core/public/plugins/image-preview", "{workspaceRoot}/apps/core/public/plugins/image-preview",
"{workspaceRoot}/apps/electron/dist/plugins/image-preview" "{workspaceRoot}/apps/electron/dist/plugins/image-preview"
] ]
} }
} },
"tags": ["plugin"]
} }

View File

@@ -0,0 +1,10 @@
{
"root": false,
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"sourceType": "module",
"extraFileExtensions": [".vue"]
},
"extends": ["plugin:vue/vue3-recommended"]
}

View File

@@ -0,0 +1,26 @@
{
"name": "@affine/vue-hello-world-plugin",
"type": "module",
"private": true,
"description": "Vue hello world plugin",
"version": "0.8.0-canary.11",
"scripts": {
"dev": "af dev",
"build": "af build"
},
"affinePlugin": {
"release": "development",
"entry": {
"core": "./src/index.ts"
}
},
"dependencies": {
"@affine/component": "workspace:*",
"@affine/sdk": "workspace:*",
"element-plus": "^2.3.9",
"vue": "^3.3.4"
},
"devDependencies": {
"@affine/plugin-cli": "workspace:*"
}
}

View File

@@ -0,0 +1,26 @@
{
"name": "@affine/vue-hello-world-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"namedInputs": {
"default": [
"{projectRoot}/**/*",
"{workspaceRoot}/packages/plugin-cli/src/**/*",
"sharedGlobals"
]
},
"targets": {
"build": {
"executor": "nx:run-script",
"options": {
"script": "build"
},
"dependsOn": ["^build"],
"inputs": ["default"],
"outputs": [
"{workspaceRoot}/apps/core/public/plugins/vue-hello-world",
"{workspaceRoot}/apps/electron/dist/plugins/vue-hello-world"
]
}
},
"tags": ["plugin"]
}

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<el-button @click="count++">
{{ count }}
</el-button>
</template>
<style scoped></style>

View File

@@ -0,0 +1,5 @@
declare module '*.vue' {
import type { ComponentOptions } from 'vue';
const component: ComponentOptions;
export default component;
}

View File

@@ -0,0 +1,18 @@
import type { PluginContext } from '@affine/sdk/entry';
import ElementPlus from 'element-plus';
import { createApp } from 'vue';
import App from './app.vue';
export const entry = (context: PluginContext) => {
context.register('headerItem', div => {
const app = createApp(App);
app.use(ElementPlus);
app.mount(div, false, false);
return () => {
app.unmount();
};
});
return () => {};
};

View File

@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.json",
"include": ["./src"],
"compilerOptions": {
"noEmit": false,
"outDir": "lib",
"jsx": "preserve"
},
"references": [
{
"path": "../../packages/sdk"
}
]
}

View File

@@ -44,5 +44,11 @@ test('plugin should exist', async ({ page }) => {
description: expect.any(String), description: expect.any(String),
affinePlugin: expect.anything(), affinePlugin: expect.anything(),
}, },
{
name: '@affine/vue-hello-world-plugin',
version: expect.any(String),
description: expect.any(String),
affinePlugin: expect.anything(),
},
]); ]);
}); });

View File

@@ -130,6 +130,9 @@
{ {
"path": "./plugins/image-preview" "path": "./plugins/image-preview"
}, },
{
"path": "./plugins/vue-hello-world"
},
// Packages // Packages
{ {
"path": "./packages/cli" "path": "./packages/cli"

586
yarn.lock

File diff suppressed because it is too large Load Diff