docgen.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. function _interopRequireDefault(obj) {
  4. return obj && obj.__esModule ? obj : { default: obj };
  5. }
  6. function _optionalChain(ops) {
  7. let lastAccessLHS = undefined;
  8. let value = ops[0];
  9. let i = 1;
  10. while (i < ops.length) {
  11. const op = ops[i];
  12. const fn = ops[i + 1];
  13. i += 2;
  14. if (
  15. (op === "optionalAccess" || op === "optionalCall") &&
  16. value == null
  17. ) {
  18. return undefined;
  19. }
  20. if (op === "access" || op === "optionalAccess") {
  21. lastAccessLHS = value;
  22. value = fn(value);
  23. } else if (op === "call" || op === "optionalCall") {
  24. value = fn((...args) => value.call(lastAccessLHS, ...args));
  25. lastAccessLHS = undefined;
  26. }
  27. }
  28. return value;
  29. }
  30. //
  31. var _fsextra = require("fs-extra");
  32. var _fsextra2 = _interopRequireDefault(_fsextra);
  33. var _ora = require("ora");
  34. var _ora2 = _interopRequireDefault(_ora);
  35. var _path = require("path");
  36. var _path2 = _interopRequireDefault(_path);
  37. var _reactdocgentypescript = require("react-docgen-typescript");
  38. /** TYPES */
  39. /** CONSTANTS */
  40. const packagesDir = _path2.default.join(__dirname, "./../..", "./packages");
  41. const sourceDir = "./src";
  42. const excludedFilePatterns = [
  43. "node_modules",
  44. "tsup.config.ts",
  45. ".test.",
  46. ".spec.",
  47. ];
  48. const excludedValueDeclarationPatterns = ["node_modules/antd/lib/list/"];
  49. const excludePropPatterns = [/^__.*/];
  50. const excludedProps = [
  51. "className",
  52. "classNames",
  53. "styles",
  54. "unstyled",
  55. "component",
  56. "key",
  57. "ref",
  58. "style",
  59. "sx",
  60. "m",
  61. "mx",
  62. "my",
  63. "mt",
  64. "ml",
  65. "mr",
  66. "mb",
  67. "p",
  68. "px",
  69. "py",
  70. "pt",
  71. "pl",
  72. "pr",
  73. "pb",
  74. ];
  75. const replacementProps = {
  76. // "null | string | number | false | true | ReactElement<any, string | JSXElementConstructor<any>> | ReactFragment | ReactPortal": "ReactNode",
  77. ReactElement:
  78. "ReactElement<any, string | ((props: any) => ReactElement<any, any>) | (new (props: any) => Component<any, any, any>)>",
  79. "ReactNode | (value: number) => ReactNode":
  80. "string | number | boolean | {} | ReactElement<any, string | ((props: any) => ReactElement<any, any>) | (new (props: any) => Component<any, any, any>)> | ReactNodeArray | ReactPortal | ((value: number) => ReactNode)",
  81. ActionButtonRenderer:
  82. "ReactNode | ({ defaultButtons: ReactNode }) => ReactNode",
  83. "DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>":
  84. "DetailedHTMLProps<HTMLDivElement>",
  85. "false | OpenNotificationParams | ((data?: unknown, values?: unknown, resource?: string) => OpenNotificationParams)":
  86. "false | OpenNotificationParams | (data, values, resource) => OpenNotificationParams",
  87. "false | OpenNotificationParams | ((error?: unknown, values?: unknown, resource?: string) => OpenNotificationParams)":
  88. "false | OpenNotificationParams | (error, values, resource) => OpenNotificationParams",
  89. 'SvgIconProps<"svg", {}>': "SvgIconProps",
  90. SpaceProps: "[`SpaceProps`](https://styled-system.com/api#space)",
  91. "((value: DeleteOneResponse<BaseRecord>) => void)":
  92. "(value: DeleteOneResponse) => void",
  93. "{ [key: string]: any; ids?: BaseKey[]; }":
  94. "{ [key]: any; ids?: BaseKey[]; }",
  95. "BaseKey | BaseKey[]":
  96. "[BaseKey](/docs/core/interface-references/#basekey) | [BaseKey[]](/docs/core/interface-references/#basekey)",
  97. BaseKey: "[BaseKey](/docs/core/interface-references/#basekey)",
  98. MetaDataQuery:
  99. "[MetaDataQuery](/docs/core/interface-references/#metadataquery)",
  100. CrudFilters: "[CrudFilters](/docs/core/interface-references/#crudfilters)",
  101. CrudSorting: "[CrudSorting](/docs/core/interface-references/#crudsorting)",
  102. };
  103. const spinner = _ora2.default.call(void 0, "Generating Refine declarations...");
  104. /** HELPERS */
  105. const getPackageNamePathMap = async (directory) => {
  106. const packages = await _fsextra2.default.readdir(directory);
  107. const packageNamePathMap = {};
  108. const includedPackages =
  109. _optionalChain([
  110. process,
  111. "access",
  112. (_2) => _2.env,
  113. "access",
  114. (_3) => _3.INCLUDED_PACKAGES,
  115. "optionalAccess",
  116. (_4) => _4.split,
  117. "call",
  118. (_5) => _5(","),
  119. ]) || [];
  120. await Promise.all(
  121. packages.map(async (packageName) => {
  122. const packagePath = _path2.default.join(
  123. directory,
  124. packageName,
  125. "package.json",
  126. );
  127. if (_fsextra2.default.existsSync(packagePath)) {
  128. const packageJson = await _fsextra2.default.readJSON(
  129. packagePath,
  130. );
  131. if (
  132. includedPackages.length == 0 ||
  133. includedPackages.some((p) => packageName.includes(p))
  134. ) {
  135. packageNamePathMap[packageJson.name] = _path2.default.join(
  136. packagePath,
  137. "..",
  138. );
  139. }
  140. }
  141. return packageName;
  142. }),
  143. );
  144. return packageNamePathMap;
  145. };
  146. const getPaths = async (packageDir, excludedPatterns) => {
  147. const dir = await _fsextra2.default.readdir(packageDir);
  148. const filtered = [];
  149. await Promise.all(
  150. dir.map(async (file) => {
  151. const result = await _fsextra2.default.pathExists(
  152. _path2.default.join(packageDir, file),
  153. );
  154. if (result) {
  155. filtered.push(file);
  156. }
  157. }),
  158. );
  159. return filtered
  160. .map((p) => _path2.default.join(packageDir, p))
  161. .filter(
  162. (p) => !excludedPatterns.some((pattern) => p.includes(pattern)),
  163. );
  164. };
  165. const _getPrefixFromDeclarationPath = async (path) => {
  166. const map = await getPackageNamePathMap(packagesDir);
  167. const packageName = Object.keys(map).find((key) => path.includes(map[key]));
  168. return packageName;
  169. };
  170. const getComponentName = (name, _fileName) => {
  171. return name;
  172. // return `${getPrefixFromDeclarationPath(fileName)}#${name}`;
  173. };
  174. const getOutputName = (packageName) => {
  175. return packageName;
  176. };
  177. const declarationFilter = (declaration) => {
  178. return (
  179. !declaration.fileName.includes("node_modules") ||
  180. declaration.fileName.includes("@refinedev")
  181. );
  182. };
  183. const valueDeclarationFilter = (tsDeclaration) => {
  184. // excludedValueDeclarationPatterns includes fileNames of source files to be ignored (partially)
  185. const sourceFileName = _optionalChain([
  186. tsDeclaration,
  187. "optionalAccess",
  188. (_6) => _6.getSourceFile,
  189. "call",
  190. (_7) => _7(),
  191. "access",
  192. (_8) => _8.fileName,
  193. ]);
  194. // if sourceFileName includes any of the excludedValueDeclarationPatterns then ignore it
  195. const isIgnored = excludedValueDeclarationPatterns.some((pattern) =>
  196. _optionalChain([
  197. sourceFileName,
  198. "optionalAccess",
  199. (_9) => _9.includes,
  200. "call",
  201. (_10) => _10(pattern),
  202. ]),
  203. );
  204. return !isIgnored;
  205. };
  206. const createParser = (configPath) => {
  207. const docgenParser = _reactdocgentypescript.withCustomConfig.call(
  208. void 0,
  209. _path2.default.join(configPath),
  210. {
  211. savePropValueAsString: true,
  212. shouldExtractLiteralValuesFromEnum: true,
  213. shouldRemoveUndefinedFromOptional: true,
  214. shouldIncludePropTagMap: true,
  215. componentNameResolver: (exp, source) => {
  216. const name = getComponentName(exp.getName(), source.fileName);
  217. if (valueDeclarationFilter(exp.valueDeclaration)) {
  218. return name;
  219. }
  220. return `IGNORED_${name}`;
  221. },
  222. propFilter: (prop) => {
  223. const isExcluded =
  224. excludedProps.includes(prop.name) ||
  225. excludePropPatterns.some((pattern) =>
  226. pattern.test(prop.name),
  227. );
  228. const isExternal =
  229. prop.declarations &&
  230. prop.declarations.length > 0 &&
  231. !Boolean(prop.declarations.find(declarationFilter));
  232. const isUnknown = typeof prop.declarations === "undefined";
  233. if (isExcluded || isExternal || isUnknown) {
  234. return false;
  235. }
  236. return true;
  237. },
  238. },
  239. );
  240. return docgenParser;
  241. };
  242. const normalizeMarkdownLinks = (value) => {
  243. return value.replace(/\[(.*?)\]\s{1}\((.*?)\)/g, (_, p1, p2) => {
  244. return `[${p1}](${p2})`;
  245. });
  246. };
  247. const prepareDeclaration = (declaration) => {
  248. const data = { ...declaration };
  249. delete data.methods;
  250. delete data.tags;
  251. data.generatedAt = Date.now();
  252. Object.keys(data.props).forEach((prop) => {
  253. data.props[prop].type.name = normalizeMarkdownLinks(
  254. data.props[prop].type.name,
  255. );
  256. delete data.props[prop].parent;
  257. delete data.props[prop].declarations;
  258. if (data.props[prop].type.raw === "ReactNode") {
  259. data.props[prop].type.name = "ReactNode";
  260. }
  261. if (data.props[prop].type.name in replacementProps) {
  262. data.props[prop].type.name =
  263. replacementProps[data.props[prop].type.name];
  264. }
  265. if (data.props[prop].type.name === "enum") {
  266. data.props[prop].type.name = data.props[prop].type.value
  267. .map((val) => val.value)
  268. .join(" | ");
  269. }
  270. });
  271. const ordered = Object.keys(data.props)
  272. // .sort()
  273. .reduce((obj, key) => {
  274. obj[key] = data.props[key];
  275. return obj;
  276. }, {});
  277. data.props = ordered;
  278. return data;
  279. };
  280. const transposeDeclarations = (declarations) => {
  281. const transposed = {};
  282. declarations.forEach((declaration) => {
  283. transposed[declaration.displayName] = declaration;
  284. });
  285. return transposed;
  286. };
  287. const generateDeclarations = async (packagePaths) => {
  288. const generated = {};
  289. await Promise.all(
  290. packagePaths.map(async ([packageName, packagePath]) => {
  291. const parser = createParser(
  292. _path2.default.join(packagePath, "./tsconfig.json"),
  293. );
  294. const sourcePath = _path2.default.join(packagePath, sourceDir);
  295. if (!(await _fsextra2.default.pathExists(sourcePath))) {
  296. spinner.fail("Component path does not exist", sourcePath);
  297. process.exit(1);
  298. }
  299. const declarationPaths = await getPaths(
  300. sourcePath,
  301. excludedFilePatterns,
  302. );
  303. const parsed = parser
  304. .parse(declarationPaths)
  305. .map(prepareDeclaration);
  306. const transposed = transposeDeclarations(parsed);
  307. const outputName = getOutputName(packageName);
  308. generated[outputName] = transposed;
  309. spinner.stop();
  310. spinner.start(`- Generated declarations - ${packageName}`);
  311. return [packageName, packagePath];
  312. }),
  313. );
  314. return generated;
  315. };
  316. /** DOCGEN */
  317. const handleDocgen = async () => {
  318. const packagePathMap = await getPackageNamePathMap(packagesDir);
  319. const packagePathMapArray = Object.entries(packagePathMap);
  320. spinner.stop();
  321. spinner.start(`- Found ${packagePathMapArray.length} packages`);
  322. const res = await generateDeclarations(packagePathMapArray);
  323. spinner.succeed("Generated declarations");
  324. return res;
  325. };
  326. function plugin() {
  327. return {
  328. name: "docusaurus-plugin-refine-docgen",
  329. getPathsToWatch: function () {
  330. return [packagesDir];
  331. },
  332. async loadContent() {
  333. if (!process.env.DISABLE_DOCGEN) {
  334. spinner.start();
  335. return await handleDocgen();
  336. }
  337. return {};
  338. },
  339. configureWebpack(config) {
  340. return {
  341. resolve: {
  342. alias: {
  343. "@docgen": _path2.default.join(
  344. _optionalChain([
  345. config,
  346. "access",
  347. (_11) => _11.resolve,
  348. "optionalAccess",
  349. (_12) => _12.alias,
  350. "optionalAccess",
  351. (_13) => _13["@generated"],
  352. ]),
  353. "docusaurus-plugin-refine-docgen",
  354. "default",
  355. ),
  356. },
  357. },
  358. };
  359. },
  360. async contentLoaded({ content, actions }) {
  361. if (!process.env.DISABLE_DOCGEN) {
  362. _ora2.default
  363. .call(void 0, "Creating Refine declaration files...")
  364. .succeed();
  365. const { createData } = actions;
  366. const data = [];
  367. Object.entries(content).forEach(
  368. ([packageName, packageDeclarations]) => {
  369. Object.entries(packageDeclarations).forEach(
  370. ([componentName, declaration]) => {
  371. data.push(
  372. createData(
  373. `${packageName}/${componentName}.json`,
  374. JSON.stringify(declaration),
  375. ),
  376. );
  377. },
  378. );
  379. },
  380. );
  381. await Promise.all(data);
  382. }
  383. },
  384. };
  385. }
  386. exports.default = plugin;