mintlify 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/README.md +21 -5
  2. package/bin/constants.js +8 -0
  3. package/bin/constants.js.map +1 -0
  4. package/bin/dev/getOpenApiContext.js +46 -0
  5. package/bin/dev/getOpenApiContext.js.map +1 -0
  6. package/bin/dev/index.js +164 -0
  7. package/bin/dev/index.js.map +1 -0
  8. package/bin/dev/injectNav.js +97 -0
  9. package/bin/dev/injectNav.js.map +1 -0
  10. package/bin/dev/slugToTitle.js +8 -0
  11. package/bin/dev/slugToTitle.js.map +1 -0
  12. package/bin/index.js +45 -224
  13. package/bin/index.js.map +1 -1
  14. package/bin/init-command/index.js +51 -0
  15. package/bin/init-command/index.js.map +1 -0
  16. package/bin/init-command/templates.js +41 -0
  17. package/bin/init-command/templates.js.map +1 -0
  18. package/bin/local-preview/categorizeFiles.js +56 -0
  19. package/bin/local-preview/categorizeFiles.js.map +1 -0
  20. package/bin/local-preview/getOpenApiContext.js +46 -0
  21. package/bin/local-preview/getOpenApiContext.js.map +1 -0
  22. package/bin/local-preview/index.js +138 -0
  23. package/bin/local-preview/index.js.map +1 -0
  24. package/bin/local-preview/injectFavicons.js +72 -0
  25. package/bin/local-preview/injectFavicons.js.map +1 -0
  26. package/bin/local-preview/listener.js +112 -0
  27. package/bin/local-preview/listener.js.map +1 -0
  28. package/bin/local-preview/metadata.js +121 -0
  29. package/bin/local-preview/metadata.js.map +1 -0
  30. package/bin/local-preview/mintConfigFile.js +43 -0
  31. package/bin/local-preview/mintConfigFile.js.map +1 -0
  32. package/bin/local-preview/openApiCheck.js +16 -0
  33. package/bin/local-preview/openApiCheck.js.map +1 -0
  34. package/bin/local-preview/slugToTitle.js +8 -0
  35. package/bin/local-preview/slugToTitle.js.map +1 -0
  36. package/bin/mint/client/.babel-plugin-macrosrc.json +5 -0
  37. package/bin/mint/client/.babelrc +4 -0
  38. package/bin/mint/client/.editorconfig +12 -0
  39. package/bin/mint/client/.eslintrc.json +7 -0
  40. package/bin/mint/client/.prettierignore +4 -0
  41. package/bin/mint/client/.prettierrc +14 -0
  42. package/bin/mint/client/.vscode/launch.json +28 -0
  43. package/bin/mint/client/README.md +46 -0
  44. package/bin/mint/client/jest.config.ts +195 -0
  45. package/bin/mint/client/next-env.d.ts +4 -0
  46. package/bin/mint/client/next.config.js +152 -0
  47. package/bin/mint/client/package.json +140 -0
  48. package/bin/mint/client/postcss.config.cjs +9 -0
  49. package/bin/mint/client/prebuild/faviconConfig.js +35 -0
  50. package/bin/mint/client/prebuild/getOpenApiContext.js +53 -0
  51. package/bin/mint/client/prebuild/index.js +117 -0
  52. package/bin/mint/client/prebuild/injectNav.js +115 -0
  53. package/bin/mint/client/prebuild/slugToTitle.js +7 -0
  54. package/bin/mint/client/rehype/withApiComponents.js +60 -0
  55. package/bin/mint/client/rehype/withCodeBlocks.js +54 -0
  56. package/bin/mint/client/rehype/withLayouts.js +113 -0
  57. package/bin/mint/client/rehype/withLinkRoles.js +13 -0
  58. package/bin/mint/client/rehype/withRawComponents.js +13 -0
  59. package/bin/mint/client/rehype/withStaticProps.js +25 -0
  60. package/bin/mint/client/rehype/withSyntaxHighlighting.js +60 -0
  61. package/bin/mint/client/remark/utils.js +369 -0
  62. package/bin/mint/client/remark/withFrames.js +55 -0
  63. package/bin/mint/client/remark/withImportsInjected.js +36 -0
  64. package/bin/mint/client/remark/withNextLinks.js +37 -0
  65. package/bin/mint/client/remark/withTableOfContents.js +71 -0
  66. package/bin/mint/client/scripts/local-to-docs.js +72 -0
  67. package/bin/mint/client/scripts/local.js +177 -0
  68. package/bin/mint/client/sentry.client.config.js +15 -0
  69. package/bin/mint/client/sentry.properties +4 -0
  70. package/bin/mint/client/sentry.server.config.js +15 -0
  71. package/bin/mint/client/src/analytics/AbstractAnalyticsImplementation.ts +50 -0
  72. package/bin/mint/client/src/analytics/AnalyticsContext.ts +5 -0
  73. package/bin/mint/client/src/analytics/AnalyticsMediator.ts +101 -0
  74. package/bin/mint/client/src/analytics/FakeAnalyticsMediator.ts +9 -0
  75. package/bin/mint/client/src/analytics/GA4Script.tsx +33 -0
  76. package/bin/mint/client/src/analytics/implementations/amplitude.ts +26 -0
  77. package/bin/mint/client/src/analytics/implementations/fathom.ts +38 -0
  78. package/bin/mint/client/src/analytics/implementations/ga4.ts +33 -0
  79. package/bin/mint/client/src/analytics/implementations/hotjar.ts +53 -0
  80. package/bin/mint/client/src/analytics/implementations/mixpanel-browser.d.ts +1 -0
  81. package/bin/mint/client/src/analytics/implementations/mixpanel.ts +52 -0
  82. package/bin/mint/client/src/analytics/implementations/posthog.ts +37 -0
  83. package/bin/mint/client/src/components/Accordion/Accordion.tsx +43 -0
  84. package/bin/mint/client/src/components/Accordion/index.ts +4 -0
  85. package/bin/mint/client/src/components/ApiExample.tsx +9 -0
  86. package/bin/mint/client/src/components/Card.tsx +51 -0
  87. package/bin/mint/client/src/components/CodeGroup.tsx +132 -0
  88. package/bin/mint/client/src/components/Editor.tsx +12 -0
  89. package/bin/mint/client/src/components/Expandable.tsx +40 -0
  90. package/bin/mint/client/src/components/Heading.tsx +84 -0
  91. package/bin/mint/client/src/components/Param.tsx +56 -0
  92. package/bin/mint/client/src/components/Request.tsx +19 -0
  93. package/bin/mint/client/src/components/ResponseField.tsx +33 -0
  94. package/bin/mint/client/src/components/TabBar.tsx +61 -0
  95. package/bin/mint/client/src/config.ts +115 -0
  96. package/bin/mint/client/src/css/bar-of-progress.css +10 -0
  97. package/bin/mint/client/src/css/base.css +29 -0
  98. package/bin/mint/client/src/css/font-awesome.css +7 -0
  99. package/bin/mint/client/src/css/fonts.css +44 -0
  100. package/bin/mint/client/src/css/main.css +11 -0
  101. package/bin/mint/client/src/css/prism.css +270 -0
  102. package/bin/mint/client/src/css/utilities.css +43 -0
  103. package/bin/mint/client/src/enums/components.ts +8 -0
  104. package/bin/mint/client/src/fonts/FiraCode-VF.woff +0 -0
  105. package/bin/mint/client/src/fonts/FiraCode-VF.woff2 +0 -0
  106. package/bin/mint/client/src/fonts/IBMPlexMono-Regular.ttf +0 -0
  107. package/bin/mint/client/src/fonts/IBMPlexMono-SemiBold.ttf +0 -0
  108. package/bin/mint/client/src/fonts/Inter-italic-latin.var.woff2 +0 -0
  109. package/bin/mint/client/src/fonts/Inter-roman-latin.var.woff2 +0 -0
  110. package/bin/mint/client/src/fonts/Pally-Variable.ttf +0 -0
  111. package/bin/mint/client/src/fonts/SourceSansPro-Regular.otf +0 -0
  112. package/bin/mint/client/src/fonts/SourceSerifPro-Regular.ttf +0 -0
  113. package/bin/mint/client/src/fonts/Synonym-Variable.ttf +0 -0
  114. package/bin/mint/client/src/fonts/Ubuntu-Mono-bold.woff2 +0 -0
  115. package/bin/mint/client/src/fonts/generated/IBMPlexMono-Regular-subset.woff2 +0 -0
  116. package/bin/mint/client/src/fonts/generated/IBMPlexMono-Regular-subset.zopfli.woff +0 -0
  117. package/bin/mint/client/src/fonts/generated/IBMPlexMono-Regular.module.css +11 -0
  118. package/bin/mint/client/src/fonts/generated/IBMPlexMono-SemiBold-subset.woff2 +0 -0
  119. package/bin/mint/client/src/fonts/generated/IBMPlexMono-SemiBold-subset.zopfli.woff +0 -0
  120. package/bin/mint/client/src/fonts/generated/IBMPlexMono-SemiBold.module.css +11 -0
  121. package/bin/mint/client/src/fonts/generated/Pally-Variable-subset.woff2 +0 -0
  122. package/bin/mint/client/src/fonts/generated/Pally-Variable-subset.zopfli.woff +0 -0
  123. package/bin/mint/client/src/fonts/generated/Pally-Variable.module.css +11 -0
  124. package/bin/mint/client/src/fonts/generated/SourceSerifPro-Regular-subset.woff2 +0 -0
  125. package/bin/mint/client/src/fonts/generated/SourceSerifPro-Regular-subset.zopfli.woff +0 -0
  126. package/bin/mint/client/src/fonts/generated/SourceSerifPro-Regular.module.css +11 -0
  127. package/bin/mint/client/src/fonts/generated/Synonym-Variable-subset.woff2 +0 -0
  128. package/bin/mint/client/src/fonts/generated/Synonym-Variable-subset.zopfli.woff +0 -0
  129. package/bin/mint/client/src/fonts/generated/Synonym-Variable.module.css +11 -0
  130. package/bin/mint/client/src/fonts/generated/TenorSans-Regular-subset.woff2 +0 -0
  131. package/bin/mint/client/src/fonts/generated/TenorSans-Regular-subset.zopfli.woff +0 -0
  132. package/bin/mint/client/src/fonts/generated/TenorSans-Regular.module.css +11 -0
  133. package/bin/mint/client/src/hooks/useActionKey.ts +20 -0
  134. package/bin/mint/client/src/hooks/useIsomorphicLayoutEffect.ts +3 -0
  135. package/bin/mint/client/src/hooks/useMedia.ts +27 -0
  136. package/bin/mint/client/src/hooks/usePrevNext.ts +34 -0
  137. package/bin/mint/client/src/hooks/useTop.ts +15 -0
  138. package/bin/mint/client/src/icons/CopyToClipboard.tsx +33 -0
  139. package/bin/mint/client/src/index.d.ts +1 -0
  140. package/bin/mint/client/src/layouts/ApiSupplemental.tsx +173 -0
  141. package/bin/mint/client/src/layouts/ContentsLayout.tsx +256 -0
  142. package/bin/mint/client/src/layouts/DocumentationLayout.tsx +44 -0
  143. package/bin/mint/client/src/layouts/OpenApiContent.tsx +301 -0
  144. package/bin/mint/client/src/layouts/SidebarLayout.tsx +412 -0
  145. package/bin/mint/client/src/layouts/UserFeedback.tsx +73 -0
  146. package/bin/mint/client/src/layouts/getGroupsInDivision.ts +25 -0
  147. package/bin/mint/client/src/layouts/isPathInGroupPages.ts +10 -0
  148. package/bin/mint/client/src/metadata.ts +58 -0
  149. package/bin/mint/client/src/openapi.ts +3 -0
  150. package/bin/mint/client/src/pages/404.tsx +73 -0
  151. package/bin/mint/client/src/pages/_app.tsx +138 -0
  152. package/bin/mint/client/src/pages/_document.tsx +57 -0
  153. package/bin/mint/client/src/pages/api/issue.ts +10 -0
  154. package/bin/mint/client/src/pages/api/name.ts +8 -0
  155. package/bin/mint/client/src/pages/api/request.ts +31 -0
  156. package/bin/mint/client/src/pages/api/suggest.ts +10 -0
  157. package/bin/mint/client/src/pages/api/syntax-highlighted-json.ts +13 -0
  158. package/bin/mint/client/src/pages/api/utils.ts +6 -0
  159. package/bin/mint/client/src/pages/index.tsx +31 -0
  160. package/bin/mint/client/src/ui/Api.tsx +359 -0
  161. package/bin/mint/client/src/ui/Footer.tsx +124 -0
  162. package/bin/mint/client/src/ui/Header.tsx +370 -0
  163. package/bin/mint/client/src/ui/Logo.tsx +55 -0
  164. package/bin/mint/client/src/ui/PageHeader.tsx +51 -0
  165. package/bin/mint/client/src/ui/Search.tsx +386 -0
  166. package/bin/mint/client/src/ui/ThemeToggle.tsx +285 -0
  167. package/bin/mint/client/src/ui/Title.tsx +22 -0
  168. package/bin/mint/client/src/ui/TopLevelLink.tsx +122 -0
  169. package/bin/mint/client/src/utils/api.ts +252 -0
  170. package/bin/mint/client/src/utils/brands.ts +217 -0
  171. package/bin/mint/client/src/utils/castArray.ts +3 -0
  172. package/bin/mint/client/src/utils/childrenArray.ts +3 -0
  173. package/bin/mint/client/src/utils/fit.ts +27 -0
  174. package/bin/mint/client/src/utils/fontAwesome.ts +577 -0
  175. package/bin/mint/client/src/utils/getAnalyticsConfig.ts +14 -0
  176. package/bin/mint/client/src/utils/getLogoHref.ts +9 -0
  177. package/bin/mint/client/src/utils/getOpenApiContext.ts +26 -0
  178. package/bin/mint/client/src/utils/importAll.ts +6 -0
  179. package/bin/mint/client/src/utils/isObject.ts +3 -0
  180. package/bin/mint/client/src/utils/kebabToTitleCase.ts +3 -0
  181. package/bin/mint/client/src/utils/loadImage.ts +8 -0
  182. package/bin/mint/client/src/utils/slugToTitle.ts +7 -0
  183. package/bin/mint/client/src/utils/wait.ts +5 -0
  184. package/bin/mint/client/tailwind.config.cjs +323 -0
  185. package/bin/mint/client/test/test.test.ts +5 -0
  186. package/bin/mint/client/tsconfig.json +36 -0
  187. package/bin/mint/client/yarn.lock +9702 -0
  188. package/bin/navigation.js +4 -0
  189. package/bin/navigation.js.map +1 -0
  190. package/bin/pageTemplate.js +30 -0
  191. package/bin/pageTemplate.js.map +1 -0
  192. package/bin/scraping/combineNavWithEmptyGroupTitles.js +20 -0
  193. package/bin/scraping/combineNavWithEmptyGroupTitles.js.map +1 -0
  194. package/bin/scraping/getSitemapLinks.js +2 -0
  195. package/bin/scraping/getSitemapLinks.js.map +1 -1
  196. package/bin/scraping/scrapeFileGettingFileNameFromUrl.js +43 -0
  197. package/bin/scraping/scrapeFileGettingFileNameFromUrl.js.map +1 -0
  198. package/bin/scraping/scrapeGettingFileNameFromUrl.js +12 -41
  199. package/bin/scraping/scrapeGettingFileNameFromUrl.js.map +1 -1
  200. package/bin/scraping/scrapePageCommands.js +48 -0
  201. package/bin/scraping/scrapePageCommands.js.map +1 -0
  202. package/bin/scraping/scrapeSectionCommands.js +90 -0
  203. package/bin/scraping/scrapeSectionCommands.js.map +1 -0
  204. package/bin/scraping/site-scrapers/getLinksRecursively.js +33 -0
  205. package/bin/scraping/site-scrapers/getLinksRecursively.js.map +1 -0
  206. package/bin/scraping/site-scrapers/scrapeDocusaurusSection.js +1 -1
  207. package/bin/scraping/site-scrapers/scrapeDocusaurusSection.js.map +1 -1
  208. package/bin/scraping/site-scrapers/scrapeGitBookSection.js +25 -48
  209. package/bin/scraping/site-scrapers/scrapeGitBookSection.js.map +1 -1
  210. package/bin/scraping/site-scrapers/scrapeReadMePage.js +2 -1
  211. package/bin/scraping/site-scrapers/scrapeReadMePage.js.map +1 -1
  212. package/bin/scraping/site-scrapers/scrapeReadMeSection.js +10 -20
  213. package/bin/scraping/site-scrapers/scrapeReadMeSection.js.map +1 -1
  214. package/bin/util.js +21 -1
  215. package/bin/util.js.map +1 -1
  216. package/bin/validation/isValidLink.js +11 -0
  217. package/bin/validation/isValidLink.js.map +1 -0
  218. package/bin/validation/stopIfInvalidLink.js +9 -0
  219. package/bin/validation/stopIfInvalidLink.js.map +1 -0
  220. package/package.json +20 -4
  221. package/src/constants.ts +10 -0
  222. package/src/index.ts +110 -272
  223. package/src/init-command/index.ts +59 -0
  224. package/src/{templates.ts → init-command/templates.ts} +0 -0
  225. package/src/local-preview/categorizeFiles.ts +74 -0
  226. package/src/local-preview/getOpenApiContext.ts +61 -0
  227. package/src/local-preview/index.ts +164 -0
  228. package/src/local-preview/injectFavicons.ts +76 -0
  229. package/src/local-preview/listener.ts +116 -0
  230. package/src/local-preview/metadata.ts +154 -0
  231. package/src/local-preview/mintConfigFile.ts +48 -0
  232. package/src/local-preview/openApiCheck.ts +19 -0
  233. package/src/local-preview/slugToTitle.ts +7 -0
  234. package/src/navigation.ts +12 -0
  235. package/src/pageTemplate.ts +32 -0
  236. package/src/scraping/combineNavWithEmptyGroupTitles.ts +21 -0
  237. package/src/scraping/getSitemapLinks.ts +2 -0
  238. package/src/scraping/scrapeFileGettingFileNameFromUrl.ts +81 -0
  239. package/src/scraping/scrapeGettingFileNameFromUrl.ts +27 -58
  240. package/src/scraping/scrapePageCommands.ts +52 -0
  241. package/src/scraping/scrapeSectionCommands.ts +110 -0
  242. package/src/scraping/site-scrapers/getLinksRecursively.ts +40 -0
  243. package/src/scraping/site-scrapers/scrapeDocusaurusSection.ts +1 -1
  244. package/src/scraping/site-scrapers/scrapeGitBookSection.ts +31 -70
  245. package/src/scraping/site-scrapers/scrapeReadMePage.ts +3 -1
  246. package/src/scraping/site-scrapers/scrapeReadMeSection.ts +21 -38
  247. package/src/util.ts +29 -1
  248. package/src/validation/isValidLink.ts +9 -0
  249. package/src/validation/stopIfInvalidLink.ts +9 -0
