mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(core): split web entry from core (#6082)
This pr is trying to split `web` and `electron` entries from `core`. It allows more platform-related optimization to be addressed in each entry. We should remove all browser/electron only codes from `core` eventually, this is the very first step for that.
This commit is contained in:
@@ -23,7 +23,10 @@
|
||||
"main": "./dist/main.js",
|
||||
"devDependencies": {
|
||||
"@affine-test/kit": "workspace:*",
|
||||
"@affine/component": "workspace:*",
|
||||
"@affine/core": "workspace:*",
|
||||
"@affine/env": "workspace:*",
|
||||
"@affine/i18n": "workspace:*",
|
||||
"@affine/native": "workspace:*",
|
||||
"@blocksuite/blocks": "0.13.0-canary-202403140735-2367cd5",
|
||||
"@blocksuite/lit": "0.13.0-canary-202403140735-2367cd5",
|
||||
@@ -38,6 +41,7 @@
|
||||
"@electron-forge/maker-zip": "^7.3.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.3.0",
|
||||
"@electron-forge/shared-types": "^7.3.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@pengx17/electron-forge-maker-appimage": "^1.0.2",
|
||||
"@toeverything/infra": "workspace:*",
|
||||
"@types/uuid": "^9.0.8",
|
||||
@@ -51,7 +55,11 @@
|
||||
"fs-extra": "^11.2.0",
|
||||
"glob": "^10.3.10",
|
||||
"jotai": "^2.6.5",
|
||||
"jotai-devtools": "^0.8.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"rxjs": "^7.8.1",
|
||||
"semver": "^7.6.0",
|
||||
"tinykeys": "patch:tinykeys@npm%3A2.1.0#~/.yarn/patches/tinykeys-npm-2.1.0-819feeaed0.patch",
|
||||
|
||||
90
packages/frontend/electron/renderer/app.tsx
Normal file
90
packages/frontend/electron/renderer/app.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import '@affine/component/theme/global.css';
|
||||
import '@affine/component/theme/theme.css';
|
||||
|
||||
import { AffineContext } from '@affine/component/context';
|
||||
import { GlobalLoading } from '@affine/component/global-loading';
|
||||
import { NotificationCenter } from '@affine/component/notification-center';
|
||||
import { WorkspaceFallback } from '@affine/core/components/workspace';
|
||||
import { GlobalScopeProvider } from '@affine/core/modules/infra-web/global-scope';
|
||||
import { CloudSessionProvider } from '@affine/core/providers/session-provider';
|
||||
import { router } from '@affine/core/router';
|
||||
import {
|
||||
performanceLogger,
|
||||
performanceRenderLogger,
|
||||
} from '@affine/core/shared';
|
||||
import createEmotionCache from '@affine/core/utils/create-emotion-cache';
|
||||
import { configureWebServices } from '@affine/core/web';
|
||||
import { createI18n, setUpLanguage } from '@affine/i18n';
|
||||
import { CacheProvider } from '@emotion/react';
|
||||
import { getCurrentStore } from '@toeverything/infra/atom';
|
||||
import { ServiceCollection } from '@toeverything/infra/di';
|
||||
import type { PropsWithChildren, ReactElement } from 'react';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
|
||||
const performanceI18nLogger = performanceLogger.namespace('i18n');
|
||||
const cache = createEmotionCache();
|
||||
|
||||
const DevTools = lazy(() =>
|
||||
import('jotai-devtools').then(m => ({ default: m.DevTools }))
|
||||
);
|
||||
|
||||
const DebugProvider = ({ children }: PropsWithChildren): ReactElement => {
|
||||
return (
|
||||
<>
|
||||
<Suspense>{process.env.DEBUG_JOTAI === 'true' && <DevTools />}</Suspense>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const future = {
|
||||
v7_startTransition: true,
|
||||
} as const;
|
||||
|
||||
async function loadLanguage() {
|
||||
performanceI18nLogger.info('start');
|
||||
|
||||
const i18n = createI18n();
|
||||
document.documentElement.lang = i18n.language;
|
||||
|
||||
performanceI18nLogger.info('set up');
|
||||
await setUpLanguage(i18n);
|
||||
performanceI18nLogger.info('done');
|
||||
}
|
||||
|
||||
let languageLoadingPromise: Promise<void> | null = null;
|
||||
|
||||
const services = new ServiceCollection();
|
||||
configureWebServices(services);
|
||||
const serviceProvider = services.provider();
|
||||
|
||||
export function App() {
|
||||
performanceRenderLogger.info('App');
|
||||
|
||||
if (!languageLoadingPromise) {
|
||||
languageLoadingPromise = loadLanguage().catch(console.error);
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<GlobalScopeProvider provider={serviceProvider}>
|
||||
<CacheProvider value={cache}>
|
||||
<AffineContext store={getCurrentStore()}>
|
||||
<CloudSessionProvider>
|
||||
<DebugProvider>
|
||||
<GlobalLoading />
|
||||
<NotificationCenter />
|
||||
<RouterProvider
|
||||
fallbackElement={<WorkspaceFallback key="RouterFallback" />}
|
||||
router={router}
|
||||
future={future}
|
||||
/>
|
||||
</DebugProvider>
|
||||
</CloudSessionProvider>
|
||||
</AffineContext>
|
||||
</CacheProvider>
|
||||
</GlobalScopeProvider>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
43
packages/frontend/electron/renderer/index.tsx
Normal file
43
packages/frontend/electron/renderer/index.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
// Side effect import, "declare global"
|
||||
import '@affine/env/constant';
|
||||
|
||||
import { setup } from '@affine/core/bootstrap/setup';
|
||||
import { performanceLogger } from '@affine/core/shared';
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import { App } from './app';
|
||||
|
||||
const performanceMainLogger = performanceLogger.namespace('main');
|
||||
function main() {
|
||||
performanceMainLogger.info('start');
|
||||
|
||||
// skip bootstrap setup for desktop onboarding
|
||||
if (window.appInfo?.windowName === 'onboarding') {
|
||||
performanceMainLogger.info('skip setup');
|
||||
} else {
|
||||
performanceMainLogger.info('setup start');
|
||||
setup();
|
||||
performanceMainLogger.info('setup done');
|
||||
}
|
||||
|
||||
mountApp();
|
||||
}
|
||||
|
||||
function mountApp() {
|
||||
performanceMainLogger.info('import app');
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const root = document.getElementById('app')!;
|
||||
performanceMainLogger.info('render app');
|
||||
createRoot(root).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
} catch (err) {
|
||||
console.error('Failed to bootstrap app', err);
|
||||
}
|
||||
29
packages/frontend/electron/renderer/tsconfig.json
Normal file
29
packages/frontend/electron/renderer/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noEmit": false,
|
||||
"outDir": "../lib",
|
||||
"allowJs": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../common/env"
|
||||
},
|
||||
{
|
||||
"path": "../../core"
|
||||
},
|
||||
{
|
||||
"path": "../../component"
|
||||
},
|
||||
{
|
||||
"path": "../../../common/infra"
|
||||
}
|
||||
],
|
||||
"include": ["."]
|
||||
}
|
||||
@@ -12,8 +12,8 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
const repoRootDir = path.join(__dirname, '..', '..', '..', '..');
|
||||
const electronRootDir = path.join(__dirname, '..');
|
||||
const publicDistDir = path.join(electronRootDir, 'resources');
|
||||
const affineCoreDir = path.join(repoRootDir, 'packages', 'frontend', 'core');
|
||||
const affineCoreOutDir = path.join(affineCoreDir, 'dist');
|
||||
const webDir = path.join(repoRootDir, 'packages', 'frontend', 'electron');
|
||||
const affineWebOutDir = path.join(webDir, 'dist');
|
||||
const publicAffineOutDir = path.join(publicDistDir, `web-static`);
|
||||
const releaseVersionEnv = process.env.RELEASE_VERSION || '';
|
||||
|
||||
@@ -21,8 +21,8 @@ console.log('build with following variables', {
|
||||
repoRootDir,
|
||||
electronRootDir,
|
||||
publicDistDir,
|
||||
affineSrcDir: affineCoreDir,
|
||||
affineSrcOutDir: affineCoreOutDir,
|
||||
affineSrcDir: webDir,
|
||||
affineSrcOutDir: affineWebOutDir,
|
||||
publicAffineOutDir,
|
||||
releaseVersionEnv,
|
||||
});
|
||||
@@ -45,7 +45,7 @@ const nxFlag = SKIP_NX_CACHE ? '--skip-nx-cache' : '';
|
||||
|
||||
// step 1: build web dist
|
||||
if (!process.env.SKIP_WEB_BUILD) {
|
||||
spawnSync('yarn', ['nx', 'build', '@affine/core', nxFlag], {
|
||||
spawnSync('yarn', ['nx', 'build', '@affine/web', nxFlag], {
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
cwd,
|
||||
@@ -58,10 +58,10 @@ if (!process.env.SKIP_WEB_BUILD) {
|
||||
});
|
||||
|
||||
// step 1.5: amend sourceMappingURL to allow debugging in devtools
|
||||
await glob('**/*.{js,css}', { cwd: affineCoreOutDir }).then(files => {
|
||||
await glob('**/*.{js,css}', { cwd: affineWebOutDir }).then(files => {
|
||||
return files.map(async file => {
|
||||
const dir = path.dirname(file);
|
||||
const fullpath = path.join(affineCoreOutDir, file);
|
||||
const fullpath = path.join(affineWebOutDir, file);
|
||||
let content = await fs.readFile(fullpath, 'utf-8');
|
||||
// replace # sourceMappingURL=76-6370cd185962bc89.js.map
|
||||
// to # sourceMappingURL=assets://./{dir}/76-6370cd185962bc89.js.map
|
||||
@@ -78,7 +78,7 @@ if (!process.env.SKIP_WEB_BUILD) {
|
||||
});
|
||||
});
|
||||
|
||||
await fs.move(affineCoreOutDir, publicAffineOutDir, { overwrite: true });
|
||||
await fs.move(affineWebOutDir, publicAffineOutDir, { overwrite: true });
|
||||
}
|
||||
|
||||
// step 2: update app-updater.yml content with build type in resources folder
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"noImplicitOverride": true
|
||||
},
|
||||
"include": ["./src"],
|
||||
"exclude": ["node_modules", "lib", "dist", "**/__tests__/**/*"],
|
||||
"exclude": ["renderer", "node_modules", "lib", "dist", "**/__tests__/**/*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../frontend/native"
|
||||
|
||||
Reference in New Issue
Block a user