feat:组件框架

This commit is contained in:
董海洋
2026-05-31 09:36:07 +08:00
parent 859acc7b71
commit b371a5341a
88 changed files with 23328 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
export const components = {};
+194
View File
@@ -0,0 +1,194 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import { filesMeta, tabsMeta } from '.';
import type { IDemoData, IRouteMeta } from 'dumi/dist/client/theme-api/types';
// Copy from React official demo.
type ReactPromise<T> = Promise<T> & {
status?: 'pending' | 'fulfilled' | 'rejected';
value?: T;
reason?: any;
};
/**
* @private Internal usage. Safe to remove
*/
export function use<T>(promise: ReactPromise<T>): T {
if (promise.status === 'fulfilled') {
return promise.value!;
} else if (promise.status === 'rejected') {
throw promise.reason;
} else if (promise.status === 'pending') {
throw promise;
} else {
promise.status = 'pending';
promise.then(
(result) => {
promise.status = 'fulfilled';
promise.value = result;
},
(reason) => {
promise.status = 'rejected';
promise.reason = reason;
},
);
throw promise;
}
}
const demoIdMap = Object.keys(filesMeta).reduce((total, current) => {
if (filesMeta[current].demoIndex) {
const { ids, getter } = filesMeta[current].demoIndex;
ids.forEach((id) => {
total[id] = getter;
});
}
return total;
}, {});
const demosCache = new Map<string, Promise<IDemoData | undefined>>();
/**
* expand context for source omit extension
* why not do this in compile-time?
* asset metadata also has extension and for reduce bundle size
*/
function expandDemoContext(context?: IDemoData['context']) {
if (context) {
Object.keys(context).forEach((src) => {
const withoutExt = src.match(/^(.+)\.(js|jsx|ts|tsx|json)$/)?.[1];
if (withoutExt && !context[withoutExt]) {
context[withoutExt] = context[src];
}
});
}
}
/**
* use demo data by id
*/
export function useDemo(id: string): IDemoData | undefined {
if (!demosCache.get(id)) {
demosCache.set(
id,
demoIdMap[id]?.().then(({ demos }) => {
// expand context for omit ext
expandDemoContext(demos[id].context);
return demos[id];
}),
);
}
return use(demosCache.get(id)!);
}
/**
* get all demos
*/
export async function getFullDemos() {
const demoFilesMeta = Object.entries(filesMeta).filter(
([_id, meta]) => meta.demoIndex,
);
return Promise.all(
demoFilesMeta.map(async ([id, meta]) => ({
id,
demos: (await meta.demoIndex.getter()).demos as Record<string, IDemoData>,
})),
).then((ret) =>
ret.reduce<Record<string, IDemoData>>((total, { id, demos }) => {
Object.values(demos).forEach((demo) => {
// set route id in runtime for reduce bundle size
demo.routeId = id;
// expand context for omit ext
expandDemoContext(demo.context);
});
Object.assign(total, demos);
return total;
}, {}),
);
}
type ITab = NonNullable<IRouteMeta['tabs']>[0];
/**
* generate final data for tab
*/
function genTab(id: string, meta?: ITab['meta']): ITab {
return {
...tabsMeta[id],
meta: meta ?? {
frontmatter: { title: tabsMeta[id].title },
toc: [],
texts: [],
},
};
}
/**
* get route meta by id
*/
export function getRouteMetaById<T extends { syncOnly?: boolean }>(
id: string,
opts?: T,
): T extends { syncOnly: true }
? IRouteMeta | undefined
: Promise<IRouteMeta> | undefined {
if (filesMeta[id]) {
const { frontmatter, toc, textGetter, tabs } = filesMeta[id];
const routeMeta: IRouteMeta = {
frontmatter,
toc,
texts: [],
};
if (opts?.syncOnly) {
if (tabs) {
routeMeta.tabs = tabs.map((tabId) =>
genTab(tabId, getRouteMetaById(tabId, opts)),
);
}
return routeMeta;
} else {
return new Promise(async (resolve) => {
if (textGetter) {
({ texts: routeMeta.texts } = await textGetter());
}
if (tabs) {
routeMeta.tabs = await Promise.all(
tabs.map(async (tabId) =>
genTab(tabId, await getRouteMetaById(tabId, opts)),
),
);
}
resolve(routeMeta);
});
}
}
}
/**
* get all routes meta
*/
export async function getFullRoutesMeta(): Promise<Record<string, IRouteMeta>> {
return await Promise.all(
Object.keys(filesMeta).map(async (id) => ({
id,
meta: await getRouteMetaById(id),
})),
).then((ret) =>
ret.reduce(
(total, { id, meta }) => {
total[id] = meta;
return total;
},
{},
),
);
}
+40
View File
@@ -0,0 +1,40 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import { frontmatter as fm0, toc as t0 } from '/Users/donghym/Desktop/day/UI/Nova/docs/guide/getting-started.md?type=frontmatter';
import { demoIndex as dmi0 } from '/Users/donghym/Desktop/day/UI/Nova/docs/guide/getting-started.md?type=demo-index';
import { frontmatter as fm1, toc as t1 } from '/Users/donghym/Desktop/day/UI/Nova/docs/components/index.md?type=frontmatter';
import { demoIndex as dmi1 } from '/Users/donghym/Desktop/day/UI/Nova/docs/components/index.md?type=demo-index';
import { frontmatter as fm2, toc as t2 } from '/Users/donghym/Desktop/day/UI/Nova/docs/index.md?type=frontmatter';
import { demoIndex as dmi2 } from '/Users/donghym/Desktop/day/UI/Nova/docs/index.md?type=demo-index';
import { frontmatter as fm3, toc as t3 } from '/Users/donghym/Desktop/day/UI/Nova/src/components/Button/index.md?type=frontmatter';
import { demoIndex as dmi3 } from '/Users/donghym/Desktop/day/UI/Nova/src/components/Button/index.md?type=demo-index';
export const filesMeta = {
'docs/guide/getting-started': {
frontmatter: fm0,
toc: t0,
demoIndex: dmi0,
textGetter: () => import(/* webpackChunkName: "meta__docs" */'/Users/donghym/Desktop/day/UI/Nova/docs/guide/getting-started.md?type=text'),
},
'docs/components/index': {
frontmatter: fm1,
toc: t1,
demoIndex: dmi1,
textGetter: () => import(/* webpackChunkName: "meta__docs" */'/Users/donghym/Desktop/day/UI/Nova/docs/components/index.md?type=text'),
},
'docs/index': {
frontmatter: fm2,
toc: t2,
demoIndex: dmi2,
textGetter: () => import(/* webpackChunkName: "meta__docs" */'/Users/donghym/Desktop/day/UI/Nova/docs/index.md?type=text'),
},
'components/Button/index': {
frontmatter: fm3,
toc: t3,
demoIndex: dmi3,
textGetter: () => import(/* webpackChunkName: "meta__components" */'/Users/donghym/Desktop/day/UI/Nova/src/components/Button/index.md?type=text'),
},
}
export { tabs as tabsMeta } from './tabs';
+40
View File
@@ -0,0 +1,40 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
import { warning } from '/Users/donghym/Desktop/day/UI/Nova/node_modules/.pnpm/rc-util@5.44.4_react-dom@19.2.6_react@19.2.6__react@19.2.6/node_modules/rc-util';
import deepmerge from '/Users/donghym/Desktop/day/UI/Nova/node_modules/.pnpm/deepmerge@4.3.1/node_modules/deepmerge';
import { getRouteMetaById } from './exports';
// Proxy do not warning since `Object.keys` will get nothing to loop
function wrapEmpty(meta, fieldName, defaultValue) {
Object.defineProperty(meta, fieldName, {
get: () => {
warning(false, `'${fieldName}' return empty in latest version, please use \`useRouteMeta\` instead.`);
return defaultValue;
},
});
}
export const patchRoutes = ({ routes }) => {
Object.values(routes).forEach((route) => {
const routeMeta = getRouteMetaById(route.id, { syncOnly: true });
if (routeMeta) {
if (process.env.NODE_ENV === 'production' && (route.meta?.frontmatter?.debug || routeMeta.frontmatter?.debug)) {
// hide route in production which set hide frontmatter
delete routes[route.id];
} else {
// merge meta to route object
route.meta = deepmerge(route.meta, routeMeta);
wrapEmpty(route.meta, 'toc', []);
wrapEmpty(route.meta, 'texts', []);
route.meta.tabs?.forEach((tab) => {
wrapEmpty(tab, 'toc', []);
wrapEmpty(tab, 'texts', []);
});
}
}
});
}
+5
View File
@@ -0,0 +1,5 @@
// @ts-nocheck
// This file is generated by Umi automatically
// DO NOT CHANGE IT MANUALLY!
export const tabs = {
}