mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
261 lines
6.4 KiB
TypeScript
261 lines
6.4 KiB
TypeScript
import fs from 'node:fs';
|
|
import { join } from 'node:path/posix';
|
|
|
|
import { Path, prettier } 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 prettier(
|
|
Object.entries(parts)
|
|
.map(([key, print]) => {
|
|
return `// #region ${key}\n${print(schema)}\n// #endregion`;
|
|
})
|
|
.join('\n\n'),
|
|
'typescript'
|
|
);
|
|
|
|
fs.writeFileSync(routerOutputFile, content, 'utf-8');
|
|
}
|
|
|
|
async function build() {
|
|
const schema = loadRoutesSchema();
|
|
const builtSchema = buildRoutes(schema);
|
|
|
|
await printRoutes(builtSchema);
|
|
}
|
|
|
|
await build();
|