dumi
Version:
📖 Documentation Generator of React Component
388 lines (386 loc) • 16.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/loaders/markdown/transformer/rehypeDemo.ts
var rehypeDemo_exports = {};
__export(rehypeDemo_exports, {
DEMO_PROP_VALUE_KEY: () => DEMO_PROP_VALUE_KEY,
DUMI_DEMO_GRID_TAG: () => DUMI_DEMO_GRID_TAG,
DUMI_DEMO_TAG: () => DUMI_DEMO_TAG,
SKIP_DEMO_PARSE: () => SKIP_DEMO_PARSE,
default: () => rehypeDemo
});
module.exports = __toCommonJS(rehypeDemo_exports);
var import_block = __toESM(require("../../../assetParsers/block"));
var import_utils = require("../../../utils");
var import_path = __toESM(require("path"));
var import_plugin_utils = require("umi/plugin-utils");
var visit;
var SKIP;
var EXIT;
var toString;
var isElement;
var DEMO_NODE_CONTAINER = "$demo-container";
var DEMO_PROP_VALUE_KEY = "$demo-prop-value-key";
var DUMI_DEMO_TAG = "DumiDemo";
var DUMI_DEMO_GRID_TAG = "DumiDemoGrid";
var SKIP_DEMO_PARSE = "pure";
var ALWAYS_DEMO_PARSE = "demo";
var skipDemoRE = new RegExp(
/** 注意前面有空格 ==> */
` ${SKIP_DEMO_PARSE}`
);
var alwaysDemoRE = new RegExp(
/** 注意前面有空格 ==> */
` ${ALWAYS_DEMO_PARSE}`
);
(async () => {
({ visit, SKIP, EXIT } = await import("unist-util-visit"));
({ toString } = await import("hast-util-to-string"));
({ isElement } = await import("hast-util-is-element"));
})();
function getCodeLang(node, opts) {
var _a, _b, _c, _d;
let lang = "";
if (typeof ((_a = node.properties) == null ? void 0 : _a.src) === "string") {
node.properties.src = opts.resolver(
import_path.default.dirname(opts.fileAbsPath),
node.properties.src
);
lang = import_path.default.extname(node.properties.src).slice(1);
} else if ([
// 插件开发者可配置 [SKIP_DEMO_PARSE_SIGN] 表示不解析 demo (优先级最高)
!Object.prototype.hasOwnProperty.call(node.data ?? {}, SKIP_DEMO_PARSE),
Array.isArray((_b = node.properties) == null ? void 0 : _b.className),
// 根据用户配置判断 pure 或者 demo
opts.resolve.codeBlockMode === "passive" ? alwaysDemoRE.test(String((_c = node.data) == null ? void 0 : _c.meta)) : !skipDemoRE.test(String((_d = node.data) == null ? void 0 : _d.meta))
// active mode (default)
].every(Boolean)) {
lang = String(node.properties.className[0]).replace(
"language-",
""
);
}
return lang;
}
function getCodeId(cwd, fileAbsPath, localId, atomId) {
const prefix = atomId || (0, import_utils.getFileIdFromFsPath)(import_path.default.relative(cwd, fileAbsPath));
return [prefix.toLowerCase(), "demo", localId.toLowerCase()].filter(Boolean).join("-");
}
function tryMarkDemoNode(node, opts) {
var _a, _b;
let isDemoNode = Boolean((_a = node.data) == null ? void 0 : _a.techStack);
if (!isDemoNode) {
const lang = getCodeLang(node, opts);
const techStack = lang && opts.techStacks.find((ts) => ts.isSupported(node, lang));
if (techStack) {
isDemoNode = true;
node.data ?? (node.data = {});
node.data.techStack = techStack;
node.data.lang = lang;
node.data.type = typeof ((_b = node.properties) == null ? void 0 : _b.src) === "string" ? "external" : "code-block";
}
}
return isDemoNode;
}
function rehypeDemo(opts) {
return async (tree, vFile) => {
const deferrers = [];
const demoIds = [];
const replaceNodes = [];
let index = 0;
visit(tree, "element", (node) => {
if (isElement(node, "pre") && node.children.length === 1 && isElement(node.children[0], "code") && tryMarkDemoNode(node.children[0], opts)) {
node.tagName = "p";
node.data ?? (node.data = {});
node.data[DEMO_NODE_CONTAINER] = true;
}
});
visit(tree, "element", (node, nodeIndex, parent) => {
if (isElement(node, "p")) {
for (let childIndex = 0; childIndex < node.children.length; childIndex += 1) {
let child = node.children[childIndex];
if (isElement(child, "code") && tryMarkDemoNode(child, opts)) {
const isFirstChild = childIndex === 0;
let nextChildIndex = childIndex + 1;
let nextChild = node.children[nextChildIndex];
let splitFrom = childIndex;
if (isFirstChild) {
node.data ?? (node.data = {});
node.data[DEMO_NODE_CONTAINER] = true;
while (nextChild) {
if (isElement(nextChild, "code") && tryMarkDemoNode(nextChild, opts) || isElement(nextChild, "br")) {
splitFrom += 1;
nextChildIndex = splitFrom + 1;
nextChild = node.children[nextChildIndex];
} else {
splitFrom += 1;
break;
}
}
if (!nextChild)
return SKIP;
}
const splitChildren = node.children.splice(splitFrom);
parent.children.splice(nodeIndex + 1, 0, {
type: "element",
tagName: "p",
children: splitChildren
});
return SKIP;
}
}
}
});
let hasOnlySign = false;
let hasSkipSign = false;
visit(tree, "element", (node) => {
var _a;
if (isElement(node, "p") && ((_a = node.data) == null ? void 0 : _a[DEMO_NODE_CONTAINER])) {
for (const codeNode of node.children) {
if (isElement(codeNode, "code")) {
hasSkipSign || (hasSkipSign = "skip" in codeNode.properties);
if ("only" in codeNode.properties) {
hasOnlySign = true;
return EXIT;
}
}
}
}
});
if (process.env.NODE_ENV === "production" && (hasOnlySign || hasSkipSign)) {
import_plugin_utils.logger.warn(
`The 'only' or 'skip' mark is not supported in production environment, please remove it. at ${vFile.data.frontmatter.filename}`
);
}
visit(tree, "element", (node) => {
var _a, _b, _c;
if (isElement(node, "p") && ((_a = node.data) == null ? void 0 : _a[DEMO_NODE_CONTAINER])) {
const demosPropData = [];
for (const codeNode of node.children) {
if (isElement(codeNode, "code")) {
const shouldSkipNonOnlyDemos = hasOnlySign && !("only" in codeNode.properties);
if (process.env.NODE_ENV !== "production" && ("skip" in codeNode.properties || shouldSkipNonOnlyDemos)) {
continue;
}
const codeType = codeNode.data.type;
const techStack = codeNode.data.techStack;
const codeValue = toString(codeNode).trim();
const parseOpts = {
id: "",
refAtomIds: vFile.data.frontmatter.atomId ? [vFile.data.frontmatter.atomId] : [],
fileAbsPath: "",
lang: codeNode.data.lang,
fileLocale: opts.fileLocale,
entryPointCode: codeType === "external" ? void 0 : codeValue,
resolver: opts.resolver,
techStack
};
const runtimeOpts = techStack.runtimeOpts;
const previewerProps = {};
let component = "";
if (codeType === "external") {
const chunkName = [vFile.data.frontmatter.atomId, "demos"].filter(Boolean).join("__");
parseOpts.fileAbsPath = (0, import_plugin_utils.winPath)(
codeNode.properties.src
);
let localId = ((_b = codeNode.properties) == null ? void 0 : _b.id) ?? import_path.default.parse(
parseOpts.fileAbsPath.replace(/\/index\.(j|t)sx?$/, "")
).name;
parseOpts.id = getCodeId(
opts.cwd,
opts.fileLocaleLessPath,
localId,
vFile.data.frontmatter.atomId
);
const importChunk = `import( /* webpackChunkName: "${chunkName}" */ '${(0, import_plugin_utils.winPath)(
parseOpts.fileAbsPath
)}?techStack=${techStack.name}')`;
if (runtimeOpts == null ? void 0 : runtimeOpts.rendererPath) {
component = `(async () => ${importChunk})()`;
} else {
component = `React.memo(React.lazy(() => ${importChunk}))`;
}
if (codeValue)
codeNode.properties.title = codeValue;
(_c = codeNode.properties).filename ?? (_c.filename = (0, import_plugin_utils.winPath)(
import_path.default.relative(opts.cwd, parseOpts.fileAbsPath)
));
} else {
const localId = [opts.fileLocale, String(index++)].filter(Boolean).join("-");
parseOpts.fileAbsPath = opts.fileAbsPath.replace(
".md",
`.${parseOpts.lang}`
);
parseOpts.id = getCodeId(
opts.cwd,
opts.fileLocaleLessPath,
localId,
vFile.data.frontmatter.atomId
);
component = techStack.transformCode(codeValue, {
type: "code-block",
fileAbsPath: parseOpts.fileAbsPath
});
}
const propDemo = { id: parseOpts.id };
demoIds.push(parseOpts.id);
deferrers.push(
(0, import_block.default)(parseOpts).then(
async ({ asset, resolveMap, frontmatter }) => {
var _a2, _b2, _c2;
if (demoIds.indexOf(parseOpts.id) !== demoIds.lastIndexOf(parseOpts.id)) {
const startLine = (_a2 = node.position) == null ? void 0 : _a2.start.line;
const suffix = startLine ? `:${startLine}` : "";
import_plugin_utils.logger.warn(
`Duplicate demo id found due to filename conflicts, please consider adding a unique id to code tag to resolve this.
at ${opts.fileAbsPath}${suffix}`
);
}
const { src, className, ...restAttrs } = codeNode.properties || {};
const validAssetAttrs = [
"title",
"snapshot",
"keywords"
];
const techStackOpts = {
type: codeType,
mdAbsPath: opts.fileAbsPath,
fileAbsPath: codeType === "external" ? parseOpts.fileAbsPath : void 0,
entryPointCode: parseOpts.entryPointCode
};
Object.keys(restAttrs).forEach((key) => {
if (restAttrs[key] === "")
restAttrs[key] = true;
});
const originalProps = Object.assign(
{},
frontmatter,
restAttrs
);
validAssetAttrs.forEach((key) => {
if (originalProps[key])
asset[key] = originalProps[key];
});
if (/ inline/.test(String((_b2 = codeNode.data) == null ? void 0 : _b2.meta)) || originalProps.inline) {
propDemo.inline = true;
return {
// TODO: special id for inline demo
id: asset.id,
component,
renderOpts: {
rendererPath: runtimeOpts == null ? void 0 : runtimeOpts.rendererPath,
preflightPath: runtimeOpts == null ? void 0 : runtimeOpts.preflightPath
}
};
}
Object.assign(
previewerProps,
await ((_c2 = techStack.generatePreviewerProps) == null ? void 0 : _c2.call(
techStack,
originalProps,
techStackOpts
)) || originalProps
);
if (previewerProps.description) {
const { unified } = await import("unified");
const { default: remarkParse } = await import("remark-parse");
const { default: remarkGfm } = await import("remark-gfm");
const { default: remarkRehype } = await import("remark-rehype");
const { default: rehypeStringify } = await import("rehype-stringify");
const { convert } = require("html-to-text");
const result = await unified().use(remarkParse).use(remarkGfm).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeStringify, { allowDangerousHtml: true }).process(previewerProps.description);
previewerProps.description = String(result.value);
asset.description = convert(result.value, {
wordwrap: false
});
}
return {
id: asset.id,
component,
asset: techStack.generateMetadata ? await techStack.generateMetadata(asset, techStackOpts) : asset,
/**
* keep `generateSources` rather than `generateResolveMap` for compatibility
*/
resolveMap: techStack.generateSources ? await techStack.generateSources(
resolveMap,
techStackOpts
) : resolveMap,
renderOpts: {
rendererPath: runtimeOpts == null ? void 0 : runtimeOpts.rendererPath,
compilePath: runtimeOpts == null ? void 0 : runtimeOpts.compilePath,
preflightPath: runtimeOpts == null ? void 0 : runtimeOpts.preflightPath
}
};
}
)
);
demosPropData.push({
demo: propDemo,
previewerProps
});
if (process.env.NODE_ENV !== "production" && "only" in codeNode.properties) {
break;
}
}
}
replaceNodes.push(node);
node.children = [];
if (demosPropData.length === 1) {
node.tagName = DUMI_DEMO_TAG;
node.data[DEMO_PROP_VALUE_KEY] = demosPropData[0];
node.JSXAttributes = [{ type: "JSXSpreadAttribute", argument: "" }];
} else {
node.tagName = DUMI_DEMO_GRID_TAG;
node.data[DEMO_PROP_VALUE_KEY] = demosPropData;
node.JSXAttributes = [
{ type: "JSXAttribute", name: "items", value: "" }
];
}
return SKIP;
}
});
await Promise.all(deferrers).then((demos) => {
vFile.data.demos = demos;
replaceNodes.forEach((node) => {
const value = JSON.stringify(node.data[DEMO_PROP_VALUE_KEY]);
if (node.JSXAttributes[0].type === "JSXAttribute") {
node.JSXAttributes[0].value = value;
} else {
node.JSXAttributes[0].argument = value;
}
});
});
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
DEMO_PROP_VALUE_KEY,
DUMI_DEMO_GRID_TAG,
DUMI_DEMO_TAG,
SKIP_DEMO_PARSE
});