refactor!: remove next.js
(#3267)
@ -10,7 +10,7 @@
|
|||||||
"tasks": {
|
"tasks": {
|
||||||
"start-web": {
|
"start-web": {
|
||||||
"name": "Start Web",
|
"name": "Start Web",
|
||||||
"command": "yarn nx dev @affine/web --port 8080",
|
"command": "yarn dev-core",
|
||||||
"runAtStart": true,
|
"runAtStart": true,
|
||||||
"preview": {
|
"preview": {
|
||||||
"port": 8080
|
"port": 8080
|
||||||
|
2
.github/deployment/front/Dockerfile
vendored
@ -1,6 +1,6 @@
|
|||||||
FROM openresty/openresty:1.21.4.1-0-buster
|
FROM openresty/openresty:1.21.4.1-0-buster
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY ./apps/web/out ./dist
|
COPY ./apps/core/dist ./dist
|
||||||
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
|
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
|
||||||
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf
|
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf
|
||||||
|
|
||||||
|
42
.github/workflows/build.yml
vendored
@ -101,8 +101,8 @@ jobs:
|
|||||||
path: ./apps/storybook/storybook-static
|
path: ./apps/storybook/storybook-static
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-web:
|
build-core:
|
||||||
name: Build @affine/web
|
name: Build @affine/core
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: development
|
environment: development
|
||||||
|
|
||||||
@ -110,13 +110,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
- name: Build Web
|
- name: Build Core
|
||||||
run: yarn nx build @affine/web
|
run: yarn nx build @affine/core
|
||||||
- name: Upload artifact
|
- name: Upload core artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: next-js-static
|
name: core
|
||||||
path: ./apps/web/out
|
path: ./apps/core/dist
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
server-test:
|
server-test:
|
||||||
@ -215,7 +215,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2, 3, 4, 5]
|
shard: [1, 2, 3, 4, 5]
|
||||||
environment: development
|
environment: development
|
||||||
needs: build-web
|
needs: build-core
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -224,11 +224,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
electron-install: false
|
electron-install: false
|
||||||
- name: Download artifact
|
- name: Download core artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: next-js-static
|
name: core
|
||||||
path: ./apps/web/out
|
path: ./apps/core/dist
|
||||||
|
|
||||||
- name: Run playwright tests
|
- name: Run playwright tests
|
||||||
run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||||
@ -260,7 +260,7 @@ jobs:
|
|||||||
name: E2E Migration Test
|
name: E2E Migration Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: development
|
environment: development
|
||||||
needs: [build-web]
|
needs: build-core
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -270,11 +270,11 @@ jobs:
|
|||||||
playwright-install: true
|
playwright-install: true
|
||||||
electron-install: false
|
electron-install: false
|
||||||
|
|
||||||
- name: Download next static
|
- name: Download core artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: next-js-static
|
name: core
|
||||||
path: ./apps/web/out
|
path: ./apps/core/dist
|
||||||
|
|
||||||
- name: Unzip
|
- name: Unzip
|
||||||
run: yarn unzip
|
run: yarn unzip
|
||||||
@ -333,7 +333,7 @@ jobs:
|
|||||||
target: x86_64-pc-windows-msvc,
|
target: x86_64-pc-windows-msvc,
|
||||||
test: true,
|
test: true,
|
||||||
}
|
}
|
||||||
needs: [build-web]
|
needs: build-core
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
@ -353,10 +353,10 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NATIVE_TEST: 'true'
|
NATIVE_TEST: 'true'
|
||||||
|
|
||||||
- name: Download static resource artifact
|
- name: Download core artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: next-js-static
|
name: core
|
||||||
path: apps/electron/resources/web-static
|
path: apps/electron/resources/web-static
|
||||||
|
|
||||||
- name: Build Plugins
|
- name: Build Plugins
|
||||||
@ -443,11 +443,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Download next static
|
- name: Download core artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: next-js-static
|
name: core
|
||||||
path: ./apps/web/out
|
path: ./apps/core/dist
|
||||||
- name: Download server dist
|
- name: Download server dist
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
4
.github/workflows/nightly-build.yml
vendored
@ -62,10 +62,10 @@ jobs:
|
|||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
|
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
|
||||||
|
|
||||||
- name: Upload Artifact (web-static)
|
- name: Upload core artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: before-make-web-static
|
name: core
|
||||||
path: apps/electron/resources/web-static
|
path: apps/electron/resources/web-static
|
||||||
|
|
||||||
make-distribution:
|
make-distribution:
|
||||||
|
6
.github/workflows/release-desktop-app.yml
vendored
@ -66,10 +66,10 @@ jobs:
|
|||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }}
|
RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }}
|
||||||
|
|
||||||
- name: Upload Artifact (web-static)
|
- name: Upload core artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: before-make-web-static
|
name: core
|
||||||
path: apps/electron/resources/web-static
|
path: apps/electron/resources/web-static
|
||||||
|
|
||||||
make-distribution:
|
make-distribution:
|
||||||
@ -120,7 +120,7 @@ jobs:
|
|||||||
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: before-make-web-static
|
name: core
|
||||||
path: apps/electron/resources/web-static
|
path: apps/electron/resources/web-static
|
||||||
|
|
||||||
- name: Build Plugins
|
- name: Build Plugins
|
||||||
|
1
.gitignore
vendored
@ -13,6 +13,7 @@
|
|||||||
/out-tsc
|
/out-tsc
|
||||||
.nyc_output
|
.nyc_output
|
||||||
.coverage
|
.coverage
|
||||||
|
.swc
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
84
apps/core/.webpack/cache-group.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { hash } from './utils.js';
|
||||||
|
|
||||||
|
function testPackageName(regexp: RegExp): (module: any) => boolean {
|
||||||
|
return (module: any) =>
|
||||||
|
module.nameForCondition && regexp.test(module.nameForCondition());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const productionCacheGroups = {
|
||||||
|
asyncVendor: {
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
name(module: any) {
|
||||||
|
// https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
|
||||||
|
const name =
|
||||||
|
module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)?.[1] ??
|
||||||
|
'unknown';
|
||||||
|
return `npm-async-${hash(name)}`;
|
||||||
|
},
|
||||||
|
priority: Number.MAX_SAFE_INTEGER,
|
||||||
|
chunks: 'async' as const,
|
||||||
|
},
|
||||||
|
mui: {
|
||||||
|
name: `npm-mui`,
|
||||||
|
test: testPackageName(/[\\/]node_modules[\\/](mui|@mui)[\\/]/),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
blocksuite: {
|
||||||
|
name: `npm-blocksuite`,
|
||||||
|
test: testPackageName(/[\\/]node_modules[\\/](@blocksuite)[\\/]/),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
react: {
|
||||||
|
name: `npm-react`,
|
||||||
|
test: testPackageName(
|
||||||
|
/[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/
|
||||||
|
),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
jotai: {
|
||||||
|
name: `npm-jotai`,
|
||||||
|
test: testPackageName(/[\\/]node_modules[\\/](jotai)[\\/]/),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
rxjs: {
|
||||||
|
name: `npm-rxjs`,
|
||||||
|
test: testPackageName(/[\\/]node_modules[\\/]rxjs[\\/]/),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
lodash: {
|
||||||
|
name: `npm-lodash`,
|
||||||
|
test: testPackageName(/[\\/]node_modules[\\/]lodash[\\/]/),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
emotion: {
|
||||||
|
name: `npm-emotion`,
|
||||||
|
test: testPackageName(/[\\/]node_modules[\\/](@emotion)[\\/]/),
|
||||||
|
priority: 200,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
vendor: {
|
||||||
|
name: 'vendor',
|
||||||
|
test: /[\\/]node_modules[\\/]/,
|
||||||
|
priority: 190,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
name: 'styles',
|
||||||
|
test: (module: any) =>
|
||||||
|
module.nameForCondition &&
|
||||||
|
/\.css$/.test(module.nameForCondition()) &&
|
||||||
|
!/^javascript/.test(module.type),
|
||||||
|
chunks: 'all' as const,
|
||||||
|
minSize: 1,
|
||||||
|
minChunks: 1,
|
||||||
|
reuseExistingChunk: true,
|
||||||
|
priority: 1000,
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
};
|
306
apps/core/.webpack/config.ts
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
import { join, resolve } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
import HTMLPlugin from 'html-webpack-plugin';
|
||||||
|
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
|
||||||
|
import { PerfseePlugin } from '@perfsee/webpack';
|
||||||
|
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
|
||||||
|
|
||||||
|
import CopyPlugin from 'copy-webpack-plugin';
|
||||||
|
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
||||||
|
import TerserPlugin from 'terser-webpack-plugin';
|
||||||
|
import webpack from 'webpack';
|
||||||
|
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||||
|
|
||||||
|
import { productionCacheGroups } from './cache-group.js';
|
||||||
|
import type { BuildFlags } from '@affine/cli/config';
|
||||||
|
import { projectRoot } from '@affine/cli/config';
|
||||||
|
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
|
||||||
|
import { computeCacheKey } from './utils.js';
|
||||||
|
import type { RuntimeConfig } from '@affine/env/global';
|
||||||
|
|
||||||
|
const IN_CI = !!process.env.CI;
|
||||||
|
|
||||||
|
export const rootPath = fileURLToPath(new URL('..', import.meta.url));
|
||||||
|
|
||||||
|
const require = createRequire(rootPath);
|
||||||
|
|
||||||
|
const OptimizeOptionOptions: (
|
||||||
|
buildFlags: BuildFlags
|
||||||
|
) => webpack.Configuration['optimization'] = buildFlags => ({
|
||||||
|
minimize: buildFlags.mode === 'production',
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
minify: TerserPlugin.swcMinify,
|
||||||
|
parallel: true,
|
||||||
|
extractComments: true,
|
||||||
|
terserOptions: {
|
||||||
|
ecma: 2020,
|
||||||
|
compress: {
|
||||||
|
unused: true,
|
||||||
|
},
|
||||||
|
mangle: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
removeEmptyChunks: true,
|
||||||
|
providedExports: true,
|
||||||
|
usedExports: true,
|
||||||
|
sideEffects: true,
|
||||||
|
removeAvailableModules: true,
|
||||||
|
runtimeChunk: {
|
||||||
|
name: 'runtime',
|
||||||
|
},
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
minSize: 1,
|
||||||
|
minChunks: 1,
|
||||||
|
maxInitialRequests: Number.MAX_SAFE_INTEGER,
|
||||||
|
maxAsyncRequests: Number.MAX_SAFE_INTEGER,
|
||||||
|
cacheGroups:
|
||||||
|
buildFlags.mode === 'production'
|
||||||
|
? productionCacheGroups
|
||||||
|
: {
|
||||||
|
default: false,
|
||||||
|
vendors: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createConfiguration: (
|
||||||
|
buildFlags: BuildFlags,
|
||||||
|
runtimeConfig: RuntimeConfig
|
||||||
|
) => webpack.Configuration = (buildFlags, runtimeConfig) => {
|
||||||
|
let publicPath = process.env.PUBLIC_PATH ?? '/';
|
||||||
|
|
||||||
|
const cacheKey = computeCacheKey(buildFlags);
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
name: 'affine',
|
||||||
|
// to set a correct base path for the source map
|
||||||
|
context: projectRoot,
|
||||||
|
output: {
|
||||||
|
environment: {
|
||||||
|
module: true,
|
||||||
|
dynamicImport: true,
|
||||||
|
},
|
||||||
|
filename: 'js/[name].js',
|
||||||
|
// In some cases webpack will emit files starts with "_" which is reserved in web extension.
|
||||||
|
chunkFilename: 'js/chunk.[name].js',
|
||||||
|
assetModuleFilename: 'assets/[hash][ext][query]',
|
||||||
|
devtoolModuleFilenameTemplate: 'webpack://[namespace]/[resource-path]',
|
||||||
|
hotUpdateChunkFilename: 'hot/[id].[fullhash].js',
|
||||||
|
hotUpdateMainFilename: 'hot/[runtime].[fullhash].json',
|
||||||
|
path: join(rootPath, 'dist'),
|
||||||
|
clean: buildFlags.mode === 'production',
|
||||||
|
globalObject: 'globalThis',
|
||||||
|
publicPath,
|
||||||
|
},
|
||||||
|
target: ['web', 'es2022'],
|
||||||
|
|
||||||
|
mode: buildFlags.mode,
|
||||||
|
|
||||||
|
devtool:
|
||||||
|
buildFlags.mode === 'production'
|
||||||
|
? buildFlags.distribution === 'desktop'
|
||||||
|
? 'inline-source-map'
|
||||||
|
: 'hidden-nosources-source-map'
|
||||||
|
: 'eval-cheap-module-source-map',
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
extensionAlias: {
|
||||||
|
'.js': ['.js', '.tsx', '.ts'],
|
||||||
|
'.mjs': ['.mjs', '.mts'],
|
||||||
|
},
|
||||||
|
extensions: ['.js', '.ts', '.tsx'],
|
||||||
|
},
|
||||||
|
|
||||||
|
cache: {
|
||||||
|
type: 'filesystem',
|
||||||
|
buildDependencies: {
|
||||||
|
config: [fileURLToPath(import.meta.url)],
|
||||||
|
},
|
||||||
|
version: cacheKey,
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
parser: {
|
||||||
|
javascript: {
|
||||||
|
// Treat as missing export as error
|
||||||
|
strictExportPresence: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.m?js?$/,
|
||||||
|
resolve: {
|
||||||
|
fullySpecified: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oneOf: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
// Compile all ts files in the workspace
|
||||||
|
include: resolve(rootPath, '..', '..'),
|
||||||
|
loader: require.resolve('swc-loader'),
|
||||||
|
options: {
|
||||||
|
// https://swc.rs/docs/configuring-swc/
|
||||||
|
jsc: {
|
||||||
|
preserveAllComments: true,
|
||||||
|
parser: {
|
||||||
|
syntax: 'typescript',
|
||||||
|
dynamicImport: true,
|
||||||
|
topLevelAwait: false,
|
||||||
|
tsx: true,
|
||||||
|
},
|
||||||
|
target: 'es2022',
|
||||||
|
externalHelpers: true,
|
||||||
|
transform: {
|
||||||
|
react: {
|
||||||
|
runtime: 'automatic',
|
||||||
|
refresh: buildFlags.mode === 'development' && {
|
||||||
|
refreshReg: '$RefreshReg$',
|
||||||
|
refreshSig: '$RefreshSig$',
|
||||||
|
emitFullSignatures: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
experimental: {
|
||||||
|
keepImportAssertions: true,
|
||||||
|
plugins: [
|
||||||
|
buildFlags.coverage && [
|
||||||
|
'swc-plugin-coverage-instrument',
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
].filter(Boolean),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.svg$/,
|
||||||
|
use: [
|
||||||
|
'thread-loader',
|
||||||
|
{
|
||||||
|
loader: '@svgr/webpack',
|
||||||
|
options: {
|
||||||
|
icon: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exclude: [/node_modules/],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg|gif|svg|webp)$/,
|
||||||
|
type: 'asset/resource',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ttf|eot|woff|woff2)$/i,
|
||||||
|
type: 'asset/resource',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.txt$/,
|
||||||
|
loader: 'raw-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
url: false,
|
||||||
|
sourceMap: false,
|
||||||
|
modules: false,
|
||||||
|
import: true,
|
||||||
|
importLoaders: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
config: resolve(
|
||||||
|
rootPath,
|
||||||
|
'.webpack',
|
||||||
|
'postcss.config.cjs'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
...(IN_CI ? [] : [new webpack.ProgressPlugin({ percentBy: 'entries' })]),
|
||||||
|
...(buildFlags.mode === 'development'
|
||||||
|
? [new ReactRefreshWebpackPlugin({ overlay: false, esModule: true })]
|
||||||
|
: []),
|
||||||
|
new HTMLPlugin({
|
||||||
|
template: join(rootPath, '.webpack', 'template.html'),
|
||||||
|
inject: 'body',
|
||||||
|
scriptLoading: 'defer',
|
||||||
|
minify: false,
|
||||||
|
chunks: ['index'],
|
||||||
|
filename: 'index.html',
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: `[name].[chunkhash:8].css`,
|
||||||
|
ignoreOrder: true,
|
||||||
|
}),
|
||||||
|
new VanillaExtractPlugin(),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': JSON.stringify({}),
|
||||||
|
runtimeConfig: JSON.stringify(runtimeConfig),
|
||||||
|
}),
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{ from: resolve(rootPath, 'public'), to: resolve(rootPath, 'dist') },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
|
optimization: OptimizeOptionOptions(buildFlags),
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
hot: 'only',
|
||||||
|
liveReload: false,
|
||||||
|
client: undefined,
|
||||||
|
historyApiFallback: true,
|
||||||
|
static: {
|
||||||
|
directory: resolve(rootPath, 'public'),
|
||||||
|
publicPath: '/',
|
||||||
|
},
|
||||||
|
} as DevServerConfiguration,
|
||||||
|
} satisfies webpack.Configuration;
|
||||||
|
|
||||||
|
if (buildFlags.mode === 'production' && process.env.PERFSEE_TOKEN) {
|
||||||
|
config.devtool = 'hidden-nosources-source-map';
|
||||||
|
config.plugins.push(
|
||||||
|
new PerfseePlugin({
|
||||||
|
project: 'affine-toeverything',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
process.env.SENTRY_AUTH_TOKEN &&
|
||||||
|
process.env.SENTRY_ORG &&
|
||||||
|
process.env.SENTRY_PROJECT
|
||||||
|
) {
|
||||||
|
config.plugins.push(
|
||||||
|
sentryWebpackPlugin({
|
||||||
|
org: process.env.SENTRY_ORG,
|
||||||
|
project: process.env.SENTRY_PROJECT,
|
||||||
|
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
20
apps/core/.webpack/postcss.config.cjs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const cssnano = require('cssnano');
|
||||||
|
|
||||||
|
module.exports = function (context) {
|
||||||
|
const plugins = [
|
||||||
|
cssnano({
|
||||||
|
preset: [
|
||||||
|
'default',
|
||||||
|
{
|
||||||
|
convertValues: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: context.from,
|
||||||
|
plugins,
|
||||||
|
to: context.to,
|
||||||
|
};
|
||||||
|
};
|
115
apps/core/.webpack/runtime-config.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import type { BlockSuiteFeatureFlags, RuntimeConfig } from '@affine/env/global';
|
||||||
|
import type { BuildFlags } from '@affine/cli/config';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
const packageJson = require('../package.json');
|
||||||
|
|
||||||
|
const editorFlags: BlockSuiteFeatureFlags = {
|
||||||
|
enable_database: true,
|
||||||
|
enable_slash_menu: true,
|
||||||
|
enable_edgeless_toolbar: true,
|
||||||
|
enable_block_hub: true,
|
||||||
|
enable_drag_handle: true,
|
||||||
|
enable_surface: true,
|
||||||
|
enable_linked_page: true,
|
||||||
|
enable_bookmark_operation: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
|
||||||
|
const buildPreset: Record<string, RuntimeConfig> = {
|
||||||
|
stable: {
|
||||||
|
enablePlugin: false,
|
||||||
|
enableTestProperties: false,
|
||||||
|
enableBroadcastChannelProvider: true,
|
||||||
|
enableDebugPage: true,
|
||||||
|
changelogUrl: 'https://affine.pro/blog/what-is-new-affine-0717',
|
||||||
|
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
|
||||||
|
enablePreloading: true,
|
||||||
|
enableNewSettingModal: true,
|
||||||
|
enableNewSettingUnstableApi: false,
|
||||||
|
enableSQLiteProvider: true,
|
||||||
|
enableMoveDatabase: false,
|
||||||
|
enableNotificationCenter: false,
|
||||||
|
enableCloud: false,
|
||||||
|
serverAPI: 'https://localhost:3010',
|
||||||
|
editorFlags,
|
||||||
|
appVersion: packageJson.version,
|
||||||
|
editorVersion: packageJson.dependencies['@blocksuite/editor'],
|
||||||
|
},
|
||||||
|
// canary will be aggressive and enable all features
|
||||||
|
canary: {
|
||||||
|
enablePlugin: true,
|
||||||
|
enableTestProperties: true,
|
||||||
|
enableBroadcastChannelProvider: true,
|
||||||
|
enableDebugPage: true,
|
||||||
|
changelogUrl: 'https://affine.pro/blog/what-is-new-affine-0717',
|
||||||
|
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
|
||||||
|
enablePreloading: true,
|
||||||
|
enableNewSettingModal: true,
|
||||||
|
enableNewSettingUnstableApi: false,
|
||||||
|
enableSQLiteProvider: true,
|
||||||
|
enableMoveDatabase: false,
|
||||||
|
enableNotificationCenter: true,
|
||||||
|
enableCloud: false,
|
||||||
|
serverAPI: 'https://localhost:3010',
|
||||||
|
editorFlags,
|
||||||
|
appVersion: packageJson.version,
|
||||||
|
editorVersion: packageJson.dependencies['@blocksuite/editor'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// beta and internal versions are the same as stable
|
||||||
|
buildPreset.beta = buildPreset.stable;
|
||||||
|
buildPreset.internal = buildPreset.stable;
|
||||||
|
|
||||||
|
const currentBuild = buildFlags.channel;
|
||||||
|
|
||||||
|
if (!(currentBuild in buildPreset)) {
|
||||||
|
throw new Error(`BUILD_TYPE ${currentBuild} is not supported`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentBuildPreset = buildPreset[currentBuild];
|
||||||
|
|
||||||
|
const environmentPreset = {
|
||||||
|
enablePlugin: process.env.ENABLE_PLUGIN
|
||||||
|
? process.env.ENABLE_PLUGIN === 'true'
|
||||||
|
: currentBuildPreset.enablePlugin,
|
||||||
|
enableTestProperties: process.env.ENABLE_TEST_PROPERTIES
|
||||||
|
? process.env.ENABLE_TEST_PROPERTIES === 'true'
|
||||||
|
: currentBuildPreset.enableTestProperties,
|
||||||
|
enableBroadcastChannelProvider: process.env.ENABLE_BC_PROVIDER
|
||||||
|
? process.env.ENABLE_BC_PROVIDER !== 'false'
|
||||||
|
: currentBuildPreset.enableBroadcastChannelProvider,
|
||||||
|
changelogUrl: process.env.CHANGELOG_URL ?? currentBuildPreset.changelogUrl,
|
||||||
|
enablePreloading: process.env.ENABLE_PRELOADING
|
||||||
|
? process.env.ENABLE_PRELOADING === 'true'
|
||||||
|
: currentBuildPreset.enablePreloading,
|
||||||
|
enableNewSettingModal: process.env.ENABLE_NEW_SETTING_MODAL
|
||||||
|
? process.env.ENABLE_NEW_SETTING_MODAL === 'true'
|
||||||
|
: currentBuildPreset.enableNewSettingModal,
|
||||||
|
enableSQLiteProvider: process.env.ENABLE_SQLITE_PROVIDER
|
||||||
|
? process.env.ENABLE_SQLITE_PROVIDER === 'true'
|
||||||
|
: currentBuildPreset.enableSQLiteProvider,
|
||||||
|
enableNewSettingUnstableApi: process.env.ENABLE_NEW_SETTING_UNSTABLE_API
|
||||||
|
? process.env.ENABLE_NEW_SETTING_UNSTABLE_API === 'true'
|
||||||
|
: currentBuildPreset.enableNewSettingUnstableApi,
|
||||||
|
enableNotificationCenter: process.env.ENABLE_NOTIFICATION_CENTER
|
||||||
|
? process.env.ENABLE_NOTIFICATION_CENTER === 'true'
|
||||||
|
: currentBuildPreset.enableNotificationCenter,
|
||||||
|
enableCloud: process.env.ENABLE_CLOUD
|
||||||
|
? process.env.ENABLE_CLOUD === 'true'
|
||||||
|
: currentBuildPreset.enableCloud,
|
||||||
|
enableMoveDatabase: process.env.ENABLE_MOVE_DATABASE
|
||||||
|
? process.env.ENABLE_MOVE_DATABASE === 'true'
|
||||||
|
: currentBuildPreset.enableMoveDatabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...currentBuildPreset,
|
||||||
|
// environment preset will overwrite current build preset
|
||||||
|
// this environment variable is for debug proposes only
|
||||||
|
// do not put them into CI
|
||||||
|
...(process.env.CI ? {} : environmentPreset),
|
||||||
|
};
|
||||||
|
}
|
45
apps/core/.webpack/template.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||||
|
/>
|
||||||
|
<title>AFFiNE</title>
|
||||||
|
<meta name="theme-color" content="#fafafa" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="icon" sizes="192x192" href="/chrome-192x192.png" />
|
||||||
|
<meta name="emotion-insertion-point" content="" />
|
||||||
|
<meta property="description" content="{description}" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:url" content="https://app.affine.pro/" />
|
||||||
|
<meta
|
||||||
|
name="twitter:title"
|
||||||
|
content="AFFiNE:There can be more than Notion and Miro."
|
||||||
|
/>
|
||||||
|
<meta name="twitter:description" content="{description}" />
|
||||||
|
<meta name="twitter:site" content="@AffineOfficial" />
|
||||||
|
<meta name="twitter:image" content="https://affine.pro/og.jpeg" />
|
||||||
|
<meta
|
||||||
|
property="og:title"
|
||||||
|
content="AFFiNE:There can be more than Notion and Miro."
|
||||||
|
/>
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together."
|
||||||
|
/>
|
||||||
|
<meta property="og:url" content="https://app.affine.pro/" />
|
||||||
|
<meta property="og:image" content="https://affine.pro/og.jpeg" />
|
||||||
|
<link
|
||||||
|
data-react-helmet="true"
|
||||||
|
rel="shortcut icon"
|
||||||
|
href="https://affine.pro/favicon.ico"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
18
apps/core/.webpack/utils.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
import type { BuildFlags } from '@affine/cli/config';
|
||||||
|
|
||||||
|
export function hash(content: string): string {
|
||||||
|
const hash = createHash('sha512');
|
||||||
|
hash.update(content);
|
||||||
|
const pkgHash = hash.digest('hex');
|
||||||
|
return pkgHash.substring(0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeCacheKey(buildFlags: BuildFlags) {
|
||||||
|
return [
|
||||||
|
'1',
|
||||||
|
'node' + process.version,
|
||||||
|
buildFlags.mode,
|
||||||
|
buildFlags.distribution,
|
||||||
|
].join('-');
|
||||||
|
}
|
23
apps/core/.webpack/webpack.config.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { createConfiguration, rootPath } from './config.js';
|
||||||
|
import { merge } from 'webpack-merge';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import type { BuildFlags } from '@affine/cli/config';
|
||||||
|
import { getRuntimeConfig } from './runtime-config.js';
|
||||||
|
|
||||||
|
export default async function (cli_env: any, _: any) {
|
||||||
|
const flags: BuildFlags = JSON.parse(
|
||||||
|
Buffer.from(cli_env.flags, 'hex').toString('utf-8')
|
||||||
|
);
|
||||||
|
console.log('build flags', flags);
|
||||||
|
const runtimeConfig = getRuntimeConfig(flags);
|
||||||
|
console.log('runtime config', runtimeConfig);
|
||||||
|
const config = createConfiguration(flags, runtimeConfig);
|
||||||
|
return merge(config, {
|
||||||
|
entry: {
|
||||||
|
index: {
|
||||||
|
asyncChunks: true,
|
||||||
|
import: resolve(rootPath, 'src/index.tsx'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
@ -1,17 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@affine/web",
|
"name": "@affine/core",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.0-canary.47",
|
"version": "0.7.0-canary.47",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"build": "yarn -T run build-core",
|
||||||
"build": "next build && next export",
|
"dev": "yarn -T run dev-core",
|
||||||
"start": "NODE_ENV=development next start",
|
"static-server": "ts-node-esm ./server.mts"
|
||||||
"static-server": "ts-node-esm server.mts"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine-test/fixtures": "workspace:*",
|
"@affine-test/fixtures": "workspace:*",
|
||||||
"@affine/component": "workspace:*",
|
"@affine/component": "workspace:*",
|
||||||
"@affine/copilot": "workspace:*",
|
|
||||||
"@affine/debug": "workspace:*",
|
"@affine/debug": "workspace:*",
|
||||||
"@affine/env": "workspace:*",
|
"@affine/env": "workspace:*",
|
||||||
"@affine/graphql": "workspace:*",
|
"@affine/graphql": "workspace:*",
|
||||||
@ -34,23 +33,23 @@
|
|||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/material": "^5.13.6",
|
"@mui/material": "^5.13.6",
|
||||||
"@react-hookz/web": "^23.1.0",
|
"@react-hookz/web": "^23.1.0",
|
||||||
"@sentry/nextjs": "^7.57.0",
|
|
||||||
"@toeverything/hooks": "workspace:*",
|
|
||||||
"@toeverything/infra": "workspace:*",
|
|
||||||
"@toeverything/plugin-infra": "workspace:*",
|
|
||||||
"async-call-rpc": "^6.3.1",
|
"async-call-rpc": "^6.3.1",
|
||||||
"cmdk": "^0.2.0",
|
"cmdk": "^0.2.0",
|
||||||
"css-spring": "^4.1.0",
|
"css-spring": "^4.1.0",
|
||||||
|
"cssnano": "^6.0.1",
|
||||||
"graphql": "^16.7.1",
|
"graphql": "^16.7.1",
|
||||||
"jotai": "^2.2.2",
|
"jotai": "^2.2.2",
|
||||||
"jotai-devtools": "^0.6.0",
|
"jotai-devtools": "^0.6.0",
|
||||||
"lit": "^2.7.5",
|
"lit": "^2.7.5",
|
||||||
"lottie-web": "^5.12.2",
|
"lottie-web": "^5.12.2",
|
||||||
|
"mini-css-extract-plugin": "^2.7.6",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
|
"postcss-loader": "^7.3.3",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-is": "^18.2.0",
|
"react-is": "18.2.0",
|
||||||
"react-resizable-panels": "^0.0.53",
|
"react-resizable-panels": "^0.0.53",
|
||||||
|
"react-router-dom": "^6.14.1",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"swr": "^2.1.5",
|
"swr": "^2.1.5",
|
||||||
"y-protocols": "^1.0.5",
|
"y-protocols": "^1.0.5",
|
||||||
@ -59,30 +58,23 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@perfsee/webpack": "^1.8.2",
|
"@perfsee/webpack": "^1.8.2",
|
||||||
"@redux-devtools/extension": "^3.2.5",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
|
||||||
"@rich-data/viewer": "^2.15.6",
|
"@sentry/webpack-plugin": "^2.4.0",
|
||||||
"@sentry/webpack-plugin": "^1.20.1",
|
"@svgr/webpack": "^8.0.1",
|
||||||
"@swc-jotai/debug-label": "^0.0.10",
|
"@swc/core": "^1.3.70",
|
||||||
"@swc-jotai/react-refresh": "^0.0.8",
|
|
||||||
"@types/react": "^18.2.14",
|
|
||||||
"@types/react-dom": "^18.2.6",
|
|
||||||
"@types/webpack-env": "^1.18.1",
|
"@types/webpack-env": "^1.18.1",
|
||||||
"@vanilla-extract/css": "^1.12.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"@vanilla-extract/next-plugin": "=2.1.2",
|
"css-loader": "^6.8.1",
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"eslint": "^8.44.0",
|
|
||||||
"eslint-config-next": "^13.4.7",
|
|
||||||
"eslint-plugin-unicorn": "^47.0.0",
|
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"next": "=13.4.2",
|
"html-webpack-plugin": "^5.5.3",
|
||||||
"next-debug-local": "^0.1.5",
|
|
||||||
"next-router-mock": "^0.9.7",
|
|
||||||
"raw-loader": "^4.0.2",
|
"raw-loader": "^4.0.2",
|
||||||
"redux": "^4.2.1",
|
"swc-loader": "^0.2.3",
|
||||||
"swc-plugin-coverage-instrument": "^0.0.18",
|
"swc-plugin-coverage-instrument": "^0.0.19",
|
||||||
|
"thread-loader": "^4.0.2",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.1.6",
|
"webpack": "^5.88.1",
|
||||||
"webpack": "^5.88.1"
|
"webpack-cli": "^5.1.4",
|
||||||
},
|
"webpack-dev-server": "^4.15.1",
|
||||||
"stableVersion": "0.0.0"
|
"webpack-merge": "^5.9.0"
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "@affine/web",
|
"name": "@affine/core",
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"root": "apps/web",
|
"root": "apps/core",
|
||||||
"sourceRoot": "apps/web/src",
|
"sourceRoot": "apps/core/src",
|
||||||
"targets": {
|
"targets": {
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "nx:run-script",
|
"executor": "nx:run-script",
|
||||||
@ -12,26 +12,29 @@
|
|||||||
"{projectRoot}/**/*",
|
"{projectRoot}/**/*",
|
||||||
"{workspaceRoot}/packages/component/src/**/*",
|
"{workspaceRoot}/packages/component/src/**/*",
|
||||||
"{workspaceRoot}/packages/debug/src/**/*",
|
"{workspaceRoot}/packages/debug/src/**/*",
|
||||||
"{workspaceRoot}/packages/debug/graphql/**/*",
|
"{workspaceRoot}/packages/graphql/src/**/*",
|
||||||
"{workspaceRoot}/packages/hooks/src/**/*",
|
"{workspaceRoot}/packages/hooks/src/**/*",
|
||||||
"{workspaceRoot}/packages/jotai/src/**/*",
|
"{workspaceRoot}/packages/jotai/src/**/*",
|
||||||
"{workspaceRoot}/packages/templates/src/**/*",
|
"{workspaceRoot}/packages/templates/src/**/*",
|
||||||
"{workspaceRoot}/packages/workspace/src/**/*",
|
"{workspaceRoot}/packages/workspace/src/**/*",
|
||||||
{
|
{
|
||||||
"env": "BUILD_TYPE"
|
"env": "BUILD_TYPE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"env": "PERFSEE_TOKEN"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"script": "build"
|
"script": "build"
|
||||||
},
|
},
|
||||||
"outputs": ["{projectRoot}/out"]
|
"outputs": ["{projectRoot}/dist"]
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"executor": "nx:run-script",
|
"executor": "nx:run-script",
|
||||||
"options": {
|
"options": {
|
||||||
"script": "dev"
|
"script": "dev"
|
||||||
},
|
},
|
||||||
"outputs": ["{projectRoot}/.next"]
|
"outputs": ["{projectRoot}/dist"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 710 KiB After Width: | Height: | Size: 710 KiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 822 KiB After Width: | Height: | Size: 822 KiB |
Before Width: | Height: | Size: 363 KiB After Width: | Height: | Size: 363 KiB |
Before Width: | Height: | Size: 945 KiB After Width: | Height: | Size: 945 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 380 KiB After Width: | Height: | Size: 380 KiB |
Before Width: | Height: | Size: 6.8 MiB After Width: | Height: | Size: 6.8 MiB |
15
apps/core/server.mts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// static server for web app
|
||||||
|
import express from 'express';
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 8080;
|
||||||
|
|
||||||
|
app.use('/', express.static('dist'));
|
||||||
|
|
||||||
|
app.get('/*', (req, res) => {
|
||||||
|
res.sendFile('index.html', { root: 'dist' });
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on port ${PORT}`);
|
||||||
|
});
|
158
apps/core/src/app.tsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import '@affine/component/theme/global.css';
|
||||||
|
import '@affine/component/theme/theme.css';
|
||||||
|
|
||||||
|
import { AffineContext } from '@affine/component/context';
|
||||||
|
import { WorkspaceFallback } from '@affine/component/workspace';
|
||||||
|
import { createI18n, setUpLanguage } from '@affine/i18n';
|
||||||
|
import { CacheProvider } from '@emotion/react';
|
||||||
|
import type { RouterState } from '@remix-run/router';
|
||||||
|
import {
|
||||||
|
currentPageIdAtom,
|
||||||
|
currentWorkspaceIdAtom,
|
||||||
|
} from '@toeverything/plugin-infra/manager';
|
||||||
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
|
import { lazy, memo, Suspense, useEffect } from 'react';
|
||||||
|
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { historyBaseAtom, MAX_HISTORY } from './atoms/history';
|
||||||
|
import createEmotionCache from './utils/create-emotion-cache';
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
lazy: () => import('./pages/index'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/404',
|
||||||
|
lazy: () => import('./pages/404'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/workspace/:workspaceId/all',
|
||||||
|
lazy: () => import('./pages/workspace/all-page'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/workspace/:workspaceId/trash',
|
||||||
|
lazy: () => import('./pages/workspace/trash-page'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/workspace/:workspaceId/:pageId',
|
||||||
|
lazy: () => import('./pages/workspace/detail-page'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
//#region atoms bootstrap
|
||||||
|
|
||||||
|
currentWorkspaceIdAtom.onMount = set => {
|
||||||
|
const callback = (state: RouterState) => {
|
||||||
|
const value = state.location.pathname.split('/')[2];
|
||||||
|
if (value) {
|
||||||
|
set(value);
|
||||||
|
localStorage.setItem('last_workspace_id', value);
|
||||||
|
} else {
|
||||||
|
set(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
callback(router.state);
|
||||||
|
|
||||||
|
const unsubscribe = router.subscribe(callback);
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
currentPageIdAtom.onMount = set => {
|
||||||
|
const callback = (state: RouterState) => {
|
||||||
|
const value = state.location.pathname.split('/')[3];
|
||||||
|
if (value) {
|
||||||
|
set(value);
|
||||||
|
} else {
|
||||||
|
set(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
callback(router.state);
|
||||||
|
|
||||||
|
const unsubscribe = router.subscribe(callback);
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
historyBaseAtom.onMount = set => {
|
||||||
|
const unsubscribe = router.subscribe(state => {
|
||||||
|
set(prev => {
|
||||||
|
const url = state.location.pathname;
|
||||||
|
console.log('push', url, prev.skip, prev.stack.length, prev.current);
|
||||||
|
if (prev.skip) {
|
||||||
|
return {
|
||||||
|
stack: [...prev.stack],
|
||||||
|
current: prev.current,
|
||||||
|
skip: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (prev.current < prev.stack.length - 1) {
|
||||||
|
const newStack = prev.stack.slice(0, prev.current);
|
||||||
|
newStack.push(url);
|
||||||
|
if (newStack.length > MAX_HISTORY) {
|
||||||
|
newStack.shift();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
stack: newStack,
|
||||||
|
current: newStack.length - 1,
|
||||||
|
skip: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const newStack = [...prev.stack, url];
|
||||||
|
if (newStack.length > MAX_HISTORY) {
|
||||||
|
newStack.shift();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
stack: newStack,
|
||||||
|
current: newStack.length - 1,
|
||||||
|
skip: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
const i18n = createI18n();
|
||||||
|
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}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App = memo(function App() {
|
||||||
|
useEffect(() => {
|
||||||
|
document.documentElement.lang = i18n.language;
|
||||||
|
// todo(himself65): this is a hack, we should use a better way to set the language
|
||||||
|
setUpLanguage(i18n)?.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<CacheProvider value={cache}>
|
||||||
|
<AffineContext>
|
||||||
|
<DebugProvider>
|
||||||
|
<Suspense fallback={<WorkspaceFallback key="RootPageLoading" />}>
|
||||||
|
<RouterProvider router={router} />
|
||||||
|
</Suspense>
|
||||||
|
</DebugProvider>
|
||||||
|
</AffineContext>
|
||||||
|
</CacheProvider>
|
||||||
|
);
|
||||||
|
});
|
52
apps/core/src/atoms/history.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
export type History = {
|
||||||
|
stack: string[];
|
||||||
|
current: number;
|
||||||
|
skip: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MAX_HISTORY = 50;
|
||||||
|
|
||||||
|
export const historyBaseAtom = atomWithStorage<History>('router-history', {
|
||||||
|
stack: [],
|
||||||
|
current: 0,
|
||||||
|
skip: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useHistoryAtom() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [base, setBase] = useAtom(historyBaseAtom);
|
||||||
|
return [
|
||||||
|
base,
|
||||||
|
useCallback(
|
||||||
|
(forward: boolean) => {
|
||||||
|
setBase(prev => {
|
||||||
|
if (forward) {
|
||||||
|
const target = Math.min(prev.stack.length - 1, prev.current + 1);
|
||||||
|
const url = prev.stack[target];
|
||||||
|
navigate(url);
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
current: target,
|
||||||
|
skip: true,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const target = Math.max(0, prev.current - 1);
|
||||||
|
const url = prev.stack[target];
|
||||||
|
navigate(url);
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
current: target,
|
||||||
|
skip: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setBase, navigate]
|
||||||
|
),
|
||||||
|
] as const;
|
||||||
|
}
|
@ -12,14 +12,11 @@ import {
|
|||||||
} from '@toeverything/plugin-infra/manager';
|
} from '@toeverything/plugin-infra/manager';
|
||||||
import { useAtomValue } from 'jotai/react';
|
import { useAtomValue } from 'jotai/react';
|
||||||
import { Provider } from 'jotai/react';
|
import { Provider } from 'jotai/react';
|
||||||
import type { NextRouter } from 'next/router';
|
|
||||||
import type { ErrorInfo, ReactElement, ReactNode } from 'react';
|
import type { ErrorInfo, ReactElement, ReactNode } from 'react';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
export type AffineErrorBoundaryProps = React.PropsWithChildren<{
|
export type AffineErrorBoundaryProps = React.PropsWithChildren;
|
||||||
router: NextRouter;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
type AffineError =
|
type AffineError =
|
||||||
| QueryParamError
|
| QueryParamError
|
||||||
@ -32,13 +29,13 @@ interface AffineErrorBoundaryState {
|
|||||||
error: AffineError | null;
|
error: AffineError | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DumpInfo = (props: Pick<AffineErrorBoundaryProps, 'router'>) => {
|
export const DumpInfo = () => {
|
||||||
const router = props.router;
|
const location = useLocation();
|
||||||
const metadata = useAtomValue(rootWorkspacesMetadataAtom);
|
const metadata = useAtomValue(rootWorkspacesMetadataAtom);
|
||||||
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
|
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
|
||||||
const currentPageId = useAtomValue(currentPageIdAtom);
|
const currentPageId = useAtomValue(currentPageIdAtom);
|
||||||
const path = router.asPath;
|
const path = location.pathname;
|
||||||
const query = router.query;
|
const query = useParams();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
@ -91,24 +88,6 @@ export class AffineErrorBoundary extends Component<
|
|||||||
Cannot find page {error.pageId} in workspace{' '}
|
Cannot find page {error.pageId} in workspace{' '}
|
||||||
{error.workspace.id}
|
{error.workspace.id}
|
||||||
</span>
|
</span>
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
this.props.router
|
|
||||||
.replace({
|
|
||||||
pathname: '/workspace/[workspaceId]/[pageId]',
|
|
||||||
query: {
|
|
||||||
workspaceId: error.workspace.id,
|
|
||||||
pageId: error.workspace.meta.pageMetas[0].id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.setState({ error: null });
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
refresh{' '}
|
|
||||||
</button>
|
|
||||||
</>
|
</>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -124,7 +103,7 @@ export class AffineErrorBoundary extends Component<
|
|||||||
<>
|
<>
|
||||||
{errorDetail}
|
{errorDetail}
|
||||||
<Provider key="JotaiProvider" store={rootStore}>
|
<Provider key="JotaiProvider" store={rootStore}>
|
||||||
<DumpInfo router={this.props.router} />
|
<DumpInfo />
|
||||||
</Provider>
|
</Provider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|