erudit 3.0.0-dev.2 → 3.0.0-dev.20

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 (286) hide show
  1. package/app/app.vue +193 -172
  2. package/app/assets/icons/graduation.svg +3 -0
  3. package/app/components/Loading.vue +23 -23
  4. package/app/components/SiteAside.vue +393 -382
  5. package/app/components/SiteMain.vue +32 -35
  6. package/app/components/ads/Ads.vue +35 -0
  7. package/app/components/ads/AdsBannerAside.vue +61 -0
  8. package/app/components/ads/AdsBannerBottom.vue +22 -0
  9. package/app/components/ads/AdsProviderCustom.vue +35 -0
  10. package/app/components/ads/AdsProviderYandex.vue +54 -0
  11. package/app/components/ads/AdsReplacer.vue +73 -0
  12. package/app/components/aside/AsideListItem.vue +74 -74
  13. package/app/components/aside/AsideMajor.vue +56 -56
  14. package/app/components/aside/AsideMinor.vue +97 -71
  15. package/app/components/aside/major/PaneContentScroll.vue +23 -23
  16. package/app/components/aside/major/PaneSwitch.vue +54 -54
  17. package/app/components/aside/major/PaneSwitchButton.vue +63 -63
  18. package/app/components/aside/major/SiteInfo.vue +85 -85
  19. package/app/components/aside/major/panes/Language.vue +79 -79
  20. package/app/components/aside/major/panes/Pages.vue +31 -34
  21. package/app/components/aside/major/panes/Search.vue +11 -11
  22. package/app/components/aside/major/panes/nav/Nav.vue +92 -91
  23. package/app/components/aside/major/panes/nav/NavBook.vue +95 -86
  24. package/app/components/aside/major/panes/nav/NavBookLoading.vue +24 -24
  25. package/app/components/aside/major/panes/nav/NavGlobal.vue +16 -16
  26. package/app/components/aside/major/panes/nav/fnav/FNav.vue +105 -105
  27. package/app/components/aside/major/panes/nav/fnav/FNavBook.vue +32 -32
  28. package/app/components/aside/major/panes/nav/fnav/FNavFlags.vue +40 -40
  29. package/app/components/aside/major/panes/nav/fnav/FNavFolder.vue +60 -60
  30. package/app/components/aside/major/panes/nav/fnav/FNavItem.vue +34 -34
  31. package/app/components/aside/major/panes/nav/fnav/FNavSeparator.vue +80 -80
  32. package/app/components/aside/major/panes/nav/fnav/FNavTopic.vue +24 -24
  33. package/app/components/aside/major/panes/other/ItemContent.vue +29 -29
  34. package/app/components/aside/major/panes/other/ItemGenerator.vue +13 -15
  35. package/app/components/aside/major/panes/other/ItemTheme.vue +54 -54
  36. package/app/components/aside/major/panes/other/Other.vue +16 -16
  37. package/app/components/aside/minor/{Contribute.vue → AsideMinorContribute.vue} +175 -145
  38. package/app/components/aside/minor/AsideMinorNews.vue +11 -11
  39. package/app/components/aside/minor/AsideMinorPane.vue +15 -15
  40. package/app/components/aside/minor/AsideMinorTopLink.vue +67 -67
  41. package/app/components/aside/minor/content/AsideMinorContent.vue +89 -92
  42. package/app/components/aside/minor/contributor/AsideMinorContributor.vue +78 -0
  43. package/app/components/aside/minor/contributor/BookContribution.vue +64 -0
  44. package/app/components/aside/minor/topic/AsideMinorTopic.vue +29 -32
  45. package/app/components/aside/minor/topic/TopicContributors.vue +177 -177
  46. package/app/components/aside/minor/topic/TopicNav.vue +49 -49
  47. package/app/components/aside/minor/topic/TopicToc.vue +213 -203
  48. package/app/components/aside/minor/topic/TopicTocItem.vue +32 -31
  49. package/app/components/aside/utils/AsideOverlayPane.vue +40 -40
  50. package/app/components/bitran/BitranContent.vue +91 -63
  51. package/app/components/bitran/RenderWrapper.vue +10 -10
  52. package/app/components/contributor/ContributorAvatar.vue +45 -43
  53. package/app/components/contributor/ContributorListItem.vue +35 -35
  54. package/app/components/main/MainBitranContent.vue +47 -0
  55. package/app/components/main/{utils/Breadcrumb.vue → MainBreadcrumb.vue} +67 -75
  56. package/app/components/main/MainDescription.vue +24 -0
  57. package/app/components/main/MainTitle.vue +76 -0
  58. package/app/components/main/content/ContentBreadcrumb.vue +28 -0
  59. package/app/components/main/{utils → content}/ContentDecoration.vue +29 -29
  60. package/app/components/main/{utils → content}/ContentPopover.vue +188 -176
  61. package/app/components/main/{utils → content}/ContentPopovers.vue +111 -105
  62. package/app/components/main/{utils → content}/ContentReferences.vue +70 -70
  63. package/app/components/main/{utils → content}/ContentSection.vue +45 -45
  64. package/app/components/main/{utils → content}/reference/ReferenceGroup.vue +38 -38
  65. package/app/components/main/{utils → content}/reference/ReferenceItem.vue +70 -68
  66. package/app/components/main/{utils → content}/reference/ReferenceSource.vue +120 -116
  67. package/app/components/main/topic/MainTopic.vue +81 -79
  68. package/app/components/main/topic/TopicPartSwitch.vue +124 -118
  69. package/app/components/preview/Preview.vue +186 -177
  70. package/app/components/preview/PreviewDisplay.vue +139 -139
  71. package/app/components/preview/PreviewFooterAction.vue +73 -73
  72. package/app/components/preview/PreviewLoading.vue +14 -14
  73. package/app/components/preview/PreviewScreen.vue +141 -99
  74. package/app/components/preview/display/Alert.vue +50 -50
  75. package/app/components/preview/display/Custom.vue +18 -18
  76. package/app/components/preview/display/GenericLink.vue +48 -48
  77. package/app/components/preview/display/PageLink.vue +22 -20
  78. package/app/components/preview/display/Unique.vue +46 -49
  79. package/app/components/transition/Fade.vue +19 -19
  80. package/app/components/tree/TreeContainer.vue +11 -11
  81. package/app/components/tree/TreeItem.vue +89 -89
  82. package/app/composables/adsAllowed.ts +11 -0
  83. package/app/composables/bitran.ts +108 -127
  84. package/app/composables/bitranLocation.ts +7 -7
  85. package/app/composables/contentData.ts +38 -36
  86. package/app/composables/contentPage.ts +168 -156
  87. package/app/composables/contentRoute.ts +45 -45
  88. package/app/composables/darkMagic.ts +24 -24
  89. package/app/composables/externalApi.ts +69 -63
  90. package/app/composables/favicon.ts +8 -8
  91. package/app/composables/formatText.ts +99 -86
  92. package/app/composables/majorPane.ts +61 -60
  93. package/app/composables/phrases.ts +77 -80
  94. package/app/composables/theme.ts +29 -29
  95. package/app/composables/url.ts +33 -33
  96. package/app/pages/_test/preview.vue +110 -110
  97. package/app/pages/article/[...articleId].vue +3 -3
  98. package/app/pages/book/[...bookId].vue +42 -47
  99. package/app/pages/contributor/[contributorId].vue +224 -0
  100. package/app/pages/contributors.vue +183 -0
  101. package/app/pages/group/[...groupId].vue +52 -65
  102. package/app/pages/index.vue +32 -32
  103. package/app/pages/practice/[...practice].vue +3 -3
  104. package/app/pages/summary/[...summaryId].vue +3 -3
  105. package/app/plugins/analytics.ts +95 -0
  106. package/app/plugins/prerender.server.ts +22 -0
  107. package/app/public/favicon/article.svg +5 -5
  108. package/app/public/favicon/default.svg +3 -3
  109. package/app/public/favicon/practice.svg +3 -3
  110. package/app/public/favicon/summary.svg +4 -4
  111. package/app/public/logotype.svg +2 -2
  112. package/app/scripts/_immediate.js +9 -9
  113. package/app/scripts/aside/index.ts +59 -59
  114. package/app/scripts/aside/major/nav.ts +26 -26
  115. package/app/scripts/aside/minor/state.ts +37 -37
  116. package/app/scripts/aside/minor/topic.ts +3 -3
  117. package/app/scripts/flag.ts +28 -28
  118. package/app/scripts/og.ts +27 -27
  119. package/app/scripts/preview/build.ts +76 -73
  120. package/app/scripts/preview/data/alert.ts +19 -19
  121. package/app/scripts/preview/data/custom.ts +8 -8
  122. package/app/scripts/preview/data/genericLink.ts +24 -24
  123. package/app/scripts/preview/data/pageLink.ts +23 -20
  124. package/app/scripts/preview/data/unique.ts +69 -70
  125. package/app/scripts/preview/data.ts +24 -24
  126. package/app/scripts/preview/display.ts +37 -37
  127. package/app/scripts/preview/footer.ts +9 -9
  128. package/app/scripts/preview/request.ts +51 -51
  129. package/app/scripts/preview/state.ts +63 -63
  130. package/app/styles/_immediate.css +7 -7
  131. package/app/styles/_util.scss +43 -43
  132. package/app/styles/_utils.scss +44 -44
  133. package/app/styles/app.scss +91 -91
  134. package/app/styles/def/_bp.scss +27 -27
  135. package/app/styles/def/_size.scss +7 -7
  136. package/app/styles/def/_z.scss +5 -5
  137. package/app/styles/normalize.scss +49 -63
  138. package/app/styles/partials/_darkMagic.scss +5 -5
  139. package/app/styles/partials/_fnav.scss +15 -15
  140. package/app/styles/partials/_preview.scss +5 -5
  141. package/bin/erudit.mjs +2 -0
  142. package/const.ts +4 -0
  143. package/globalPath.ts +21 -21
  144. package/globals/bitran.ts +1 -47
  145. package/globals/content.ts +27 -22
  146. package/globals/contributor.ts +5 -5
  147. package/globals/erudit.ts +5 -5
  148. package/globals/register.ts +18 -18
  149. package/languages/en.ts +103 -95
  150. package/languages/ru.ts +107 -99
  151. package/module/bitran.ts +66 -34
  152. package/module/config.ts +35 -34
  153. package/module/imports.ts +67 -46
  154. package/module/index.ts +47 -47
  155. package/module/logger.ts +10 -10
  156. package/module/paths.ts +22 -22
  157. package/module/restart.ts +61 -61
  158. package/nuxt.config.ts +131 -112
  159. package/package.json +22 -15
  160. package/server/api/aside/major/nav/bookIds.ts +5 -5
  161. package/server/api/aside/major/nav/bookNav/[...bookId].ts +17 -20
  162. package/server/api/aside/major/nav/global.ts +7 -7
  163. package/server/api/aside/minor/book/[...bookId].ts +18 -0
  164. package/server/api/aside/minor/contributor/[contributorId].ts +18 -0
  165. package/server/api/aside/minor/group/[...groupId].ts +18 -0
  166. package/server/api/aside/minor/news.ts +7 -7
  167. package/server/api/aside/minor/topic.ts +36 -0
  168. package/server/api/bitran/content/[...location].ts +13 -0
  169. package/server/api/bitran/toc/[...location].ts +9 -0
  170. package/server/api/content/data.ts +75 -72
  171. package/server/api/contributor/count.ts +6 -6
  172. package/server/api/contributor/list.ts +44 -0
  173. package/server/api/contributor/page/[contributorId].ts +14 -0
  174. package/server/api/fake/content.ts +11 -11
  175. package/server/api/fake/shared/languages.ts +12 -12
  176. package/server/api/language/functions.ts +12 -12
  177. package/server/api/language/phrase/[phraseId].ts +19 -19
  178. package/server/api/language/phraseIds.ts +8 -8
  179. package/server/api/prerender.ts +19 -0
  180. package/server/api/preview/page/[...parts].ts +78 -51
  181. package/server/api/preview/unique/{[location].ts → [...location].ts} +48 -55
  182. package/server/api/problem/generator/[...path].ts +26 -0
  183. package/server/plugin/bitran/content.ts +252 -187
  184. package/server/plugin/bitran/{products → elements}/include.ts +229 -230
  185. package/server/plugin/bitran/location.ts +43 -25
  186. package/server/plugin/bitran/toc.ts +94 -83
  187. package/server/plugin/bitran/transpiler.ts +18 -46
  188. package/server/plugin/build/close.ts +12 -10
  189. package/server/plugin/build/jobs/content/builderArgs.ts +8 -8
  190. package/server/plugin/build/jobs/content/generic.ts +191 -176
  191. package/server/plugin/build/jobs/content/parse.ts +113 -100
  192. package/server/plugin/build/jobs/content/path.ts +6 -6
  193. package/server/plugin/build/jobs/content/type/book.ts +9 -9
  194. package/server/plugin/build/jobs/content/type/group.ts +37 -37
  195. package/server/plugin/build/jobs/content/type/topic.ts +36 -36
  196. package/server/plugin/build/jobs/contributors.ts +69 -66
  197. package/server/plugin/build/jobs/language.ts +36 -36
  198. package/server/plugin/build/jobs/nav.ts +345 -210
  199. package/server/plugin/build/process.ts +32 -25
  200. package/server/plugin/build/rebuild.ts +66 -55
  201. package/server/plugin/build/setup.ts +19 -21
  202. package/server/plugin/content/context.ts +119 -112
  203. package/server/plugin/db/entities/Book.ts +7 -7
  204. package/server/plugin/db/entities/Content.ts +45 -49
  205. package/server/plugin/db/entities/Contribution.ts +10 -10
  206. package/server/plugin/db/entities/Contributor.ts +25 -16
  207. package/server/plugin/db/entities/File.ts +10 -0
  208. package/server/plugin/db/entities/Group.ts +14 -14
  209. package/server/plugin/db/entities/Hash.ts +15 -15
  210. package/server/plugin/db/entities/Topic.ts +20 -20
  211. package/server/plugin/db/entities/Unique.ts +21 -21
  212. package/server/plugin/db/reset.ts +12 -0
  213. package/server/plugin/db/setup.ts +49 -34
  214. package/server/plugin/global.ts +16 -18
  215. package/server/plugin/importer.ts +16 -12
  216. package/server/plugin/index.ts +9 -9
  217. package/server/plugin/logger.ts +23 -23
  218. package/server/plugin/nav/node.ts +27 -26
  219. package/server/plugin/nav/utils.ts +179 -129
  220. package/server/plugin/repository/asideMinor.ts +51 -0
  221. package/server/plugin/repository/book.ts +39 -21
  222. package/server/plugin/repository/content.ts +240 -238
  223. package/server/plugin/repository/contentId.ts +40 -0
  224. package/server/plugin/repository/contributor.ts +98 -8
  225. package/server/plugin/repository/file.ts +10 -0
  226. package/server/plugin/repository/frontNav.ts +145 -148
  227. package/server/plugin/repository/topic.ts +35 -32
  228. package/server/tsconfig.json +9 -9
  229. package/shared/aside/minor.ts +55 -50
  230. package/shared/asset.ts +22 -15
  231. package/shared/bitran/content.ts +9 -0
  232. package/shared/bitran/contentId.ts +56 -0
  233. package/shared/bitran/toc.ts +8 -8
  234. package/shared/breadcrumb.ts +7 -0
  235. package/shared/content/bookId.ts +12 -0
  236. package/shared/content/context.ts +9 -9
  237. package/shared/content/data/base.ts +32 -32
  238. package/shared/content/data/index.ts +5 -5
  239. package/shared/content/data/type/book.ts +5 -5
  240. package/shared/content/data/type/group.ts +6 -6
  241. package/shared/content/data/type/topic.ts +11 -11
  242. package/shared/content/previousNext.ts +9 -9
  243. package/shared/contributor.ts +33 -5
  244. package/shared/frontNav.ts +41 -41
  245. package/shared/icons.ts +38 -38
  246. package/shared/image.ts +5 -5
  247. package/shared/link.ts +28 -25
  248. package/shared/popover.ts +8 -0
  249. package/shared/types/language.ts +79 -75
  250. package/shared/utils/objectsEqual.ts +4 -4
  251. package/shared/utils/stringColor.ts +9 -9
  252. package/test/contentId.test.ts +91 -0
  253. package/tsconfig.json +8 -8
  254. package/utils/contentPath.ts +67 -0
  255. package/utils/slash.ts +11 -0
  256. package/utils/stress.ts +9 -9
  257. package/app/components/ads/BannerTemplate.vue +0 -51
  258. package/app/components/ads/BottomBanner.vue +0 -45
  259. package/app/components/ads/LeftBanner.vue +0 -50
  260. package/app/components/aside/minor/AsideMinorContributor.vue +0 -5
  261. package/app/components/main/utils/ContentDescription.vue +0 -19
  262. package/app/components/main/utils/ContentFlag.vue +0 -15
  263. package/app/components/main/utils/ContentTitle.vue +0 -39
  264. package/app/composables/bitranContent.ts +0 -37
  265. package/app/pages/members.vue +0 -6
  266. package/app/styles/default.scss +0 -83
  267. package/server/api/aside/minor/path.ts +0 -78
  268. package/server/api/bitran/content/[location].ts +0 -7
  269. package/server/api/bitran/toc/[location].ts +0 -7
  270. package/server/plugin/bitran/products/link.ts +0 -116
  271. package/server/plugin/bitran/setup.ts +0 -9
  272. package/server/plugin/content/absoluteId.ts +0 -94
  273. package/shared/bitran/context.ts +0 -8
  274. package/shared/bitran/default.ts +0 -46
  275. package/shared/bitran/link/Link.vue +0 -167
  276. package/shared/bitran/link/factory.ts +0 -24
  277. package/shared/bitran/link/icon.svg +0 -3
  278. package/shared/bitran/link/languages/en.ts +0 -7
  279. package/shared/bitran/link/languages/ru.ts +0 -7
  280. package/shared/bitran/link/renderer.ts +0 -21
  281. package/shared/bitran/link/shared.ts +0 -17
  282. package/shared/bitran/link/target.ts +0 -134
  283. package/shared/bitran/link/transpiler.ts +0 -10
  284. package/shared/bitran/location.ts +0 -166
  285. package/test/bitran/link/target.test.ts +0 -141
  286. package/test/bitran/location.test.ts +0 -143
