feat:组件框架
This commit is contained in:
@@ -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;
|
||||
},
|
||||
{},
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user