erudit 3.0.0-dev.2 → 3.0.0-dev.21

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 (321) hide show
  1. package/app/app.vue +195 -172
  2. package/app/assets/icons/cameo-add.svg +3 -0
  3. package/app/assets/icons/diamond.svg +3 -0
  4. package/app/assets/icons/graduation.svg +3 -0
  5. package/app/components/Avatar.vue +118 -0
  6. package/app/components/EruditLink.vue +17 -0
  7. package/app/components/Loading.vue +23 -23
  8. package/app/components/SiteAside.vue +393 -382
  9. package/app/components/SiteMain.vue +32 -35
  10. package/app/components/ads/Ads.vue +33 -0
  11. package/app/components/ads/AdsBannerAside.vue +61 -0
  12. package/app/components/ads/AdsBannerBottom.vue +22 -0
  13. package/app/components/ads/AdsProviderCustom.vue +35 -0
  14. package/app/components/ads/AdsProviderYandex.vue +91 -0
  15. package/app/components/ads/AdsReplacer.vue +73 -0
  16. package/app/components/aside/AsideListItem.vue +91 -74
  17. package/app/components/aside/AsideMajor.vue +56 -56
  18. package/app/components/aside/AsideMinor.vue +97 -71
  19. package/app/components/aside/major/PaneContentScroll.vue +23 -23
  20. package/app/components/aside/major/PaneSwitch.vue +54 -54
  21. package/app/components/aside/major/PaneSwitchButton.vue +63 -63
  22. package/app/components/aside/major/SiteInfo.vue +85 -85
  23. package/app/components/aside/major/panes/Language.vue +79 -79
  24. package/app/components/aside/major/panes/Pages.vue +50 -34
  25. package/app/components/aside/major/panes/Search.vue +11 -11
  26. package/app/components/aside/major/panes/nav/Nav.vue +92 -91
  27. package/app/components/aside/major/panes/nav/NavBook.vue +95 -86
  28. package/app/components/aside/major/panes/nav/NavBookLoading.vue +24 -24
  29. package/app/components/aside/major/panes/nav/NavGlobal.vue +16 -16
  30. package/app/components/aside/major/panes/nav/fnav/FNav.vue +105 -105
  31. package/app/components/aside/major/panes/nav/fnav/FNavBook.vue +32 -32
  32. package/app/components/aside/major/panes/nav/fnav/FNavFlags.vue +40 -40
  33. package/app/components/aside/major/panes/nav/fnav/FNavFolder.vue +60 -60
  34. package/app/components/aside/major/panes/nav/fnav/FNavItem.vue +34 -34
  35. package/app/components/aside/major/panes/nav/fnav/FNavSeparator.vue +80 -80
  36. package/app/components/aside/major/panes/nav/fnav/FNavTopic.vue +24 -24
  37. package/app/components/aside/major/panes/other/ItemContent.vue +29 -29
  38. package/app/components/aside/major/panes/other/ItemGenerator.vue +13 -15
  39. package/app/components/aside/major/panes/other/ItemTheme.vue +54 -54
  40. package/app/components/aside/major/panes/other/Other.vue +16 -16
  41. package/app/components/aside/minor/{Contribute.vue → AsideMinorContribute.vue} +175 -145
  42. package/app/components/aside/minor/AsideMinorNews.vue +11 -11
  43. package/app/components/aside/minor/AsideMinorPane.vue +15 -15
  44. package/app/components/aside/minor/AsideMinorTopLink.vue +66 -67
  45. package/app/components/aside/minor/content/AsideMinorContent.vue +89 -92
  46. package/app/components/aside/minor/contributor/AsideMinorContributor.vue +78 -0
  47. package/app/components/aside/minor/contributor/BookContribution.vue +64 -0
  48. package/app/components/aside/minor/topic/AsideMinorTopic.vue +31 -32
  49. package/app/components/aside/minor/topic/TopicContributors.vue +183 -177
  50. package/app/components/aside/minor/topic/TopicNav.vue +49 -49
  51. package/app/components/aside/minor/topic/TopicToc.vue +212 -203
  52. package/app/components/aside/minor/topic/TopicTocItem.vue +19 -31
  53. package/app/components/aside/utils/AsideOverlayPane.vue +40 -40
  54. package/app/components/bitran/BitranContent.vue +90 -63
  55. package/app/components/bitran/RenderWrapper.vue +10 -10
  56. package/app/components/contributor/ContributorListItem.vue +43 -35
  57. package/app/components/main/MainActionButton.vue +51 -0
  58. package/app/components/main/MainBitranContent.vue +55 -0
  59. package/app/components/main/{utils/Breadcrumb.vue → MainBreadcrumb.vue} +63 -75
  60. package/app/components/main/MainDescription.vue +24 -0
  61. package/app/components/main/MainSection.vue +58 -0
  62. package/app/components/main/MainTitle.vue +76 -0
  63. package/app/components/main/cameo/MainCameo.vue +135 -0
  64. package/app/components/main/cameo/MainCameoData.vue +232 -0
  65. package/app/components/main/content/ContentBreadcrumb.vue +28 -0
  66. package/app/components/main/{utils → content}/ContentDecoration.vue +29 -29
  67. package/app/components/main/{utils → content}/ContentPopover.vue +188 -176
  68. package/app/components/main/{utils → content}/ContentPopovers.vue +111 -105
  69. package/app/components/main/{utils → content}/ContentReferences.vue +70 -70
  70. package/app/components/main/{utils → content}/reference/ReferenceGroup.vue +38 -38
  71. package/app/components/main/{utils → content}/reference/ReferenceItem.vue +70 -68
  72. package/app/components/main/{utils → content}/reference/ReferenceSource.vue +120 -116
  73. package/app/components/main/topic/MainTopic.vue +76 -79
  74. package/app/components/main/topic/TopicPartSwitch.vue +119 -118
  75. package/app/components/preview/Preview.vue +186 -177
  76. package/app/components/preview/PreviewDisplay.vue +139 -139
  77. package/app/components/preview/PreviewFooterAction.vue +73 -73
  78. package/app/components/preview/PreviewLoading.vue +14 -14
  79. package/app/components/preview/PreviewScreen.vue +141 -99
  80. package/app/components/preview/display/Alert.vue +50 -50
  81. package/app/components/preview/display/Custom.vue +18 -18
  82. package/app/components/preview/display/GenericLink.vue +48 -48
  83. package/app/components/preview/display/PageLink.vue +22 -20
  84. package/app/components/preview/display/Unique.vue +46 -49
  85. package/app/components/sponsor/SponsorTier1.vue +89 -0
  86. package/app/components/sponsor/SponsorTier2.vue +109 -0
  87. package/app/components/transition/Fade.vue +19 -19
  88. package/app/components/tree/TreeContainer.vue +11 -11
  89. package/app/components/tree/TreeItem.vue +93 -89
  90. package/app/composables/adsAllowed.ts +11 -0
  91. package/app/composables/asset.ts +12 -0
  92. package/app/composables/bitran.ts +108 -127
  93. package/app/composables/bitranLocation.ts +7 -7
  94. package/app/composables/contentData.ts +38 -36
  95. package/app/composables/contentPage.ts +168 -156
  96. package/app/composables/contentRoute.ts +45 -45
  97. package/app/composables/darkMagic.ts +24 -24
  98. package/app/composables/externalApi.ts +69 -63
  99. package/app/composables/favicon.ts +8 -8
  100. package/app/composables/formatText.ts +99 -86
  101. package/app/composables/head.ts +24 -0
  102. package/app/composables/majorPane.ts +62 -60
  103. package/app/composables/phrases.ts +77 -80
  104. package/app/composables/theme.ts +29 -29
  105. package/app/composables/url.ts +43 -33
  106. package/app/pages/_test/preview.vue +110 -110
  107. package/app/pages/article/[...articleId].vue +3 -3
  108. package/app/pages/book/[...bookId].vue +42 -47
  109. package/app/pages/contributor/[contributorId].vue +227 -0
  110. package/app/pages/contributors.vue +184 -0
  111. package/app/pages/group/[...groupId].vue +53 -65
  112. package/app/pages/index.vue +32 -32
  113. package/app/pages/practice/[...practice].vue +3 -3
  114. package/app/pages/sponsors.vue +95 -0
  115. package/app/pages/summary/[...summaryId].vue +3 -3
  116. package/app/plugins/analytics.ts +95 -0
  117. package/app/plugins/prerender.server.ts +34 -0
  118. package/app/public/favicon/article.svg +5 -5
  119. package/app/public/favicon/default.svg +3 -3
  120. package/app/public/favicon/practice.svg +3 -3
  121. package/app/public/favicon/summary.svg +4 -4
  122. package/app/public/logotype.svg +2 -2
  123. package/app/scripts/_immediate.js +9 -9
  124. package/app/scripts/aside/index.ts +59 -59
  125. package/app/scripts/aside/major/nav.ts +26 -26
  126. package/app/scripts/aside/minor/state.ts +37 -37
  127. package/app/scripts/flag.ts +28 -28
  128. package/app/scripts/og.ts +28 -27
  129. package/app/scripts/preview/build.ts +76 -73
  130. package/app/scripts/preview/data/alert.ts +19 -19
  131. package/app/scripts/preview/data/custom.ts +8 -8
  132. package/app/scripts/preview/data/genericLink.ts +24 -24
  133. package/app/scripts/preview/data/pageLink.ts +23 -20
  134. package/app/scripts/preview/data/unique.ts +69 -70
  135. package/app/scripts/preview/data.ts +24 -24
  136. package/app/scripts/preview/display.ts +37 -37
  137. package/app/scripts/preview/footer.ts +9 -9
  138. package/app/scripts/preview/request.ts +51 -51
  139. package/app/scripts/preview/state.ts +63 -63
  140. package/app/styles/_immediate.css +7 -7
  141. package/app/styles/_util.scss +43 -43
  142. package/app/styles/_utils.scss +44 -44
  143. package/app/styles/app.scss +91 -91
  144. package/app/styles/def/_bp.scss +27 -27
  145. package/app/styles/def/_size.scss +7 -7
  146. package/app/styles/def/_z.scss +5 -5
  147. package/app/styles/normalize.scss +49 -63
  148. package/app/styles/partials/_darkMagic.scss +5 -5
  149. package/app/styles/partials/_fnav.scss +15 -15
  150. package/app/styles/partials/_preview.scss +5 -5
  151. package/bin/erudit.mjs +2 -0
  152. package/const.ts +3 -0
  153. package/globalPath.ts +21 -21
  154. package/globals/bitran.ts +1 -47
  155. package/globals/cameo.ts +5 -0
  156. package/globals/content.ts +27 -22
  157. package/globals/contributor.ts +5 -5
  158. package/globals/erudit.ts +5 -5
  159. package/globals/register.ts +23 -18
  160. package/globals/sponsor.ts +17 -0
  161. package/languages/en.ts +108 -95
  162. package/languages/ru.ts +112 -99
  163. package/module/bitran.ts +66 -34
  164. package/module/config.ts +35 -34
  165. package/module/imports.ts +74 -46
  166. package/module/index.ts +47 -47
  167. package/module/logger.ts +10 -10
  168. package/module/paths.ts +22 -22
  169. package/module/restart.ts +61 -61
  170. package/nuxt.config.ts +126 -112
  171. package/package.json +22 -15
  172. package/server/api/aside/major/nav/bookIds.ts +5 -5
  173. package/server/api/aside/major/nav/bookNav/[...bookId].ts +17 -20
  174. package/server/api/aside/major/nav/global.ts +7 -7
  175. package/server/api/aside/minor/book/[...bookId].ts +18 -0
  176. package/server/api/aside/minor/contributor/[contributorId].ts +18 -0
  177. package/server/api/aside/minor/group/[...groupId].ts +18 -0
  178. package/server/api/aside/minor/news.ts +7 -7
  179. package/server/api/aside/minor/topic.ts +36 -0
  180. package/server/api/bitran/content/[...location].ts +13 -0
  181. package/server/api/bitran/toc/[...location].ts +9 -0
  182. package/server/api/cameo/data/[cameoId].ts +42 -0
  183. package/server/api/cameo/ids.ts +5 -0
  184. package/server/api/content/data.ts +75 -72
  185. package/server/api/contributor/count.ts +6 -6
  186. package/server/api/contributor/list.ts +44 -0
  187. package/server/api/contributor/page/[contributorId].ts +14 -0
  188. package/server/api/fake/content.ts +11 -11
  189. package/server/api/fake/shared/languages.ts +12 -12
  190. package/server/api/language/functions.ts +12 -12
  191. package/server/api/language/phrase/[phraseId].ts +19 -19
  192. package/server/api/language/phraseIds.ts +8 -8
  193. package/server/api/prerender/assets/cameo.ts +14 -0
  194. package/server/api/prerender/assets/contributor.ts +12 -0
  195. package/server/api/prerender/assets/sponsor.ts +13 -0
  196. package/server/api/prerender/cameos.ts +12 -0
  197. package/server/api/prerender/language.ts +9 -0
  198. package/server/api/preview/page/[...parts].ts +78 -51
  199. package/server/api/preview/unique/{[location].ts → [...location].ts} +48 -55
  200. package/server/api/problem/generator/[...path].ts +26 -0
  201. package/server/api/sponsor/cameo/data/[sponsorId].ts +51 -0
  202. package/server/api/sponsor/cameo/ids.ts +5 -0
  203. package/server/api/sponsor/count.ts +5 -0
  204. package/server/api/sponsor/list.ts +36 -0
  205. package/server/plugin/bitran/content.ts +252 -187
  206. package/server/plugin/bitran/{products → elements}/include.ts +229 -230
  207. package/server/plugin/bitran/location.ts +43 -25
  208. package/server/plugin/bitran/toc.ts +94 -83
  209. package/server/plugin/bitran/transpiler.ts +18 -46
  210. package/server/plugin/build/close.ts +12 -10
  211. package/server/plugin/build/jobs/content/builderArgs.ts +8 -8
  212. package/server/plugin/build/jobs/content/generic.ts +191 -176
  213. package/server/plugin/build/jobs/content/parse.ts +113 -100
  214. package/server/plugin/build/jobs/content/path.ts +6 -6
  215. package/server/plugin/build/jobs/content/type/book.ts +9 -9
  216. package/server/plugin/build/jobs/content/type/group.ts +37 -37
  217. package/server/plugin/build/jobs/content/type/topic.ts +36 -36
  218. package/server/plugin/build/jobs/contributors.ts +69 -66
  219. package/server/plugin/build/jobs/language.ts +36 -36
  220. package/server/plugin/build/jobs/nav.ts +345 -210
  221. package/server/plugin/build/process.ts +34 -25
  222. package/server/plugin/build/rebuild.ts +68 -55
  223. package/server/plugin/build/setup.ts +19 -21
  224. package/server/plugin/content/context.ts +119 -112
  225. package/server/plugin/db/entities/Book.ts +7 -7
  226. package/server/plugin/db/entities/Content.ts +45 -49
  227. package/server/plugin/db/entities/Contribution.ts +10 -10
  228. package/server/plugin/db/entities/Contributor.ts +25 -16
  229. package/server/plugin/db/entities/File.ts +10 -0
  230. package/server/plugin/db/entities/Group.ts +14 -14
  231. package/server/plugin/db/entities/Hash.ts +15 -15
  232. package/server/plugin/db/entities/Topic.ts +20 -20
  233. package/server/plugin/db/entities/Unique.ts +21 -21
  234. package/server/plugin/db/reset.ts +12 -0
  235. package/server/plugin/db/setup.ts +49 -34
  236. package/server/plugin/global.ts +18 -18
  237. package/server/plugin/importer.ts +16 -12
  238. package/server/plugin/index.ts +9 -9
  239. package/server/plugin/logger.ts +23 -23
  240. package/server/plugin/nav/node.ts +27 -26
  241. package/server/plugin/nav/utils.ts +179 -129
  242. package/server/plugin/repository/asideMinor.ts +51 -0
  243. package/server/plugin/repository/book.ts +39 -21
  244. package/server/plugin/repository/cameo.ts +16 -0
  245. package/server/plugin/repository/content.ts +240 -238
  246. package/server/plugin/repository/contentId.ts +40 -0
  247. package/server/plugin/repository/contributor.ts +129 -8
  248. package/server/plugin/repository/file.ts +10 -0
  249. package/server/plugin/repository/frontNav.ts +145 -148
  250. package/server/plugin/repository/topic.ts +35 -32
  251. package/server/plugin/sponsor/build.ts +82 -0
  252. package/server/plugin/sponsor/index.ts +5 -0
  253. package/server/plugin/sponsor/repository.ts +56 -0
  254. package/server/routes/asset/[...assetPath].ts +34 -0
  255. package/server/routes/robots.txt.ts +9 -0
  256. package/server/routes/sitemap.xml.ts +103 -0
  257. package/server/tsconfig.json +9 -9
  258. package/shared/aside/minor.ts +55 -50
  259. package/shared/asset.ts +17 -15
  260. package/shared/bitran/content.ts +9 -0
  261. package/shared/bitran/contentId.ts +56 -0
  262. package/shared/bitran/toc.ts +8 -8
  263. package/shared/breadcrumb.ts +7 -0
  264. package/shared/content/bookId.ts +12 -0
  265. package/shared/content/context.ts +9 -9
  266. package/shared/content/data/base.ts +32 -32
  267. package/shared/content/data/index.ts +5 -5
  268. package/shared/content/data/type/book.ts +5 -5
  269. package/shared/content/data/type/group.ts +6 -6
  270. package/shared/content/data/type/topic.ts +11 -11
  271. package/shared/content/previousNext.ts +9 -9
  272. package/shared/contributor.ts +34 -5
  273. package/shared/frontNav.ts +41 -41
  274. package/shared/icons.ts +38 -38
  275. package/shared/image.ts +5 -5
  276. package/shared/link.ts +28 -25
  277. package/shared/popover.ts +8 -0
  278. package/shared/types/language.ts +84 -75
  279. package/shared/utils/objectsEqual.ts +4 -4
  280. package/shared/utils/stringColor.ts +9 -9
  281. package/test/contentId.test.ts +91 -0
  282. package/test/utils/url.test.ts +99 -0
  283. package/tsconfig.json +8 -8
  284. package/utils/contentPath.ts +67 -0
  285. package/utils/ext.ts +41 -0
  286. package/utils/stress.ts +9 -9
  287. package/utils/url.ts +23 -0
  288. package/app/components/ads/BannerTemplate.vue +0 -51
  289. package/app/components/ads/BottomBanner.vue +0 -45
  290. package/app/components/ads/LeftBanner.vue +0 -50
  291. package/app/components/aside/minor/AsideMinorContributor.vue +0 -5
  292. package/app/components/contributor/ContributorAvatar.vue +0 -43
  293. package/app/components/main/utils/ContentDescription.vue +0 -19
  294. package/app/components/main/utils/ContentFlag.vue +0 -15
  295. package/app/components/main/utils/ContentSection.vue +0 -45
  296. package/app/components/main/utils/ContentTitle.vue +0 -39
  297. package/app/composables/bitranContent.ts +0 -37
  298. package/app/pages/members.vue +0 -6
  299. package/app/public/user.svg +0 -10
  300. package/app/scripts/aside/minor/topic.ts +0 -3
  301. package/app/styles/default.scss +0 -83
  302. package/server/api/aside/minor/path.ts +0 -78
  303. package/server/api/bitran/content/[location].ts +0 -7
  304. package/server/api/bitran/toc/[location].ts +0 -7
  305. package/server/plugin/bitran/products/link.ts +0 -116
  306. package/server/plugin/bitran/setup.ts +0 -9
  307. package/server/plugin/content/absoluteId.ts +0 -94
  308. package/shared/bitran/context.ts +0 -8
  309. package/shared/bitran/default.ts +0 -46
  310. package/shared/bitran/link/Link.vue +0 -167
  311. package/shared/bitran/link/factory.ts +0 -24
  312. package/shared/bitran/link/icon.svg +0 -3
  313. package/shared/bitran/link/languages/en.ts +0 -7
  314. package/shared/bitran/link/languages/ru.ts +0 -7
  315. package/shared/bitran/link/renderer.ts +0 -21
  316. package/shared/bitran/link/shared.ts +0 -17
  317. package/shared/bitran/link/target.ts +0 -134
  318. package/shared/bitran/link/transpiler.ts +0 -10
  319. package/shared/bitran/location.ts +0 -166
  320. package/test/bitran/link/target.test.ts +0 -141
  321. package/test/bitran/location.test.ts +0 -143