@@ -1,83 +1,94 @@
1
- import { BlockErrorNode, type ElementNode } from '@bitran-js/core';
2
- import { IncludeNode } from '@erudit-js/bitran-elements/include/shared';
3
- import {
4
- headingName,
5
- HeadingNode,
6
- } from '@erudit-js/bitran-elements/heading/shared';
7
-
8
- import type { BitranLocation } from '@erudit/shared/bitran/location';
9
- import type { Toc } from '@erudit/shared/bitran/toc';
10
- import { ERUDIT_SERVER } from '@server/global';
11
- import { getRawBitranContent } from './content';
12
- import { createBitranTranspiler } from './transpiler';
13
- import { traverseInclude } from './products/include';
14
-
15
- export async function getBitranToc(location: BitranLocation) {
16
- const rawContent = await getRawBitranContent(location);
17
- const bitranCore = await createBitranTranspiler();
18
-
19
- const toc: Toc = [];
20
-
21
- await bitranCore.parser.parse(rawContent.biCode, {
22
- async step(node) {
23
- if (node instanceof IncludeNode) {
24
- await traverseInclude(node, rawContent.context, {
25
- step: async ({ _node }) => tryAddToToc(_node),
26
- });
27
- } else tryAddToToc(node);
28
- },
29
- });
30
-
31
- return toc;
32
-
33
- function tryAddToToc(node: ElementNode) {
34
- const tocItemBase = {
35
- id: node.id,
36
- productName: node.name,
37
- };
38
-
39
- // Check if node is manually excluded from TOC
40
- if (typeof node.meta?.toc === 'boolean' && !node.meta?.toc) return;
41
-
42
- if (node instanceof HeadingNode) {
43
- toc.push({
44
- ...tocItemBase,
45
- level: node.parseData.level,
46
- title: node.parseData.title,
47
- });
48
- return;
49
- }
50
-
51
- const notHeadingLevel = () => {
52
- // Toc items without heading are at top level
53
- if (toc.length === 0) return 0;
54
-
55
- const lastTocItem = toc.at(-1)!;
56
-
57
- // Going one level deep if previous item was a heading and copy deep level if not
58
- return lastTocItem.productName === headingName
59
- ? lastTocItem.level + 1
60
- : lastTocItem.level;
61
- };
62
-
63
- if (ERUDIT_SERVER.BITRAN_CONFIG?.toc?.includes(node.name)) {
64
- // Erudit Config says to add these products to TOC
65
- toc.push({
66
- ...tocItemBase,
67
- level: notHeadingLevel(),
68
- title: node.meta?.title || node.parseData?.title,
69
- });
70
- return;
71
- }
72
-
73
- if (node.meta?.toc) {
74
- // Show in Toc any product with truthy `toc` meta property
75
- toc.push({
76
- ...tocItemBase,
77
- level: notHeadingLevel(),
78
- title: node.meta?.title || node.parseData?.title,
79
- });
80
- return;
81
- }
82
- }
83
- }
1
+ import { type ElementNode } from '@bitran-js/core';
2
+ import { NO_ALIASES, type BitranLocation } from '@erudit-js/cog/schema';
3
+ import {
4
+ headingName,
5
+ HeadingNode,
6
+ } from '@erudit-js/bitran-elements/heading/shared';
7
+ import {
8
+ ProblemNode,
9
+ ProblemsNode,
10
+ } from '@erudit-js/bitran-elements/problem/shared';
11
+
12
+ import type { Toc } from '@erudit/shared/bitran/toc';
13
+ import { ERUDIT_SERVER } from '@server/global';
14
+ import { getBitranContent } from './content';
15
+ import { createBitranTranspiler } from './transpiler';
16
+
17
+ export async function getBitranToc(location: BitranLocation) {
18
+ const content = await getBitranContent(location, false);
19
+
20
+ const bitranCore = await createBitranTranspiler({
21
+ eruditConfig: ERUDIT_SERVER.CONFIG,
22
+ insideInclude: false,
23
+ context: { aliases: NO_ALIASES(), location },
24
+ });
25
+
26
+ const toc: Toc = [];
27
+
28
+ await bitranCore.parser.parse(content.biCode, {
29
+ async step(node) {
30
+ tryAddToToc(node as ElementNode);
31
+ },
32
+ });
33
+
34
+ return toc;
35
+
36
+ function tryAddToToc(node: ElementNode) {
37
+ const tocItemBase = {
38
+ id: node.id,
39
+ productName: node.name,
40
+ };
41
+
42
+ // Check if node is manually excluded from TOC
43
+ if (typeof node.meta?.toc === 'boolean' && !node.meta?.toc) return;
44
+
45
+ if (node instanceof HeadingNode) {
46
+ toc.push({
47
+ ...tocItemBase,
48
+ level: node.parseData.level - 1,
49
+ title: node.parseData.title,
50
+ });
51
+ return;
52
+ }
53
+
54
+ const notHeadingLevel = () => {
55
+ // If TOC is empty, we're at the root level
56
+ if (toc.length === 0) return 0;
57
+
58
+ // Find the most recent heading to determine parent level
59
+ for (let i = toc.length - 1; i >= 0; i--) {
60
+ if (toc[i].productName === headingName) {
61
+ // Found a heading, return its level + 1
62
+ return toc[i].level + 1;
63
+ }
64
+ }
65
+
66
+ // No heading found in TOC, place at root level
67
+ return 0;
68
+ };
69
+
70
+ // Problems by default are in TOC
71
+ if (node instanceof ProblemsNode || node instanceof ProblemNode) {
72
+ toc.push({
73
+ ...tocItemBase,
74
+ level: notHeadingLevel(),
75
+ title: node.meta?.title || node.parseData.info.title,
76
+ });
77
+ return;
78
+ }
79
+
80
+ if (
81
+ ERUDIT_SERVER.CONFIG.bitran?.toc?.includes(node.name) ||
82
+ node.meta?.toc
83
+ ) {
84
+ // Erudit Bitran config says to add these products to TOC
85
+ // Or show in TOC any product with truthy `toc` meta property
86
+ toc.push({
87
+ ...tocItemBase,
88
+ level: notHeadingLevel(),
89
+ title: node.meta?.title || node.parseData?.title,
90
+ });
91
+ return;
92
+ }
93
+ }
94
+ }
@@ -1,46 +1,18 @@
1
- import {
2
- defineBitranTranspiler,
3
- type ElementTranspilers,
4
- } from '@bitran-js/transpiler';
5
-
6
- import { ERUDIT_SERVER } from '@server/global';
7
-
8
- // Default Elements
9
- import { aliasesName } from '@erudit-js/bitran-elements/aliases/shared';
10
- import { aliasesTranspiler } from '@erudit-js/bitran-elements/aliases/transpiler';
11
- import { headingName } from '@erudit-js/bitran-elements/heading/shared';
12
- import { headingTranspiler } from '@erudit-js/bitran-elements/heading/transpiler';
13
- import { includeName } from '@erudit-js/bitran-elements/include/shared';
14
- import { includeTranspiler } from '@erudit-js/bitran-elements/include/transpiler';
15
- import { linkName } from '@erudit/shared/bitran/link/shared';
16
- import { linkTranspiler } from '@erudit/shared/bitran/link/transpiler';
17
-
18
- export async function createBitranTranspiler() {
19
- const projectTranspilers = await getProjectTranspilers();
20
-
21
- const defaultTranspilers = {
22
- [aliasesName]: aliasesTranspiler,
23
- [includeName]: includeTranspiler,
24
- [headingName]: headingTranspiler,
25
- [linkName]: linkTranspiler,
26
- };
27
-
28
- const bitranTranspiler = defineBitranTranspiler({
29
- ...projectTranspilers,
30
- ...defaultTranspilers,
31
- });
32
-
33
- return bitranTranspiler;
34
- }
35
-
36
- async function getProjectTranspilers(): Promise<ElementTranspilers> {
37
- const bitranElements = ERUDIT_SERVER?.BITRAN_CONFIG?.elements;
38
-
39
- if (!bitranElements) return {};
40
-
41
- const projectTranspilers: ElementTranspilers = {};
42
- for (const [name, bitranElement] of Object.entries(bitranElements))
43
- projectTranspilers[name] = await bitranElement.transpiler();
44
-
45
- return projectTranspilers;
46
- }
1
+ import { defineBitranTranspiler } from '@bitran-js/transpiler';
2
+ import {
3
+ setEruditBitranRuntime,
4
+ type EruditBitranRuntime,
5
+ } from '@erudit-js/cog/schema';
6
+
7
+ import getServerTranspilers from '#erudit/bitran/server';
8
+
9
+ export async function createBitranTranspiler(runtime: EruditBitranRuntime) {
10
+ const serverTranspiler = await getServerTranspilers();
11
+ const bitranTranspiler = defineBitranTranspiler(serverTranspiler);
12
+
13
+ [bitranTranspiler.parser, bitranTranspiler.stringifier].forEach((item) => {
14
+ setEruditBitranRuntime(item, runtime);
15
+ });
16
+
17
+ return bitranTranspiler;
18
+ }
@@ -1,10 +1,12 @@
1
- import { debug, logger } from '@server/logger';
2
- import { ERUDIT_SERVER } from '@server/global';
3
-
4
- export async function close() {
5
- debug.start('Shutting down server...');
6
-
7
- await ERUDIT_SERVER.DB?.destroy();
8
-
9
- logger.success('Server shut down gracefully!');
10
- }
1
+ import { debug, logger } from '@server/logger';
2
+ import { ERUDIT_SERVER } from '@server/global';
3
+
4
+ export async function close() {
5
+ debug.start('Shutting down server...');
6
+
7
+ if (ERUDIT_SERVER.DB && ERUDIT_SERVER.DB.isInitialized) {
8
+ await ERUDIT_SERVER.DB.destroy();
9
+ }
10
+
11
+ logger.success('Server shut down gracefully!');
12
+ }
@@ -1,8 +1,8 @@
1
- import type { DbContent } from '@server/db/entities/Content';
2
- import type { NavNode } from '@server/nav/node';
3
-
4
- export interface BuilderFunctionArgs<T = any> {
5
- navNode: NavNode;
6
- dbContent: DbContent;
7
- config?: Partial<T>;
8
- }
1
+ import type { DbContent } from '@server/db/entities/Content';
2
+ import type { NavNode } from '@server/nav/node';
3
+
4
+ export interface BuilderFunctionArgs<T = any> {
5
+ navNode: NavNode;
6
+ dbContent: DbContent;
7
+ config?: Partial<T>;
8
+ }
@@ -1,176 +1,191 @@
1
- import { existsSync } from 'node:fs';
2
- import { globSync } from 'glob';
3
- import chalk from 'chalk';
4
- import sizeOf from 'image-size';
5
- import {
6
- contentTypes,
7
- type ContentConfig,
8
- type ContentReferences,
9
- type ContentType,
10
- } from 'erudit-cog/schema';
11
- import { resolvePaths } from 'erudit-cog/kit';
12
-
13
- import { PROJECT_DIR } from '#erudit/globalPaths';
14
- import { stress } from '@erudit/utils/stress';
15
-
16
- import { debug, logger } from '@server/logger';
17
- import { ERUDIT_SERVER } from '@server/global';
18
- import { walkNav } from '@server/nav/utils';
19
- import { isRootNode, type NavNode } from '@server/nav/node';
20
- import { DbContent } from '@server/db/entities/Content';
21
- import { IMPORT } from '@server/importer';
22
- import { contributorExists } from '@server/repository/contributor';
23
- import { DbContribution } from '@server/db/entities/Contribution';
24
-
25
- import { contentAsset } from '@erudit/shared/asset';
26
- import type { ImageData } from '@erudit/shared/image';
27
-
28
- import type { BuilderFunctionArgs } from './builderArgs';
29
- import { contentItemPath } from './path';
30
- import { buildBook } from './type/book';
31
- import { buildGroup } from './type/group';
32
- import { buildTopic } from './type/topic';
33
-
34
- const typeBuilders: Record<ContentType, Function> = {
35
- book: buildBook,
36
- group: buildGroup,
37
- topic: buildTopic,
38
- };
39
-
40
- export async function buildContent() {
41
- if (!ERUDIT_SERVER.NAV) return;
42
-
43
- debug.start('Building content...');
44
-
45
- const counters: Record<ContentType, number> = Object.fromEntries(
46
- contentTypes.map((contentType) => [contentType, 0]),
47
- ) as any;
48
-
49
- await walkNav(async (node) => {
50
- if (isRootNode(node)) return;
51
- counters[node.type]++;
52
- await addContentItem(node);
53
- });
54
-
55
- logger.success(
56
- 'Content built successfully!',
57
- chalk.dim(
58
- '(' +
59
- Object.entries(counters)
60
- .map(([k, v]) => `${k.at(0)!.toUpperCase()}: ${v}`)
61
- .join(', ') +
62
- ')',
63
- ),
64
- );
65
- }
66
-
67
- async function addContentItem(navNode: NavNode) {
68
- debug.start(
69
- `Adding ${stress(navNode.type)} content item ${stress(navNode.id)}...`,
70
- );
71
-
72
- const dbContent = new DbContent();
73
- dbContent.contentId = navNode.id;
74
- dbContent.fullId = navNode.fullId;
75
- dbContent.type = navNode.type;
76
- dbContent.decoration = getDecoration(navNode);
77
- dbContent.ogImage = getOgImageData(navNode);
78
- dbContent.references = await getContentReferences(navNode);
79
-
80
- let config: Partial<ContentConfig> | undefined;
81
-
82
- try {
83
- config = (await IMPORT(contentItemPath(navNode, navNode.type), {
84
- default: true,
85
- })) as Partial<ContentConfig>;
86
-
87
- dbContent.title = config.title;
88
- dbContent.navTitle = config.navTitle;
89
- dbContent.description = config.description;
90
- dbContent.flags = config.flags;
91
- dbContent.dependencies = config.dependencies;
92
-
93
- if (config.seo) {
94
- dbContent.seo = {
95
- title: config.seo?.title,
96
- description: config.seo?.description,
97
- };
98
- }
99
-
100
- await addContributions(navNode, config.contributors);
101
- } catch {}
102
-
103
- await ERUDIT_SERVER.DB.manager.save(dbContent);
104
-
105
- await typeBuilders[navNode.type](<BuilderFunctionArgs>{
106
- navNode,
107
- dbContent,
108
- config,
109
- });
110
- }
111
-
112
- function getDecoration(navNode: NavNode) {
113
- if (existsSync(contentItemPath(navNode, 'decoration.svg')))
114
- return `/${navNode.path}/decoration.svg`;
115
-
116
- return undefined;
117
- }
118
-
119
- async function addContributions(navNode: NavNode, contributors?: string[]) {
120
- if (!contributors || !contributors.length) {
121
- if (navNode.type !== 'book' && navNode.type !== 'group')
122
- logger.warn(
123
- `${navNode.type.at(0)!.toUpperCase() + navNode.type.slice(1)} ${stress(navNode.id)} has no contributors!`,
124
- );
125
-
126
- return;
127
- }
128
-
129
- for (const contributorId of contributors) {
130
- if (!(await contributorExists(contributorId))) {
131
- logger.warn(
132
- `Skipping unknown contributor ${stress(contributorId)} when adding ${navNode.type} ${stress(navNode.id)}!`,
133
- );
134
- continue;
135
- }
136
-
137
- const dbContribution = new DbContribution();
138
- dbContribution.contentId = navNode.id;
139
- dbContribution.contributorId = contributorId;
140
- await ERUDIT_SERVER.DB.manager.save(dbContribution);
141
- }
142
- }
143
-
144
- function getOgImageData(navNode: NavNode): ImageData | undefined {
145
- const ogImagePath = globSync(
146
- contentItemPath(navNode, 'og-image.{svg,webp,jpg,png}'),
147
- ).pop();
148
-
149
- if (ogImagePath) {
150
- const size = sizeOf(ogImagePath);
151
- return {
152
- src: contentAsset(
153
- resolvePaths(ogImagePath).replace(
154
- PROJECT_DIR + '/content/',
155
- '',
156
- ),
157
- ),
158
- width: size.width!,
159
- height: size.height!,
160
- };
161
- }
162
-
163
- return undefined;
164
- }
165
-
166
- async function getContentReferences(navNode: NavNode) {
167
- try {
168
- const references = await IMPORT(
169
- contentItemPath(navNode, `references`),
170
- { default: true },
171
- );
172
- return references as ContentReferences;
173
- } catch (error) {}
174
-
175
- return undefined;
176
- }
1
+ import { existsSync } from 'node:fs';
2
+ import { globSync } from 'glob';
3
+ import chalk from 'chalk';
4
+ import { imageSizeFromFile } from 'image-size/fromFile';
5
+ import {
6
+ contentTypes,
7
+ type ContentConfig,
8
+ type ContentReferences,
9
+ type ContentType,
10
+ } from '@erudit-js/cog/schema';
11
+ import { resolvePaths } from '@erudit-js/cog/kit';
12
+
13
+ import { PROJECT_DIR } from '#erudit/globalPaths';
14
+ import { scanContentPaths } from '@erudit/utils/contentPath';
15
+ import { stress } from '@erudit/utils/stress';
16
+
17
+ import { debug, logger } from '@server/logger';
18
+ import { ERUDIT_SERVER } from '@server/global';
19
+ import { walkNav } from '@server/nav/utils';
20
+ import { isRootNode, type NavNode } from '@server/nav/node';
21
+ import { DbContent } from '@server/db/entities/Content';
22
+ import { IMPORT } from '@server/importer';
23
+ import { contributorExists } from '@server/repository/contributor';
24
+ import { DbContribution } from '@server/db/entities/Contribution';
25
+ import { DbFile } from '@server/db/entities/File';
26
+
27
+ import { contentAsset } from '@erudit/shared/asset';
28
+ import type { ImageData } from '@erudit/shared/image';
29
+
30
+ import type { BuilderFunctionArgs } from './builderArgs';
31
+ import { contentItemPath } from './path';
32
+ import { buildBook } from './type/book';
33
+ import { buildGroup } from './type/group';
34
+ import { buildTopic } from './type/topic';
35
+
36
+ const typeBuilders: Record<ContentType, Function> = {
37
+ book: buildBook,
38
+ group: buildGroup,
39
+ topic: buildTopic,
40
+ };
41
+
42
+ export async function buildContent() {
43
+ if (!ERUDIT_SERVER.NAV) return;
44
+
45
+ debug.start('Building content...');
46
+
47
+ await scanContentFiles();
48
+
49
+ const counters: Record<ContentType, number> = Object.fromEntries(
50
+ contentTypes.map((contentType) => [contentType, 0]),
51
+ ) as any;
52
+
53
+ await walkNav(async (node) => {
54
+ if (isRootNode(node)) return;
55
+ counters[node.type]++;
56
+ await addContentItem(node);
57
+ });
58
+
59
+ logger.success(
60
+ 'Content built successfully!',
61
+ chalk.dim(
62
+ '(' +
63
+ Object.entries(counters)
64
+ .map(([k, v]) => `${k.at(0)!.toUpperCase()}: ${v}`)
65
+ .join(', ') +
66
+ ')',
67
+ ),
68
+ );
69
+ }
70
+
71
+ async function scanContentFiles() {
72
+ const contentFiles = scanContentPaths(PROJECT_DIR + '/content');
73
+ for (const [path, fullPath] of Object.entries(contentFiles)) {
74
+ const dbFile = new DbFile();
75
+ dbFile.path = path;
76
+ dbFile.fullPath = fullPath;
77
+ await ERUDIT_SERVER.DB.manager.save(dbFile);
78
+ }
79
+ }
80
+
81
+ async function addContentItem(navNode: NavNode) {
82
+ debug.start(
83
+ `Adding ${stress(navNode.type)} content item ${stress(navNode.shortId)}...`,
84
+ );
85
+
86
+ const dbContent = new DbContent();
87
+ dbContent.contentId = navNode.fullId;
88
+ dbContent.type = navNode.type;
89
+ dbContent.decoration = getDecoration(navNode);
90
+ dbContent.ogImage = await getOgImageData(navNode);
91
+ dbContent.references = await getContentReferences(navNode);
92
+
93
+ let config: Partial<ContentConfig> | undefined;
94
+
95
+ try {
96
+ config = (await IMPORT(contentItemPath(navNode, navNode.type), {
97
+ default: true,
98
+ })) as Partial<ContentConfig>;
99
+
100
+ dbContent.title = config.title;
101
+ dbContent.navTitle = config.navTitle;
102
+ dbContent.description = config.description;
103
+ dbContent.flags = config.flags;
104
+ dbContent.dependencies = config.dependencies;
105
+
106
+ if (config.seo) {
107
+ dbContent.seo = {
108
+ title: config.seo?.title,
109
+ description: config.seo?.description,
110
+ };
111
+ }
112
+
113
+ await addContributions(navNode, config.contributors);
114
+ } catch {}
115
+
116
+ await ERUDIT_SERVER.DB.manager.save(dbContent);
117
+
118
+ await typeBuilders[navNode.type](<BuilderFunctionArgs>{
119
+ navNode,
120
+ dbContent,
121
+ config,
122
+ });
123
+ }
124
+
125
+ function getDecoration(navNode: NavNode) {
126
+ if (existsSync(contentItemPath(navNode, 'decoration.svg')))
127
+ return `/${navNode.fsPath}/decoration.svg`;
128
+
129
+ return undefined;
130
+ }
131
+
132
+ async function addContributions(navNode: NavNode, contributors?: string[]) {
133
+ if (!contributors || !contributors.length) {
134
+ if (navNode.type !== 'book' && navNode.type !== 'group')
135
+ logger.warn(
136
+ `${navNode.type.at(0)!.toUpperCase() + navNode.type.slice(1)} ${stress(navNode.fullId)} has no contributors!`,
137
+ );
138
+
139
+ return;
140
+ }
141
+
142
+ for (const contributorId of contributors) {
143
+ if (!(await contributorExists(contributorId))) {
144
+ logger.warn(
145
+ `Skipping unknown contributor ${stress(contributorId)} when adding ${navNode.type} ${stress(navNode.fullId)}!`,
146
+ );
147
+ continue;
148
+ }
149
+
150
+ const dbContribution = new DbContribution();
151
+ dbContribution.contentId = navNode.fullId;
152
+ dbContribution.contributorId = contributorId;
153
+ await ERUDIT_SERVER.DB.manager.save(dbContribution);
154
+ }
155
+ }
156
+
157
+ async function getOgImageData(
158
+ navNode: NavNode,
159
+ ): Promise<ImageData | undefined> {
160
+ const ogImagePath = globSync(
161
+ contentItemPath(navNode, 'og-image.{svg,webp,jpg,png}'),
162
+ ).pop();
163
+
164
+ if (ogImagePath) {
165
+ const size = await imageSizeFromFile(ogImagePath);
166
+ return {
167
+ src: contentAsset(
168
+ resolvePaths(ogImagePath).replace(
169
+ PROJECT_DIR + '/content/',
170
+ '',
171
+ ),
172
+ ),
173
+ width: size.width,
174
+ height: size.height,
175
+ };
176
+ }
177
+
178
+ return undefined;
179
+ }
180
+
181
+ async function getContentReferences(navNode: NavNode) {
182
+ try {
183
+ const references = await IMPORT(
184
+ contentItemPath(navNode, `references`),
185
+ { default: true },
186
+ );
187
+ return references as ContentReferences;
188
+ } catch (error) {}
189
+
190
+ return undefined;
191
+ }