snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-16 04:01:20 +05:00
commit 702cc698a7
9990 changed files with 1073618 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
# Routes
## Usage
### Path Factories
```ts
import { FACTORIES } from '@affine/routes';
const path = FACTORIES.workspace.doc({ workspaceId: '123', docId: '456' });
// ^^^^ with typecheck
```
### Register router
```tsx
import { ROUTES } from '@affine/routes';
function Routes() {
return <Router path={ROUTES.admin.index} element={} />;
}
```
### Path Parameter
```ts
import { RouteParamsTypes } from '@affine/routes';
function Doc() {
const { workspaceId, docId } =
useParams<RouteParamsTypes['workspace']['doc']>();
}
function Attachment() {
const { workspaceId, docId, attachmentId } =
useParams<RouteParamsTypes['workspace']['doc']['attachment']>();
}
```

View File

@@ -0,0 +1,260 @@
import fs from 'node:fs';
import { join } from 'node:path/posix';
import { formatCode, Path } from '@affine-tools/utils';
import { parse } from 'path-to-regexp';
const curdir = Path.dir(import.meta.url);
const inputFile = curdir.join('routes.json').value;
const routerOutputFile = curdir.join('src', 'routes.ts').value;
interface RawRoutesSchema {
route: string;
children: {
[key: string]: string | RawRoutesSchema;
};
}
interface RouteSchema {
name: string;
fromParent: string;
fromRoot: string;
children: Array<RouteSchema>;
}
function loadRoutesSchema(): RouteSchema {
const rawSchema = JSON.parse(
fs.readFileSync(inputFile, 'utf-8')
) as RawRoutesSchema;
const build = (
name: string,
schema: RawRoutesSchema | string,
fromRoot: string = ''
): RouteSchema => {
if (typeof schema === 'string') {
return {
name,
fromParent: schema,
fromRoot: join(fromRoot, schema),
children: [],
};
}
const absolute = join(fromRoot, schema.route);
return {
name,
fromParent: schema.route,
fromRoot: absolute,
children: Object.entries(schema.children).map(([key, value]) => {
return build(key, value, absolute);
}),
};
};
return build('home', rawSchema);
}
interface BuiltRouteSchema {
name: string;
type?: string;
factory: string;
fromRoot: string;
fromParent: string;
children: BuiltRouteSchema[];
parent: BuiltRouteSchema | null;
}
function buildRoutes(
schema: RouteSchema,
parent: BuiltRouteSchema | null = null
): BuiltRouteSchema {
const { tokens } = parse(schema.fromRoot);
const types: string[] = [];
const factories: string[] = [];
for (const token of tokens) {
switch (token.type) {
case 'param':
case 'wildcard':
types.push(token.name);
factories.push(`\${params.${token.name}}`);
break;
case 'text': {
factories.push(token.value);
break;
}
}
}
// [a, b, c] -> '{ a: string; b: string; c: string }'
const type = types.length
? `{ ${types.map(type => `${type}: string`).join('; ')} }`
: undefined;
const builtResult: BuiltRouteSchema = {
name: schema.name,
type,
factory: factories.join(''),
fromRoot: schema.fromRoot,
fromParent: schema.fromParent,
parent: parent,
children: [],
};
builtResult.children = schema.children.map(child =>
buildRoutes(child, builtResult)
);
return builtResult;
}
function printSchemaTypes(schema: BuiltRouteSchema, level: number = 1): string {
const types: string[] = [];
if (schema.type) {
if (schema.children.length) {
types.push(`index: ${schema.type};`);
} else {
return schema.type;
}
}
for (const child of schema.children) {
const childType = printSchemaTypes(child, level + 1);
if (childType) {
types.push(`${child.name}: ${childType};`);
}
}
if (types.length > 0) {
const output = `{ ${types.join('\n')} }`;
return level === 1 ? `export interface RouteParamsTypes ${output}` : output;
}
return '';
}
function printAbsolutePaths(schema: BuiltRouteSchema, level = 1): string {
const absolutes: string[] = [];
if (schema.children.length) {
absolutes.push(`index: '${schema.fromRoot}'`);
} else {
return `'${schema.fromRoot}'`;
}
for (const child of schema.children) {
const childRoute = printAbsolutePaths(child, level + 1);
absolutes.push(`${child.name}: ${childRoute}`);
}
const output = `{ ${absolutes.join('\n,')} }`;
return level === 1 ? `export const ROUTES = ${output}` : output;
}
function printRelativePaths(schema: BuiltRouteSchema, level = 1): string {
const relatives: string[] = [];
if (schema.children.length) {
relatives.push(`index: '${schema.fromParent}'`);
} else {
return `'${schema.fromParent}'`;
}
for (const child of schema.children) {
const childRoute = printRelativePaths(child, level + 1);
relatives.push(`${child.name}: ${childRoute}`);
}
const output = `{ ${relatives.join('\n,')} }`;
return level === 1 ? `export const RELATIVE_ROUTES = ${output}` : output;
}
function printFactories(schema: BuiltRouteSchema): string {
const factories: string[] = [];
const factory = schema.type
? `(params: ${schema.type}) => \`${schema.factory}\``
: `() => '${schema.factory}'`;
let parent: BuiltRouteSchema | null = schema.parent;
let nameFromRoot: string[] = [schema.name];
while (parent) {
// ignore home
if (parent.parent) {
nameFromRoot = [parent.name, ...nameFromRoot];
}
parent = parent.parent;
}
if (schema.children.length) {
// with children, we do
const visitor = nameFromRoot.join('_');
// 1. const workspace_doc = () => 'workspace/doc'
factories.push(`const ${visitor} = ${factory}`);
for (const child of schema.children) {
// 2. generate children for workspace_doc
factories.push(printFactories(child));
}
// 3. workspace.doc = workspace_doc
const parentNameFromRoot = nameFromRoot.slice(0, -1).join('_');
if (parentNameFromRoot) {
factories.push(`${parentNameFromRoot}.${schema.name} = ${visitor}`);
}
} else {
// without children, we directly
const parentNameFromRoot = nameFromRoot.slice(0, -1).join('_');
if (parentNameFromRoot) {
// parent.child = () => 'child'
factories.push(`${parentNameFromRoot}.${schema.name} = ${factory}`);
} else {
// const route = () => 'route'
factories.push(`const ${schema.name} = ${factory}`);
}
}
const output = factories.join('\n');
if (!schema.parent) {
const firstLevelNames = schema.children.map(child => child.name);
firstLevelNames.push(schema.name);
return `${output}\nexport const FACTORIES = { ${firstLevelNames.join(
', '
)} };`;
}
return output;
}
async function printRoutes(schema: BuiltRouteSchema) {
const parts = {
['Path Parameter Types']: printSchemaTypes,
['Absolute Paths']: printAbsolutePaths,
['Relative Paths']: printRelativePaths,
['Path Factories']: printFactories,
};
const content = await formatCode(
Object.entries(parts)
.map(([key, print]) => {
return `// #region ${key}\n${print(schema)}\n// #endregion`;
})
.join('\n\n'),
routerOutputFile
);
fs.writeFileSync(routerOutputFile, content, 'utf-8');
}
async function build() {
const schema = loadRoutesSchema();
const builtSchema = buildRoutes(schema);
await printRoutes(builtSchema);
}
await build();

