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,51 @@
1
+ import {
2
+ getContentContext,
3
+ getTopicPartContext,
4
+ getContributorContext,
5
+ } from '@erudit/server/plugin/content/context';
6
+
7
+ import type { PreviewDataPageLink } from '@app/scripts/preview/data/pageLink';
8
+ import type { LinkTargetPageType } from '@erudit/shared/bitran/products/link/target';
9
+ import type { Context, ContextItem } from '@erudit/shared/content/context';
10
+
11
+ export default defineEventHandler<Promise<PreviewDataPageLink>>(
12
+ async (event) => {
13
+ const urlParts = (event.context.params?.parts || '').split('/');
14
+ const pageType: LinkTargetPageType = urlParts.shift() as any;
15
+ const contentId: string = urlParts.join('/');
16
+
17
+ const context: Context = await (async () => {
18
+ switch (pageType) {
19
+ case 'book':
20
+ case 'group':
21
+ return await getContentContext(contentId);
22
+ case 'article':
23
+ case 'summary':
24
+ case 'practice':
25
+ return await getTopicPartContext(pageType, contentId);
26
+ case 'contributor':
27
+ return await getContributorContext(contentId);
28
+ }
29
+
30
+ throw createError(`Can't handle "${pageType}" page type!`);
31
+ })();
32
+
33
+ const lastContextItem = context.pop() as ContextItem;
34
+ const contextStr = context
35
+ .filter((c) => !c.hidden)
36
+ .map((c) => c.title)
37
+ .join(' / ');
38
+
39
+ return <PreviewDataPageLink>{
40
+ type: 'page-link',
41
+ footer: {
42
+ secondary:
43
+ (contextStr ? contextStr + ' • ' : '') +
44
+ lastContextItem.type,
45
+ primary: lastContextItem.title,
46
+ href: lastContextItem.href,
47
+ iconName: lastContextItem.icon,
48
+ },
49
+ };
50
+ },
51
+ );
@@ -0,0 +1,55 @@
1
+ import type { BitranContent } from 'bitran';
2
+
3
+ import { parseUrlLocation } from '@server/bitran/location';
4
+ import { getLocationContext } from '@server/content/context';
5
+ import { ERUDIT_SERVER } from '@server/global';
6
+ import { DbUnique } from '@server/db/entities/Unique';
7
+ import { getBitranContent } from '@server/bitran/content';
8
+
9
+ import type { Context } from '@shared/content/context';
10
+ import type { BitranContext } from '@shared/bitran/context';
11
+ import { stringifyBitranLocation } from '@shared/bitran/location';
12
+
13
+ interface ReturnType {
14
+ context: Context;
15
+ productTitle?: string;
16
+ bitran: {
17
+ productName: string;
18
+ context: BitranContext;
19
+ content: BitranContent;
20
+ };
21
+ }
22
+
23
+ export default defineEventHandler<Promise<ReturnType>>(async (event) => {
24
+ const location = parseUrlLocation(getRouterParam(event, 'location') || '');
25
+
26
+ const context = await getLocationContext(location);
27
+
28
+ const dbUnique = await (async () => {
29
+ const dbUnique = ERUDIT_SERVER.DB.manager.findOne(DbUnique, {
30
+ select: ['productName', 'title', 'context'],
31
+ where: { location: stringifyBitranLocation(location) },
32
+ });
33
+
34
+ if (!dbUnique)
35
+ throw createError(
36
+ `Missing unique "${stringifyBitranLocation(location)}"!`,
37
+ );
38
+
39
+ return dbUnique as any as DbUnique;
40
+ })();
41
+
42
+ const bitran = await (async () => {
43
+ return {
44
+ content: await getBitranContent(location),
45
+ context: dbUnique.context,
46
+ productName: dbUnique.productName,
47
+ };
48
+ })();
49
+
50
+ return <ReturnType>{
51
+ context,
52
+ productTitle: dbUnique.title,
53
+ bitran,
54
+ };
55
+ });
@@ -0,0 +1,176 @@
1
+ import type { BitranContent, BitranPreRenderData } from 'bitran';
2
+ import { createRenderData } from 'bitran/core';
3
+ import { BlockNode } from 'bitran/dom';
4
+ import { BlocksNode, ErrorNode } from 'bitran/default';
5
+ import type { BitranContext } from '@erudit/shared/bitran/context';
6
+ import {
7
+ stringifyBitranLocation,
8
+ type BitranLocation,
9
+ } from '@erudit/shared/bitran/location';
10
+ import { ERUDIT_SERVER } from '../global';
11
+ import { DbUnique } from '../db/entities/Unique';
12
+ import { isTopicPart, type TopicPart } from 'erudit-cog/schema';
13
+ import { DbTopic } from '../db/entities/Topic';
14
+ import { DbGroup } from '../db/entities/Group';
15
+ import { NO_ALIASES } from '@erudit/shared/bitran/alias';
16
+ import { createBitranCore } from './core';
17
+ import { IncludeNode } from '@erudit/shared/bitran/products/include/core';
18
+ import { LinkNode } from '@erudit/shared/bitran/products/link/core';
19
+ import { resolveLinkTarget } from './products/link';
20
+ import { traverseInclude } from './products/include';
21
+
22
+ interface RawBitranContent {
23
+ context: BitranContext;
24
+ biCode: string;
25
+ }
26
+
27
+ export async function getBitranContent(
28
+ location: BitranLocation,
29
+ ): Promise<BitranContent> {
30
+ const rawContent = await getRawBitranContent(location);
31
+ const bitranContent = await createBitranContent(
32
+ rawContent.context,
33
+ rawContent.biCode,
34
+ );
35
+ return bitranContent;
36
+ }
37
+
38
+ export async function getRawBitranContent(
39
+ location: BitranLocation,
40
+ // @ts-ignore
41
+ ): Promise<RawBitranContent> {
42
+ const throwNotFound = () => {
43
+ throw createError({
44
+ statusCode: 404,
45
+ statusText: `Can't find content for location "${stringifyBitranLocation(location)}"!`,
46
+ });
47
+ };
48
+
49
+ if (location.unique) {
50
+ const dbUnique = await ERUDIT_SERVER.DB.manager.findOne(DbUnique, {
51
+ where: { location: stringifyBitranLocation(location) },
52
+ });
53
+
54
+ if (!dbUnique) throwNotFound();
55
+
56
+ return {
57
+ biCode: dbUnique!.content,
58
+ context: dbUnique!.context,
59
+ };
60
+ }
61
+
62
+ if (isTopicPart(location.type)) {
63
+ const topicPart = location.type as TopicPart;
64
+
65
+ const dbTopic = await ERUDIT_SERVER.DB.manager.findOne(DbTopic, {
66
+ select: ['contentId', topicPart],
67
+ where: { contentId: location.path },
68
+ });
69
+
70
+ if (!dbTopic) throwNotFound();
71
+
72
+ return {
73
+ biCode: dbTopic![topicPart]!,
74
+ context: { location, aliases: NO_ALIASES() },
75
+ };
76
+ }
77
+
78
+ if (location.type === 'group') {
79
+ const dbGroup = await ERUDIT_SERVER.DB.manager.findOne(DbGroup, {
80
+ select: ['contentId', 'content'],
81
+ where: { contentId: location.path },
82
+ });
83
+
84
+ if (!dbGroup) throwNotFound();
85
+
86
+ return {
87
+ biCode: dbGroup!.content!,
88
+ context: { location, aliases: NO_ALIASES() },
89
+ };
90
+ }
91
+
92
+ throwNotFound();
93
+ }
94
+
95
+ async function createBitranContent(
96
+ context: BitranContext,
97
+ biCode: string,
98
+ ): Promise<BitranContent> {
99
+ const bitranCore = await createBitranCore(context);
100
+
101
+ const root = await bitranCore.parser.parse(biCode, {
102
+ step: async (productNode) => {
103
+ if (productNode instanceof IncludeNode)
104
+ await resolveInclude(productNode, context);
105
+ },
106
+ });
107
+
108
+ const finalContent = bitranCore.stringifier.stringify(root);
109
+
110
+ // Building render data
111
+ const renderData: BitranPreRenderData = {};
112
+ await bitranCore.parser.parse(finalContent, {
113
+ step: async (productNode) => {
114
+ const id = productNode.getId();
115
+ const productCore = bitranCore.products[productNode.name]!;
116
+
117
+ if (productNode instanceof LinkNode) {
118
+ try {
119
+ renderData[id] = {
120
+ status: 'success',
121
+ data: await resolveLinkTarget(productNode.parseData, {
122
+ location: context.location,
123
+ aliases: context.aliases ?? {},
124
+ }),
125
+ };
126
+ } catch (error: any) {
127
+ renderData[id] = {
128
+ status: 'error',
129
+ errorMessage: error?.message || error,
130
+ };
131
+ }
132
+ return;
133
+ }
134
+
135
+ if (id)
136
+ renderData[id] = await createRenderData(
137
+ 'pre',
138
+ productNode,
139
+ productCore,
140
+ );
141
+ },
142
+ });
143
+
144
+ return {
145
+ biCode: finalContent,
146
+ renderData,
147
+ };
148
+ }
149
+
150
+ async function resolveInclude(
151
+ includeNode: IncludeNode,
152
+ context: BitranContext,
153
+ ): Promise<IncludeNode | ErrorNode> {
154
+ let _blocks;
155
+
156
+ try {
157
+ const blocks: BlockNode[] = [];
158
+
159
+ await traverseInclude(includeNode, context, {
160
+ step: async ({ _location, _node, _bitranCore }) => {
161
+ if (_node instanceof BlockNode) blocks.push(_node);
162
+ },
163
+ });
164
+
165
+ const blocksNode = new BlocksNode(includeNode);
166
+ blocksNode.setNodes(blocks);
167
+
168
+ _blocks = blocksNode;
169
+ includeNode.parseData.blocks = _blocks;
170
+ } catch (error: any) {
171
+ includeNode.parseData.error = error?.message || error;
172
+ }
173
+
174
+ includeNode.parseData.resolved = true;
175
+ return includeNode;
176
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ createBitranCore as _createBitranCore,
3
+ type GenericProductCore,
4
+ } from 'bitran/core';
5
+
6
+ import { ERUDIT_SERVER } from '@server/global';
7
+ import type { BitranContext } from '@erudit/shared/bitran/context';
8
+
9
+ // Erudit Products
10
+ import { headingName } from '@erudit/shared/bitran/products/heading/shared';
11
+ import { heading } from '@erudit/shared/bitran/products/heading/core';
12
+ import { aliasName } from '@erudit/shared/bitran/products/alias/shared';
13
+ import { defineAlias } from '@erudit/shared/bitran/products/alias/core';
14
+ import { includeName } from '@erudit/shared/bitran/products/include/shared';
15
+ import { include } from '@erudit/shared/bitran/products/include/core';
16
+ import { linkName } from '@erudit/shared/bitran/products/link/shared';
17
+ import { link } from '@erudit/shared/bitran/products/link/core';
18
+
19
+ type Products = Record<string, GenericProductCore>;
20
+
21
+ export async function createBitranCore(context: BitranContext) {
22
+ const projectProducts = await getProjectProducts();
23
+
24
+ const eruditProducts: Products = {
25
+ [aliasName]: defineAlias(context), // Exec aliases logic on server and pass resolved aliases as RenderData -> no actions on client at all
26
+ [includeName]: include,
27
+ [headingName]: heading,
28
+ [linkName]: link,
29
+ };
30
+
31
+ const bitranCore = _createBitranCore({
32
+ products: {
33
+ ...projectProducts,
34
+ ...eruditProducts,
35
+ },
36
+ });
37
+
38
+ return bitranCore;
39
+ }
40
+
41
+ async function getProjectProducts(): Promise<Products> {
42
+ const productDefinitions = ERUDIT_SERVER?.BITRAN_CONFIG?.products;
43
+
44
+ if (!productDefinitions) return {};
45
+
46
+ const projectProducts: Products = {};
47
+ for (const [name, definition] of Object.entries(productDefinitions))
48
+ projectProducts[name] = await definition.core();
49
+
50
+ return projectProducts;
51
+ }
@@ -0,0 +1,25 @@
1
+ import {
2
+ decodeBitranLocation,
3
+ parseBitranLocation,
4
+ } from '@erudit/shared/bitran/location';
5
+
6
+ export function parseUrlLocation(urlLocation: string) {
7
+ urlLocation = decodeURIComponent(urlLocation);
8
+ urlLocation = decodeBitranLocation(urlLocation);
9
+
10
+ if (!urlLocation)
11
+ throw createError({
12
+ statusCode: 500,
13
+ statusText: 'Empty content location router parameter!',
14
+ });
15
+
16
+ try {
17
+ return parseBitranLocation(urlLocation);
18
+ } catch (error: any) {
19
+ throw createError({
20
+ statusCode: 404,
21
+ statusText:
22
+ error?.message || `Can't parse location "${urlLocation}"!`,
23
+ });
24
+ }
25
+ }
@@ -0,0 +1,229 @@
1
+ import type { BitranCore } from 'bitran/core';
2
+ import { BlockNode, type ProductNode } from 'bitran/dom';
3
+
4
+ import type { BitranContext } from '@erudit/shared/bitran/context';
5
+ import { IncludeNode } from '@erudit/shared/bitran/products/include/core';
6
+ import {
7
+ parseBitranLocation,
8
+ parsePartialBitranLocation,
9
+ stringifyBitranLocation,
10
+ } from '@erudit/shared/bitran/location';
11
+ import {
12
+ tryReplaceAlias,
13
+ type BitranAliases,
14
+ } from '@erudit/shared/bitran/alias';
15
+ import {
16
+ toAbsoluteContentId,
17
+ toAbsoluteContentLocation,
18
+ } from '../../content/absoluteId';
19
+ import { ERUDIT_SERVER } from '../../global';
20
+ import { DbUnique } from '../../db/entities/Unique';
21
+ import { createBitranCore } from '../core';
22
+ import { AliasNode } from '@erudit/shared/bitran/products/alias/core';
23
+ import { LinkNode } from '@erudit/shared/bitran/products/link/core';
24
+ import { createLinkTarget } from '@erudit/shared/bitran/products/link/target';
25
+
26
+ export type TraverseEnterFn = (payload: {
27
+ _location: string;
28
+ _bitranCore: BitranCore;
29
+ _biCode: string;
30
+ }) => Promise<any>;
31
+
32
+ export type TraverseStepFn = (payload: {
33
+ _location: string;
34
+ _bitranCore: BitranCore;
35
+ _node: ProductNode;
36
+ }) => Promise<any>;
37
+
38
+ export type TraverseLeaveFn = (payload: { _location: string }) => Promise<any>;
39
+
40
+ /**
41
+ * This operation is heavy as fuck.
42
+ * It consumes a lot of memory and is slow.
43
+ * Use as rarely as possible!
44
+ */
45
+ export async function traverseInclude(
46
+ includeNode: IncludeNode,
47
+ context: BitranContext,
48
+ listeners: {
49
+ enter?: TraverseEnterFn;
50
+ step?: TraverseStepFn;
51
+ leave?: TraverseLeaveFn;
52
+ },
53
+ ) {
54
+ const entryLocation = stringifyBitranLocation(
55
+ parsePartialBitranLocation(includeNode.getId(), context.location),
56
+ );
57
+
58
+ const travelMap: Record<string, string | null> = {
59
+ // Not displayed when error, but needed for checking infinite loops
60
+ [entryLocation]: null,
61
+ };
62
+
63
+ try {
64
+ await _traverseStep(
65
+ includeNode,
66
+ entryLocation,
67
+ context.aliases,
68
+ listeners,
69
+ travelMap,
70
+ );
71
+ } catch (message) {
72
+ let finalMessage = `Include Traversal Error!\n\n${message}\n\n`;
73
+
74
+ for (const [location, includeTarget] of Object.entries(
75
+ travelMap,
76
+ ).reverse()) {
77
+ if (includeTarget === null) continue;
78
+
79
+ finalMessage += `at "${printIncludeTarget(includeTarget)}" in "${location}"\n`;
80
+ }
81
+
82
+ throw new Error(finalMessage);
83
+ }
84
+ }
85
+
86
+ async function _traverseStep(
87
+ includeNode: IncludeNode,
88
+ location: string,
89
+ aliases: BitranAliases,
90
+ listeners: {
91
+ enter?: TraverseEnterFn;
92
+ step?: TraverseStepFn;
93
+ leave?: TraverseLeaveFn;
94
+ },
95
+ travelMap: Record<string, string | null>,
96
+ ) {
97
+ let includeTargetLocation: string;
98
+
99
+ try {
100
+ const parsedLocation = parseBitranLocation(location);
101
+
102
+ includeTargetLocation = stringifyBitranLocation(
103
+ parsePartialBitranLocation(
104
+ tryReplaceAlias(includeNode.parseData.location, aliases),
105
+ parsedLocation,
106
+ ),
107
+ );
108
+
109
+ includeTargetLocation = toAbsoluteContentLocation(
110
+ includeTargetLocation,
111
+ parsedLocation.path!,
112
+ );
113
+ } catch (error) {
114
+ travelMap[location] = includeNode.parseData.location;
115
+ throw error;
116
+ }
117
+
118
+ if (includeTargetLocation in travelMap)
119
+ throw `Include "${printIncludeTarget(includeNode.parseData.location)}" targets "${includeTargetLocation}" which creates infinite loop!`;
120
+
121
+ travelMap[location] = includeNode.parseData.location;
122
+
123
+ //
124
+ // Loading Unique this Include is referencing to.
125
+ //
126
+
127
+ const dbUnique = await ERUDIT_SERVER.DB.manager.findOne(DbUnique, {
128
+ where: { location: includeTargetLocation },
129
+ });
130
+
131
+ if (!dbUnique)
132
+ throw `Include "${printIncludeTarget(includeNode.parseData.location)}" is targeting to non-existing unique "${includeTargetLocation}"!`;
133
+
134
+ //
135
+ // Creating Bitran core within loaded unique's location context.
136
+ //
137
+
138
+ const context: BitranContext = {
139
+ location: parseBitranLocation(includeTargetLocation),
140
+ aliases: dbUnique.context.aliases,
141
+ };
142
+
143
+ const bitranCore = await createBitranCore(context);
144
+
145
+ listeners.enter &&
146
+ (await listeners.enter({
147
+ _biCode: dbUnique.content,
148
+ _location: includeTargetLocation,
149
+ _bitranCore: bitranCore,
150
+ }));
151
+
152
+ //
153
+ // Parsing unique content and sub-traversing all includes if any.
154
+ //
155
+
156
+ let stepErrorMessage: string | undefined;
157
+
158
+ await bitranCore.parser.parse(dbUnique.content, {
159
+ step: async (node) => {
160
+ if (stepErrorMessage) return;
161
+
162
+ // Skip Alias nodes to prevent them being injected into calling document context and possibly conflicting with other aliases, includes and links...
163
+ if (node instanceof AliasNode) return;
164
+
165
+ // Replacing block ids with absolute ones to make possible to link to them
166
+ if (node instanceof BlockNode)
167
+ node.meta.id = Object.keys(travelMap)
168
+ .concat([location])
169
+ .join('__');
170
+
171
+ if (node instanceof LinkNode) {
172
+ const linkTarget = createLinkTarget(node.parseData.target, {
173
+ location: context.location,
174
+ aliases: context.aliases ?? {},
175
+ });
176
+
177
+ switch (linkTarget.type) {
178
+ case 'unique':
179
+ node.parseData.target = toAbsoluteContentLocation(
180
+ linkTarget.strlocation,
181
+ context.location?.path!,
182
+ );
183
+ break;
184
+ case 'page':
185
+ node.parseData.target = `page|${linkTarget.pageType}|${toAbsoluteContentId(linkTarget.path!, context.location?.path!)}`;
186
+ break;
187
+ }
188
+ }
189
+
190
+ if (!(node instanceof IncludeNode)) {
191
+ listeners.step &&
192
+ (await listeners.step({
193
+ _location: includeTargetLocation,
194
+ _node: node,
195
+ _bitranCore: bitranCore,
196
+ }));
197
+
198
+ return;
199
+ }
200
+
201
+ try {
202
+ await _traverseStep(
203
+ node,
204
+ includeTargetLocation,
205
+ context.aliases,
206
+ listeners,
207
+ travelMap,
208
+ );
209
+ } catch (message: any) {
210
+ stepErrorMessage = message;
211
+ }
212
+ },
213
+ });
214
+
215
+ if (stepErrorMessage) throw stepErrorMessage;
216
+
217
+ //
218
+ // Leaving from current Include
219
+ //
220
+
221
+ listeners.leave &&
222
+ (await listeners.leave({
223
+ _location: includeTargetLocation,
224
+ }));
225
+ }
226
+
227
+ function printIncludeTarget(target: string) {
228
+ return `<~ ${target}`;
229
+ }
@@ -0,0 +1,114 @@
1
+ import {
2
+ createLinkTarget,
3
+ type LinkTarget,
4
+ } from '@erudit/shared/bitran/products/link/target';
5
+ import {
6
+ toAbsoluteContentId,
7
+ toAbsoluteContentLocation,
8
+ } from '../../content/absoluteId';
9
+ import type { BitranContext } from '@erudit/shared/bitran/context';
10
+ import type { LinkData } from '@erudit/shared/bitran/products/link/shared';
11
+ import { DbUnique } from '../../db/entities/Unique';
12
+ import { ERUDIT_SERVER } from '../../global';
13
+ import {
14
+ parseBitranLocation,
15
+ stringifyBitranLocation,
16
+ } from '@erudit/shared/bitran/location';
17
+ import {
18
+ createBitranLocationLink,
19
+ createContentLink,
20
+ createContributorLink,
21
+ createTopicPartLink,
22
+ } from '@erudit/shared/link';
23
+ import { DbContent } from '../../db/entities/Content';
24
+ import { isTopicPart } from 'erudit-cog/schema';
25
+ import { DbContributor } from '../../db/entities/Contributor';
26
+
27
+ export async function resolveLinkTarget(
28
+ linkData: LinkData,
29
+ context: BitranContext,
30
+ ): Promise<LinkTarget> {
31
+ const linkTarget = createLinkTarget(linkData.target, context);
32
+
33
+ if (linkTarget.type === 'unique') {
34
+ const absoluteLocation = toAbsoluteContentLocation(
35
+ linkTarget.strlocation,
36
+ context.location?.path!,
37
+ );
38
+
39
+ const dbUnique = await ERUDIT_SERVER.DB.manager.findOne(DbUnique, {
40
+ select: ['location', 'productName'],
41
+ where: { location: absoluteLocation },
42
+ });
43
+
44
+ if (!dbUnique)
45
+ throw new Error(
46
+ `Unique "${linkTarget.strlocation}" does not exist!`,
47
+ );
48
+
49
+ const targetLocation = parseBitranLocation(dbUnique.location);
50
+
51
+ linkTarget._productName = dbUnique.productName;
52
+ linkTarget._href = createBitranLocationLink(targetLocation);
53
+ linkTarget._absoluteStrLocation =
54
+ stringifyBitranLocation(targetLocation);
55
+ } else if (linkTarget.type === 'page') {
56
+ const absoluteContentId = toAbsoluteContentId(
57
+ linkTarget.path!,
58
+ context.location?.path,
59
+ );
60
+
61
+ switch (linkTarget.pageType) {
62
+ case 'article':
63
+ case 'summary':
64
+ case 'practice':
65
+ case 'group':
66
+ case 'book':
67
+ {
68
+ const dbContent = await ERUDIT_SERVER.DB.manager.findOne(
69
+ DbContent,
70
+ {
71
+ where: { contentId: absoluteContentId },
72
+ },
73
+ );
74
+
75
+ if (!dbContent)
76
+ throw new Error(
77
+ `Page "${linkTarget.pageType}|${absoluteContentId}" does not exist!`,
78
+ );
79
+
80
+ linkTarget._href = isTopicPart(linkTarget.pageType)
81
+ ? createTopicPartLink(
82
+ linkTarget.pageType,
83
+ absoluteContentId,
84
+ )
85
+ : createContentLink(
86
+ linkTarget.pageType,
87
+ absoluteContentId,
88
+ );
89
+ }
90
+ break;
91
+
92
+ case 'contributor':
93
+ {
94
+ const dbContributor =
95
+ await ERUDIT_SERVER.DB.manager.findOne(DbContributor, {
96
+ select: ['contributorId'],
97
+ where: { contributorId: absoluteContentId },
98
+ });
99
+
100
+ if (!dbContributor)
101
+ throw new Error(
102
+ `Contributor "${absoluteContentId}" does not exist!`,
103
+ );
104
+
105
+ linkTarget._href = createContributorLink(
106
+ dbContributor.contributorId,
107
+ );
108
+ }
109
+ break;
110
+ }
111
+ }
112
+
113
+ return linkTarget;
114
+ }