package/shared/link.ts CHANGED
@@ -1,25 +1,28 @@
1
- import type { ContentType, TopicPart } from 'erudit-cog/schema';
2
- import type { BitranLocation } from './bitran/location';
3
-
4
- export function createBitranLocationLink(location: BitranLocation) {
5
- let link = `/${location.type}/${location.path}`;
6
-
7
- if (location.unique) link += `#${location.unique}`;
8
-
9
- return link;
10
- }
11
-
12
- export function createContentLink(contentType: ContentType, contentId: string) {
13
- // if (contentType === 'topic')
14
- // throw Error(`Use 'createTopicPartLink' to create links to topics!`);
15
-
16
- return `/${contentType}/${contentId}`;
17
- }
18
-
19
- export function createTopicPartLink(topicPart: TopicPart, contentId: string) {
20
- return `/${topicPart}/${contentId}`;
21
- }
22
-
23
- export function createContributorLink(contributorId: string) {
24
- return `/contributor/${contributorId}`;
25
- }
1
+ import type {
2
+ BitranLocation,
3
+ ContentType,
4
+ TopicPart,
5
+ } from '@erudit-js/cog/schema';
6
+
7
+ export function createBitranLocationLink(location: BitranLocation) {
8
+ let link = `/${location.type}/${location.path}/`;
9
+
10
+ if (location.unique) link += `#${location.unique}`;
11
+
12
+ return link;
13
+ }
14
+
15
+ export function createContentLink(contentType: ContentType, contentId: string) {
16
+ // if (contentType === 'topic')
17
+ // throw Error(`Use 'createTopicPartLink' to create links to topics!`);
18
+
19
+ return `/${contentType}/${contentId}/`;
20
+ }
21
+
22
+ export function createTopicPartLink(topicPart: TopicPart, contentId: string) {
23
+ return `/${topicPart}/${contentId}/`;
24
+ }
25
+
26
+ export function createContributorLink(contributorId: string) {
27
+ return `/contributor/${contributorId}/`;
28
+ }
@@ -0,0 +1,8 @@
1
+ import type { MyIconName } from '#my-icons';
2
+
3
+ export interface PopoverData {
4
+ icon: MyIconName | (string & {});
5
+ title: string;
6
+ description: string;
7
+ color: string;
8
+ }
@@ -1,75 +1,84 @@
1
- export interface EruditPhrases {
2
- _language_title: string;
3
- _language_code: string;
4
-
5
- site_info_title: string;
6
- site_info_slogan: string;
7
- seo_index_title: string;
8
- seo_index_description: string;
9
- seo_article_description: (contentTitle: string) => string;
10
- seo_summary_description: (contentTitle: string) => string;
11
- seo_practice_description: (contentTitle: string) => string;
12
- index: string;
13
- pages: string;
14
- search: string;
15
- language: string;
16
- other: string;
17
- ads_replacer: string;
18
- theme: string;
19
- theme_system: string;
20
- theme_light: string;
21
- theme_dark: string;
22
- content: string;
23
- generator: string;
24
- main_page: string;
25
- members: string;
26
- add_translation: string;
27
- empty_nav: string;
28
- flag_dev: string;
29
- flag_dev_description: string;
30
- flag_advanced: string;
31
- flag_advanced_description: string;
32
- flag_secondary: string;
33
- flag_secondary_description: string;
34
- popover_dependencies: string;
35
- popover_dependencies_description: string;
36
- to_index: string;
37
- about_book: string;
38
- close: string;
39
- back: string;
40
- goto: string;
41
- error: string;
42
- external_link: string;
43
- external_link_warn: string;
44
- internal_link: string;
45
- internal_link_warn: string;
46
- book: string;
47
- group: string;
48
- topic: string;
49
- article: string;
50
- summary: string;
51
- practice: string;
52
- contributor: string;
53
- element_id: string;
54
- preview_missing_title: string;
55
- preview_missing_explain: string;
56
- preview_missing_explain_mismatch: string;
57
- preview_hash_mismatch_title: string;
58
- preview_hash_mismatch_explain: string;
59
- current_page_hash: string;
60
- expected_page_hash: string;
61
- empty_toc: string;
62
- contributors: string;
63
- no_contributors: string;
64
- make_contribution: string;
65
- material_improvement: string;
66
- how_to_improve: string;
67
- edit_page: string;
68
- report_problem: string;
69
- references: string;
70
- reference_source_featured: string;
71
- references_description: string;
72
- }
73
-
74
- export type EruditPhraseId = keyof Partial<EruditPhrases>;
75
- export type EruditPhrase = EruditPhrases[keyof EruditPhrases];
1
+ export interface EruditPhrases {
2
+ _language_title: string;
3
+ _language_code: string;
4
+
5
+ site_info_title: string;
6
+ site_info_slogan: string;
7
+ seo_index_title: string;
8
+ seo_index_description: string;
9
+ seo_article_description: (contentTitle: string) => string;
10
+ seo_summary_description: (contentTitle: string) => string;
11
+ seo_practice_description: (contentTitle: string) => string;
12
+ index: string;
13
+ pages: string;
14
+ search: string;
15
+ language: string;
16
+ other: string;
17
+ ads_replacer: string;
18
+ theme: string;
19
+ theme_system: string;
20
+ theme_light: string;
21
+ theme_dark: string;
22
+ content: string;
23
+ main_page: string;
24
+ contributors: string;
25
+ contributors_page_description: string;
26
+ contributors_page_invite: string;
27
+ become_contributor: string;
28
+ contributor: string;
29
+ contribution: string;
30
+ contributions_explain: (count: number) => string;
31
+ contributor_description: (name: string) => string;
32
+ editor: string;
33
+ add_translation: string;
34
+ empty_nav: string;
35
+ flag_dev: string;
36
+ flag_dev_description: string;
37
+ flag_advanced: string;
38
+ flag_advanced_description: string;
39
+ flag_secondary: string;
40
+ flag_secondary_description: string;
41
+ popover_dependencies: string;
42
+ popover_dependencies_description: string;
43
+ to_index: string;
44
+ about_book: string;
45
+ close: string;
46
+ back: string;
47
+ goto: string;
48
+ error: string;
49
+ external_link: string;
50
+ external_link_warn: string;
51
+ internal_link: string;
52
+ internal_link_warn: string;
53
+ book: string;
54
+ group: string;
55
+ topic: string;
56
+ article: string;
57
+ summary: string;
58
+ practice: string;
59
+ element_id: string;
60
+ preview_missing_title: string;
61
+ preview_missing_explain: string;
62
+ preview_missing_explain_mismatch: string;
63
+ preview_hash_mismatch_title: string;
64
+ preview_hash_mismatch_explain: string;
65
+ current_page_hash: string;
66
+ expected_page_hash: string;
67
+ empty_toc: string;
68
+ no_contributors: string;
69
+ make_contribution: string;
70
+ material_improvement: string;
71
+ how_to_improve: string;
72
+ edit_page: string;
73
+ report_problem: string;
74
+ references: string;
75
+ reference_source_featured: string;
76
+ references_description: string;
77
+ // Sponsors
78
+ sponsors: string;
79
+ sponsors_description: string;
80
+ become_sponsor: string;
81
+ }
82
+
83
+ export type EruditPhraseId = keyof Partial<EruditPhrases>;
84
+ export type EruditPhrase = EruditPhrases[keyof EruditPhrases];
@@ -1,4 +1,4 @@
1
- export function objectsEqual(a: any, b: any) {
2
- const str = (obj: any) => JSON.stringify(obj)?.split('').sort().join('');
3
- return str(a) === str(b);
4
- }
1
+ export function objectsEqual(a: any, b: any) {
2
+ const str = (obj: any) => JSON.stringify(obj)?.split('').sort().join('');
3
+ return str(a) === str(b);
4
+ }
@@ -1,9 +1,9 @@
1
- export function stringColor(text: string) {
2
- let hash = 0;
3
-
4
- for (let i = 0; i < text.length; i++) {
5
- hash = text.charCodeAt(i) + ((hash << 5) - hash);
6
- }
7
-
8
- return `hsl(${hash % 360}, 85%, 35%)`;
9
- }
1
+ export function stringColor(text: string) {
2
+ let hash = 0;
3
+
4
+ for (let i = 0; i < text.length; i++) {
5
+ hash = text.charCodeAt(i) + ((hash << 5) - hash);
6
+ }
7
+
8
+ return `hsl(${hash % 360}, 85%, 35%)`;
9
+ }
@@ -0,0 +1,91 @@
1
+ import {
2
+ toAbsoluteContentPath,
3
+ resolveContentPath,
4
+ } from '@erudit/shared/bitran/contentId';
5
+
6
+ describe('resolveContentPath', () => {
7
+ it('should handle empty paths', () => {
8
+ expect(resolveContentPath('')).toBe('');
9
+ });
10
+
11
+ it('should not change paths without special symbols', () => {
12
+ expect(resolveContentPath('foo/bar/baz')).toBe('foo/bar/baz');
13
+ });
14
+
15
+ it('should skip empty path parts', () => {
16
+ expect(resolveContentPath('/foo///bar/baz/')).toBe('foo/bar/baz');
17
+ });
18
+
19
+ it('should handle ".." parts', () => {
20
+ const testCases = [
21
+ ['foo/bar/../baz', 'foo/baz'],
22
+ ['../../../foo/bar/baz', 'foo/bar/baz'],
23
+ ['foo/../../../bar/baz', 'bar/baz'],
24
+ ['../../..', ''],
25
+ ];
26
+
27
+ for (const [input, expected] of testCases) {
28
+ expect(resolveContentPath(input!)).toBe(expected);
29
+ }
30
+ });
31
+
32
+ it('should handle "." parts', () => {
33
+ const testCases = [
34
+ ['.', ''],
35
+ ['./foo/bar/baz', 'foo/bar/baz'],
36
+ ['./foo/./bar', 'foo/bar'],
37
+ ['foo/./././bar', 'foo/bar'],
38
+ ];
39
+
40
+ for (const [input, expected] of testCases) {
41
+ expect(resolveContentPath(input!)).toBe(expected);
42
+ }
43
+ });
44
+
45
+ it('should handle paths with both "." and ".." parts', () => {
46
+ const testCases = [
47
+ ['./../.', ''],
48
+ ['foo/./../bar', 'foo/bar'],
49
+ ];
50
+
51
+ for (const [input, expected] of testCases) {
52
+ expect(resolveContentPath(input!)).toBe(expected);
53
+ }
54
+ });
55
+ });
56
+
57
+ describe('toAbsoluteContentPath', () => {
58
+ const bookIds = ['combinatorics', 'group/book'];
59
+
60
+ it('should not use context on absolute paths', () => {
61
+ expect(
62
+ toAbsoluteContentPath('/foo/bar/../baz', 'qux/vaz', bookIds),
63
+ ).toBe('foo/baz');
64
+ });
65
+
66
+ it('should fallback to absolute path if not inside book', () => {
67
+ expect(
68
+ toAbsoluteContentPath('~/foo/bar', 'unknown-book', bookIds),
69
+ ).toBe('foo/bar');
70
+ });
71
+
72
+ it('should correctly handle book-relative paths', () => {
73
+ expect(toAbsoluteContentPath('~/foo/bar', 'group/book', bookIds)).toBe(
74
+ 'group/book/foo/bar',
75
+ );
76
+ });
77
+
78
+ it('should by default append context before given content id', () => {
79
+ const testCases = [
80
+ ['baz/qux', 'foo/bar', 'foo/bar/baz/qux'],
81
+ ['./baz/qux', 'foo/bar', 'foo/bar/baz/qux'],
82
+ ['../baz/qux', 'foo/bar', 'foo/baz/qux'],
83
+ ];
84
+
85
+ for (const [contentId, context, expected] of testCases) {
86
+ expect(toAbsoluteContentPath(contentId!, context!, bookIds)).toBe(
87
+ expected,
88
+ );
89
+ }
90
+ });
91
+ });
@@ -0,0 +1,99 @@
1
+ import { trailingSlash, normalizeUrl } from '../../utils/url';
2
+
3
+ describe('trailingSlash', () => {
4
+ describe('when add is true', () => {
5
+ it('should add trailing slash to path without one', () => {
6
+ expect(trailingSlash('/path', true)).toBe('/path/');
7
+ });
8
+
9
+ it('should preserve trailing slash when path already has one', () => {
10
+ expect(trailingSlash('/path/', true)).toBe('/path/');
11
+ });
12
+
13
+ it('should return root path unchanged', () => {
14
+ expect(trailingSlash('/', true)).toBe('/');
15
+ });
16
+
17
+ it('should add trailing slash to empty path', () => {
18
+ expect(trailingSlash('', true)).toBe('/');
19
+ });
20
+ });
21
+
22
+ describe('when add is false', () => {
23
+ it('should remove trailing slash from path', () => {
24
+ expect(trailingSlash('/path/', false)).toBe('/path');
25
+ });
26
+
27
+ it('should preserve path without trailing slash', () => {
28
+ expect(trailingSlash('/path', false)).toBe('/path');
29
+ });
30
+
31
+ it('should return root path unchanged', () => {
32
+ expect(trailingSlash('/', false)).toBe('/');
33
+ });
34
+
35
+ it('should handle empty path', () => {
36
+ expect(trailingSlash('', false)).toBe('');
37
+ });
38
+ });
39
+ });
40
+
41
+ describe('normalizeUrl', () => {
42
+ describe('with protocol', () => {
43
+ it('should normalize multiple slashes in path', () => {
44
+ expect(
45
+ normalizeUrl('https://example.com//path//to///resource'),
46
+ ).toBe('https://example.com/path/to/resource');
47
+ });
48
+
49
+ it('should preserve protocol and domain', () => {
50
+ expect(normalizeUrl('https://example.com/path')).toBe(
51
+ 'https://example.com/path',
52
+ );
53
+ });
54
+
55
+ it('should handle different protocols', () => {
56
+ expect(normalizeUrl('http://example.com//path')).toBe(
57
+ 'http://example.com/path',
58
+ );
59
+ });
60
+
61
+ it('should handle custom protocols', () => {
62
+ expect(normalizeUrl('custom+protocol://example.com//path')).toBe(
63
+ 'custom+protocol://example.com/path',
64
+ );
65
+ });
66
+ });
67
+
68
+ describe('without protocol', () => {
69
+ it('should normalize multiple slashes in relative path', () => {
70
+ expect(normalizeUrl('/path//to///resource')).toBe(
71
+ '/path/to/resource',
72
+ );
73
+ });
74
+
75
+ it('should handle single slash', () => {
76
+ expect(normalizeUrl('/')).toBe('/');
77
+ });
78
+
79
+ it('should normalize empty segments', () => {
80
+ expect(normalizeUrl('///')).toBe('/');
81
+ });
82
+
83
+ it('should handle path without leading slash', () => {
84
+ expect(normalizeUrl('path//to/resource')).toBe('path/to/resource');
85
+ });
86
+ });
87
+
88
+ describe('edge cases', () => {
89
+ it('should handle empty string', () => {
90
+ expect(normalizeUrl('')).toBe('');
91
+ });
92
+
93
+ it('should handle query parameters and fragments', () => {
94
+ expect(
95
+ normalizeUrl('https://example.com//path?query=1#fragment'),
96
+ ).toBe('https://example.com/path?query=1#fragment');
97
+ });
98
+ });
99
+ });
package/tsconfig.json CHANGED
@@ -1,8 +1,8 @@
1
- {
2
- "extends": "../../playground/.erudit/nuxt/.nuxt/tsconfig.json",
3
- "compilerOptions": {
4
- "experimentalDecorators": true,
5
- "emitDecoratorMetadata": true,
6
- "types": ["@types/jest"]
7
- }
8
- }
1
+ {
2
+ "extends": "../../playground/.erudit/nuxt/.nuxt/tsconfig.json",
3
+ "compilerOptions": {
4
+ "experimentalDecorators": true,
5
+ "emitDecoratorMetadata": true,
6
+ "types": ["@types/jest"]
7
+ }
8
+ }
@@ -0,0 +1,67 @@
1
+ import { readdirSync, statSync } from 'fs';
2
+
3
+ // A directory is considered a "content directory" if it contains book, group, or topic with a file having a .js or .ts extension.
4
+ // When a directory qualifies as a content directory, its name is simplified by removing any leading digits and a '+' or '-' (e.g., "29+foo" becomes "foo").
5
+
6
+ const contentFileRegex = /^(book|group|topic)\.(js|ts)$/i;
7
+
8
+ function normalizePath(path: string): string {
9
+ return path.replace(/\\/g, '/');
10
+ }
11
+
12
+ export function isContentDirectory(directory: string): boolean {
13
+ if (!statSync(directory).isDirectory()) {
14
+ return false;
15
+ }
16
+
17
+ return readdirSync(directory).some((file) => {
18
+ return contentFileRegex.test(file);
19
+ });
20
+ }
21
+
22
+ export function getContentPath(path: string, cwd?: string): string {
23
+ path = normalizePath(path);
24
+ cwd = cwd ? normalizePath(cwd) : undefined;
25
+
26
+ const unresolvedParts = path.split('/').filter(Boolean);
27
+ const resolvedParts: string[] = [];
28
+
29
+ let currentPath = cwd || '';
30
+ while (unresolvedParts.length) {
31
+ const currentPart = unresolvedParts.shift()!;
32
+ currentPath += '/' + currentPart;
33
+ if (isContentDirectory(currentPath)) {
34
+ resolvedParts.push(currentPart.replace(/^\d+(?:\+|-)/, ''));
35
+ } else {
36
+ resolvedParts.push(currentPart);
37
+ }
38
+ }
39
+
40
+ return resolvedParts.join('/');
41
+ }
42
+
43
+ export function scanContentPaths(directory: string): Record<string, string> {
44
+ directory = normalizePath(directory);
45
+
46
+ const map: Record<string, string> = {};
47
+
48
+ function traverse(currentPath: string) {
49
+ const files = readdirSync(currentPath);
50
+
51
+ for (const file of files) {
52
+ const fullPath = currentPath + '/' + file;
53
+ const relativePath = fullPath.slice(directory.length + 1);
54
+ const stat = statSync(fullPath);
55
+
56
+ if (stat.isDirectory()) {
57
+ traverse(fullPath);
58
+ } else {
59
+ map[getContentPath(relativePath, directory)] = relativePath;
60
+ }
61
+ }
62
+ }
63
+
64
+ traverse(directory);
65
+
66
+ return map;
67
+ }
package/utils/ext.ts ADDED
@@ -0,0 +1,41 @@
1
+ export const imageExtensions = [
2
+ 'png',
3
+ 'jpg',
4
+ 'jpeg',
5
+ 'gif',
6
+ 'webp',
7
+ 'avif',
8
+ ] as const;
9
+
10
+ export const videoExtensions = ['mp4', 'webm', 'ogg'] as const;
11
+
12
+ export type FileType = 'image' | 'video' | 'unknown';
13
+
14
+ export function detectFileType(filename: string): FileType {
15
+ const ext = filename.split('.').pop()?.toLowerCase();
16
+
17
+ if (ext) {
18
+ if (imageExtensions.includes(ext as (typeof imageExtensions)[number])) {
19
+ return 'image';
20
+ }
21
+
22
+ if (videoExtensions.includes(ext as (typeof videoExtensions)[number])) {
23
+ return 'video';
24
+ }
25
+ }
26
+
27
+ return 'unknown';
28
+ }
29
+
30
+ export function detectStrictFileType<T extends FileType>(
31
+ filename: string,
32
+ ...types: T[]
33
+ ): T {
34
+ const fileType = detectFileType(filename);
35
+ if (!types.includes(fileType as T)) {
36
+ throw new Error(
37
+ `Invalid file type "${fileType}" of file "${filename}"! Expected one of: ${types.join(', ')}.`,
38
+ );
39
+ }
40
+ return fileType as T;
41
+ }
package/utils/stress.ts CHANGED
@@ -1,9 +1,9 @@
1
- import chalk, { type ChalkInstance } from 'chalk';
2
-
3
- export function stress(message: string, contentChalk?: ChalkInstance) {
4
- return (
5
- chalk.dim(`'`) +
6
- (contentChalk ? contentChalk(message) : chalk.cyanBright(message)) +
7
- chalk.dim(`'`)
8
- );
9
- }
1
+ import chalk, { type ChalkInstance } from 'chalk';
2
+
3
+ export function stress(message: string, contentChalk?: ChalkInstance) {
4
+ return (
5
+ chalk.dim(`'`) +
6
+ (contentChalk ? contentChalk(message) : chalk.cyanBright(message)) +
7
+ chalk.dim(`'`)
8
+ );
9
+ }
package/utils/url.ts ADDED
@@ -0,0 +1,23 @@
1
+ export function trailingSlash(path: string, add: boolean): string {
2
+ if (path === '/') {
3
+ return '/';
4
+ }
5
+
6
+ if (add) {
7
+ return path.endsWith('/') ? path : `${path}/`;
8
+ }
9
+
10
+ return path.endsWith('/') ? path.slice(0, -1) : path;
11
+ }
12
+
13
+ export function normalizeUrl(url: string): string {
14
+ // Handle protocol and origin separately
15
+ const protocolMatch = url.match(/^([a-z][a-z\d+\-.]*:\/\/[^\/]+)/i);
16
+ const protocol = protocolMatch ? protocolMatch[1] : '';
17
+ const pathPart = protocol ? url.slice(protocol.length) : url;
18
+
19
+ // Normalize path by removing empty segments
20
+ const normalizedPath = pathPart.replace(/\/+/g, '/');
21
+
22
+ return protocol + normalizedPath;
23
+ }
@@ -1,51 +0,0 @@
1
- <script lang="ts" setup>
2
- const props = defineProps<{
3
- bannerId: string;
4
- }>();
5
-
6
- const { getElementId, registerBanner } = useDarkMagic();
7
- const phrase = await usePhrases('ads_replacer', '_language_code');
8
-
9
- onMounted(() => {
10
- registerBanner(props.bannerId);
11
- });
12
- </script>
13
-
14
- <template>
15
- <section :class="$style.darkMagicContainer">
16
- <div :id="getElementId(bannerId)">
17
- <div :class="$style.darkMagicReplacer">
18
- <MyIcon name="pirate" />
19
- <div v-html="phrase.ads_replacer"></div>
20
- </div>
21
- </div>
22
- </section>
23
- </template>
24
-
25
- <style lang="scss" module>
26
- .darkMagicContainer {
27
- --maxHeight: 300px;
28
-
29
- > div {
30
- width: 100%;
31
- max-height: var(--maxHeight);
32
- }
33
- }
34
-
35
- .darkMagicReplacer {
36
- max-height: var(--maxHeight);
37
- height: var(--maxHeight);
38
- width: 100%;
39
- background: var(--bgAccent);
40
- box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1) inset;
41
- color: var(--textMuted);
42
-
43
- @include dark {
44
- box-shadow: 0px 0px 3px 1px rgba(white, 0.025) inset;
45
- }
46
-
47
- strong {
48
- color: inherit;
49
- }
50
- }
51
- </style>