@@ -0,0 +1,21 @@
1
+ import { Navigation } from "../navigation.js";
2
+
3
+ export default function combineNavWithEmptyGroupTitles(navArray: Navigation[]) {
4
+ let newNavArray = [];
5
+
6
+ navArray.forEach((nav: Navigation) => {
7
+ // The first run through the loop will always have -1 as the index.
8
+ // JavaScript returns undefined when we look for an index outside the size of the array.
9
+ const prev = newNavArray[newNavArray.length - 1];
10
+ if (prev == null) {
11
+ newNavArray.push(nav);
12
+ } else if (!nav.group && !prev.group) {
13
+ // Joins multiple groups without a title together IF they occur side by side
14
+ prev.pages = prev.pages.concat(nav.pages);
15
+ } else {
16
+ newNavArray.push(nav);
17
+ }
18
+ });
19
+
20
+ return newNavArray;
21
+ }
@@ -1,5 +1,7 @@
1
1
  import axios from "axios";
2
2
 
3
+ // Not in use.
4
+ // Gets all links in a sitemap.
3
5
  export const getSitemapLinks = async (url: URL) => {
4
6
  const hostname = url.hostname.replace(".", "\\.");
5
7
  const regex = new RegExp(`https?:\/\/${hostname}.+?(?=<\/loc>)`, "gmi");
@@ -0,0 +1,81 @@
1
+ import path from "path";
2
+ import axios from "axios";
3
+ import { getHtmlWithPuppeteer } from "../browser.js";
4
+ import { createPage } from "../util.js";
5
+
6
+ export async function scrapeFileGettingFileNameFromUrl(
7
+ pathname: string,
8
+ cliDir: string,
9
+ origin: string,
10
+ overwrite: boolean,
11
+ scrapePageFunc: (
12
+ html: string,
13
+ origin: string,
14
+ cliDir: string,
15
+ imageBaseDir: string
16
+ ) => Promise<{
17
+ title?: string;
18
+ description?: string;
19
+ markdown?: string;
20
+ }>,
21
+ puppeteer = false,
22
+ baseToRemove?: string
23
+ ) {
24
+ // Skip scraping external links
25
+ if (pathname.startsWith("https://") || pathname.startsWith("http://")) {
26
+ return pathname;
27
+ }
28
+
29
+ // Removes file name from the end
30
+ const splitSubpath = pathname.split("/");
31
+ let folders = splitSubpath.slice(0, splitSubpath.length - 1).join("/");
32
+
33
+ // Remove base dir if passed in
34
+ if (baseToRemove && folders.startsWith(baseToRemove)) {
35
+ folders = folders.replace(baseToRemove, "");
36
+ }
37
+
38
+ // TO DO: Improve this by putting each page's images in a separate
39
+ // folder named after the title of the page.
40
+ const imageBaseDir = path.join(cliDir, "images", folders);
41
+
42
+ // Scrape each page separately
43
+ const href = new URL(pathname, origin).href;
44
+ let html: string;
45
+ if (puppeteer) {
46
+ html = await getHtmlWithPuppeteer(href);
47
+ } else {
48
+ const res = await axios.default.get(href);
49
+ html = res.data;
50
+ }
51
+
52
+ const { title, description, markdown } = await scrapePageFunc(
53
+ html,
54
+ origin,
55
+ cliDir,
56
+ imageBaseDir
57
+ );
58
+
59
+ // Check if page didn't have content
60
+ if (!title && !markdown) {
61
+ return undefined;
62
+ }
63
+
64
+ const newFileLocation = folders ? path.join(cliDir, folders) : cliDir;
65
+
66
+ // Default to introduction.mdx if we encountered index.html
67
+ const fileName = splitSubpath[splitSubpath.length - 1] || "introduction";
68
+
69
+ // Will create subfolders as needed
70
+ createPage(
71
+ title,
72
+ description,
73
+ markdown,
74
+ overwrite,
75
+ newFileLocation,
76
+ fileName
77
+ );
78
+
79
+ // Removes first slash if we are in a folder, Mintlify doesn't need it
80
+ return folders ? path.join(folders, fileName).substring(1) : fileName;
81
+ }
@@ -1,12 +1,10 @@
1
- import path from "path";
2
- import axios from "axios";
3
- import { getHtmlWithPuppeteer } from "../browser.js";
4
- import { createPage } from "../util.js";
1
+ import { NavigationEntry, isNavigation } from "../navigation.js";
2
+ import { scrapeFileGettingFileNameFromUrl } from "./scrapeFileGettingFileNameFromUrl.js";
5
3
 
6
4
  export async function scrapeGettingFileNameFromUrl(
5
+ navEntry: NavigationEntry,
7
6
  cliDir: string,
8
7
  origin: string,
9
- pathname: string,
10
8
  overwrite: boolean,
11
9
  scrapePageFunc: (
12
10
  html: string,
@@ -20,62 +18,33 @@ export async function scrapeGettingFileNameFromUrl(
20
18
  }>,
21
19
  puppeteer = false,
22
20
  baseToRemove?: string
23
- ) {
24
- // Skip scraping external links
25
- if (pathname.startsWith("https://") || pathname.startsWith("http://")) {
26
- return pathname;
21
+ ): Promise<NavigationEntry> {
22
+ if (isNavigation(navEntry)) {
23
+ const newPages = [];
24
+ for (const nestedNavEntry of navEntry.pages) {
25
+ newPages.push(
26
+ await scrapeGettingFileNameFromUrl(
27
+ nestedNavEntry,
28
+ cliDir,
29
+ origin,
30
+ overwrite,
31
+ scrapePageFunc,
32
+ puppeteer,
33
+ baseToRemove
34
+ )
35
+ );
36
+ }
37
+ navEntry.pages = newPages;
38
+ return navEntry;
27
39
  }
28
40
 
29
- // Removes file name from the end
30
- const splitSubpath = pathname.split("/");
31
- let folders = splitSubpath.slice(0, splitSubpath.length - 1).join("/");
32
-
33
- // Remove base dir if passed in
34
- if (baseToRemove && folders.startsWith(baseToRemove)) {
35
- folders = folders.replace(baseToRemove, "");
36
- }
37
-
38
- // TO DO: Improve this by putting each page's images in a separate
39
- // folder named after the title of the page.
40
- const imageBaseDir = path.join(cliDir, "images", folders);
41
-
42
- // Scrape each page separately
43
- const href = new URL(pathname, origin).href;
44
- let html: string;
45
- if (puppeteer) {
46
- html = await getHtmlWithPuppeteer(href);
47
- } else {
48
- const res = await axios.default.get(href);
49
- html = res.data;
50
- }
51
-
52
- const { title, description, markdown } = await scrapePageFunc(
53
- html,
54
- origin,
41
+ return await scrapeFileGettingFileNameFromUrl(
42
+ navEntry,
55
43
  cliDir,
56
- imageBaseDir
57
- );
58
-
59
- // Check if page didn't have content
60
- if (!title && !markdown) {
61
- return undefined;
62
- }
63
-
64
- const newFileLocation = folders ? path.join(cliDir, folders) : cliDir;
65
-
66
- // Default to introduction.mdx if we encountered index.html
67
- const fileName = splitSubpath[splitSubpath.length - 1] || "introduction";
68
-
69
- // Will create subfolders as needed
70
- createPage(
71
- title,
72
- description,
73
- markdown,
44
+ origin,
74
45
  overwrite,
75
- newFileLocation,
76
- fileName
46
+ scrapePageFunc,
47
+ puppeteer,
48
+ baseToRemove
77
49
  );
78
-
79
- // Removes first slash if we are in a folder, Mintlify doesn't need it
80
- return folders ? path.join(folders, fileName).substring(1) : fileName;
81
50
  }
@@ -0,0 +1,52 @@
1
+ import axios from "axios";
2
+ import { scrapePage } from "./scrapePage.js";
3
+ import { scrapeDocusaurusPage } from "./site-scrapers/scrapeDocusaurusPage.js";
4
+ import { scrapeGitBookPage } from "./site-scrapers/scrapeGitBookPage.js";
5
+ import { scrapeReadMePage } from "./site-scrapers/scrapeReadMePage.js";
6
+ import { detectFramework, Frameworks } from "./detectFramework.js";
7
+ import { getHrefFromArgs } from "../util.js";
8
+ import { getHtmlWithPuppeteer } from "../browser.js";
9
+
10
+ function validateFramework(framework) {
11
+ if (!framework) {
12
+ console.log(
13
+ "Could not detect the framework automatically. Please use one of:"
14
+ );
15
+ console.log("scrape-page-docusaurus");
16
+ console.log("scrape-page-gitbook");
17
+ console.log("scrape-page-readme");
18
+ return process.exit(1);
19
+ }
20
+ }
21
+
22
+ export async function scrapePageWrapper(argv, scrapeFunc, puppeteer = false) {
23
+ const href = getHrefFromArgs(argv);
24
+ let html: string;
25
+ if (puppeteer) {
26
+ html = await getHtmlWithPuppeteer(href);
27
+ } else {
28
+ const res = await axios.default.get(href);
29
+ html = res.data;
30
+ }
31
+ await scrapePage(scrapeFunc, href, html, argv.overwrite);
32
+ process.exit(0);
33
+ }
34
+
35
+ export async function scrapePageAutomatically(argv: any) {
36
+ const href = getHrefFromArgs(argv);
37
+ const res = await axios.default.get(href);
38
+ const html = res.data;
39
+ const framework = detectFramework(html);
40
+
41
+ validateFramework(framework);
42
+
43
+ console.log("Detected framework: " + framework);
44
+
45
+ if (framework === Frameworks.DOCUSAURUS) {
46
+ await scrapePageWrapper(argv, scrapeDocusaurusPage);
47
+ } else if (framework === Frameworks.GITBOOK) {
48
+ await scrapePageWrapper(argv, scrapeGitBookPage, true);
49
+ } else if (framework === Frameworks.README) {
50
+ await scrapePageWrapper(argv, scrapeReadMePage);
51
+ }
52
+ }
@@ -0,0 +1,110 @@
1
+ import axios from "axios";
2
+ import { detectFramework, Frameworks } from "./detectFramework.js";
3
+ import { getHrefFromArgs, getOrigin } from "../util.js";
4
+ import { scrapeSection } from "./scrapeSection.js";
5
+ import { scrapeDocusaurusSection } from "./site-scrapers/scrapeDocusaurusSection.js";
6
+ import { scrapeGitBookSection } from "./site-scrapers/scrapeGitBookSection.js";
7
+ import { scrapeReadMeSection } from "./site-scrapers/scrapeReadMeSection.js";
8
+ import { startBrowser } from "../browser.js";
9
+
10
+ function validateFramework(framework: Frameworks | undefined) {
11
+ if (!framework) {
12
+ console.log(
13
+ "Could not detect the framework automatically. Please use one of:"
14
+ );
15
+ console.log("scrape-page-docusaurus");
16
+ console.log("scrape-page-gitbook");
17
+ console.log("scrape-page-readme");
18
+ return process.exit(1);
19
+ }
20
+ }
21
+
22
+ export async function scrapeSectionAxiosWrapper(argv: any, scrapeFunc: any) {
23
+ const href = getHrefFromArgs(argv);
24
+ const res = await axios.default.get(href);
25
+ const html = res.data;
26
+ await scrapeSection(scrapeFunc, html, getOrigin(href), argv.overwrite);
27
+ process.exit(0);
28
+ }
29
+
30
+ export async function scrapeGitbookSectionCommand(argv: any) {
31
+ await scrapeSectionGitBookWrapper(argv, scrapeGitBookSection);
32
+ }
33
+
34
+ async function scrapeSectionGitBookWrapper(argv: any, scrapeFunc: any) {
35
+ const href = getHrefFromArgs(argv);
36
+
37
+ const browser = await startBrowser();
38
+ const page = await browser.newPage();
39
+ await page.goto(href, {
40
+ waitUntil: "networkidle2",
41
+ });
42
+
43
+ let prevEncountered: string[] = [];
44
+ let encounteredHref = ["fake"];
45
+
46
+ // Loop until we've encountered every link
47
+ while (!encounteredHref.every((href) => prevEncountered.includes(href))) {
48
+ prevEncountered = encounteredHref;
49
+ encounteredHref = await page.evaluate(
50
+ (encounteredHref) => {
51
+ const icons: HTMLElement[] = Array.from(
52
+ document.querySelectorAll('path[d="M9 18l6-6-6-6"]')
53
+ );
54
+
55
+ const linksFound: string[] = [];
56
+ icons.forEach(async (icon: HTMLElement) => {
57
+ const toClick = icon?.parentElement?.parentElement;
58
+ const link = toClick?.parentElement?.parentElement;
59
+
60
+ // Skip icons not in the side navigation
61
+ if (!link?.hasAttribute("href")) {
62
+ return;
63
+ }
64
+
65
+ const href = link.getAttribute("href");
66
+
67
+ // Should never occur but we keep it as a fail-safe
68
+ if (href?.startsWith("https://") || href?.startsWith("http://")) {
69
+ return;
70
+ }
71
+
72
+ // Click any links we haven't seen before
73
+ if (href && !encounteredHref.includes(href)) {
74
+ toClick?.click();
75
+ }
76
+ if (href) {
77
+ linksFound.push(href);
78
+ }
79
+ });
80
+
81
+ return linksFound;
82
+ },
83
+ encounteredHref // Need to pass array into the browser
84
+ );
85
+ }
86
+
87
+ const html = await page.content();
88
+ browser.close();
89
+ await scrapeSection(scrapeFunc, html, getOrigin(href), argv.overwrite);
90
+ process.exit(0);
91
+ }
92
+
93
+ export async function scrapeSectionAutomatically(argv: any) {
94
+ const href = getHrefFromArgs(argv);
95
+ const res = await axios.default.get(href);
96
+ const html = res.data;
97
+ const framework = detectFramework(html);
98
+
99
+ validateFramework(framework);
100
+
101
+ console.log("Detected framework: " + framework);
102
+
103
+ if (framework === Frameworks.DOCUSAURUS) {
104
+ await scrapeSectionAxiosWrapper(argv, scrapeDocusaurusSection);
105
+ } else if (framework === Frameworks.GITBOOK) {
106
+ await scrapeGitbookSectionCommand(argv);
107
+ } else if (framework === Frameworks.README) {
108
+ await scrapeSectionAxiosWrapper(argv, scrapeReadMeSection);
109
+ }
110
+ }
@@ -0,0 +1,40 @@
1
+ // Used by GitBook and ReadMe section scrapers
2
+ export default function getLinksRecursively(linkSections: any, $: any) {
3
+ if (linkSections == null || linkSections.length === 0) {
4
+ return [];
5
+ }
6
+
7
+ return linkSections
8
+ .map((i, s) => {
9
+ const subsection = $(s);
10
+ const link = subsection.children().first();
11
+
12
+ const linkHref = link.attr("href");
13
+
14
+ // Skip missing links. For example, GitBook uses
15
+ // empty divs are used for styling a line beside the nav.
16
+ // Skip external links until Mintlify supports them
17
+ if (
18
+ !linkHref ||
19
+ linkHref.startsWith("https://") ||
20
+ linkHref.startsWith("http://")
21
+ ) {
22
+ return undefined;
23
+ }
24
+
25
+ const childLinks = subsection.children().eq(1).children();
26
+
27
+ if (childLinks.length > 0) {
28
+ // Put the section link in the list of pages.
29
+ // When we support the section itself being a link we should update this
30
+ return {
31
+ group: link.text(),
32
+ pages: [linkHref, ...getLinksRecursively(childLinks, $)],
33
+ };
34
+ }
35
+
36
+ return linkHref;
37
+ })
38
+ .toArray()
39
+ .filter(Boolean);
40
+ }
@@ -59,9 +59,9 @@ export async function scrapeDocusaurusSection(
59
59
  // Docusaurus requires a directory on all sections wheras we use root.
60
60
  // /docs is their default directory so we remove it
61
61
  scrapeGettingFileNameFromUrl(
62
+ pathname,
62
63
  cliDir,
63
64
  origin,
64
- pathname,
65
65
  overwrite,
66
66
  scrapeDocusaurusPage,
67
67
  false,
@@ -1,7 +1,9 @@
1
1
  import cheerio from "cheerio";
2
+ import { NavigationEntry } from "../../navigation.js";
2
3
  import { scrapeGettingFileNameFromUrl } from "../scrapeGettingFileNameFromUrl.js";
3
- import { getSitemapLinks } from "../getSitemapLinks.js";
4
4
  import { scrapeGitBookPage } from "./scrapeGitBookPage.js";
5
+ import combineNavWithEmptyGroupTitles from "../combineNavWithEmptyGroupTitles.js";
6
+ import getLinksRecursively from "./getLinksRecursively.js";
5
7
 
6
8
  export async function scrapeGitBookSection(
7
9
  html: string,
@@ -22,95 +24,54 @@ export async function scrapeGitBookSection(
22
24
  .children();
23
25
 
24
26
  // Get all links per group
25
- let allNavPathnames = [];
26
27
  const groupsConfig = navigationSections
27
- .map((i, section) => {
28
+ .map((i, s) => {
29
+ const section = $(s);
28
30
  const sectionTitle = $(section)
29
31
  .find('div > div[dir="auto"]')
30
32
  .first()
31
33
  .text();
32
34
 
33
- const linkPaths = $(section)
34
- .find("a[href]")
35
- .map((i, link) => {
36
- const linkHref = $(link).attr("href");
35
+ // Only present if the nested navigation is not in a group
36
+ const firstLink = section.children().eq(0);
37
+ const firstHref = firstLink.attr("href");
37
38
 
38
- // Skip external links until Mintlify supports them
39
- if (
40
- linkHref.startsWith("https://") ||
41
- linkHref.startsWith("http://")
42
- ) {
43
- return undefined;
44
- }
39
+ const linkSections = section.children().eq(1).children();
40
+ const pages = getLinksRecursively(linkSections, $);
45
41
 
46
- return linkHref;
47
- })
48
- .toArray();
49
-
50
- allNavPathnames = allNavPathnames.concat(linkPaths);
51
-
52
- // Follows the same structure as mint.json
53
42
  return {
54
- group: sectionTitle,
55
- pages: linkPaths,
43
+ group: sectionTitle || alternateTitle(firstLink, pages),
44
+ pages: firstHref ? [firstHref, ...pages] : pages,
56
45
  };
57
46
  })
58
- .toArray();
47
+ .toArray()
48
+ .filter(Boolean);
59
49
 
60
- // Scrape every link not in the navigation. Nested docs
61
- // don't show up in navigation without clicking buttons,
62
- // so this lets us download the files for the user to add
63
- // manually to mint.json.
64
- const sitemapPaths = (await getSitemapLinks(new URL("sitemap.xml", origin)))
65
- .map((sitemapLinks: string) => {
66
- return new URL(sitemapLinks).pathname;
67
- })
68
- .filter((pathname: string) => !allNavPathnames.includes(pathname));
50
+ // Merge groups with empty titles together
51
+ const reducedGroupsConfig = combineNavWithEmptyGroupTitles(groupsConfig);
69
52
 
70
- const sitemapPathnamesForConfig = [];
71
- for (const pathname of sitemapPaths) {
72
- sitemapPathnamesForConfig.push(
73
- await scrapeGettingFileNameFromUrl(
53
+ // Scrape each link in the navigation.
54
+ const groupsConfigCleanPaths = await Promise.all(
55
+ reducedGroupsConfig.map(async (navEntry: NavigationEntry) => {
56
+ return await scrapeGettingFileNameFromUrl(
57
+ navEntry,
74
58
  cliDir,
75
59
  origin,
76
- pathname,
77
60
  overwrite,
78
61
  scrapeGitBookPage,
79
62
  true
80
- )
81
- );
82
- }
83
-
84
- // Scrape each link in the navigation.
85
- const groupsConfigCleanPaths = await Promise.all(
86
- groupsConfig.map(async (groupConfig) => {
87
- const newPages = [];
88
- for (const pathname of groupConfig.pages) {
89
- newPages.push(
90
- await scrapeGettingFileNameFromUrl(
91
- cliDir,
92
- origin,
93
- pathname,
94
- overwrite,
95
- scrapeGitBookPage,
96
- true
97
- )
98
- );
99
- }
100
- groupConfig.pages = newPages;
101
- return groupConfig;
63
+ );
102
64
  })
103
65
  );
104
66
 
105
- if (sitemapPathnamesForConfig.length > 0) {
106
- return groupsConfigCleanPaths.concat([
107
- {
108
- group:
109
- "ATTENTION! WE CANNOT DETECT GROUPS FOR NESTED DOCS. PLEASE MOVE THEM INTO THEIR ORIGINAL GROUPS.",
110
- pages: sitemapPathnamesForConfig,
111
- },
112
- ]);
113
- }
114
-
115
67
  return groupsConfigCleanPaths;
116
68
  }
69
+
70
+ function alternateTitle(firstLink, pages) {
71
+ // Only assign titles to nested navigation menus outside a section.
72
+ // Others should not have a title so we can merge them into one section.
73
+ if (pages.length > 0) {
74
+ return firstLink?.text();
75
+ }
76
+ return "";
77
+ }
@@ -22,7 +22,9 @@ export async function scrapeReadMePage(
22
22
  if (content.length === 0) {
23
23
  content = $(".rm-Article > .markdown-body");
24
24
  }
25
- const contentHtml = content.html();
25
+
26
+ // API Pages don't have a markdown body in the same position so there's no HTML
27
+ let contentHtml = content.html() || "";
26
28
 
27
29
  const origToWritePath = await downloadAllImages(
28
30
  $,
@@ -1,6 +1,8 @@
1
1
  import cheerio from "cheerio";
2
2
  import { scrapeReadMePage } from "./scrapeReadMePage.js";
3
3
  import { scrapeGettingFileNameFromUrl } from "../scrapeGettingFileNameFromUrl.js";
4
+ import getLinksRecursively from "./getLinksRecursively.js";
5
+ import { NavigationEntry } from "../../navigation.js";
4
6
 
5
7
  export async function scrapeReadMeSection(
6
8
  html: string,
@@ -18,60 +20,41 @@ export async function scrapeReadMeSection(
18
20
  .find(".rm-Sidebar-section");
19
21
 
20
22
  const groupsConfig = navigationSections
21
- .map((i, section) => {
22
- const sectionTitle = $(section).find("h3").first().text();
23
+ .map((i, s) => {
24
+ const section = $(s);
25
+ const sectionTitle = section.find("h3").first().text();
23
26
 
24
27
  // Get all links, then use filter to remove duplicates.
25
28
  // There are duplicates because of nested navigation, eg:
26
29
  // subgroupTitle -> /first-page
27
30
  // -- First Page -> /first-page ** DUPLICATE **
28
31
  // -- Second Page -> /second-page
29
- const linkPaths = $(section)
30
- .find("a[href]")
31
- .map((i, link) => {
32
- const linkHref = $(link).attr("href");
33
-
34
- // Skip external links until Mintlify supports them
35
- if (
36
- linkHref.startsWith("https://") ||
37
- linkHref.startsWith("http://")
38
- ) {
39
- return undefined;
40
- }
41
-
42
- return linkHref;
43
- })
44
- .toArray()
45
- .filter(
46
- (value: string, index: number, self) => self.indexOf(value) === index
47
- );
32
+ const linkSections = section.find(".rm-Sidebar-list").first().children();
33
+ const pages = getLinksRecursively(linkSections, $).filter(
34
+ (value: string, index: number, self) => self.indexOf(value) === index
35
+ );
48
36
 
49
37
  // Follows the same structure as mint.json
50
38
  return {
51
39
  group: sectionTitle,
52
- pages: linkPaths,
40
+ pages: pages,
53
41
  };
54
42
  })
55
43
  .toArray();
56
44
 
57
45
  return await Promise.all(
58
- groupsConfig.map(async (groupConfig) => {
59
- groupConfig.pages = await Promise.all(
60
- groupConfig.pages.map(async (pathname: string) =>
61
- // ReadMe requires a directory on all sections wheras we use root.
62
- // /docs is their default directory so we remove it
63
- scrapeGettingFileNameFromUrl(
64
- cliDir,
65
- origin,
66
- pathname,
67
- overwrite,
68
- scrapeReadMePage,
69
- false,
70
- "/docs"
71
- )
72
- )
46
+ groupsConfig.map(async (navEntry: NavigationEntry) => {
47
+ return await scrapeGettingFileNameFromUrl(
48
+ // ReadMe requires a directory on all sections wheras we use root.
49
+ // /docs is their default directory so we remove it
50
+ navEntry,
51
+ cliDir,
52
+ origin,
53
+ overwrite,
54
+ scrapeReadMePage,
55
+ false,
56
+ "/docs"
73
57
  );
74
- return groupConfig;
75
58
  })
76
59
  );
77
60
  }