erudit 2.0.0-dev.7

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 (314) hide show
  1. package/.nuxtrc +1 -0
  2. package/app/app.vue +172 -0
  3. package/app/assets/icons/alert.svg +3 -0
  4. package/app/assets/icons/angle-right.svg +3 -0
  5. package/app/assets/icons/arrow-in-text.svg +3 -0
  6. package/app/assets/icons/arrow-left.svg +3 -0
  7. package/app/assets/icons/arrow-up-to-right.svg +3 -0
  8. package/app/assets/icons/aside-open.svg +3 -0
  9. package/app/assets/icons/asterisk.svg +3 -0
  10. package/app/assets/icons/book-question.svg +3 -0
  11. package/app/assets/icons/book.svg +3 -0
  12. package/app/assets/icons/bug.svg +3 -0
  13. package/app/assets/icons/chip.svg +3 -0
  14. package/app/assets/icons/circle-help.svg +3 -0
  15. package/app/assets/icons/construction.svg +3 -0
  16. package/app/assets/icons/cross.svg +3 -0
  17. package/app/assets/icons/draw.svg +3 -0
  18. package/app/assets/icons/ellipsis-vertical.svg +3 -0
  19. package/app/assets/icons/file-check.svg +3 -0
  20. package/app/assets/icons/file-lines.svg +3 -0
  21. package/app/assets/icons/file-star.svg +3 -0
  22. package/app/assets/icons/folder-open.svg +3 -0
  23. package/app/assets/icons/folder.svg +3 -0
  24. package/app/assets/icons/globe.svg +3 -0
  25. package/app/assets/icons/house.svg +3 -0
  26. package/app/assets/icons/link-external.svg +3 -0
  27. package/app/assets/icons/link.svg +3 -0
  28. package/app/assets/icons/moon.svg +3 -0
  29. package/app/assets/icons/outline/book.svg +3 -0
  30. package/app/assets/icons/outline/file-lines.svg +3 -0
  31. package/app/assets/icons/pirate.svg +3 -0
  32. package/app/assets/icons/plus-circle.svg +3 -0
  33. package/app/assets/icons/plus.svg +3 -0
  34. package/app/assets/icons/puzzle.svg +3 -0
  35. package/app/assets/icons/search.svg +3 -0
  36. package/app/assets/icons/shuffle.svg +3 -0
  37. package/app/assets/icons/star.svg +3 -0
  38. package/app/assets/icons/sun-moon.svg +3 -0
  39. package/app/assets/icons/sun.svg +3 -0
  40. package/app/assets/icons/user.svg +3 -0
  41. package/app/assets/icons/users.svg +3 -0
  42. package/app/components/Loading.vue +23 -0
  43. package/app/components/SiteAside.vue +382 -0
  44. package/app/components/SiteMain.vue +35 -0
  45. package/app/components/ads/BannerTemplate.vue +51 -0
  46. package/app/components/ads/BottomBanner.vue +45 -0
  47. package/app/components/ads/LeftBanner.vue +50 -0
  48. package/app/components/aside/AsideListItem.vue +74 -0
  49. package/app/components/aside/AsideMajor.vue +56 -0
  50. package/app/components/aside/AsideMinor.vue +71 -0
  51. package/app/components/aside/major/PaneContentScroll.vue +23 -0
  52. package/app/components/aside/major/PaneSwitch.vue +54 -0
  53. package/app/components/aside/major/PaneSwitchButton.vue +63 -0
  54. package/app/components/aside/major/SiteInfo.vue +85 -0
  55. package/app/components/aside/major/panes/Language.vue +79 -0
  56. package/app/components/aside/major/panes/Pages.vue +34 -0
  57. package/app/components/aside/major/panes/Search.vue +3 -0
  58. package/app/components/aside/major/panes/nav/Nav.vue +91 -0
  59. package/app/components/aside/major/panes/nav/NavBook.vue +86 -0
  60. package/app/components/aside/major/panes/nav/NavBookLoading.vue +24 -0
  61. package/app/components/aside/major/panes/nav/NavGlobal.vue +16 -0
  62. package/app/components/aside/major/panes/nav/fnav/FNav.vue +105 -0
  63. package/app/components/aside/major/panes/nav/fnav/FNavBook.vue +32 -0
  64. package/app/components/aside/major/panes/nav/fnav/FNavFlags.vue +40 -0
  65. package/app/components/aside/major/panes/nav/fnav/FNavFolder.vue +60 -0
  66. package/app/components/aside/major/panes/nav/fnav/FNavItem.vue +34 -0
  67. package/app/components/aside/major/panes/nav/fnav/FNavSeparator.vue +80 -0
  68. package/app/components/aside/major/panes/nav/fnav/FNavTopic.vue +24 -0
  69. package/app/components/aside/major/panes/other/ItemContent.vue +29 -0
  70. package/app/components/aside/major/panes/other/ItemGenerator.vue +15 -0
  71. package/app/components/aside/major/panes/other/ItemTheme.vue +54 -0
  72. package/app/components/aside/major/panes/other/Other.vue +16 -0
  73. package/app/components/aside/minor/AsideMinorContributor.vue +5 -0
  74. package/app/components/aside/minor/AsideMinorNews.vue +11 -0
  75. package/app/components/aside/minor/AsideMinorPane.vue +16 -0
  76. package/app/components/aside/minor/AsideMinorTopLink.vue +67 -0
  77. package/app/components/aside/minor/Contribute.vue +145 -0
  78. package/app/components/aside/minor/content/AsideMinorContent.vue +92 -0
  79. package/app/components/aside/minor/topic/AsideMinorTopic.vue +32 -0
  80. package/app/components/aside/minor/topic/TopicContributors.vue +177 -0
  81. package/app/components/aside/minor/topic/TopicNav.vue +49 -0
  82. package/app/components/aside/minor/topic/TopicToc.vue +202 -0
  83. package/app/components/aside/minor/topic/TopicTocItem.vue +31 -0
  84. package/app/components/aside/utils/AsideOverlayPane.vue +40 -0
  85. package/app/components/bitran/BitranContent.vue +64 -0
  86. package/app/components/bitran/RenderWrapper.vue +12 -0
  87. package/app/components/contributor/ContributorAvatar.vue +43 -0
  88. package/app/components/contributor/ContributorListItem.vue +35 -0
  89. package/app/components/main/topic/MainTopic.vue +79 -0
  90. package/app/components/main/topic/TopicPartSwitch.vue +118 -0
  91. package/app/components/main/utils/Breadcrumb.vue +75 -0
  92. package/app/components/main/utils/ContentDecoration.vue +29 -0
  93. package/app/components/main/utils/ContentDescription.vue +20 -0
  94. package/app/components/main/utils/ContentFlag.vue +16 -0
  95. package/app/components/main/utils/ContentPopover.vue +176 -0
  96. package/app/components/main/utils/ContentPopovers.vue +105 -0
  97. package/app/components/main/utils/ContentReferences.vue +76 -0
  98. package/app/components/main/utils/ContentSection.vue +42 -0
  99. package/app/components/main/utils/ContentTitle.vue +37 -0
  100. package/app/components/main/utils/reference/ReferenceGroup.vue +41 -0
  101. package/app/components/main/utils/reference/ReferenceItem.vue +64 -0
  102. package/app/components/main/utils/reference/ReferenceSource.vue +110 -0
  103. package/app/components/preview/Preview.vue +177 -0
  104. package/app/components/preview/PreviewDisplay.vue +139 -0
  105. package/app/components/preview/PreviewFooterAction.vue +73 -0
  106. package/app/components/preview/PreviewLoading.vue +15 -0
  107. package/app/components/preview/PreviewScreen.vue +99 -0
  108. package/app/components/preview/display/Alert.vue +50 -0
  109. package/app/components/preview/display/Custom.vue +18 -0
  110. package/app/components/preview/display/GenericLink.vue +48 -0
  111. package/app/components/preview/display/PageLink.vue +20 -0
  112. package/app/components/preview/display/Unique.vue +49 -0
  113. package/app/components/transition/Fade.vue +22 -0
  114. package/app/components/tree/TreeContainer.vue +12 -0
  115. package/app/components/tree/TreeItem.vue +89 -0
  116. package/app/composables/bitran.ts +132 -0
  117. package/app/composables/bitranContent.ts +36 -0
  118. package/app/composables/bitranLocation.ts +7 -0
  119. package/app/composables/contentData.ts +36 -0
  120. package/app/composables/contentPage.ts +156 -0
  121. package/app/composables/contentRoute.ts +45 -0
  122. package/app/composables/darkMagic.ts +24 -0
  123. package/app/composables/externalApi.ts +63 -0
  124. package/app/composables/favicon.ts +8 -0
  125. package/app/composables/formatText.ts +86 -0
  126. package/app/composables/majorPane.ts +60 -0
  127. package/app/composables/phrases.ts +80 -0
  128. package/app/composables/theme.ts +29 -0
  129. package/app/composables/url.ts +33 -0
  130. package/app/pages/_test/preview.vue +110 -0
  131. package/app/pages/article/[...articleId].vue +3 -0
  132. package/app/pages/book/[...bookId].vue +47 -0
  133. package/app/pages/group/[...groupId].vue +64 -0
  134. package/app/pages/index.vue +32 -0
  135. package/app/pages/members.vue +7 -0
  136. package/app/pages/practice/[...practice].vue +3 -0
  137. package/app/pages/summary/[...summaryId].vue +3 -0
  138. package/app/public/favicon/article.svg +10 -0
  139. package/app/public/favicon/default.svg +10 -0
  140. package/app/public/favicon/practice.svg +10 -0
  141. package/app/public/favicon/summary.svg +10 -0
  142. package/app/public/logotype.svg +17 -0
  143. package/app/public/og-default.png +0 -0
  144. package/app/public/user.svg +10 -0
  145. package/app/scripts/_immediate.js +4 -0
  146. package/app/scripts/aside/index.ts +59 -0
  147. package/app/scripts/aside/major/nav.ts +26 -0
  148. package/app/scripts/aside/minor/state.ts +37 -0
  149. package/app/scripts/aside/minor/topic.ts +3 -0
  150. package/app/scripts/flag.ts +28 -0
  151. package/app/scripts/og.ts +27 -0
  152. package/app/scripts/preview/build.ts +84 -0
  153. package/app/scripts/preview/data/alert.ts +19 -0
  154. package/app/scripts/preview/data/custom.ts +8 -0
  155. package/app/scripts/preview/data/genericLink.ts +24 -0
  156. package/app/scripts/preview/data/pageLink.ts +20 -0
  157. package/app/scripts/preview/data/unique.ts +70 -0
  158. package/app/scripts/preview/data.ts +26 -0
  159. package/app/scripts/preview/display.ts +39 -0
  160. package/app/scripts/preview/footer.ts +9 -0
  161. package/app/scripts/preview/request.ts +51 -0
  162. package/app/scripts/preview/state.ts +63 -0
  163. package/app/styles/_immediate.css +3 -0
  164. package/app/styles/_util.scss +50 -0
  165. package/app/styles/_utils.scss +44 -0
  166. package/app/styles/app.scss +91 -0
  167. package/app/styles/def/_bp.scss +24 -0
  168. package/app/styles/def/_size.scss +7 -0
  169. package/app/styles/def/_z.scss +5 -0
  170. package/app/styles/default.scss +85 -0
  171. package/app/styles/normalize.scss +63 -0
  172. package/app/styles/partials/_darkMagic.scss +7 -0
  173. package/app/styles/partials/_fnav.scss +18 -0
  174. package/app/styles/partials/_preview.scss +7 -0
  175. package/globalPath.ts +24 -0
  176. package/globals/bitran.ts +39 -0
  177. package/globals/content.ts +22 -0
  178. package/globals/contributor.ts +5 -0
  179. package/globals/erudit.ts +5 -0
  180. package/globals/register.ts +18 -0
  181. package/languages/en.ts +95 -0
  182. package/languages/ru.ts +99 -0
  183. package/module/bitran.ts +34 -0
  184. package/module/config.ts +34 -0
  185. package/module/imports.ts +46 -0
  186. package/module/index.ts +35 -0
  187. package/module/logger.ts +10 -0
  188. package/module/paths.ts +22 -0
  189. package/module/restart.ts +61 -0
  190. package/nuxt.config.ts +96 -0
  191. package/package.json +32 -0
  192. package/server/api/aside/major/nav/bookIds.ts +5 -0
  193. package/server/api/aside/major/nav/bookNav/[...bookId].ts +20 -0
  194. package/server/api/aside/major/nav/global.ts +7 -0
  195. package/server/api/aside/minor/news.ts +7 -0
  196. package/server/api/aside/minor/path.ts +78 -0
  197. package/server/api/bitran/content/[location].ts +7 -0
  198. package/server/api/bitran/toc/[location].ts +7 -0
  199. package/server/api/content/data.ts +72 -0
  200. package/server/api/contributor/count.ts +6 -0
  201. package/server/api/fake/content.ts +11 -0
  202. package/server/api/fake/shared/languages.ts +12 -0
  203. package/server/api/language/functions.ts +12 -0
  204. package/server/api/language/phrase/[phraseId].ts +19 -0
  205. package/server/api/language/phraseIds.ts +8 -0
  206. package/server/api/preview/page/[...parts].ts +51 -0
  207. package/server/api/preview/unique/[location].ts +55 -0
  208. package/server/plugin/bitran/content.ts +176 -0
  209. package/server/plugin/bitran/core.ts +51 -0
  210. package/server/plugin/bitran/location.ts +25 -0
  211. package/server/plugin/bitran/products/include.ts +229 -0
  212. package/server/plugin/bitran/products/link.ts +114 -0
  213. package/server/plugin/bitran/setup.ts +10 -0
  214. package/server/plugin/bitran/toc.ts +82 -0
  215. package/server/plugin/build/close.ts +10 -0
  216. package/server/plugin/build/jobs/content/builderArgs.ts +8 -0
  217. package/server/plugin/build/jobs/content/generic.ts +176 -0
  218. package/server/plugin/build/jobs/content/parse.ts +90 -0
  219. package/server/plugin/build/jobs/content/path.ts +6 -0
  220. package/server/plugin/build/jobs/content/type/book.ts +9 -0
  221. package/server/plugin/build/jobs/content/type/group.ts +37 -0
  222. package/server/plugin/build/jobs/content/type/topic.ts +36 -0
  223. package/server/plugin/build/jobs/contributors.ts +66 -0
  224. package/server/plugin/build/jobs/language.ts +36 -0
  225. package/server/plugin/build/jobs/nav.ts +209 -0
  226. package/server/plugin/build/process.ts +25 -0
  227. package/server/plugin/build/rebuild.ts +55 -0
  228. package/server/plugin/build/setup.ts +21 -0
  229. package/server/plugin/content/absoluteId.ts +94 -0
  230. package/server/plugin/content/context.ts +112 -0
  231. package/server/plugin/db/entities/Book.ts +7 -0
  232. package/server/plugin/db/entities/Content.ts +49 -0
  233. package/server/plugin/db/entities/Contribution.ts +10 -0
  234. package/server/plugin/db/entities/Contributor.ts +16 -0
  235. package/server/plugin/db/entities/Group.ts +14 -0
  236. package/server/plugin/db/entities/Hash.ts +15 -0
  237. package/server/plugin/db/entities/Topic.ts +20 -0
  238. package/server/plugin/db/entities/Unique.ts +21 -0
  239. package/server/plugin/db/setup.ts +34 -0
  240. package/server/plugin/global.ts +18 -0
  241. package/server/plugin/importer.ts +12 -0
  242. package/server/plugin/index.ts +9 -0
  243. package/server/plugin/logger.ts +23 -0
  244. package/server/plugin/nav/node.ts +26 -0
  245. package/server/plugin/nav/utils.ts +129 -0
  246. package/server/plugin/repository/book.ts +21 -0
  247. package/server/plugin/repository/content.ts +238 -0
  248. package/server/plugin/repository/contributor.ts +8 -0
  249. package/server/plugin/repository/frontNav.ts +148 -0
  250. package/server/plugin/repository/topic.ts +32 -0
  251. package/server/tsconfig.json +7 -0
  252. package/shared/aside/minor.ts +50 -0
  253. package/shared/asset.ts +15 -0
  254. package/shared/bitran/alias.ts +17 -0
  255. package/shared/bitran/context.ts +7 -0
  256. package/shared/bitran/location.ts +166 -0
  257. package/shared/bitran/products/alias/core/factory.ts +46 -0
  258. package/shared/bitran/products/alias/core/index.ts +13 -0
  259. package/shared/bitran/products/alias/render/Alias.vue +10 -0
  260. package/shared/bitran/products/alias/render/icon.svg +3 -0
  261. package/shared/bitran/products/alias/render/index.ts +17 -0
  262. package/shared/bitran/products/alias/render/languages/en.ts +5 -0
  263. package/shared/bitran/products/alias/render/languages/ru.ts +5 -0
  264. package/shared/bitran/products/alias/shared.ts +11 -0
  265. package/shared/bitran/products/heading/core/factory.ts +53 -0
  266. package/shared/bitran/products/heading/core/index.ts +19 -0
  267. package/shared/bitran/products/heading/render/Heading.vue +47 -0
  268. package/shared/bitran/products/heading/render/icon.svg +3 -0
  269. package/shared/bitran/products/heading/render/index.ts +17 -0
  270. package/shared/bitran/products/heading/render/languages/en.ts +5 -0
  271. package/shared/bitran/products/heading/render/languages/ru.ts +5 -0
  272. package/shared/bitran/products/heading/shared.ts +13 -0
  273. package/shared/bitran/products/include/core/factory.ts +61 -0
  274. package/shared/bitran/products/include/core/index.ts +13 -0
  275. package/shared/bitran/products/include/render/Include.vue +13 -0
  276. package/shared/bitran/products/include/render/icon.svg +3 -0
  277. package/shared/bitran/products/include/render/index.ts +18 -0
  278. package/shared/bitran/products/include/render/languages/en.ts +5 -0
  279. package/shared/bitran/products/include/render/languages/ru.ts +5 -0
  280. package/shared/bitran/products/include/shared.ts +15 -0
  281. package/shared/bitran/products/link/core/factory.ts +20 -0
  282. package/shared/bitran/products/link/core/index.ts +17 -0
  283. package/shared/bitran/products/link/render/Link.vue +174 -0
  284. package/shared/bitran/products/link/render/icon.svg +3 -0
  285. package/shared/bitran/products/link/render/index.ts +17 -0
  286. package/shared/bitran/products/link/render/languages/en.ts +5 -0
  287. package/shared/bitran/products/link/render/languages/ru.ts +5 -0
  288. package/shared/bitran/products/link/shared.ts +15 -0
  289. package/shared/bitran/products/link/target.ts +134 -0
  290. package/shared/bitran/toc.ts +8 -0
  291. package/shared/content/context.ts +9 -0
  292. package/shared/content/data/base.ts +32 -0
  293. package/shared/content/data/index.ts +5 -0
  294. package/shared/content/data/type/book.ts +5 -0
  295. package/shared/content/data/type/group.ts +6 -0
  296. package/shared/content/data/type/topic.ts +11 -0
  297. package/shared/content/previousNext.ts +9 -0
  298. package/shared/contributor.ts +5 -0
  299. package/shared/frontNav.ts +41 -0
  300. package/shared/icons.ts +38 -0
  301. package/shared/image.ts +5 -0
  302. package/shared/link.ts +25 -0
  303. package/shared/types/language.ts +75 -0
  304. package/shared/utils/objectsEqual.ts +4 -0
  305. package/shared/utils/stringColor.ts +9 -0
  306. package/test/bitran/alias.test.ts +44 -0
  307. package/test/bitran/location.test.ts +143 -0
  308. package/test/bitran/products/alias.test.ts +83 -0
  309. package/test/bitran/products/heading.test.ts +119 -0
  310. package/test/bitran/products/include.test.ts +77 -0
  311. package/test/bitran/products/link/factory.test.ts +30 -0
  312. package/test/bitran/products/link/target.test.ts +138 -0
  313. package/tsconfig.json +8 -0
  314. package/utils/stress.ts +9 -0