View File

@@ -0,0 +1,21 @@
{
"name": "@affine/routes",
"version": "0.27.5",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "r build.ts"
},
"devDependencies": {
"@affine-tools/cli": "workspace:*",
"@affine-tools/utils": "workspace:*",
"path-to-regexp": "^8.4.2",
"vitest": "^4.1.8"
},
"peerDependencies": {
"react": "^19.2.1"
}
}

View File

@@ -0,0 +1,26 @@
{
"$schema": "./schema.json",
"route": "/",
"children": {
"admin": {
"route": "admin",
"children": {
"auth": "auth",
"setup": "setup",
"dashboard": "dashboard",
"accounts": "accounts",
"workspaces": "workspaces",
"queue": "queue",
"ai": "ai",
"settings": {
"route": "settings",
"children": {
"module": ":module"
}
},
"about": "about",
"notFound": "404"
}
}
}
}

View File

@@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "AFFiNE Routes Schema",
"definitions": {
"route": {
"type": "string",
"description": "route path"
},
"children": {
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"required": ["route", "children"],
"properties": {
"route": { "$ref": "#/definitions/route" },
"children": { "$ref": "#/definitions/children" }
}
}
]
}
}
},
"properties": {
"route": { "$ref": "#/definitions/route" },
"children": { "$ref": "#/definitions/children" }
}
}

