refactor!: remove next.js (#3267)
This commit is contained in:
1 parent
79227a1e7c
commit
47f12f77f2
296 files changed
+4115
-3617
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
+21
-21
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
/out-tsc
|
/out-tsc
|
||||||
.nyc_output
|
.nyc_output
|
||||||
.coverage
|
.coverage
|
||||||
|
.swc
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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('-');
|
||||||
|
}
|
||||||
@@ -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"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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}`);
|
||||||
|
});
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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>
|
||||||
|
);
|
||||||
|
});
|
||||||
File renamed without changes.
File renamed without changes.
@@ -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;
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
-28
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 296 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user