@@ -0,0 +1,34 @@
1
+ import { DataSource } from 'typeorm';
2
+
3
+ import { PROJECT_DIR } from '#erudit/globalPaths';
4
+ import { ERUDIT_SERVER } from '@server/global';
5
+
6
+ // Database Entities
7
+ import { DbContributor } from './entities/Contributor';
8
+ import { DbBook } from './entities/Book';
9
+ import { DbContent } from './entities/Content';
10
+ import { DbContribution } from './entities/Contribution';
11
+ import { DbGroup } from './entities/Group';
12
+ import { DbHash } from './entities/Hash';
13
+ import { DbTopic } from './entities/Topic';
14
+ import { DbUnique } from './entities/Unique';
15
+
16
+ export async function setupDatabase() {
17
+ ERUDIT_SERVER.DB = new DataSource({
18
+ type: 'sqlite',
19
+ database: PROJECT_DIR + '/.erudit/data.sqlite',
20
+ synchronize: true,
21
+ entities: [
22
+ DbBook,
23
+ DbContent,
24
+ DbContribution,
25
+ DbContributor,
26
+ DbGroup,
27
+ DbHash,
28
+ DbTopic,
29
+ DbUnique,
30
+ ],
31
+ });
32
+
33
+ await ERUDIT_SERVER.DB.initialize();
34
+ }
@@ -0,0 +1,18 @@
1
+ import type { DataSource } from 'typeorm';
2
+ import type { EruditConfig } from 'erudit-cog/schema';
3
+
4
+ import type { EruditPhrases } from '@shared/types/language';
5
+ import type { NavNode, RootNavNode } from '@server/nav/node';
6
+ import type { EruditBitranConfig } from '@erudit/globals/bitran';
7
+
8
+ interface EruditServer {
9
+ BUILD_PROMISE: Promise<void>;
10
+ CONFIG: Partial<EruditConfig>;
11
+ LANGUAGE: { phrases: EruditPhrases; functions: Record<string, Function> };
12
+ DB: DataSource;
13
+ NAV?: RootNavNode;
14
+ NAV_BOOKS?: Record<string, NavNode>;
15
+ BITRAN_CONFIG?: Partial<EruditBitranConfig>;
16
+ }
17
+
18
+ export const ERUDIT_SERVER: EruditServer = {} as any;
@@ -0,0 +1,12 @@
1
+ import { createJiti } from 'jiti';
2
+ import { ERUDIT_DIR } from '#erudit/globalPaths';
3
+
4
+ const jiti = createJiti(ERUDIT_DIR, {
5
+ // Enable reimporting same files during process in development mode
6
+ fsCache: !import.meta.dev,
7
+ moduleCache: !import.meta.dev,
8
+ });
9
+
10
+ export async function IMPORT(...args: Parameters<typeof jiti.import>) {
11
+ return jiti.import(...args) as Promise<any>;
12
+ }
@@ -0,0 +1,9 @@
1
+ import { ERUDIT_SERVER } from './global';
2
+ import { build } from './build/process';
3
+ import { close } from './build/close';
4
+
5
+ export default defineNitroPlugin((_nitro) => {
6
+ ERUDIT_SERVER.BUILD_PROMISE = build();
7
+ _nitro.hooks.hook('request', async () => await ERUDIT_SERVER.BUILD_PROMISE);
8
+ _nitro.hooks.hook('close', close);
9
+ });
@@ -0,0 +1,23 @@
1
+ import chalk from 'chalk';
2
+ import { createConsola } from 'consola';
3
+ import { brandColorTitle } from 'erudit-cog/utils/brand';
4
+
5
+ import eruditConfig from '#erudit/config';
6
+
7
+ // TODO: Consola has buggy behaviour when called inside Nitro
8
+ // @see https://github.com/nitrojs/nitro/issues/3079
9
+
10
+ export const logger = createConsola({
11
+ defaults: {
12
+ //tag: brandColorTitle + ' Server',
13
+ message: chalk.gray(`[${brandColorTitle} Server]`),
14
+ },
15
+ });
16
+
17
+ export const debug = createConsola({
18
+ defaults: {
19
+ //tag: brandColorTitle + ' Server Debug',
20
+ message: chalk.gray(`[${brandColorTitle} Server Debug]`),
21
+ level: eruditConfig?.debug?.log ? 3 : -999,
22
+ },
23
+ });
@@ -0,0 +1,26 @@
1
+ import type { ContentType } from 'erudit-cog/schema';
2
+
3
+ export interface NavNode {
4
+ type: ContentType;
5
+ path: string;
6
+ id: string;
7
+ fullId: string;
8
+ skip: boolean;
9
+ children?: NavNode[];
10
+ parent?: NavNode | RootNavNode;
11
+ }
12
+
13
+ export interface RootNavNode {
14
+ type: '#root';
15
+ children?: NavNode[];
16
+ }
17
+
18
+ export function isRootNode(node: NavNode | RootNavNode) {
19
+ return node?.type === '#root';
20
+ }
21
+
22
+ export function createRootNode(): RootNavNode {
23
+ return {
24
+ type: '#root',
25
+ };
26
+ }
@@ -0,0 +1,129 @@
1
+ import { ERUDIT_SERVER } from '@server/global';
2
+ import { isRootNode, type NavNode } from '@server/nav/node';
3
+
4
+ export async function walkNav(
5
+ step: (node: NavNode) => Promise<void | false>,
6
+ from: NavNode | typeof ERUDIT_SERVER.NAV = ERUDIT_SERVER.NAV,
7
+ ) {
8
+ if (!from) return;
9
+
10
+ const stepResult = isRootNode(from) ? true : await step(from);
11
+
12
+ if (stepResult !== false)
13
+ if (from.children)
14
+ for (const child of from.children) await walkNav(step, child);
15
+ }
16
+
17
+ //
18
+ //
19
+ //
20
+
21
+ export function getNavBookOf(target: string | NavNode): NavNode | undefined {
22
+ const id = typeof target === 'string' ? target : target.id;
23
+
24
+ if (!id || !ERUDIT_SERVER.NAV_BOOKS) return undefined;
25
+
26
+ const idParts = id.split('/');
27
+
28
+ while (idParts.length > 0) {
29
+ const book = ERUDIT_SERVER.NAV_BOOKS[idParts.join('/')];
30
+
31
+ if (book) return book;
32
+
33
+ idParts.pop();
34
+ }
35
+
36
+ return undefined;
37
+ }
38
+
39
+ export async function getPreviousNextNav(contentId: string) {
40
+ let previousNav: NavNode | undefined, nextNav: NavNode | undefined;
41
+ let book: NavNode | undefined;
42
+ let finish = false;
43
+
44
+ await walkNav(async (navNode) => {
45
+ if (navNode.id === contentId) {
46
+ book = getNavBookOf(navNode);
47
+ finish = true;
48
+ return;
49
+ }
50
+
51
+ if (finish && !nextNav) {
52
+ nextNav = navNode;
53
+ return false;
54
+ }
55
+
56
+ if (!finish) previousNav = navNode;
57
+ });
58
+
59
+ if (book) {
60
+ //
61
+ // Can't lead out of current book (if givent content item is inside the book)
62
+ //
63
+
64
+ if (previousNav)
65
+ if (getNavBookOf(previousNav)?.fullId !== book.fullId)
66
+ previousNav = undefined;
67
+
68
+ if (nextNav)
69
+ if (getNavBookOf(nextNav)?.fullId !== book.fullId)
70
+ nextNav = undefined;
71
+ } else {
72
+ //
73
+ // If not inside the book and tries to lead inside a book, then force lead to book's about page
74
+ //
75
+
76
+ if (previousNav) previousNav = getNavBookOf(previousNav) || previousNav;
77
+
78
+ if (nextNav) nextNav = getNavBookOf(nextNav) || nextNav;
79
+ }
80
+
81
+ return {
82
+ previousNav,
83
+ nextNav,
84
+ };
85
+ }
86
+
87
+ export async function getNavNode(
88
+ contentId: string,
89
+ ): Promise<NavNode | undefined> {
90
+ let navNode: NavNode | undefined;
91
+
92
+ await walkNav(async (_navNode) => {
93
+ if (_navNode.id === contentId) {
94
+ navNode = _navNode;
95
+ return false;
96
+ }
97
+ });
98
+
99
+ return navNode;
100
+ }
101
+
102
+ export async function getIdsUp(contentId: string): Promise<string[]> {
103
+ const startNavNode = await getNavNode(contentId);
104
+
105
+ if (!startNavNode) return [];
106
+
107
+ const ids: string[] = [];
108
+
109
+ let currentNavNode: any = startNavNode;
110
+ while (currentNavNode?.id) {
111
+ ids.push(currentNavNode.id);
112
+ currentNavNode = currentNavNode.parent;
113
+ }
114
+
115
+ return ids;
116
+ }
117
+
118
+ export async function isSkipId(contentId: string) {
119
+ let hidden = false;
120
+
121
+ await walkNav(async (navNode) => {
122
+ if (navNode.id === contentId) {
123
+ hidden = navNode.skip;
124
+ return false;
125
+ }
126
+ });
127
+
128
+ return hidden;
129
+ }
@@ -0,0 +1,21 @@
1
+ import { ERUDIT_SERVER } from '@server/global';
2
+ import { getNavBookOf } from '@server/nav/utils';
3
+ import { DbContent } from '@server/db/entities/Content';
4
+
5
+ export async function getContentBookFor(contentId: string) {
6
+ const navBook = getNavBookOf(contentId);
7
+
8
+ if (!navBook) return undefined;
9
+
10
+ const bookId = navBook.id;
11
+
12
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
13
+ select: ['title'],
14
+ where: { contentId: bookId },
15
+ });
16
+
17
+ return {
18
+ contentId: bookId,
19
+ title: dbContent?.title || bookId,
20
+ };
21
+ }
@@ -0,0 +1,238 @@
1
+ import type { ContentType } from 'erudit-cog/schema';
2
+ import { Like } from 'typeorm';
3
+
4
+ import { ERUDIT_SERVER } from '@server/global';
5
+ import { DbContent } from '@server/db/entities/Content';
6
+ import { DbContribution } from '@server/db/entities/Contribution';
7
+
8
+ import { getContentContext } from '@server/content/context';
9
+ import type { ContentGenericData } from '@shared/content/data/base';
10
+ import { getIdsUp, getPreviousNextNav } from '../nav/utils';
11
+ import { createContentLink, createTopicPartLink } from '@erudit/shared/link';
12
+ import { getTopicPart } from './topic';
13
+ import type { PreviousNextItem } from '@erudit/shared/content/previousNext';
14
+ import type { NavNode } from '../nav/node';
15
+ import { toAbsoluteContentId } from '../content/absoluteId';
16
+
17
+ import type { ContentContributor } from '@erudit/shared/contributor';
18
+ import { DbContributor } from '../db/entities/Contributor';
19
+
20
+ export async function getContentGenericData(
21
+ contentId: string,
22
+ ): Promise<ContentGenericData> {
23
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
24
+ where: { contentId },
25
+ });
26
+
27
+ if (!dbContent)
28
+ throw createError({
29
+ statusCode: 404,
30
+ message: `Content item "${contentId}" not found!`,
31
+ });
32
+
33
+ const previousNext = await getPreviousNext(contentId);
34
+ const decoration = await getContentDecoration(contentId);
35
+ const flags = await getContentFlags(contentId);
36
+
37
+ const contentPage: ContentGenericData = {
38
+ contentId,
39
+ type: dbContent.type,
40
+ title: dbContent.title || undefined,
41
+ description: dbContent.description || undefined,
42
+ flags,
43
+ previousNext,
44
+ decoration,
45
+ context: await getContentContext(contentId),
46
+ seo: dbContent.seo,
47
+ ogImage: dbContent.ogImage,
48
+ dependencies: dbContent.dependencies
49
+ ? await getContentDependencies(contentId, dbContent.dependencies)
50
+ : undefined,
51
+ references: dbContent.references,
52
+ };
53
+
54
+ return contentPage;
55
+ }
56
+
57
+ export async function getPreviousNext(contentId: string) {
58
+ const { previousNav, nextNav } = await getPreviousNextNav(contentId);
59
+
60
+ async function getItemData(navNode: NavNode): Promise<PreviousNextItem> {
61
+ const title = await getContentTitle(navNode.id);
62
+
63
+ const link = await (async () => {
64
+ if (navNode.type === 'topic')
65
+ return createTopicPartLink(
66
+ await getTopicPart(navNode.id),
67
+ navNode.id,
68
+ );
69
+
70
+ return createContentLink(navNode.type, navNode.id);
71
+ })();
72
+
73
+ return {
74
+ title,
75
+ link,
76
+ };
77
+ }
78
+
79
+ return {
80
+ previous: previousNav ? await getItemData(previousNav) : undefined,
81
+ next: nextNav ? await getItemData(nextNav) : undefined,
82
+ };
83
+ }
84
+
85
+ export async function getContentDecoration(contentId: string) {
86
+ const idsUp = await getIdsUp(contentId);
87
+
88
+ for (const id of idsUp) {
89
+ const decoration = (
90
+ await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
91
+ select: ['decoration'],
92
+ where: { contentId: id },
93
+ })
94
+ )?.decoration;
95
+
96
+ if (decoration) return decoration;
97
+ }
98
+
99
+ return undefined;
100
+ }
101
+
102
+ export async function getContentFlags(contentId: string) {
103
+ const idsUp = await getIdsUp(contentId);
104
+ const flags: any = {};
105
+
106
+ for (const id of idsUp.reverse()) {
107
+ const dbFlags = (
108
+ await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
109
+ select: ['flags'],
110
+ where: { contentId: id },
111
+ })
112
+ )?.flags;
113
+
114
+ if (dbFlags)
115
+ for (const [flagName, flagValue] of Object.entries(dbFlags))
116
+ flags[flagName] = flagValue;
117
+ }
118
+
119
+ for (const [flagName, flagValue] of Object.entries(flags))
120
+ if (flagValue === false) delete flags[flagName];
121
+
122
+ return flags;
123
+ }
124
+
125
+ export async function getContentDependencies(
126
+ contentId: string,
127
+ strDependencies: string[],
128
+ ) {
129
+ const dependencyIds = strDependencies.map((rawDependency) =>
130
+ toAbsoluteContentId(rawDependency, contentId),
131
+ );
132
+ const dependencies: Record<string, string> = {};
133
+
134
+ for (const dependencyId of dependencyIds)
135
+ dependencies[await getContentLink(dependencyId)] =
136
+ await getContentTitle(dependencyId);
137
+
138
+ return dependencies;
139
+ }
140
+
141
+ export async function getContentTitle(contentId: string) {
142
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
143
+ select: ['title'],
144
+ where: { contentId },
145
+ });
146
+
147
+ return dbContent?.title || contentId.split('/').pop()!;
148
+ }
149
+
150
+ export async function getContentLink(contentId: string) {
151
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
152
+ select: ['type'],
153
+ where: { contentId },
154
+ });
155
+
156
+ if (!dbContent)
157
+ throw createError({
158
+ statusCode: 404,
159
+ statusText: `Missing "${contentId}" content item!`,
160
+ });
161
+
162
+ if (dbContent.type !== 'topic')
163
+ return createContentLink(dbContent.type, contentId);
164
+
165
+ const topicPart = await getTopicPart(contentId);
166
+
167
+ return createTopicPartLink(topicPart, contentId);
168
+ }
169
+
170
+ export async function getFullContentId(contentId: string) {
171
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
172
+ select: ['fullId'],
173
+ where: { contentId },
174
+ });
175
+
176
+ return dbContent?.fullId;
177
+ }
178
+
179
+ export async function getContentContributors(contentId: string) {
180
+ const contributorIds = await (async () => {
181
+ const idHash: Record<string, true> = {};
182
+
183
+ (
184
+ await ERUDIT_SERVER.DB.manager.find(DbContribution, {
185
+ select: ['contributorId'],
186
+ where: { contentId },
187
+ })
188
+ ).forEach(
189
+ (contribution) => (idHash[contribution.contributorId] = true),
190
+ );
191
+
192
+ const type: ContentType = (await ERUDIT_SERVER.DB.manager.findOne(
193
+ DbContent,
194
+ {
195
+ select: ['type'],
196
+ where: { contentId },
197
+ },
198
+ ))!.type;
199
+
200
+ if (type === 'book' || type === 'group') {
201
+ // Collecting contributors for all sub-content items
202
+ (
203
+ await ERUDIT_SERVER.DB.manager.find(DbContribution, {
204
+ select: ['contributorId'],
205
+ where: { contentId: Like(`${contentId}/%`) },
206
+ })
207
+ ).forEach(
208
+ (contribution) => (idHash[contribution.contributorId] = true),
209
+ );
210
+ }
211
+
212
+ return Object.keys(idHash);
213
+ })();
214
+
215
+ const contentContributors: ContentContributor[] = [];
216
+
217
+ for (const contributorId of contributorIds) {
218
+ const dbContributor = (await ERUDIT_SERVER.DB.manager.findOne(
219
+ DbContributor,
220
+ {
221
+ select: ['contributorId', 'displayName', 'avatar'],
222
+ where: { contributorId },
223
+ },
224
+ ))!;
225
+
226
+ const contentContributor: ContentContributor = { contributorId };
227
+
228
+ if (dbContributor.displayName)
229
+ contentContributor.displayName = dbContributor.displayName;
230
+
231
+ if (dbContributor.avatar)
232
+ contentContributor.avatar = dbContributor.avatar;
233
+
234
+ contentContributors.push(contentContributor);
235
+ }
236
+
237
+ return contentContributors.length ? contentContributors : undefined;
238
+ }
@@ -0,0 +1,8 @@
1
+ import { DbContributor } from '@server/db/entities/Contributor';
2
+ import { ERUDIT_SERVER } from '@server/global';
3
+
4
+ export async function contributorExists(contributorId: string) {
5
+ return await ERUDIT_SERVER.DB.manager.exists(DbContributor, {
6
+ where: { contributorId },
7
+ });
8
+ }
@@ -0,0 +1,148 @@
1
+ import type {
2
+ FrontNav,
3
+ FrontNavBase,
4
+ FrontNavBook,
5
+ FrontNavFolder,
6
+ FrontNavItem,
7
+ FrontNavSeparator,
8
+ FrontNavTopic,
9
+ } from '@erudit/shared/frontNav';
10
+
11
+ import type { NavNode, RootNavNode } from '@server/nav/node';
12
+ import { ERUDIT_SERVER } from '@server/global';
13
+ import { DbContent } from '@server/db/entities/Content';
14
+ import { DbGroup } from '@server/db/entities/Group';
15
+
16
+ import { getTopicPart } from './topic';
17
+
18
+ export async function createGlobalFrontNav(
19
+ from: RootNavNode | NavNode | undefined,
20
+ ): Promise<FrontNav | []> {
21
+ if (!from || !from.children) return [];
22
+
23
+ return await Promise.all(
24
+ from.children.map((node) =>
25
+ toFrontNavItem({ node, level: 0, includeBookNav: true }),
26
+ ),
27
+ );
28
+ }
29
+
30
+ export async function createBookFrontNav(from: NavNode): Promise<FrontNavBook> {
31
+ return await toFrontNavBook({ node: from, level: 0, includeBookNav: true });
32
+ }
33
+
34
+ type ToFuncArg = {
35
+ node: NavNode;
36
+ level: number;
37
+ includeBookNav?: boolean;
38
+ };
39
+
40
+ async function toFrontNavItem(arg: ToFuncArg): Promise<FrontNavItem> {
41
+ switch (arg.node.type) {
42
+ case 'book':
43
+ return await toFrontNavBook(arg);
44
+ case 'topic':
45
+ return await toFrontNavTopic(arg);
46
+ case 'group':
47
+ const dbGroup = await ERUDIT_SERVER.DB.manager.findOne(DbGroup, {
48
+ select: ['type'],
49
+ where: { contentId: arg.node.id },
50
+ });
51
+
52
+ if (!dbGroup)
53
+ throw new Error(
54
+ `Missing group content item "${arg.node.id}" when creating front nav!`,
55
+ );
56
+
57
+ if (dbGroup.type === 'folder') return await toFrontNavFolder(arg);
58
+ else return await toFrontNavSeparator(arg);
59
+ }
60
+ }
61
+
62
+ async function toFrontNavBook({
63
+ node,
64
+ level,
65
+ includeBookNav,
66
+ }: ToFuncArg): Promise<FrontNavBook> {
67
+ return {
68
+ type: 'book',
69
+ children: await toFrontNavChildren({
70
+ node,
71
+ level: 0,
72
+ includeBookNav,
73
+ }),
74
+ ...(await toFrontNavBase({ node, level })),
75
+ };
76
+ }
77
+
78
+ async function toFrontNavSeparator({
79
+ node,
80
+ level,
81
+ includeBookNav,
82
+ }: ToFuncArg): Promise<FrontNavSeparator> {
83
+ return {
84
+ type: 'separator',
85
+ children: await toFrontNavChildren({ node, level, includeBookNav }),
86
+ ...(await toFrontNavBase({ node, level })),
87
+ };
88
+ }
89
+
90
+ async function toFrontNavFolder({
91
+ node,
92
+ level,
93
+ includeBookNav,
94
+ }: ToFuncArg): Promise<FrontNavFolder> {
95
+ return {
96
+ type: 'folder',
97
+ children: await toFrontNavChildren({
98
+ node,
99
+ level: level + 1,
100
+ includeBookNav,
101
+ }),
102
+ ...(await toFrontNavBase({ node, level })),
103
+ };
104
+ }
105
+
106
+ async function toFrontNavTopic({
107
+ node,
108
+ level,
109
+ }: ToFuncArg): Promise<FrontNavTopic> {
110
+ const topicPart = await getTopicPart(node.id);
111
+ return {
112
+ type: 'topic',
113
+ part: topicPart,
114
+ ...(await toFrontNavBase({ node, level })),
115
+ };
116
+ }
117
+
118
+ async function toFrontNavBase({
119
+ node,
120
+ level,
121
+ }: ToFuncArg): Promise<Omit<FrontNavBase, 'type'>> {
122
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(DbContent, {
123
+ select: ['title', 'navTitle', 'flags'],
124
+ where: { contentId: node.id },
125
+ });
126
+
127
+ return {
128
+ id: node.id,
129
+ fullId: node.fullId,
130
+ level,
131
+ flags: dbContent?.flags,
132
+ label:
133
+ dbContent?.navTitle ||
134
+ dbContent?.title ||
135
+ node.id.split('/').pop()!,
136
+ };
137
+ }
138
+
139
+ async function toFrontNavChildren({ node, includeBookNav, level }: ToFuncArg) {
140
+ if (node.type === 'book' && !includeBookNav) return undefined;
141
+ if (!node.children) return undefined;
142
+
143
+ return Promise.all(
144
+ node.children!.map((child) =>
145
+ toFrontNavItem({ node: child, level, includeBookNav }),
146
+ ),
147
+ );
148
+ }