build: allow node package depends on workspace packages (#11963)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added a unified CLI entry point for server operations and introduced a new CLI executable alias.
  - Centralized and simplified server startup, allowing selection between CLI and server modes.
  - Added static migration module aggregation for easier migration management.

- **Bug Fixes**
  - Improved platform-specific native module loading for better compatibility and reliability.

- **Refactor**
  - Streamlined server build, startup, and artifact management processes.
  - Reorganized and simplified workflow and configuration files for backend services.
  - Transitioned export styles and import mechanisms for native modules to enhance maintainability.

- **Chores**
  - Removed unused dependencies and configuration files.
  - Updated test cases to reflect refined server flavor logic.
  - Adjusted package and build configurations for consistency and clarity.

- **Revert**
  - Removed legacy scripts and loaders no longer needed after refactor.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
forehalo 2025-04-24 10:36:51 +00:00
parent 4ffa37d1c3
commit f4ffdb9995
No known key found for this signature in database
GPG Key ID: 56709255DC7EC728
31 changed files with 331 additions and 332 deletions

View File

@ -10,4 +10,4 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends openssl && \
rm -rf /var/lib/apt/lists/*
CMD ["node", "--import", "./scripts/register.js", "./dist/index.js"]
CMD ["node", "./dist/main.js"]

View File

@ -1,60 +0,0 @@
services:
affine:
image: ghcr.io/toeverything/affine-graphql:stable
container_name: affine_selfhosted
command:
['sh', '-c', 'node ./scripts/self-host-predeploy && node ./dist/index.js']
ports:
- '3010:3010'
- '5555:5555'
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
volumes:
# custom configurations
- ~/.affine/self-host/config:/root/.affine/config
# blob storage
- ~/.affine/self-host/storage:/root/.affine/storage
logging:
driver: 'json-file'
options:
max-size: '1000m'
restart: unless-stopped
environment:
- NODE_OPTIONS="--import=./scripts/register.js"
- AFFINE_CONFIG_PATH=/root/.affine/config
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgres://affine:affine@postgres:5432/affine
- NODE_ENV=production
# Telemetry allows us to collect data on how you use the affine. This data will helps us improve the app and provide better features.
# Uncomment next line if you wish to quit telemetry.
# - TELEMETRY_ENABLE=false
redis:
image: redis
container_name: affine_redis
restart: unless-stopped
volumes:
- ~/.affine/self-host/redis:/data
healthcheck:
test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']
interval: 10s
timeout: 5s
retries: 5
postgres:
image: postgres:16
container_name: affine_postgres
restart: unless-stopped
volumes:
- ~/.affine/self-host/postgres:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U affine']
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_USER: affine
POSTGRES_PASSWORD: affine
POSTGRES_DB: affine
PGDATA: /var/lib/postgresql/data/pgdata

View File

@ -13,31 +13,6 @@ permissions:
packages: 'write'
jobs:
build-server:
name: Build Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server @types/affine__env
- name: Build Server
run: |
find packages/backend/server/src -type d -name "__tests__" -exec rm -rf {} +
rm -rf packages/backend/server/src/seed
yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
@ -143,7 +118,7 @@ jobs:
matrix:
targets:
- name: x86_64-unknown-linux-gnu
file: server-native.node
file: server-native.x64.node
- name: aarch64-unknown-linux-gnu
file: server-native.arm64.node
- name: armv7-unknown-linux-gnueabihf
@ -164,11 +139,46 @@ jobs:
with:
target: ${{ matrix.targets.name }}
package: '@affine/server-native'
- name: Rename ${{ matrix.targets.file }}
run: |
mv ./packages/backend/native/server-native.node ./packages/backend/native/${{ matrix.targets.file }}
- name: Upload ${{ matrix.targets.file }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.targets.file }}
path: ./packages/backend/native/server-native.node
name: server-native-${{ matrix.targets.file }}
path: ./packages/backend/native/${{ matrix.targets.file }}
if-no-files-found: error
build-server:
name: Build Server
runs-on: ubuntu-latest
needs:
- build-server-native
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server @types/affine__env
- name: Download server-native
uses: actions/download-artifact@v4
with:
pattern: server-native-*
merge-multiple: true
path: ./packages/backend/native
- name: List server-native files
run: ls -alh ./packages/backend/native
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-images:
@ -179,7 +189,6 @@ jobs:
- build-web
- build-mobile
- build-admin
- build-server-native
steps:
- uses: actions/checkout@v4
- name: Download server dist
@ -187,25 +196,6 @@ jobs:
with:
name: server-dist
path: ./packages/backend/server/dist
- name: Download server-native.node
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
- name: Download server-native.node arm64
uses: actions/download-artifact@v4
with:
name: server-native.arm64.node
path: ./packages/backend/native
- name: Download server-native.node arm64
uses: actions/download-artifact@v4
with:
name: server-native.armv7.node
path: .
- name: move server-native files
run: |
mv ./packages/backend/native/server-native.node ./packages/backend/server/server-native.arm64.node
mv server-native.node ./packages/backend/server/server-native.armv7.node
- name: Setup env
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"

View File

@ -582,7 +582,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
@ -644,7 +644,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Prepare Server Test Environment
uses: ./.github/actions/server-test-env
@ -885,7 +885,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Prepare Server Test Environment
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.apifilter.outputs.changed == 'true' }}
@ -982,7 +982,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Prepare Server Test Environment
if: ${{ steps.check-blocksuite-update.outputs.skip != 'true' || steps.e2efilter.outputs.changed == 'true' }}
@ -1077,7 +1077,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Download affine.linux-x64-gnu.node
uses: actions/download-artifact@v4

View File

@ -73,7 +73,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Prepare Server Test Environment
env:
@ -142,7 +142,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: server-native.node
path: ./packages/backend/server
path: ./packages/backend/native
- name: Prepare Server Test Environment
env:

View File

@ -58,7 +58,6 @@
"@magic-works/i18n-codegen": "^0.6.1",
"@playwright/test": "=1.51.1",
"@smarttools/eslint-plugin-rxjs": "^1.0.8",
"@swc/core": "^1.10.1",
"@taplo/cli": "^0.7.0",
"@toeverything/infra": "workspace:*",
"@types/eslint": "^9.6.1",

View File

@ -1,15 +1,14 @@
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
/** @type {import('.')} */
const binding = require('./server-native.node');
let binding;
try {
binding = require('./server-native.node');
} catch {
binding =
process.arch === 'arm64'
? require('./server-native.arm64.node')
: process.arch === 'arm'
? require('./server-native.armv7.node')
: require('./server-native.x64.node');
}
export const mergeUpdatesInApplyWay = binding.mergeUpdatesInApplyWay;
export const verifyChallengeResponse = binding.verifyChallengeResponse;
export const mintChallengeResponse = binding.mintChallengeResponse;
export const getMime = binding.getMime;
export const Tokenizer = binding.Tokenizer;
export const fromModelName = binding.fromModelName;
export const htmlSanitize = binding.htmlSanitize;
export const parseDoc = binding.parseDoc;
module.exports = binding;

View File

@ -4,7 +4,6 @@
"engines": {
"node": ">= 10.16.0 < 11 || >= 11.8.0"
},
"type": "module",
"main": "./index.js",
"module": "./index.js",
"types": "index.d.ts",

View File

@ -8,7 +8,7 @@
"run-test": "./scripts/run-test.ts"
},
"scripts": {
"build": "tsc -b",
"build": "affine bundle -p @affine/server",
"dev": "nodemon ./src/index.ts",
"dev:mail": "email dev -d src/mails",
"test": "ava --concurrency 1 --serial",
@ -17,14 +17,16 @@
"test:copilot:coverage": "c8 ava --timeout=5m \"src/__tests__/copilot-*.spec.ts\"",
"e2e": "cross-env TEST_MODE=e2e ava --serial",
"e2e:coverage": "cross-env TEST_MODE=e2e c8 ava --serial",
"data-migration": "cross-env NODE_ENV=development r ./src/data/index.ts",
"data-migration": "cross-env NODE_ENV=development SERVER_FLAVOR=script r ./src/index.ts",
"init": "yarn prisma migrate dev && yarn data-migration run",
"seed": "r ./src/seed/index.ts",
"genconfig": "r ./scripts/genconfig.ts",
"predeploy": "yarn prisma migrate deploy && node --import ./scripts/register.js ./dist/data/index.js run",
"cli": "cross-env SERVER_FLAVOR=script node ./dist/main.js",
"predeploy": "yarn prisma migrate deploy && yarn cli run",
"postinstall": "prisma generate"
},
"dependencies": {
"@affine/server-native": "workspace:*",
"@ai-sdk/google": "^1.2.10",
"@ai-sdk/openai": "^1.3.18",
"@ai-sdk/perplexity": "^1.1.6",
@ -72,6 +74,7 @@
"ai": "^4.3.4",
"bullmq": "^5.40.2",
"cookie-parser": "^1.4.7",
"cross-env": "^7.0.3",
"date-fns": "^4.0.0",
"dotenv": "^16.4.7",
"eventemitter2": "^6.4.9",
@ -117,7 +120,6 @@
"@affine-tools/cli": "workspace:*",
"@affine-tools/utils": "workspace:*",
"@affine/graphql": "workspace:*",
"@affine/server-native": "workspace:*",
"@faker-js/faker": "^9.6.0",
"@nestjs/testing": "patch:@nestjs/testing@npm%3A10.4.15#~/.yarn/patches/@nestjs-testing-npm-10.4.15-d591a1705a.patch",
"@types/cookie-parser": "^1.4.8",
@ -138,7 +140,6 @@
"@types/supertest": "^6.0.2",
"ava": "^6.2.0",
"c8": "^10.1.3",
"cross-env": "^7.0.3",
"nodemon": "^3.1.7",
"react-email": "4.0.7",
"sinon": "^20.0.0",

View File

@ -1,11 +0,0 @@
import { create, createEsmHooks } from 'ts-node';
const service = create({
experimentalSpecifierResolution: 'node',
transpileOnly: true,
logError: true,
skipProject: true,
});
const hooks = createEsmHooks(service);
export const resolve = hooks.resolve;

View File

@ -1,4 +0,0 @@
import { register } from 'node:module';
import { pathToFileURL } from 'node:url';
register('./scripts/loader.js', pathToFileURL('./'));

View File

@ -111,7 +111,7 @@ test('should tell flavors correctly', t => {
sync: true,
renderer: true,
doc: true,
script: true,
script: false,
});
process.env.SERVER_FLAVOR = 'graphql';
@ -122,6 +122,15 @@ test('should tell flavors correctly', t => {
doc: false,
script: false,
});
process.env.SERVER_FLAVOR = 'script';
t.deepEqual(new Env().flavors, {
graphql: false,
sync: false,
renderer: false,
doc: false,
script: true,
});
});
test('should tell selfhosted correctly', t => {

View File

@ -1,16 +1,12 @@
import { Logger } from '@nestjs/common';
import { CommandFactory } from 'nest-commander';
async function bootstrap() {
process.env.SERVER_FLAVOR = 'script';
import { CliAppModule } from './data/app';
await import('../prelude');
const { CliAppModule } = await import('./app');
export async function run() {
await CommandFactory.run(CliAppModule, new Logger()).catch(e => {
console.error(e);
process.exit(1);
});
process.exit(0);
}
await bootstrap();

View File

@ -1,5 +1,5 @@
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { appendFileSync, writeFileSync } from 'node:fs';
import { join, parse } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Logger } from '@nestjs/common';
@ -45,15 +45,21 @@ export class CreateCommand extends CommandRunner {
const timestamp = Date.now();
const content = this.createScript(upperFirst(camelCase(name)) + timestamp);
const fileName = `${timestamp}-${kebabCase(name)}.ts`;
const filePath = join(
const migrationDir = join(
fileURLToPath(import.meta.url),
'../../migrations',
fileName
'../../migrations'
);
const fileName = `${timestamp}-${kebabCase(name)}.ts`;
const filePath = join(migrationDir, fileName);
this.logger.log(`Creating ${fileName}...`);
writeFileSync(filePath, content);
const indexFile = join(migrationDir, 'index.ts');
appendFileSync(
indexFile,
`export * from './${parse(fileName).name}';`,
'utf-8'
);
this.logger.log(`Migration file created at ${filePath}`);
this.logger.log('Done');
}

View File

@ -1,49 +1,28 @@
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { Logger } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { PrismaClient } from '@prisma/client';
import { once } from 'lodash-es';
import { Command, CommandRunner } from 'nest-commander';
import * as migrations from '../migrations';
interface Migration {
file: string;
name: string;
always?: boolean;
up: (db: PrismaClient, injector: ModuleRef) => Promise<void>;
down: (db: PrismaClient, injector: ModuleRef) => Promise<void>;
}
export const collectMigrations = once(async () => {
const folder = join(fileURLToPath(import.meta.url), '../../migrations');
const migrationFiles = readdirSync(folder)
.filter(desc =>
desc.endsWith(import.meta.url.endsWith('.ts') ? '.ts' : '.js')
)
.map(desc => join(folder, desc));
migrationFiles.sort((a, b) => a.localeCompare(b));
const migrations: Migration[] = await Promise.all(
migrationFiles.map(async file => {
return import(pathToFileURL(file).href).then(mod => {
const migration = mod[Object.keys(mod)[0]];
return {
file,
name: migration.name,
always: migration.always,
up: migration.up,
down: migration.down,
};
});
})
);
return migrations;
export const collectMigrations = once(() => {
return Object.values(migrations).map(migration => {
return {
name: migration.name,
// @ts-expect-error optional
always: migration.always,
up: migration.up,
down: migration.down,
};
}) as Migration[];
});
@Command({
@ -60,7 +39,7 @@ export class RunCommand extends CommandRunner {
}
override async run(): Promise<void> {
const migrations = await collectMigrations();
const migrations = collectMigrations();
const done: Migration[] = [];
for (const migration of migrations) {
const exists = await this.db.dataMigration.count({
@ -85,7 +64,7 @@ export class RunCommand extends CommandRunner {
}
async runOne(name: string) {
const migrations = await collectMigrations();
const migrations = collectMigrations();
const migration = migrations.find(m => m.name === name);
if (!migration) {
@ -162,7 +141,7 @@ export class RevertCommand extends CommandRunner {
throw new Error('A migration name is required');
}
const migrations = await collectMigrations();
const migrations = collectMigrations();
const migration = migrations.find(m => m.name === name);

View File

@ -0,0 +1,7 @@
export * from './0001-refresh-features';
export * from './1698398506533-guid';
export * from './1703756315970-unamed-account';
export * from './1721299086340-refresh-unnamed-user';
export * from './1732861452428-migrate-invite-status';
export * from './1733125339942-universal-subscription';
export * from './1738590347632-feature-redundant';

View File

@ -102,7 +102,8 @@ export class Env implements AppEnv {
sync: this.isFlavor(Flavor.Sync),
renderer: this.isFlavor(Flavor.Renderer),
doc: this.isFlavor(Flavor.Doc),
script: this.isFlavor(Flavor.Script),
// Script in a special flavor, return true only when it is set explicitly
script: this.FLAVOR === Flavor.Script,
};
}

View File

@ -1,20 +1,11 @@
/// <reference types="./global.d.ts" />
import './prelude';
import { Logger } from '@nestjs/common';
import { run as runCli } from './cli';
import { run as runServer } from './server';
import { createApp } from './app';
import { Config, URLHelper } from './base';
const app = await createApp();
const config = app.get(Config);
const url = app.get(URLHelper);
const listeningHost = '0.0.0.0';
await app.listen(config.server.port, listeningHost);
const logger = new Logger('App');
logger.log(`AFFiNE Server is running in [${env.DEPLOYMENT_TYPE}] mode`);
logger.log(`Listening on http://${listeningHost}:${config.server.port}`);
logger.log(`And the public server should be recognized as ${url.home}`);
if (env.flavors.script) {
await runCli();
} else {
await runServer();
}

View File

@ -1,17 +1,4 @@
import { createRequire } from 'node:module';
let serverNativeModule: typeof import('@affine/server-native');
try {
serverNativeModule = await import('@affine/server-native');
} catch {
const require = createRequire(import.meta.url);
serverNativeModule =
process.arch === 'arm64'
? require('../server-native.arm64.node')
: process.arch === 'arm'
? require('../server-native.armv7.node')
: require('../server-native.node');
}
import serverNativeModule from '@affine/server-native';
export const mergeUpdatesInApplyWay = serverNativeModule.mergeUpdatesInApplyWay;

View File

@ -9,6 +9,7 @@ import {
CloudThrottlerGuard,
Config,
GlobalExceptionFilter,
URLHelper,
} from './base';
import { SocketIoAdapter } from './base/websocket';
import { AuthGuard } from './core/auth';
@ -16,7 +17,7 @@ import { serverTimingAndCache } from './middleware/timing';
const OneMB = 1024 * 1024;
export async function createApp() {
export async function run() {
const { AppModule } = await import('./app.module');
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
@ -28,7 +29,8 @@ export async function createApp() {
app.useBodyParser('raw', { limit: 100 * OneMB });
app.useLogger(app.get(AFFiNELogger));
const logger = app.get(AFFiNELogger);
app.useLogger(logger);
const config = app.get(Config);
if (config.server.path) {
@ -57,5 +59,12 @@ export async function createApp() {
const adapter = new SocketIoAdapter(app);
app.useWebSocketAdapter(adapter);
return app;
const url = app.get(URLHelper);
const listeningHost = '0.0.0.0';
await app.listen(config.server.port, listeningHost);
logger.log(`AFFiNE Server is running in [${env.DEPLOYMENT_TYPE}] mode`);
logger.log(`Listening on http://${listeningHost}:${config.server.port}`);
logger.log(`And the public server should be recognized as ${url.home}`);
}

View File

@ -12,9 +12,9 @@
},
"include": ["./src"],
"references": [
{ "path": "../native" },
{ "path": "../../../tools/cli" },
{ "path": "../../../tools/utils" },
{ "path": "../../common/graphql" },
{ "path": "../native" }
{ "path": "../../common/graphql" }
]
}

View File

@ -34,7 +34,6 @@
"devDependencies": {
"@affine/templates": "workspace:*",
"@emotion/react": "^11.14.0",
"@swc/core": "^1.10.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.1.0",
"@types/react": "^19.0.1",

View File

@ -84,7 +84,6 @@
"zod": "^3.24.1"
},
"devDependencies": {
"@swc/core": "^1.10.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.1.0",
"@types/animejs": "^3.1.12",

5
tools/cli/bin/cli.js Executable file
View File

@ -0,0 +1,5 @@
import { spawnSync } from 'node:child_process';
spawnSync('yarn', ['r', 'affine.ts', ...process.argv.slice(2)], {
stdio: 'inherit',
});

View File

@ -4,6 +4,7 @@
"type": "module",
"private": true,
"bin": {
"affine": "./bin/cli.js",
"r": "./bin/runner.js"
},
"exports": {
@ -21,6 +22,7 @@
"@napi-rs/simple-git": "^0.1.19",
"@perfsee/webpack": "^1.13.0",
"@sentry/webpack-plugin": "^3.0.0",
"@swc/core": "^1.10.1",
"@tailwindcss/postcss": "^4.0.0",
"@vanilla-extract/webpack-plugin": "^2.3.15",
"autoprefixer": "^10.4.20",
@ -34,6 +36,7 @@
"lodash-es": "^4.17.21",
"mime-types": "^3.0.0",
"mini-css-extract-plugin": "^2.9.2",
"node-loader": "^2.1.0",
"postcss": "^8.4.49",
"postcss-loader": "^8.1.1",
"prettier": "^3.4.2",

View File

@ -10,12 +10,16 @@ import WebpackDevServer, {
} from 'webpack-dev-server';
import { Option, PackageCommand } from './command';
import { createHTMLTargetConfig, createWorkerTargetConfig } from './webpack';
import {
createHTMLTargetConfig,
createNodeTargetConfig,
createWorkerTargetConfig,
} from './webpack';
function getBundleConfigs(pkg: Package) {
function getBaseWorkerConfigs(pkg: Package) {
const core = new Package('@affine/core');
const workerConfigs = [
return [
createWorkerTargetConfig(
pkg,
core.srcPath.join(
@ -31,7 +35,9 @@ function getBundleConfigs(pkg: Package) {
core.srcPath.join('blocksuite/extensions/turbo-painter.worker.ts').value
),
];
}
function getBundleConfigs(pkg: Package) {
switch (pkg.name) {
case '@affine/admin': {
return [createHTMLTargetConfig(pkg, pkg.srcPath.join('index.tsx').value)];
@ -40,6 +46,7 @@ function getBundleConfigs(pkg: Package) {
case '@affine/mobile':
case '@affine/ios':
case '@affine/android': {
const workerConfigs = getBaseWorkerConfigs(pkg);
workerConfigs.push(
createWorkerTargetConfig(
pkg,
@ -58,6 +65,8 @@ function getBundleConfigs(pkg: Package) {
];
}
case '@affine/electron-renderer': {
const workerConfigs = getBaseWorkerConfigs(pkg);
return [
createHTMLTargetConfig(
pkg,
@ -78,10 +87,14 @@ function getBundleConfigs(pkg: Package) {
...workerConfigs,
];
}
case '@affine/server': {
return [createNodeTargetConfig(pkg, pkg.srcPath.join('index.ts').value)];
}
}
throw new Error(`Unsupported package: ${pkg.name}`);
}
const IN_CI = !!process.env.CI;
const httpProxyMiddlewareLogLevel = IN_CI ? 'silent' : 'error';

View File

@ -2,7 +2,7 @@ import { createRequire } from 'node:module';
import path from 'node:path';
import { getBuildConfig } from '@affine-tools/utils/build-config';
import { ProjectRoot } from '@affine-tools/utils/path';
import { Path, ProjectRoot } from '@affine-tools/utils/path';
import { Package } from '@affine-tools/utils/workspace';
import { PerfseePlugin } from '@perfsee/webpack';
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
@ -75,10 +75,7 @@ export function createHTMLTargetConfig(
},
entry,
output: {
environment: {
module: true,
dynamicImport: true,
},
environment: { module: true, dynamicImport: true },
filename: buildConfig.debug
? 'js/[name].js'
: 'js/[name].[contenthash:8].js',
@ -127,12 +124,7 @@ export function createHTMLTargetConfig(
},
//#region rules
rules: [
{
test: /\.m?js?$/,
resolve: {
fullySpecified: false,
},
},
{ test: /\.m?js?$/, resolve: { fullySpecified: false } },
{
test: /\.js$/,
enforce: 'pre',
@ -185,9 +177,7 @@ export function createHTMLTargetConfig(
target: 'es2022',
externalHelpers: false,
transform: {
react: {
runtime: 'automatic',
},
react: { runtime: 'automatic' },
useDefineForClassFields: false,
decoratorVersion: '2022-03',
},
@ -200,18 +190,9 @@ export function createHTMLTargetConfig(
test: /\.(png|jpg|gif|svg|webp|mp4|zip)$/,
type: 'asset/resource',
},
{
test: /\.(ttf|eot|woff|woff2)$/,
type: 'asset/resource',
},
{
test: /\.txt$/,
type: 'asset/source',
},
{
test: /\.inline\.svg$/,
type: 'asset/inline',
},
{ test: /\.(ttf|eot|woff|woff2)$/, type: 'asset/resource' },
{ test: /\.txt$/, type: 'asset/source' },
{ test: /\.inline\.svg$/, type: 'asset/inline' },
{
test: /\.css$/,
use: [
@ -242,12 +223,7 @@ export function createHTMLTargetConfig(
]
: [
cssnano({
preset: [
'default',
{
convertValues: false,
},
],
preset: ['default', { convertValues: false }],
}),
],
},
@ -298,9 +274,7 @@ export function createHTMLTargetConfig(
new WebpackS3Plugin(),
!buildConfig.debug &&
process.env.PERFSEE_TOKEN &&
new PerfseePlugin({
project: 'affine-toeverything',
}),
new PerfseePlugin({ project: 'affine-toeverything' }),
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT &&
@ -325,9 +299,7 @@ export function createHTMLTargetConfig(
]),
//#endregion
stats: {
errorDetails: true,
},
stats: { errorDetails: true },
//#region optimization
optimization: {
@ -339,12 +311,8 @@ export function createHTMLTargetConfig(
extractComments: true,
terserOptions: {
ecma: 2020,
compress: {
unused: true,
},
mangle: {
keep_classnames: true,
},
compress: { unused: true },
mangle: { keep_classnames: true },
},
}),
],
@ -353,9 +321,7 @@ export function createHTMLTargetConfig(
usedExports: true,
sideEffects: true,
removeAvailableModules: true,
runtimeChunk: {
name: 'runtime',
},
runtimeChunk: { name: 'runtime' },
splitChunks: {
chunks: 'all',
minSize: 1,
@ -382,11 +348,7 @@ export function createHTMLTargetConfig(
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
default: { minChunks: 2, priority: -20, reuseExistingChunk: true },
styles: {
name: 'styles',
type: 'css/mini-extract',
@ -416,9 +378,7 @@ export function createWorkerTargetConfig(
outputModule: false,
syncWebAssembly: true,
},
entry: {
[workerName]: entry,
},
entry: { [workerName]: entry },
output: {
filename: `js/${workerName}-${buildConfig.appVersion}.worker.js`,
path: pkg.distPath.value,
@ -432,14 +392,9 @@ export function createWorkerTargetConfig(
devtool: buildConfig.debug ? 'cheap-module-source-map' : 'source-map',
resolve: {
symlinks: true,
extensionAlias: {
'.js': ['.js', '.ts'],
'.mjs': ['.mjs', '.mts'],
},
extensionAlias: { '.js': ['.js', '.ts'], '.mjs': ['.mjs', '.mts'] },
extensions: ['.js', '.ts'],
alias: {
yjs: ProjectRoot.join('node_modules', 'yjs').value,
},
alias: { yjs: ProjectRoot.join('node_modules', 'yjs').value },
},
module: {
@ -454,12 +409,7 @@ export function createWorkerTargetConfig(
},
},
rules: [
{
test: /\.m?js?$/,
resolve: {
fullySpecified: false,
},
},
{ test: /\.m?js?$/, resolve: { fullySpecified: false } },
{
test: /\.js$/,
enforce: 'pre',
@ -508,9 +458,7 @@ export function createWorkerTargetConfig(
{} as Record<string, string>
)
),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT &&
@ -520,9 +468,7 @@ export function createWorkerTargetConfig(
authToken: process.env.SENTRY_AUTH_TOKEN,
}),
]),
stats: {
errorDetails: true,
},
stats: { errorDetails: true },
optimization: {
minimize: !buildConfig.debug,
minimizer: [
@ -532,12 +478,8 @@ export function createWorkerTargetConfig(
extractComments: true,
terserOptions: {
ecma: 2020,
compress: {
unused: true,
},
mangle: {
keep_classnames: true,
},
compress: { unused: true },
mangle: { keep_classnames: true },
},
}),
],
@ -549,8 +491,120 @@ export function createWorkerTargetConfig(
runtimeChunk: false,
splitChunks: false,
},
performance: {
hints: false,
},
performance: { hints: false },
};
}
export function createNodeTargetConfig(
pkg: Package,
entry: string
): Omit<webpack.Configuration, 'name'> & { name: string } {
return {
name: entry,
context: ProjectRoot.value,
experiments: {
topLevelAwait: true,
outputModule: pkg.packageJson.type === 'module',
syncWebAssembly: true,
},
entry: { index: entry },
output: {
filename: `main.js`,
path: pkg.distPath.value,
clean: true,
globalObject: 'globalThis',
},
target: ['node', 'es2022'],
externals: (data, callback) => {
if (
data.request &&
// import ... from 'module'
/^[a-zA-Z@]/.test(data.request) &&
// not workspace deps
!pkg.deps.some(dep => data.request!.startsWith(dep.name))
) {
callback(null, true);
} else {
callback(null, false);
}
},
externalsPresets: { node: true },
node: { __dirname: false, __filename: false },
mode: 'none',
devtool: 'source-map',
resolve: {
symlinks: true,
extensionAlias: { '.js': ['.js', '.ts'], '.mjs': ['.mjs', '.mts'] },
extensions: ['.js', '.ts', '.tsx', '.node'],
alias: { yjs: ProjectRoot.join('node_modules', 'yjs').value },
},
module: {
parser: {
javascript: { url: false, importMeta: false, createRequire: false },
},
rules: [
{
test: /\.js$/,
enforce: 'pre',
include: /@blocksuite/,
use: ['source-map-loader'],
},
{
test: /\.node$/,
loader: Path.dir(import.meta.url).join('node-loader.js').value,
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'swc-loader',
options: {
// https://swc.rs/docs/configuring-swc/
jsc: {
preserveAllComments: true,
parser: {
syntax: 'typescript',
dynamicImport: true,
topLevelAwait: true,
tsx: true,
decorators: true,
},
target: 'es2022',
externalHelpers: false,
transform: {
legacyDecorator: true,
decoratorMetadata: true,
react: { runtime: 'automatic' },
},
},
sourceMaps: true,
inlineSourcesContent: true,
},
},
],
},
plugins: compact([
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
new webpack.IgnorePlugin({
checkResource(resource) {
const lazyImports = [
'@nestjs/microservices',
'@nestjs/websockets/socket-module',
'@apollo/subgraph',
'@apollo/gateway',
'@as-integrations/fastify',
'ts-morph',
'class-validator',
'class-transformer',
];
return lazyImports.some(lazyImport =>
resource.startsWith(lazyImport)
);
},
}),
]),
stats: { errorDetails: true },
optimization: { nodeEnv: false },
performance: { hints: false },
ignoreWarnings: [/^(?!CriticalDependenciesWarning$)/],
};
}

View File

@ -0,0 +1,18 @@
import { parse } from 'node:path';
export const raw = true;
/**
* @type {import('webpack').LoaderDefinitionFunction}
*/
export default function loader(content) {
const name = parse(this.resourcePath).base;
this.emitFile(name, content);
return `
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const binding = require('./${name}')
export default binding
`;
}

View File

@ -8,13 +8,12 @@ export interface YarnWorkspaceItem {
export interface CommonPackageJsonContent {
name: string;
type?: 'module' | 'commonjs';
version: string;
private?: boolean;
dependencies?: { [key: string]: string };
devDependencies?: { [key: string]: string };
scripts?: { [key: string]: string };
main?: string;
exports?: {
[key: string]: string;
};
exports?: { [key: string]: string | { [key: string]: string } };
}

View File

@ -963,10 +963,10 @@ export const PackageList = [
location: 'packages/backend/server',
name: '@affine/server',
workspaceDependencies: [
'packages/backend/native',
'tools/cli',
'tools/utils',
'packages/common/graphql',
'packages/backend/native',
],
},
{

View File

@ -122,6 +122,7 @@ __metadata:
"@napi-rs/simple-git": "npm:^0.1.19"
"@perfsee/webpack": "npm:^1.13.0"
"@sentry/webpack-plugin": "npm:^3.0.0"
"@swc/core": "npm:^1.10.1"
"@tailwindcss/postcss": "npm:^4.0.0"
"@types/lodash-es": "npm:^4.17.12"
"@types/mime-types": "npm:^2.1.4"
@ -139,6 +140,7 @@ __metadata:
lodash-es: "npm:^4.17.21"
mime-types: "npm:^3.0.0"
mini-css-extract-plugin: "npm:^2.9.2"
node-loader: "npm:^2.1.0"
postcss: "npm:^8.4.49"
postcss-loader: "npm:^8.1.1"
prettier: "npm:^3.4.2"
@ -155,6 +157,7 @@ __metadata:
webpack-dev-server: "npm:^5.2.0"
webpack-merge: "npm:^6.0.1"
bin:
affine: ./bin/cli.js
r: ./bin/runner.js
languageName: unknown
linkType: soft
@ -417,7 +420,6 @@ __metadata:
"@radix-ui/react-slot": "npm:^1.1.1"
"@radix-ui/react-toolbar": "npm:^1.1.1"
"@sentry/react": "npm:^9.2.0"
"@swc/core": "npm:^1.10.1"
"@testing-library/dom": "npm:^10.4.0"
"@testing-library/react": "npm:^16.1.0"
"@toeverything/infra": "workspace:*"
@ -760,7 +762,6 @@ __metadata:
"@magic-works/i18n-codegen": "npm:^0.6.1"
"@playwright/test": "npm:=1.51.1"
"@smarttools/eslint-plugin-rxjs": "npm:^1.0.8"
"@swc/core": "npm:^1.10.1"
"@taplo/cli": "npm:^0.7.0"
"@toeverything/infra": "workspace:*"
"@types/eslint": "npm:^9.6.1"
@ -14286,7 +14287,6 @@ __metadata:
"@affine/templates": "workspace:*"
"@emotion/react": "npm:^11.14.0"
"@preact/signals-core": "npm:^1.8.0"
"@swc/core": "npm:^1.10.1"
"@testing-library/dom": "npm:^10.4.0"
"@testing-library/react": "npm:^16.1.0"
"@types/react": "npm:^19.0.1"
@ -25152,7 +25152,7 @@ __metadata:
languageName: node
linkType: hard
"loader-utils@npm:^2.0.0":
"loader-utils@npm:^2.0.0, loader-utils@npm:^2.0.3":
version: 2.0.4
resolution: "loader-utils@npm:2.0.4"
dependencies:
@ -27414,6 +27414,17 @@ __metadata:
languageName: node
linkType: hard
"node-loader@npm:^2.1.0":
version: 2.1.0
resolution: "node-loader@npm:2.1.0"
dependencies:
loader-utils: "npm:^2.0.3"
peerDependencies:
webpack: ^5.0.0
checksum: 10/d2f20b1e0f946055fcbbf365c3927ffecfff9aee3b5cc2d71e45229ca27010267d3d6fdea04dcb7c0bc7ce9b87878105b8c1d15c05065c813b5c8ec5ef1fb4d1
languageName: node
linkType: hard
"node-releases@npm:^2.0.19":
version: 2.0.19
resolution: "node-releases@npm:2.0.19"