build: improve webpack config (#3561)
This commit is contained in:
@@ -79,6 +79,10 @@ export const createConfiguration: (
|
|||||||
name: 'affine',
|
name: 'affine',
|
||||||
// to set a correct base path for the source map
|
// to set a correct base path for the source map
|
||||||
context: projectRoot,
|
context: projectRoot,
|
||||||
|
experiments: {
|
||||||
|
topLevelAwait: true,
|
||||||
|
outputModule: false,
|
||||||
|
},
|
||||||
output: {
|
output: {
|
||||||
environment: {
|
environment: {
|
||||||
module: true,
|
module: true,
|
||||||
@@ -130,6 +134,10 @@ export const createConfiguration: (
|
|||||||
module: {
|
module: {
|
||||||
parser: {
|
parser: {
|
||||||
javascript: {
|
javascript: {
|
||||||
|
// Do not mock Node.js globals
|
||||||
|
node: false,
|
||||||
|
requireJs: false,
|
||||||
|
import: true,
|
||||||
// Treat as missing export as error
|
// Treat as missing export as error
|
||||||
strictExportPresence: true,
|
strictExportPresence: true,
|
||||||
},
|
},
|
||||||
@@ -137,6 +145,20 @@ export const createConfiguration: (
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.m?js?$/,
|
test: /\.m?js?$/,
|
||||||
|
enforce: 'pre',
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: require.resolve('source-map-loader'),
|
||||||
|
options: {
|
||||||
|
filterSourceMappingUrl: (
|
||||||
|
_url: string,
|
||||||
|
resourcePath: string
|
||||||
|
) => {
|
||||||
|
return resourcePath.includes('@blocksuite');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
fullySpecified: false,
|
fullySpecified: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,23 +15,21 @@ export default async function (cli_env: any, _: any) {
|
|||||||
const config = createConfiguration(flags, runtimeConfig);
|
const config = createConfiguration(flags, runtimeConfig);
|
||||||
return merge(config, {
|
return merge(config, {
|
||||||
entry: {
|
entry: {
|
||||||
'polyfill-ses': {
|
'polyfill/ses': {
|
||||||
asyncChunks: false,
|
|
||||||
import: resolve(rootPath, 'src/polyfill/ses.ts'),
|
import: resolve(rootPath, 'src/polyfill/ses.ts'),
|
||||||
},
|
},
|
||||||
plugin: {
|
plugin: {
|
||||||
asyncChunks: true,
|
dependOn: ['polyfill/ses'],
|
||||||
dependOn: ['polyfill-ses'],
|
|
||||||
import: resolve(rootPath, 'src/bootstrap/register-plugins.ts'),
|
import: resolve(rootPath, 'src/bootstrap/register-plugins.ts'),
|
||||||
},
|
},
|
||||||
index: {
|
app: {
|
||||||
asyncChunks: false,
|
chunkLoading: 'import',
|
||||||
dependOn: ['polyfill-ses', 'plugin'],
|
dependOn: ['polyfill/ses', 'plugin'],
|
||||||
import: resolve(rootPath, 'src/index.tsx'),
|
import: resolve(rootPath, 'src/index.tsx'),
|
||||||
},
|
},
|
||||||
'_plugin/index.test': {
|
'_plugin/index.test': {
|
||||||
asyncChunks: false,
|
chunkLoading: 'import',
|
||||||
dependOn: ['polyfill-ses', 'plugin'],
|
dependOn: ['polyfill/ses', 'plugin'],
|
||||||
import: resolve(rootPath, 'src/_plugin/index.test.tsx'),
|
import: resolve(rootPath, 'src/_plugin/index.test.tsx'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -41,7 +39,7 @@ export default async function (cli_env: any, _: any) {
|
|||||||
inject: 'body',
|
inject: 'body',
|
||||||
scriptLoading: 'module',
|
scriptLoading: 'module',
|
||||||
minify: false,
|
minify: false,
|
||||||
chunks: ['index', 'plugin', 'polyfill-ses'],
|
chunks: ['app', 'plugin', 'polyfill/ses'],
|
||||||
filename: 'index.html',
|
filename: 'index.html',
|
||||||
}),
|
}),
|
||||||
new HTMLPlugin({
|
new HTMLPlugin({
|
||||||
@@ -49,7 +47,7 @@ export default async function (cli_env: any, _: any) {
|
|||||||
inject: 'body',
|
inject: 'body',
|
||||||
scriptLoading: 'module',
|
scriptLoading: 'module',
|
||||||
minify: false,
|
minify: false,
|
||||||
chunks: ['_plugin/index.test', 'plugin', 'polyfill-ses'],
|
chunks: ['_plugin/index.test', 'plugin', 'polyfill/ses'],
|
||||||
filename: '_plugin/index.html',
|
filename: '_plugin/index.html',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"html-webpack-plugin": "^5.5.3",
|
"html-webpack-plugin": "^5.5.3",
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
|
"source-map-loader": "^4.0.1",
|
||||||
"style-loader": "^3.3.3",
|
"style-loader": "^3.3.3",
|
||||||
"swc-loader": "^0.2.3",
|
"swc-loader": "^0.2.3",
|
||||||
"swc-plugin-coverage-instrument": "^0.0.19",
|
"swc-plugin-coverage-instrument": "^0.0.19",
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "@affine/core",
|
"name": "@affine/core",
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"projectType": "application",
|
|
||||||
"root": "apps/core",
|
|
||||||
"sourceRoot": "apps/core/src",
|
|
||||||
"targets": {
|
"targets": {
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "nx:run-script",
|
"executor": "nx:run-script",
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": ["tag:plugin"],
|
||||||
|
"target": "build",
|
||||||
|
"params": "ignore"
|
||||||
|
},
|
||||||
|
"^build"
|
||||||
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
|
"{projectRoot}/.webpack/**/*",
|
||||||
"{projectRoot}/**/*",
|
"{projectRoot}/**/*",
|
||||||
"{projectRoot}/public/plugins/**/*",
|
"{projectRoot}/public/**/*",
|
||||||
"{workspaceRoot}/packages/component/src/**/*",
|
"{workspaceRoot}/packages/component/src/**/*",
|
||||||
"{workspaceRoot}/packages/debug/src/**/*",
|
"{workspaceRoot}/packages/debug/src/**/*",
|
||||||
"{workspaceRoot}/packages/graphql/src/**/*",
|
"{workspaceRoot}/packages/graphql/src/**/*",
|
||||||
@@ -46,7 +51,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"script": "build"
|
"script": "build"
|
||||||
},
|
},
|
||||||
"outputs": ["{projectRoot}/dist", "{projectRoot}/public/plugins"]
|
"outputs": ["{projectRoot}/dist"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ const PORT = process.env.PORT || 8080;
|
|||||||
|
|
||||||
app.use('/', express.static('dist'));
|
app.use('/', express.static('dist'));
|
||||||
|
|
||||||
app.get('/*', (_, res) => {
|
app.get('/*', (req, res) => {
|
||||||
|
if (req.url.startsWith('/plugins')) {
|
||||||
|
res.sendFile(req.url, { root: 'dist' });
|
||||||
|
}
|
||||||
res.sendFile('index.html', { root: 'dist' });
|
res.sendFile('index.html', { root: 'dist' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import { createRoot } from 'react-dom/client';
|
|||||||
import { _pluginNestedImportsMap } from '../bootstrap/plugins/setup';
|
import { _pluginNestedImportsMap } from '../bootstrap/plugins/setup';
|
||||||
import { pluginRegisterPromise } from '../bootstrap/register-plugins';
|
import { pluginRegisterPromise } from '../bootstrap/register-plugins';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const { setup } = await import('../bootstrap/setup');
|
||||||
|
await setup();
|
||||||
const root = document.getElementById('app');
|
const root = document.getElementById('app');
|
||||||
assertExists(root);
|
assertExists(root);
|
||||||
|
|
||||||
@@ -22,7 +25,9 @@ const App = () => {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div data-plugins-load-status="success">Successfully loaded plugins:</div>
|
<div data-plugins-load-status="success">
|
||||||
|
Successfully loaded plugins:
|
||||||
|
</div>
|
||||||
{plugins.map(plugin => {
|
{plugins.map(plugin => {
|
||||||
return <div key={plugin}>{plugin}</div>;
|
return <div key={plugin}>{plugin}</div>;
|
||||||
})}
|
})}
|
||||||
@@ -37,3 +42,6 @@ createRoot(root).render(
|
|||||||
</Provider>
|
</Provider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await main();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import '@affine/component/theme/theme.css';
|
|||||||
|
|
||||||
import { AffineContext } from '@affine/component/context';
|
import { AffineContext } from '@affine/component/context';
|
||||||
import { WorkspaceFallback } from '@affine/component/workspace';
|
import { WorkspaceFallback } from '@affine/component/workspace';
|
||||||
import { createI18n, setUpLanguage } from '@affine/i18n';
|
|
||||||
import { CacheProvider } from '@emotion/react';
|
import { CacheProvider } from '@emotion/react';
|
||||||
import { use } from 'foxact/use';
|
import { use } from 'foxact/use';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
@@ -13,7 +12,6 @@ import { RouterProvider } from 'react-router-dom';
|
|||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import createEmotionCache from './utils/create-emotion-cache';
|
import createEmotionCache from './utils/create-emotion-cache';
|
||||||
|
|
||||||
const i18n = createI18n();
|
|
||||||
const cache = createEmotionCache();
|
const cache = createEmotionCache();
|
||||||
|
|
||||||
const DevTools = lazy(() =>
|
const DevTools = lazy(() =>
|
||||||
@@ -33,18 +31,16 @@ const future = {
|
|||||||
v7_startTransition: true,
|
v7_startTransition: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const languageLoadingPromise = new Promise<void>(resolve => {
|
async function loadLanguage() {
|
||||||
if (environment.isBrowser) {
|
if (environment.isBrowser) {
|
||||||
|
const { createI18n, setUpLanguage } = await import('@affine/i18n');
|
||||||
|
const i18n = createI18n();
|
||||||
document.documentElement.lang = i18n.language;
|
document.documentElement.lang = i18n.language;
|
||||||
setUpLanguage(i18n)
|
await setUpLanguage(i18n);
|
||||||
.then(() => resolve())
|
|
||||||
.catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const languageLoadingPromise = loadLanguage().catch(console.error);
|
||||||
|
|
||||||
export const App = memo(function App() {
|
export const App = memo(function App() {
|
||||||
use(languageLoadingPromise);
|
use(languageLoadingPromise);
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
import {
|
|
||||||
migrateDatabaseBlockTo3,
|
|
||||||
migrateToSubdoc,
|
|
||||||
} from '@affine/env/blocksuite';
|
|
||||||
import { setupGlobal } from '@affine/env/global';
|
|
||||||
import type {
|
|
||||||
LocalIndexedDBDownloadProvider,
|
|
||||||
WorkspaceAdapter,
|
|
||||||
} from '@affine/env/workspace';
|
|
||||||
import { WorkspaceFlavour, WorkspaceVersion } from '@affine/env/workspace';
|
|
||||||
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
|
|
||||||
import {
|
|
||||||
type RootWorkspaceMetadataV2,
|
|
||||||
rootWorkspacesMetadataAtom,
|
|
||||||
workspaceAdaptersAtom,
|
|
||||||
} from '@affine/workspace/atom';
|
|
||||||
import {
|
|
||||||
migrateLocalBlobStorage,
|
|
||||||
upgradeV1ToV2,
|
|
||||||
} from '@affine/workspace/migration';
|
|
||||||
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
|
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
|
||||||
import { rootStore } from '@toeverything/infra/atom';
|
|
||||||
|
|
||||||
import { WorkspaceAdapters } from '../adapters/workspace';
|
|
||||||
|
|
||||||
console.log('setup global');
|
|
||||||
setupGlobal();
|
|
||||||
|
|
||||||
rootStore.set(
|
|
||||||
workspaceAdaptersAtom,
|
|
||||||
WorkspaceAdapters as Record<
|
|
||||||
WorkspaceFlavour,
|
|
||||||
WorkspaceAdapter<WorkspaceFlavour>
|
|
||||||
>
|
|
||||||
);
|
|
||||||
|
|
||||||
const value = localStorage.getItem('jotai-workspaces');
|
|
||||||
if (value) {
|
|
||||||
try {
|
|
||||||
const metadata = JSON.parse(value) as RootWorkspaceMetadata[];
|
|
||||||
const promises: Promise<void>[] = [];
|
|
||||||
const newMetadata = [...metadata];
|
|
||||||
metadata.forEach(oldMeta => {
|
|
||||||
if (!('version' in oldMeta)) {
|
|
||||||
const adapter = WorkspaceAdapters[oldMeta.flavour];
|
|
||||||
assertExists(adapter);
|
|
||||||
const upgrade = async () => {
|
|
||||||
const workspace = await adapter.CRUD.get(oldMeta.id);
|
|
||||||
if (!workspace) {
|
|
||||||
console.warn('cannot find workspace', oldMeta.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
|
|
||||||
console.warn('not supported');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const doc = workspace.blockSuiteWorkspace.doc;
|
|
||||||
const provider = createIndexedDBDownloadProvider(workspace.id, doc, {
|
|
||||||
awareness: workspace.blockSuiteWorkspace.awarenessStore.awareness,
|
|
||||||
}) as LocalIndexedDBDownloadProvider;
|
|
||||||
provider.sync();
|
|
||||||
await provider.whenReady;
|
|
||||||
const newDoc = migrateToSubdoc(doc);
|
|
||||||
if (doc === newDoc) {
|
|
||||||
console.log('doc not changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newWorkspace = upgradeV1ToV2(workspace);
|
|
||||||
await migrateDatabaseBlockTo3(newWorkspace.blockSuiteWorkspace.doc);
|
|
||||||
|
|
||||||
const newId = await adapter.CRUD.create(
|
|
||||||
newWorkspace.blockSuiteWorkspace
|
|
||||||
);
|
|
||||||
|
|
||||||
await adapter.CRUD.delete(workspace as any);
|
|
||||||
console.log('migrated', oldMeta.id, newId);
|
|
||||||
const index = newMetadata.findIndex(meta => meta.id === oldMeta.id);
|
|
||||||
newMetadata[index] = {
|
|
||||||
...oldMeta,
|
|
||||||
id: newId,
|
|
||||||
version: WorkspaceVersion.DatabaseV3,
|
|
||||||
};
|
|
||||||
await migrateLocalBlobStorage(workspace.id, newId);
|
|
||||||
console.log('migrate to v2');
|
|
||||||
};
|
|
||||||
|
|
||||||
// create a new workspace and push it to metadata
|
|
||||||
promises.push(upgrade());
|
|
||||||
} else if (oldMeta.version < WorkspaceVersion.DatabaseV3) {
|
|
||||||
const adapter = WorkspaceAdapters[oldMeta.flavour];
|
|
||||||
assertExists(adapter);
|
|
||||||
promises.push(
|
|
||||||
(async () => {
|
|
||||||
const workspace = await adapter.CRUD.get(oldMeta.id);
|
|
||||||
if (workspace) {
|
|
||||||
const provider = createIndexedDBDownloadProvider(
|
|
||||||
workspace.id,
|
|
||||||
workspace.blockSuiteWorkspace.doc,
|
|
||||||
{
|
|
||||||
awareness:
|
|
||||||
workspace.blockSuiteWorkspace.awarenessStore.awareness,
|
|
||||||
}
|
|
||||||
) as LocalIndexedDBDownloadProvider;
|
|
||||||
provider.sync();
|
|
||||||
await provider.whenReady;
|
|
||||||
await migrateDatabaseBlockTo3(workspace.blockSuiteWorkspace.doc);
|
|
||||||
}
|
|
||||||
const index = newMetadata.findIndex(meta => meta.id === oldMeta.id);
|
|
||||||
console.log('migrate to v3');
|
|
||||||
newMetadata[index] = {
|
|
||||||
...oldMeta,
|
|
||||||
version: WorkspaceVersion.DatabaseV3,
|
|
||||||
};
|
|
||||||
})()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises)
|
|
||||||
.then(() => {
|
|
||||||
console.log('migration done');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.error('migration failed');
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
localStorage.setItem('jotai-workspaces', JSON.stringify(newMetadata));
|
|
||||||
window.dispatchEvent(new CustomEvent('migration-done'));
|
|
||||||
window.$migrationDone = true;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error('error when migrating data', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createFirst = (): RootWorkspaceMetadataV2[] => {
|
|
||||||
const Plugins = Object.values(WorkspaceAdapters).sort(
|
|
||||||
(a, b) => a.loadPriority - b.loadPriority
|
|
||||||
);
|
|
||||||
|
|
||||||
return Plugins.flatMap(Plugin => {
|
|
||||||
return Plugin.Events['app:init']?.().map(
|
|
||||||
id =>
|
|
||||||
<RootWorkspaceMetadataV2>{
|
|
||||||
id,
|
|
||||||
flavour: Plugin.flavour,
|
|
||||||
version: WorkspaceVersion.DatabaseV3,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}).filter((ids): ids is RootWorkspaceMetadataV2 => !!ids);
|
|
||||||
};
|
|
||||||
|
|
||||||
await rootStore
|
|
||||||
.get(rootWorkspacesMetadataAtom)
|
|
||||||
.then(meta => {
|
|
||||||
if (meta.length === 0 && localStorage.getItem('is-first-open') === null) {
|
|
||||||
const result = createFirst();
|
|
||||||
console.info('create first workspace', result);
|
|
||||||
localStorage.setItem('is-first-open', 'false');
|
|
||||||
rootStore.set(rootWorkspacesMetadataAtom, result);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
180
apps/core/src/bootstrap/setup.ts
Normal file
180
apps/core/src/bootstrap/setup.ts
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import {
|
||||||
|
migrateDatabaseBlockTo3,
|
||||||
|
migrateToSubdoc,
|
||||||
|
} from '@affine/env/blocksuite';
|
||||||
|
import { setupGlobal } from '@affine/env/global';
|
||||||
|
import type {
|
||||||
|
LocalIndexedDBDownloadProvider,
|
||||||
|
WorkspaceAdapter,
|
||||||
|
} from '@affine/env/workspace';
|
||||||
|
import { WorkspaceFlavour, WorkspaceVersion } from '@affine/env/workspace';
|
||||||
|
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
|
||||||
|
import {
|
||||||
|
type RootWorkspaceMetadataV2,
|
||||||
|
rootWorkspacesMetadataAtom,
|
||||||
|
workspaceAdaptersAtom,
|
||||||
|
} from '@affine/workspace/atom';
|
||||||
|
import {
|
||||||
|
migrateLocalBlobStorage,
|
||||||
|
upgradeV1ToV2,
|
||||||
|
} from '@affine/workspace/migration';
|
||||||
|
import { createIndexedDBDownloadProvider } from '@affine/workspace/providers';
|
||||||
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
|
import { rootStore } from '@toeverything/infra/atom';
|
||||||
|
|
||||||
|
import { WorkspaceAdapters } from '../adapters/workspace';
|
||||||
|
|
||||||
|
async function tryMigration() {
|
||||||
|
const value = localStorage.getItem('jotai-workspaces');
|
||||||
|
if (value) {
|
||||||
|
try {
|
||||||
|
const metadata = JSON.parse(value) as RootWorkspaceMetadata[];
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
const newMetadata = [...metadata];
|
||||||
|
metadata.forEach(oldMeta => {
|
||||||
|
if (!('version' in oldMeta)) {
|
||||||
|
const adapter = WorkspaceAdapters[oldMeta.flavour];
|
||||||
|
assertExists(adapter);
|
||||||
|
const upgrade = async () => {
|
||||||
|
const workspace = await adapter.CRUD.get(oldMeta.id);
|
||||||
|
if (!workspace) {
|
||||||
|
console.warn('cannot find workspace', oldMeta.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (workspace.flavour !== WorkspaceFlavour.LOCAL) {
|
||||||
|
console.warn('not supported');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const doc = workspace.blockSuiteWorkspace.doc;
|
||||||
|
const provider = createIndexedDBDownloadProvider(
|
||||||
|
workspace.id,
|
||||||
|
doc,
|
||||||
|
{
|
||||||
|
awareness:
|
||||||
|
workspace.blockSuiteWorkspace.awarenessStore.awareness,
|
||||||
|
}
|
||||||
|
) as LocalIndexedDBDownloadProvider;
|
||||||
|
provider.sync();
|
||||||
|
await provider.whenReady;
|
||||||
|
const newDoc = migrateToSubdoc(doc);
|
||||||
|
if (doc === newDoc) {
|
||||||
|
console.log('doc not changed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newWorkspace = upgradeV1ToV2(workspace);
|
||||||
|
await migrateDatabaseBlockTo3(newWorkspace.blockSuiteWorkspace.doc);
|
||||||
|
|
||||||
|
const newId = await adapter.CRUD.create(
|
||||||
|
newWorkspace.blockSuiteWorkspace
|
||||||
|
);
|
||||||
|
|
||||||
|
await adapter.CRUD.delete(workspace as any);
|
||||||
|
console.log('migrated', oldMeta.id, newId);
|
||||||
|
const index = newMetadata.findIndex(meta => meta.id === oldMeta.id);
|
||||||
|
newMetadata[index] = {
|
||||||
|
...oldMeta,
|
||||||
|
id: newId,
|
||||||
|
version: WorkspaceVersion.DatabaseV3,
|
||||||
|
};
|
||||||
|
await migrateLocalBlobStorage(workspace.id, newId);
|
||||||
|
console.log('migrate to v2');
|
||||||
|
};
|
||||||
|
|
||||||
|
// create a new workspace and push it to metadata
|
||||||
|
promises.push(upgrade());
|
||||||
|
} else if (oldMeta.version < WorkspaceVersion.DatabaseV3) {
|
||||||
|
console.log('migrate to v3');
|
||||||
|
const adapter = WorkspaceAdapters[oldMeta.flavour];
|
||||||
|
assertExists(adapter);
|
||||||
|
promises.push(
|
||||||
|
(async () => {
|
||||||
|
const workspace = await adapter.CRUD.get(oldMeta.id);
|
||||||
|
if (workspace) {
|
||||||
|
const provider = createIndexedDBDownloadProvider(
|
||||||
|
workspace.id,
|
||||||
|
workspace.blockSuiteWorkspace.doc,
|
||||||
|
{
|
||||||
|
awareness:
|
||||||
|
workspace.blockSuiteWorkspace.awarenessStore.awareness,
|
||||||
|
}
|
||||||
|
) as LocalIndexedDBDownloadProvider;
|
||||||
|
provider.sync();
|
||||||
|
await provider.whenReady;
|
||||||
|
await migrateDatabaseBlockTo3(
|
||||||
|
workspace.blockSuiteWorkspace.doc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const index = newMetadata.findIndex(
|
||||||
|
meta => meta.id === oldMeta.id
|
||||||
|
);
|
||||||
|
console.log('migrate to v3');
|
||||||
|
newMetadata[index] = {
|
||||||
|
...oldMeta,
|
||||||
|
version: WorkspaceVersion.DatabaseV3,
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises)
|
||||||
|
.then(() => {
|
||||||
|
console.log('migration done');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.error('migration failed');
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
localStorage.setItem('jotai-workspaces', JSON.stringify(newMetadata));
|
||||||
|
window.dispatchEvent(new CustomEvent('migration-done'));
|
||||||
|
window.$migrationDone = true;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('error when migrating data', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFirstAppData() {
|
||||||
|
const createFirst = (): RootWorkspaceMetadataV2[] => {
|
||||||
|
const Plugins = Object.values(WorkspaceAdapters).sort(
|
||||||
|
(a, b) => a.loadPriority - b.loadPriority
|
||||||
|
);
|
||||||
|
|
||||||
|
return Plugins.flatMap(Plugin => {
|
||||||
|
return Plugin.Events['app:init']?.().map(
|
||||||
|
id =>
|
||||||
|
<RootWorkspaceMetadataV2>{
|
||||||
|
id,
|
||||||
|
flavour: Plugin.flavour,
|
||||||
|
version: WorkspaceVersion.DatabaseV3,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}).filter((ids): ids is RootWorkspaceMetadataV2 => !!ids);
|
||||||
|
};
|
||||||
|
if (localStorage.getItem('is-first-open') !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = createFirst();
|
||||||
|
console.info('create first workspace', result);
|
||||||
|
localStorage.setItem('is-first-open', 'false');
|
||||||
|
rootStore.set(rootWorkspacesMetadataAtom, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setup() {
|
||||||
|
rootStore.set(
|
||||||
|
workspaceAdaptersAtom,
|
||||||
|
WorkspaceAdapters as Record<
|
||||||
|
WorkspaceFlavour,
|
||||||
|
WorkspaceAdapter<WorkspaceFlavour>
|
||||||
|
>
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('setup global');
|
||||||
|
setupGlobal();
|
||||||
|
|
||||||
|
createFirstAppData();
|
||||||
|
await tryMigration();
|
||||||
|
await rootStore.get(rootWorkspacesMetadataAtom);
|
||||||
|
console.log('setup done');
|
||||||
|
}
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
|
import { WorkspaceFallback } from '@affine/component/workspace';
|
||||||
import { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
import { StrictMode } from 'react';
|
import { StrictMode, Suspense } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await import('./bootstrap/before-app');
|
const { setup } = await import('./bootstrap/setup');
|
||||||
|
await setup();
|
||||||
const { App } = await import('./app');
|
const { App } = await import('./app');
|
||||||
const root = document.getElementById('app');
|
const root = document.getElementById('app');
|
||||||
assertExists(root);
|
assertExists(root);
|
||||||
|
|
||||||
createRoot(root).render(
|
createRoot(root).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
<Suspense fallback={<WorkspaceFallback key="AppLoading" />}>
|
||||||
<App />
|
<App />
|
||||||
|
</Suspense>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"description": "AFFiNE App",
|
"description": "AFFiNE App",
|
||||||
"homepage": "https://github.com/toeverything/AFFiNE",
|
"homepage": "https://github.com/toeverything/AFFiNE",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"start": "electron .",
|
||||||
"dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs",
|
"dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs",
|
||||||
"dev:prod": "yarn node scripts/dev.mjs",
|
"dev:prod": "yarn node scripts/dev.mjs",
|
||||||
"build": "NODE_ENV=production zx scripts/build-layers.mjs",
|
"build": "NODE_ENV=production zx scripts/build-layers.mjs",
|
||||||
|
|||||||
@@ -7,7 +7,14 @@
|
|||||||
"targets": {
|
"targets": {
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "nx:run-script",
|
"executor": "nx:run-script",
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"projects": ["tag:plugin"],
|
||||||
|
"target": "build",
|
||||||
|
"params": "ignore"
|
||||||
|
},
|
||||||
|
"^build"
|
||||||
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"script": "build"
|
"script": "build"
|
||||||
},
|
},
|
||||||
|
|||||||
4
nx.json
4
nx.json
@@ -33,9 +33,7 @@
|
|||||||
"{projectRoot}/build",
|
"{projectRoot}/build",
|
||||||
"{projectRoot}/out",
|
"{projectRoot}/out",
|
||||||
"{projectRoot}/storybook-static",
|
"{projectRoot}/storybook-static",
|
||||||
"{workspaceRoot}/packages/i18n/src/i18n-generated.ts",
|
"{workspaceRoot}/packages/i18n/src/i18n-generated.ts"
|
||||||
"{workspaceRoot}/apps/electron/dist/plugins",
|
|
||||||
"{workspaceRoot}/apps/core/public/plugins"
|
|
||||||
],
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
"{workspaceRoot}/infra/**/*",
|
"{workspaceRoot}/infra/**/*",
|
||||||
|
|||||||
4
packages/env/src/global.ts
vendored
4
packages/env/src/global.ts
vendored
@@ -206,10 +206,6 @@ export function setupGlobal() {
|
|||||||
}
|
}
|
||||||
globalThis.environment = environment;
|
globalThis.environment = environment;
|
||||||
|
|
||||||
if (environment.isBrowser) {
|
|
||||||
globalThis.editorVersion = global.editorVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
let prefixUrl: string;
|
let prefixUrl: string;
|
||||||
if (!isBrowser || isDesktop) {
|
if (!isBrowser || isDesktop) {
|
||||||
// SSR or Desktop
|
// SSR or Desktop
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "@affine/bookmark-plugin",
|
"name": "@affine/bookmark-plugin",
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-script",
|
||||||
|
"options": {
|
||||||
|
"script": "build"
|
||||||
|
},
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": ["{projectRoot}/**/*"],
|
||||||
|
"outputs": [
|
||||||
|
"{workspaceRoot}/apps/core/public/plugins/bookmark",
|
||||||
|
"{workspaceRoot}/apps/electron/dist/plugins/bookmark"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"tags": ["plugin"]
|
"tags": ["plugin"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
{
|
{
|
||||||
"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"]
|
"tags": ["plugin"],
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-script",
|
||||||
|
"options": {
|
||||||
|
"script": "build"
|
||||||
|
},
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": ["{projectRoot}/**/*"],
|
||||||
|
"outputs": [
|
||||||
|
"{workspaceRoot}/apps/core/public/plugins/copilot",
|
||||||
|
"{workspaceRoot}/apps/electron/dist/plugins/copilot"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
{
|
{
|
||||||
"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"]
|
"tags": ["plugin"],
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-script",
|
||||||
|
"options": {
|
||||||
|
"script": "build"
|
||||||
|
},
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": ["{projectRoot}/**/*"],
|
||||||
|
"outputs": [
|
||||||
|
"{workspaceRoot}/apps/core/public/plugins/hello-world",
|
||||||
|
"{workspaceRoot}/apps/electron/dist/plugins/hello-world"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { PluginContext } from '@affine/sdk/entry';
|
import type { PluginContext } from '@affine/sdk/entry';
|
||||||
import { currentWorkspaceAtom, rootStore } from '@affine/sdk/entry';
|
|
||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
import { lazy } from 'react';
|
import { lazy } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
@@ -11,7 +10,6 @@ const HeaderItem = lazy(() =>
|
|||||||
export const entry = (context: PluginContext) => {
|
export const entry = (context: PluginContext) => {
|
||||||
console.log('register');
|
console.log('register');
|
||||||
console.log('hello, world!');
|
console.log('hello, world!');
|
||||||
console.log(rootStore.get(currentWorkspaceAtom));
|
|
||||||
context.register('headerItem', div => {
|
context.register('headerItem', div => {
|
||||||
const root = createRoot(div);
|
const root = createRoot(div);
|
||||||
root.render(createElement(HeaderItem));
|
root.render(createElement(HeaderItem));
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
{
|
{
|
||||||
"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"]
|
"tags": ["plugin"],
|
||||||
|
"targets": {
|
||||||
|
"build": {
|
||||||
|
"executor": "nx:run-script",
|
||||||
|
"options": {
|
||||||
|
"script": "build"
|
||||||
|
},
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"inputs": ["{projectRoot}/**/*"],
|
||||||
|
"outputs": [
|
||||||
|
"{workspaceRoot}/apps/core/public/plugins/image-preview",
|
||||||
|
"{workspaceRoot}/apps/electron/dist/plugins/image-preview"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ test('database migration', async ({ page, context }) => {
|
|||||||
await switchToNext();
|
await switchToNext();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
await page.goto('http://localhost:8081/');
|
await page.goto('http://localhost:8081/');
|
||||||
|
await page.click('text=hello');
|
||||||
await page.waitForSelector('v-line', {
|
await page.waitForSelector('v-line', {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
});
|
||||||
|
|||||||
21
yarn.lock
21
yarn.lock
@@ -260,6 +260,7 @@ __metadata:
|
|||||||
react-router-dom: ^6.14.2
|
react-router-dom: ^6.14.2
|
||||||
rxjs: ^7.8.1
|
rxjs: ^7.8.1
|
||||||
ses: ^0.18.5
|
ses: ^0.18.5
|
||||||
|
source-map-loader: ^4.0.1
|
||||||
style-loader: ^3.3.3
|
style-loader: ^3.3.3
|
||||||
swc-loader: ^0.2.3
|
swc-loader: ^0.2.3
|
||||||
swc-plugin-coverage-instrument: ^0.0.19
|
swc-plugin-coverage-instrument: ^0.0.19
|
||||||
@@ -13254,6 +13255,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"abab@npm:^2.0.6":
|
||||||
|
version: 2.0.6
|
||||||
|
resolution: "abab@npm:2.0.6"
|
||||||
|
checksum: 6ffc1af4ff315066c62600123990d87551ceb0aafa01e6539da77b0f5987ac7019466780bf480f1787576d4385e3690c81ccc37cfda12819bf510b8ab47e5a3e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"abbrev@npm:1, abbrev@npm:^1.0.0":
|
"abbrev@npm:1, abbrev@npm:^1.0.0":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "abbrev@npm:1.1.1"
|
resolution: "abbrev@npm:1.1.1"
|
||||||
@@ -29183,6 +29191,19 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"source-map-loader@npm:^4.0.1":
|
||||||
|
version: 4.0.1
|
||||||
|
resolution: "source-map-loader@npm:4.0.1"
|
||||||
|
dependencies:
|
||||||
|
abab: ^2.0.6
|
||||||
|
iconv-lite: ^0.6.3
|
||||||
|
source-map-js: ^1.0.2
|
||||||
|
peerDependencies:
|
||||||
|
webpack: ^5.72.1
|
||||||
|
checksum: 4ddca8b03dc61f406effd4bffe70de4b87fef48bae6f737017b2dabcbc7d609133325be1e73838e9265331de28039111d729fcbb8bce88a6018a816bef510eb1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"source-map-support@npm:0.5.13":
|
"source-map-support@npm:0.5.13":
|
||||||
version: 0.5.13
|
version: 0.5.13
|
||||||
resolution: "source-map-support@npm:0.5.13"
|
resolution: "source-map-support@npm:0.5.13"
|
||||||
|
|||||||
Reference in New Issue
Block a user