View File

@@ -0,0 +1,13 @@
import { describe, expect, it } from 'vitest';
import { FACTORIES } from '../routes';
describe('PATH_FACTORIES', () => {
it('should generate correct paths', () => {
expect(
FACTORIES.admin.settings.module({
module: 'auth',
})
).toBe('/admin/settings/auth');
});
});

View File

@@ -0,0 +1,2 @@
export * from './lazy';
export * from './routes';

View File

@@ -0,0 +1,37 @@
import React, {
type ComponentProps,
type ComponentType,
lazy as reactLazy,
Suspense,
} from 'react';
export function lazy<T extends ComponentType<any>>(
factory: () => Promise<Record<any, T>>,
fallback?: React.ReactNode
) {
const LazyComponent = reactLazy(() =>
factory().then(mod => {
if ('default' in mod) {
return { default: mod.default };
} else {
const components = Object.values(mod);
if (components.length > 1) {
console.warn('Lazy loaded module has more then one exports');
}
return {
default: components[0],
};
}
})
);
return function LazyRoute(props: ComponentProps<T>) {
return React.createElement(
Suspense,
{
fallback,
},
React.createElement(LazyComponent, props)
);
};
}

View File

@@ -0,0 +1,59 @@
// #region Path Parameter Types
export interface RouteParamsTypes {
admin: { settings: { module: { module: string } } };
}
// #endregion
// #region Absolute Paths
export const ROUTES = {
index: '/',
admin: {
index: '/admin',
auth: '/admin/auth',
setup: '/admin/setup',
dashboard: '/admin/dashboard',
accounts: '/admin/accounts',
workspaces: '/admin/workspaces',
ai: '/admin/ai',
settings: { index: '/admin/settings', module: '/admin/settings/:module' },
about: '/admin/about',
notFound: '/admin/404',
},
};
// #endregion
// #region Relative Paths
export const RELATIVE_ROUTES = {
index: '/',
admin: {
index: 'admin',
auth: 'auth',
setup: 'setup',
dashboard: 'dashboard',
accounts: 'accounts',
workspaces: 'workspaces',
ai: 'ai',
settings: { index: 'settings', module: ':module' },
about: 'about',
notFound: '404',
},
};
// #endregion
// #region Path Factories
const home = () => '/';
const admin = () => '/admin';
admin.auth = () => '/admin/auth';
admin.setup = () => '/admin/setup';
admin.dashboard = () => '/admin/dashboard';
admin.accounts = () => '/admin/accounts';
admin.workspaces = () => '/admin/workspaces';
admin.ai = () => '/admin/ai';
const admin_settings = () => '/admin/settings';
admin_settings.module = (params: { module: string }) =>
`/admin/settings/${params.module}`;
admin.settings = admin_settings;
admin.about = () => '/admin/about';
admin.notFound = () => '/admin/404';
export const FACTORIES = { admin, home };
// #endregion

View File

@@ -0,0 +1,13 @@
{
"extends": "../../../tsconfig.web.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
},
"include": ["./src"],
"references": [
{ "path": "../../../tools/cli" },
{ "path": "../../../tools/utils" }
]
}