@openedx/frontend-base 1.0.0-alpha.0

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 (356) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +112 -0
  3. package/config/babel/babel.config.js +28 -0
  4. package/config/config-helpers/createConfig.js +13 -0
  5. package/config/config-helpers/createLintConfig.js +16 -0
  6. package/config/config-helpers/getBaseConfig.js +12 -0
  7. package/config/defaultConfigPaths.js +35 -0
  8. package/config/eslint/base.eslint.config.js +113 -0
  9. package/config/index.js +12 -0
  10. package/config/jest/jest.config.js +30 -0
  11. package/config/tsconfig.json +32 -0
  12. package/config/types.js +25 -0
  13. package/config/webpack/common-config/all/getCodeRules.js +52 -0
  14. package/config/webpack/common-config/all/getFileLoaderRules.js +26 -0
  15. package/config/webpack/common-config/all/getIgnoreWarnings.js +14 -0
  16. package/config/webpack/common-config/all/getImageMinimizer.js +25 -0
  17. package/config/webpack/common-config/all/getStylesheetRule.js +112 -0
  18. package/config/webpack/common-config/dev/getDevServer.js +38 -0
  19. package/config/webpack/common-config/index.js +18 -0
  20. package/config/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
  21. package/config/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
  22. package/config/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
  23. package/config/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
  24. package/config/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
  25. package/config/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
  26. package/config/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
  27. package/config/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
  28. package/config/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
  29. package/config/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
  30. package/config/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
  31. package/config/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
  32. package/config/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
  33. package/config/webpack/types.js +2 -0
  34. package/config/webpack/utils/getLocalAliases.js +65 -0
  35. package/config/webpack/utils/getPublicPath.js +6 -0
  36. package/config/webpack/utils/getResolvedSiteConfigPath.js +32 -0
  37. package/config/webpack/utils/paragonUtils.js +138 -0
  38. package/config/webpack/webpack.config.build.js +80 -0
  39. package/config/webpack/webpack.config.dev.js +76 -0
  40. package/config/webpack/webpack.config.dev.shell.js +110 -0
  41. package/eslint.config.js +18 -0
  42. package/frontend-base.d.ts +8 -0
  43. package/index.ts +7 -0
  44. package/jest.config.js +7 -0
  45. package/openedx-frontend-base.tgz +0 -0
  46. package/package.json +149 -0
  47. package/runtime/analytics/MockAnalyticsService.js +71 -0
  48. package/runtime/analytics/SegmentAnalyticsService.js +243 -0
  49. package/runtime/analytics/index.ts +12 -0
  50. package/runtime/analytics/interface.js +145 -0
  51. package/runtime/auth/AxiosCsrfTokenService.js +60 -0
  52. package/runtime/auth/AxiosJwtAuthService.js +363 -0
  53. package/runtime/auth/AxiosJwtTokenService.js +134 -0
  54. package/runtime/auth/LocalForageCache.js +76 -0
  55. package/runtime/auth/MockAuthService.js +278 -0
  56. package/runtime/auth/index.ts +19 -0
  57. package/runtime/auth/interceptors/createCsrfTokenProviderInterceptor.js +36 -0
  58. package/runtime/auth/interceptors/createJwtTokenProviderInterceptor.js +37 -0
  59. package/runtime/auth/interceptors/createProcessAxiosRequestErrorInterceptor.js +20 -0
  60. package/runtime/auth/interceptors/createRetryInterceptor.js +74 -0
  61. package/runtime/auth/interface.js +309 -0
  62. package/runtime/auth/utils.js +105 -0
  63. package/runtime/babel.config.js +3 -0
  64. package/runtime/config/index.ts +295 -0
  65. package/runtime/constants.ts +68 -0
  66. package/runtime/i18n/index.js +118 -0
  67. package/runtime/i18n/injectIntlWithShim.jsx +48 -0
  68. package/runtime/i18n/lib.ts +272 -0
  69. package/runtime/index.ts +134 -0
  70. package/runtime/initialize.js +352 -0
  71. package/runtime/jest.config.js +32 -0
  72. package/runtime/logging/MockLoggingService.js +31 -0
  73. package/runtime/logging/NewRelicLoggingService.js +184 -0
  74. package/runtime/logging/index.ts +9 -0
  75. package/runtime/logging/interface.js +109 -0
  76. package/runtime/logging/types.ts +4 -0
  77. package/runtime/react/AuthenticatedPageRoute.jsx +43 -0
  78. package/runtime/react/CombinedAppProvider.tsx +46 -0
  79. package/runtime/react/CurrentAppContext.tsx +25 -0
  80. package/runtime/react/CurrentAppProvider.tsx +46 -0
  81. package/runtime/react/Divider.tsx +5 -0
  82. package/runtime/react/ErrorBoundary.jsx +47 -0
  83. package/runtime/react/ErrorPage.jsx +72 -0
  84. package/runtime/react/LoginRedirect.jsx +16 -0
  85. package/runtime/react/PageWrap.jsx +24 -0
  86. package/runtime/react/SiteContext.tsx +32 -0
  87. package/runtime/react/SiteProvider.tsx +78 -0
  88. package/runtime/react/hooks.ts +106 -0
  89. package/runtime/react/index.ts +19 -0
  90. package/runtime/routing/index.ts +1 -0
  91. package/runtime/routing/utils.ts +34 -0
  92. package/runtime/scripts/GoogleAnalyticsLoader.ts +59 -0
  93. package/runtime/scripts/index.ts +1 -0
  94. package/runtime/setupTest.js +49 -0
  95. package/runtime/slots/Slot.tsx +32 -0
  96. package/runtime/slots/SlotContext.tsx +8 -0
  97. package/runtime/slots/hooks.ts +35 -0
  98. package/runtime/slots/index.ts +7 -0
  99. package/runtime/slots/layout/DefaultSlotLayout.tsx +9 -0
  100. package/runtime/slots/layout/hooks.ts +65 -0
  101. package/runtime/slots/layout/index.ts +5 -0
  102. package/runtime/slots/layout/types.ts +45 -0
  103. package/runtime/slots/layout/utils.ts +14 -0
  104. package/runtime/slots/types.ts +23 -0
  105. package/runtime/slots/utils.ts +59 -0
  106. package/runtime/slots/widget/WidgetContext.tsx +9 -0
  107. package/runtime/slots/widget/WidgetProvider.tsx +30 -0
  108. package/runtime/slots/widget/hooks.ts +105 -0
  109. package/runtime/slots/widget/iframe/IFrameContentWrapper.messages.tsx +16 -0
  110. package/runtime/slots/widget/iframe/IFrameContentWrapper.tsx +84 -0
  111. package/runtime/slots/widget/iframe/IFrameWidget.tsx +59 -0
  112. package/runtime/slots/widget/iframe/constants.ts +19 -0
  113. package/runtime/slots/widget/iframe/hooks.ts +179 -0
  114. package/runtime/slots/widget/iframe/index.ts +6 -0
  115. package/runtime/slots/widget/iframe/types.ts +7 -0
  116. package/runtime/slots/widget/index.ts +6 -0
  117. package/runtime/slots/widget/types.ts +134 -0
  118. package/runtime/slots/widget/utils.tsx +201 -0
  119. package/runtime/subscriptions.ts +60 -0
  120. package/runtime/testing/index.ts +9 -0
  121. package/runtime/testing/initializeMockApp.ts +81 -0
  122. package/runtime/testing/mockMessages.ts +23 -0
  123. package/runtime/utils.js +178 -0
  124. package/shell/DefaultLayout.tsx +18 -0
  125. package/shell/DefaultMain.tsx +7 -0
  126. package/shell/Logo.tsx +28 -0
  127. package/shell/Shell.messages.ts +61 -0
  128. package/shell/Shell.tsx +18 -0
  129. package/shell/app.scss +149 -0
  130. package/shell/app.ts +24 -0
  131. package/shell/babel.config.js +3 -0
  132. package/shell/dev/devFooter/app.tsx +43 -0
  133. package/shell/dev/devFooter/index.ts +1 -0
  134. package/shell/dev/devHeader/BarContext.tsx +13 -0
  135. package/shell/dev/devHeader/BarLink.tsx +16 -0
  136. package/shell/dev/devHeader/BarProvider.tsx +25 -0
  137. package/shell/dev/devHeader/CoursesLink.tsx +16 -0
  138. package/shell/dev/devHeader/FooContext.tsx +13 -0
  139. package/shell/dev/devHeader/FooLink.tsx +16 -0
  140. package/shell/dev/devHeader/FooProvider.tsx +25 -0
  141. package/shell/dev/devHeader/app.tsx +53 -0
  142. package/shell/dev/devHeader/index.ts +1 -0
  143. package/shell/dev/devHeader/providers.tsx +11 -0
  144. package/shell/dev/devHome/HomePage.tsx +28 -0
  145. package/shell/dev/devHome/app.ts +18 -0
  146. package/shell/dev/devHome/i18n/index.ts +27 -0
  147. package/shell/dev/devHome/index.ts +1 -0
  148. package/shell/dev/devHome/messages.ts +11 -0
  149. package/shell/dev/devUser/app.tsx +24 -0
  150. package/shell/dev/devUser/index.ts +1 -0
  151. package/shell/dev/index.ts +5 -0
  152. package/shell/dev/slotShowcase/HorizontalSlotLayout.tsx +11 -0
  153. package/shell/dev/slotShowcase/LayoutWithOptions.tsx +17 -0
  154. package/shell/dev/slotShowcase/SlotShowcasePage.tsx +66 -0
  155. package/shell/dev/slotShowcase/WidgetWithOptions.tsx +11 -0
  156. package/shell/dev/slotShowcase/app.tsx +373 -0
  157. package/shell/dev/slotShowcase/index.ts +1 -0
  158. package/shell/footer/CenterLinks.tsx +11 -0
  159. package/shell/footer/CopyrightNotice.tsx +36 -0
  160. package/shell/footer/Footer.tsx +34 -0
  161. package/shell/footer/LabeledLinkColumn.tsx +19 -0
  162. package/shell/footer/LanguageMenu.tsx +35 -0
  163. package/shell/footer/LanguageMenuItem.tsx +23 -0
  164. package/shell/footer/LeftLinks.tsx +11 -0
  165. package/shell/footer/LegalNotices.tsx +17 -0
  166. package/shell/footer/PoweredBy.tsx +17 -0
  167. package/shell/footer/RevealLinks.tsx +43 -0
  168. package/shell/footer/RightLinks.tsx +11 -0
  169. package/shell/footer/app.tsx +73 -0
  170. package/shell/footer/data/api.ts +48 -0
  171. package/shell/footer/index.ts +2 -0
  172. package/shell/header/AuthenticatedMenu.tsx +32 -0
  173. package/shell/header/Header.tsx +17 -0
  174. package/shell/header/anonymous-menu/AnonymousMenu.tsx +14 -0
  175. package/shell/header/anonymous-menu/LoginButton.tsx +14 -0
  176. package/shell/header/anonymous-menu/RegisterButton.tsx +15 -0
  177. package/shell/header/app.tsx +142 -0
  178. package/shell/header/desktop/DesktopLayout.tsx +22 -0
  179. package/shell/header/desktop/PrimaryNavLinks.tsx +10 -0
  180. package/shell/header/desktop/SecondaryNavLinks.tsx +10 -0
  181. package/shell/header/index.ts +2 -0
  182. package/shell/header/mobile/MobileLayout.tsx +47 -0
  183. package/shell/header/mobile/MobileNavLinks.tsx +10 -0
  184. package/shell/i18n/index.ts +25 -0
  185. package/shell/index.ts +7 -0
  186. package/shell/jest.config.js +30 -0
  187. package/shell/menus/LinkMenuItem.tsx +64 -0
  188. package/shell/menus/NavDropdownMenuSlot.tsx +29 -0
  189. package/shell/menus/ProfileLinkMenuItem.tsx +33 -0
  190. package/shell/menus/data/utils.ts +19 -0
  191. package/shell/public/index.html +10 -0
  192. package/shell/router/createRouter.ts +17 -0
  193. package/shell/router/getAppRoutes.ts +21 -0
  194. package/shell/setupTest.js +48 -0
  195. package/shell/site.config.dev.tsx +49 -0
  196. package/shell/site.tsx +41 -0
  197. package/test-site/app.d.ts +15 -0
  198. package/test-site/dist/176.436443549ebb858db483.js +2 -0
  199. package/test-site/dist/176.436443549ebb858db483.js.map +1 -0
  200. package/test-site/dist/362.536eff787d2380fe246c.js +2 -0
  201. package/test-site/dist/362.536eff787d2380fe246c.js.map +1 -0
  202. package/test-site/dist/653.486966b108d224551296.js +2 -0
  203. package/test-site/dist/653.486966b108d224551296.js.map +1 -0
  204. package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
  205. package/test-site/dist/806.323cf6496ad0a7fe73a7.js +3 -0
  206. package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +106 -0
  207. package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +1 -0
  208. package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +4 -0
  209. package/test-site/dist/app.612058b36c74787759ac.css +61 -0
  210. package/test-site/dist/app.612058b36c74787759ac.css.map +1 -0
  211. package/test-site/dist/app.612058b36c74787759ac.js +2 -0
  212. package/test-site/dist/app.612058b36c74787759ac.js.map +1 -0
  213. package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +1 -0
  214. package/test-site/dist/index.html +1 -0
  215. package/test-site/dist/report.html +39 -0
  216. package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +2 -0
  217. package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +1 -0
  218. package/test-site/eslint.config.js +12 -0
  219. package/test-site/package-lock.json +19226 -0
  220. package/test-site/package.json +29 -0
  221. package/test-site/public/index.html +10 -0
  222. package/test-site/site.config.build.tsx +27 -0
  223. package/test-site/site.config.dev.tsx +27 -0
  224. package/test-site/src/authenticated-page/AuthenticatedPage.tsx +18 -0
  225. package/test-site/src/authenticated-page/i18n/index.ts +27 -0
  226. package/test-site/src/authenticated-page/index.tsx +28 -0
  227. package/test-site/src/example-page/ExamplePage.tsx +79 -0
  228. package/test-site/src/example-page/Image.tsx +11 -0
  229. package/test-site/src/example-page/ParagonPreview.jsx +66 -0
  230. package/test-site/src/example-page/apple.jpg +0 -0
  231. package/test-site/src/example-page/apple.svg +1 -0
  232. package/test-site/src/example-page/index.ts +16 -0
  233. package/test-site/src/i18n/README.md +3 -0
  234. package/test-site/src/i18n/messages/frontend-app-sample/ar.json +4 -0
  235. package/test-site/src/i18n/messages/frontend-app-sample/eo.json +1 -0
  236. package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +4 -0
  237. package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +1 -0
  238. package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +3 -0
  239. package/test-site/src/iframe-widget/IframeWidget.tsx +14 -0
  240. package/test-site/src/iframe-widget/index.ts +16 -0
  241. package/test-site/src/index.tsx +3 -0
  242. package/test-site/src/messages.js +11 -0
  243. package/test-site/src/site.scss +11 -0
  244. package/test-site/tsconfig.json +14 -0
  245. package/tools/babel/babel.config.js +27 -0
  246. package/tools/babel.config.js +3 -0
  247. package/tools/cli/README.md +29 -0
  248. package/tools/cli/commands/pack.ts +9 -0
  249. package/tools/cli/commands/release.ts +27 -0
  250. package/tools/cli/commands/serve.ts +43 -0
  251. package/tools/cli/intl-imports.ts +274 -0
  252. package/tools/cli/openedx.ts +101 -0
  253. package/tools/cli/transifex-utils.ts +75 -0
  254. package/tools/cli/utils/ensureConfigFilenameOption.ts +40 -0
  255. package/tools/cli/utils/formatter.ts +10 -0
  256. package/tools/cli/utils/getResolvedConfigPath.ts +23 -0
  257. package/tools/cli/utils/prettyPrintTitle.ts +15 -0
  258. package/tools/cli/utils/printUsage.ts +53 -0
  259. package/tools/config-helpers/createConfig.ts +8 -0
  260. package/tools/config-helpers/createLintConfig.ts +14 -0
  261. package/tools/config-helpers/getBaseConfig.ts +11 -0
  262. package/tools/defaultConfigPaths.ts +30 -0
  263. package/tools/dist/babel/babel.config.js +28 -0
  264. package/tools/dist/cli/commands/pack.js +14 -0
  265. package/tools/dist/cli/commands/release.js +28 -0
  266. package/tools/dist/cli/commands/serve.js +44 -0
  267. package/tools/dist/cli/intl-imports.js +233 -0
  268. package/tools/dist/cli/openedx.js +100 -0
  269. package/tools/dist/cli/transifex-utils.js +68 -0
  270. package/tools/dist/cli/utils/ensureConfigFilenameOption.js +42 -0
  271. package/tools/dist/cli/utils/formatter.js +10 -0
  272. package/tools/dist/cli/utils/getResolvedConfigPath.js +28 -0
  273. package/tools/dist/cli/utils/prettyPrintTitle.js +17 -0
  274. package/tools/dist/cli/utils/printUsage.js +48 -0
  275. package/tools/dist/config-helpers/createConfig.js +13 -0
  276. package/tools/dist/config-helpers/createLintConfig.js +16 -0
  277. package/tools/dist/config-helpers/getBaseConfig.js +12 -0
  278. package/tools/dist/defaultConfigPaths.js +35 -0
  279. package/tools/dist/eslint/base.eslint.config.js +113 -0
  280. package/tools/dist/eslint.config.js +11 -0
  281. package/tools/dist/index.js +12 -0
  282. package/tools/dist/jest/jest.config.js +30 -0
  283. package/tools/dist/jest.config.js +20 -0
  284. package/tools/dist/types.js +25 -0
  285. package/tools/dist/typescript/tsconfig.json +32 -0
  286. package/tools/dist/webpack/common-config/all/getCodeRules.js +52 -0
  287. package/tools/dist/webpack/common-config/all/getFileLoaderRules.js +26 -0
  288. package/tools/dist/webpack/common-config/all/getIgnoreWarnings.js +14 -0
  289. package/tools/dist/webpack/common-config/all/getImageMinimizer.js +25 -0
  290. package/tools/dist/webpack/common-config/all/getStylesheetRule.js +112 -0
  291. package/tools/dist/webpack/common-config/dev/getDevServer.js +38 -0
  292. package/tools/dist/webpack/common-config/index.js +18 -0
  293. package/tools/dist/webpack/common-config/site/getHtmlWebpackPlugin.js +16 -0
  294. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.js +91 -0
  295. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/index.js +7 -0
  296. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +3 -0
  297. package/tools/dist/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +108 -0
  298. package/tools/dist/webpack/plugins/paragon-webpack-plugin/index.js +7 -0
  299. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +64 -0
  300. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +53 -0
  301. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/index.js +9 -0
  302. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +114 -0
  303. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +146 -0
  304. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +126 -0
  305. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +57 -0
  306. package/tools/dist/webpack/types.js +2 -0
  307. package/tools/dist/webpack/utils/getLocalAliases.js +65 -0
  308. package/tools/dist/webpack/utils/getPublicPath.js +6 -0
  309. package/tools/dist/webpack/utils/getResolvedSiteConfigPath.js +32 -0
  310. package/tools/dist/webpack/utils/paragonUtils.js +138 -0
  311. package/tools/dist/webpack/webpack.config.build.js +80 -0
  312. package/tools/dist/webpack/webpack.config.dev.js +76 -0
  313. package/tools/dist/webpack/webpack.config.dev.shell.js +110 -0
  314. package/tools/eslint/base.eslint.config.js +124 -0
  315. package/tools/eslint/modules.d.ts +5 -0
  316. package/tools/eslint.config.js +15 -0
  317. package/tools/index.ts +3 -0
  318. package/tools/jest/jest.config.js +30 -0
  319. package/tools/jest.config.js +19 -0
  320. package/tools/tsconfig.json +24 -0
  321. package/tools/types.ts +21 -0
  322. package/tools/typescript/tsconfig.json +32 -0
  323. package/tools/webpack/common-config/README.md +15 -0
  324. package/tools/webpack/common-config/all/getCodeRules.ts +51 -0
  325. package/tools/webpack/common-config/all/getFileLoaderRules.ts +23 -0
  326. package/tools/webpack/common-config/all/getIgnoreWarnings.ts +13 -0
  327. package/tools/webpack/common-config/all/getImageMinimizer.ts +26 -0
  328. package/tools/webpack/common-config/all/getStylesheetRule.ts +111 -0
  329. package/tools/webpack/common-config/dev/getDevServer.ts +35 -0
  330. package/tools/webpack/common-config/index.ts +6 -0
  331. package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +11 -0
  332. package/tools/webpack/modules.d.ts +6 -0
  333. package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +102 -0
  334. package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +21 -0
  335. package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +7 -0
  336. package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +3 -0
  337. package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +1 -0
  338. package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +134 -0
  339. package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +3 -0
  340. package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +71 -0
  341. package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +72 -0
  342. package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +6 -0
  343. package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +131 -0
  344. package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +144 -0
  345. package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +106 -0
  346. package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +54 -0
  347. package/tools/webpack/types.ts +69 -0
  348. package/tools/webpack/utils/getLocalAliases.ts +65 -0
  349. package/tools/webpack/utils/getPublicPath.ts +3 -0
  350. package/tools/webpack/utils/getResolvedSiteConfigPath.ts +28 -0
  351. package/tools/webpack/utils/paragonUtils.ts +152 -0
  352. package/tools/webpack/webpack.config.build.ts +93 -0
  353. package/tools/webpack/webpack.config.dev.shell.ts +122 -0
  354. package/tools/webpack/webpack.config.dev.ts +90 -0
  355. package/tsconfig.json +23 -0
  356. package/types.ts +99 -0
@@ -0,0 +1,34 @@
1
+ import { RouteObject } from 'react-router';
2
+
3
+ import { RoleRouteObject } from '../../types';
4
+ import { getSiteConfig } from '../config';
5
+
6
+ export function getUrlByRouteRole(role: string) {
7
+ const { apps, externalRoutes } = getSiteConfig();
8
+
9
+ if (apps) {
10
+ for (const app of apps) {
11
+ if (Array.isArray(app.routes)) {
12
+ for (const route of app.routes) {
13
+ if (route?.handle?.role === role) {
14
+ return route.path ?? null;
15
+ }
16
+ }
17
+ }
18
+ }
19
+ }
20
+
21
+ if (externalRoutes) {
22
+ for (const externalRoute of externalRoutes) {
23
+ if (externalRoute.role === role) {
24
+ return externalRoute.url;
25
+ }
26
+ }
27
+ }
28
+
29
+ return null;
30
+ }
31
+
32
+ export function isRoleRouteObject(match: RouteObject): match is RoleRouteObject {
33
+ return match.handle !== undefined && 'role' in match.handle;
34
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @implements {GoogleAnalyticsLoader}
3
+ * @memberof module:GoogleAnalytics
4
+ */
5
+ class GoogleAnalyticsLoader {
6
+ analyticsId: string;
7
+
8
+ constructor({ config }) {
9
+ this.analyticsId = config.GOOGLE_ANALYTICS_4_ID;
10
+ }
11
+
12
+ loadScript() {
13
+ if (!this.analyticsId) {
14
+ return;
15
+ }
16
+
17
+ global.googleAnalytics = global.googleAnalytics || [];
18
+ // @ts-expect-error We just added googleAnalytics to global, it's there.
19
+ const { googleAnalytics } = global;
20
+
21
+ // If the snippet was invoked do nothing.
22
+ if (googleAnalytics.invoked) {
23
+ return;
24
+ }
25
+
26
+ // Invoked flag, to make sure the snippet
27
+ // is never invoked twice.
28
+ googleAnalytics.invoked = true;
29
+
30
+ googleAnalytics.load = (key, options) => {
31
+ const scriptSrc = document.createElement('script');
32
+ scriptSrc.type = 'text/javascript';
33
+ scriptSrc.async = true;
34
+ scriptSrc.src = `https://www.googletagmanager.com/gtag/js?id=${key}`;
35
+
36
+ const scriptGtag = document.createElement('script');
37
+ scriptGtag.innerHTML = `
38
+ window.dataLayer = window.dataLayer || [];
39
+ function gtag(){dataLayer.push(arguments);}
40
+ gtag('js', new Date());
41
+ gtag('config', '${key}');
42
+ `;
43
+
44
+ // Insert our scripts next to the first script element.
45
+ const first = document.getElementsByTagName('script')[0];
46
+ if (first?.parentNode === null) {
47
+ throw new Error('No script to insert Google analytics script before.');
48
+ }
49
+ first.parentNode.insertBefore(scriptSrc, first);
50
+ first.parentNode.insertBefore(scriptGtag, first);
51
+ googleAnalytics._loadOptions = options;
52
+ };
53
+
54
+ // Load GoogleAnalytics with your key.
55
+ googleAnalytics.load(this.analyticsId);
56
+ }
57
+ }
58
+
59
+ export default GoogleAnalyticsLoader;
@@ -0,0 +1 @@
1
+ export { default as GoogleAnalyticsLoader } from './GoogleAnalyticsLoader';
@@ -0,0 +1,49 @@
1
+ import '@testing-library/jest-dom';
2
+
3
+ import siteConfig from 'site.config';
4
+ import { mergeSiteConfig } from './config';
5
+
6
+ jest.mock('universal-cookie', () => {
7
+ const mCookie = {
8
+ get: jest.fn(),
9
+ remove: jest.fn(),
10
+ };
11
+ return jest.fn(() => mCookie);
12
+ });
13
+
14
+ mergeSiteConfig(siteConfig);
15
+
16
+ global.PARAGON_THEME = {
17
+ paragon: {
18
+ version: '1.0.0',
19
+ themeUrls: {
20
+ core: {
21
+ fileName: 'core.min.css',
22
+ },
23
+ defaults: {
24
+ light: 'light',
25
+ },
26
+ variants: {
27
+ light: {
28
+ fileName: 'light.min.css',
29
+ },
30
+ },
31
+ },
32
+ },
33
+ brand: {
34
+ version: '1.0.0',
35
+ themeUrls: {
36
+ core: {
37
+ fileName: 'core.min.css',
38
+ },
39
+ defaults: {
40
+ light: 'light',
41
+ },
42
+ variants: {
43
+ light: {
44
+ fileName: 'light.min.css',
45
+ },
46
+ },
47
+ },
48
+ },
49
+ };
@@ -0,0 +1,32 @@
1
+ import { ComponentType, createElement, isValidElement, ReactNode } from 'react';
2
+ import DefaultSlotLayout from './layout/DefaultSlotLayout';
3
+ import { useLayoutForSlotId } from './layout/hooks';
4
+ import SlotContext from './SlotContext';
5
+
6
+ interface SlotProps {
7
+ id: string,
8
+ children?: ReactNode,
9
+ layout?: ComponentType | ReactNode,
10
+ [key: string]: unknown,
11
+ }
12
+
13
+ export default function Slot({ id, children, layout = DefaultSlotLayout, ...props }: SlotProps) {
14
+ let layoutElement: ComponentType | ReactNode = layout;
15
+
16
+ const overrideLayout = useLayoutForSlotId(id);
17
+
18
+ // Weed out any ReactNode types that aren't actually JSX.
19
+ if (overrideLayout && overrideLayout !== null && overrideLayout !== undefined && typeof overrideLayout !== 'boolean') {
20
+ layoutElement = overrideLayout;
21
+ }
22
+
23
+ if (!isValidElement(layoutElement)) {
24
+ layoutElement = createElement(layoutElement as ComponentType);
25
+ }
26
+
27
+ return (
28
+ <SlotContext.Provider value={{ id, children, ...props }}>
29
+ {layoutElement}
30
+ </SlotContext.Provider>
31
+ );
32
+ }
@@ -0,0 +1,8 @@
1
+ import { createContext, ReactNode } from 'react';
2
+
3
+ const SlotContext = createContext<{ id: string, children?: ReactNode, [key: string]: unknown }>({
4
+ id: '',
5
+ children: null,
6
+ });
7
+
8
+ export default SlotContext;
@@ -0,0 +1,35 @@
1
+ import { useContext, useEffect, useState } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+
4
+ import { SlotOperation } from './types';
5
+ import { getSlotOperations } from './utils';
6
+ import SlotContext from './SlotContext';
7
+ import { createWidgetAppendOperation } from './widget';
8
+
9
+ /**
10
+ * The useSlotOperations hook will trigger re-renders when the slot configuration changes.
11
+ * It is a fundamental hook that is used by many of the others to ensure they're using up-to-date
12
+ * config as it changes.
13
+ */
14
+ export function useSlotOperations(id: string) {
15
+ const { children } = useSlotContext();
16
+ const location = useLocation();
17
+ const [operations, setOperations] = useState<SlotOperation[]>([]);
18
+
19
+ useEffect(() => {
20
+ // Setting default content has to happen inside `useEffect()` so that re-renders only happen
21
+ // when [children] props change. This avoids an endless render loop. After all, the whole
22
+ // point of a slot is to modify its children via slot operations.
23
+ const defaultOperation = createWidgetAppendOperation('defaultContent', id, children);
24
+ setOperations(getSlotOperations(id, defaultOperation));
25
+
26
+ // We depend on [location] to force re-renders on navigation. This guarantees changes in active
27
+ // roles (and thus, changes in what conditional widgets are shown) properly.
28
+ }, [id, children, location]);
29
+
30
+ return operations;
31
+ }
32
+
33
+ export function useSlotContext() {
34
+ return useContext(SlotContext);
35
+ }
@@ -0,0 +1,7 @@
1
+ export * from './hooks';
2
+ export type * from './types';
3
+ export * from './layout';
4
+ export { default as Slot } from './Slot';
5
+ export { default as SlotContext } from './SlotContext';
6
+ export * from './utils';
7
+ export * from './widget';
@@ -0,0 +1,9 @@
1
+ import { useWidgets } from '../widget/hooks';
2
+
3
+ export default function DefaultSlotLayout() {
4
+ const widgets = useWidgets();
5
+
6
+ return (
7
+ <>{widgets}</>
8
+ );
9
+ }
@@ -0,0 +1,65 @@
1
+ import { ComponentType, ReactNode, useEffect, useState } from 'react';
2
+ import { useSlotContext, useSlotOperations } from '../hooks';
3
+ import { isSlotOperationConditionSatisfied } from '../utils';
4
+ import { LayoutComponentProps, LayoutElementProps, LayoutOperation } from './types';
5
+ import { isLayoutOperation, isLayoutOptionsOperation, isLayoutReplaceOperation } from './utils';
6
+
7
+ function hasLayoutComponentProps(operation: LayoutOperation): operation is (LayoutOperation & LayoutComponentProps) {
8
+ return isLayoutOperation(operation) && 'component' in operation;
9
+ }
10
+
11
+ function hasLayoutElementProps(operation: LayoutOperation): operation is (LayoutOperation & LayoutElementProps) {
12
+ return isLayoutOperation(operation) && 'element' in operation;
13
+ }
14
+
15
+ export function useLayoutForSlotId(id: string) {
16
+ const operations = useSlotOperations(id);
17
+ let layoutElement: ReactNode | ComponentType = null;
18
+
19
+ for (const operation of operations) {
20
+ if (isSlotOperationConditionSatisfied(operation)) {
21
+ if (isLayoutReplaceOperation(operation)) {
22
+ if (hasLayoutComponentProps(operation)) {
23
+ layoutElement = operation.component;
24
+ } else if (hasLayoutElementProps(operation)) {
25
+ layoutElement = operation.element;
26
+ }
27
+ }
28
+ }
29
+ }
30
+ return layoutElement;
31
+ }
32
+
33
+ /**
34
+ * useLayoutOptions iterates through the slot's operations to find any which are "layout
35
+ * options" operations. It merges these into a single object and returns them - operations are
36
+ * merged in declaration order, meaning last one in wins. useLayoutOptions only triggers a
37
+ * re-render when the options change.
38
+ */
39
+ export function useLayoutOptions() {
40
+ const { id } = useSlotContext();
41
+ return useLayoutOptionsForId(id);
42
+ }
43
+
44
+ export function useLayoutOptionsForId(id: string) {
45
+ const operations = useSlotOperations(id);
46
+ const [options, setOptions] = useState<Record<string, unknown>>({});
47
+
48
+ useEffect(() => {
49
+ const findOptions = () => {
50
+ let nextOptions: Record<string, unknown> = {};
51
+ for (const operation of operations) {
52
+ if (isSlotOperationConditionSatisfied(operation)) {
53
+ if (isLayoutOptionsOperation(operation)) {
54
+ nextOptions = { ...nextOptions, ...operation.options };
55
+ }
56
+ }
57
+ }
58
+ return nextOptions;
59
+ };
60
+
61
+ setOptions(findOptions());
62
+ }, [operations]);
63
+
64
+ return options;
65
+ }
@@ -0,0 +1,5 @@
1
+ export { default as DefaultSlotLayout } from './DefaultSlotLayout';
2
+ export * from './hooks';
3
+ export type * from './types';
4
+ export { LayoutOperationTypes } from './types';
5
+ export * from './utils';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Base UI Layout Operations
3
+ *
4
+ * "Layout" operations are a sub-type of UI operations which affect the layout component of a UI
5
+ * slot. They have no effect on the widgets loaded into a UI slot.
6
+ */
7
+
8
+ import { ReactNode } from 'react';
9
+ import { BaseSlotOperation } from '../types';
10
+
11
+ export enum LayoutOperationTypes {
12
+ OPTIONS = 'layoutOptions',
13
+ REPLACE = 'layoutReplace',
14
+ }
15
+
16
+ export interface BaseLayoutOperation extends BaseSlotOperation {
17
+ op: LayoutOperationTypes,
18
+ }
19
+
20
+ // Concrete UI Layout Operations
21
+
22
+ export type LayoutOptionsOperation = BaseLayoutOperation & {
23
+ op: LayoutOperationTypes.OPTIONS,
24
+ options: Record<string, unknown>,
25
+ };
26
+
27
+ export interface LayoutComponentProps {
28
+ component: React.ComponentType,
29
+ }
30
+
31
+ export interface LayoutElementProps {
32
+ element: ReactNode,
33
+ }
34
+
35
+ export type LayoutRendererProps = (
36
+ LayoutComponentProps | LayoutElementProps
37
+ );
38
+
39
+ export type LayoutReplaceOperation = BaseLayoutOperation & LayoutRendererProps & {
40
+ op: LayoutOperationTypes.REPLACE,
41
+ };
42
+
43
+ // Aggregate UI Layout Operations
44
+
45
+ export type LayoutOperation = LayoutOptionsOperation | LayoutReplaceOperation;
@@ -0,0 +1,14 @@
1
+ import { SlotOperation } from '../types';
2
+ import { LayoutOperation, LayoutOperationTypes, LayoutOptionsOperation, LayoutReplaceOperation } from './types';
3
+
4
+ export function isLayoutOperation(operation: SlotOperation): operation is LayoutOperation {
5
+ return Object.values(LayoutOperationTypes).includes(operation.op as LayoutOperationTypes);
6
+ }
7
+
8
+ export function isLayoutOptionsOperation(operation: SlotOperation): operation is LayoutOptionsOperation {
9
+ return isLayoutOperation(operation) && operation.op === LayoutOperationTypes.OPTIONS;
10
+ }
11
+
12
+ export function isLayoutReplaceOperation(operation: SlotOperation): operation is LayoutReplaceOperation {
13
+ return isLayoutOperation(operation) && operation.op === LayoutOperationTypes.REPLACE;
14
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Base Slot Operations
3
+ *
4
+ * These are properties shared by all slot operations defined below.
5
+ */
6
+
7
+ import { LayoutOperation } from './layout/types';
8
+ import { WidgetOperation } from './widget/types';
9
+
10
+ export interface SlotOperationCondition {
11
+ active?: string[],
12
+ authenticated?: boolean,
13
+ callback?: () => boolean,
14
+ }
15
+
16
+ export interface BaseSlotOperation {
17
+ slotId: string,
18
+ condition?: SlotOperationCondition,
19
+ }
20
+
21
+ // Aggregate Slot Operations
22
+
23
+ export type SlotOperation = WidgetOperation | LayoutOperation;
@@ -0,0 +1,59 @@
1
+ import { getAuthenticatedUser } from '../auth';
2
+ import { getActiveRoles, getSiteConfig } from '../config';
3
+ import { SlotOperation } from './types';
4
+
5
+ export function getSlotOperations(id: string, defaultOperation?: SlotOperation) {
6
+ const { apps } = getSiteConfig();
7
+ const ops: SlotOperation[] = [];
8
+
9
+ if (defaultOperation) {
10
+ ops.push(defaultOperation);
11
+ }
12
+
13
+ if (apps) {
14
+ apps.forEach((app) => {
15
+ if (Array.isArray(app.slots)) {
16
+ app.slots.forEach((operation) => {
17
+ if (operation.slotId === id) {
18
+ ops.push(operation);
19
+ }
20
+ });
21
+ }
22
+ });
23
+ }
24
+
25
+ return ops;
26
+ }
27
+
28
+ export function isSlotOperationConditionSatisfied(operation: SlotOperation) {
29
+ const { condition } = operation;
30
+ if (condition?.authenticated !== undefined) {
31
+ const isAuthenticated = getAuthenticatedUser() !== null;
32
+ // If we failed the authenticated condition, return false.
33
+ if (condition.authenticated !== isAuthenticated) {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ if (condition?.active !== undefined) {
39
+ let activeConditionRoleFound = false;
40
+ const activeRoles: string[] = getActiveRoles();
41
+ for (const conditionRole of condition.active) {
42
+ if (activeRoles.includes(conditionRole)) {
43
+ activeConditionRoleFound = true;
44
+ break;
45
+ }
46
+ }
47
+ // If we couldn't find an active role in our list, then we've failed this condition.
48
+ if (!activeConditionRoleFound) {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ if (condition?.callback !== undefined && condition.callback() === false) {
54
+ return false;
55
+ }
56
+
57
+ // If there was no condition, we return true.
58
+ return true;
59
+ }
@@ -0,0 +1,9 @@
1
+ import { createContext } from 'react';
2
+
3
+ const WidgetContext = createContext<{ slotId: string, widgetId: string, role?: string }>({
4
+ slotId: '',
5
+ widgetId: '',
6
+ role: undefined,
7
+ });
8
+
9
+ export default WidgetContext;
@@ -0,0 +1,30 @@
1
+ import { ReactNode, useEffect } from 'react';
2
+
3
+ import { addActiveWidgetRole, removeActiveWidgetRole } from '../../config';
4
+ import WidgetContext from './WidgetContext';
5
+
6
+ interface WidgetProviderProps {
7
+ children: ReactNode,
8
+ slotId: string,
9
+ widgetId: string,
10
+ role?: string,
11
+ }
12
+
13
+ export default function WidgetProvider({ children, slotId, widgetId, role }: WidgetProviderProps) {
14
+ useEffect(() => {
15
+ if (role !== undefined) {
16
+ addActiveWidgetRole(role);
17
+ }
18
+ return () => {
19
+ if (role !== undefined) {
20
+ removeActiveWidgetRole(role);
21
+ }
22
+ };
23
+ }, [role]);
24
+
25
+ return (
26
+ <WidgetContext.Provider value={{ slotId, widgetId, role }}>
27
+ {children}
28
+ </WidgetContext.Provider>
29
+ );
30
+ }
@@ -0,0 +1,105 @@
1
+ import { useContext, useEffect, useState } from 'react';
2
+ import { useSlotContext, useSlotOperations } from '../hooks';
3
+ import { isSlotOperationConditionSatisfied } from '../utils';
4
+ import { WidgetOperation } from './types';
5
+ import { createWidgets, isWidgetAbsoluteOperation, isWidgetOperation, isWidgetOptionsOperation, isWidgetRelativeOperation } from './utils';
6
+ import WidgetContext from './WidgetContext';
7
+
8
+ export function useWidgets() {
9
+ const { id, ...props } = useSlotContext();
10
+ delete props.children;
11
+ return useWidgetsForId(id, props);
12
+ }
13
+
14
+ export function useWidgetsForId(id: string, componentProps?: Record<string, unknown>) {
15
+ const operations = useSortedWidgetOperations(id);
16
+ return createWidgets(operations, componentProps);
17
+ }
18
+
19
+ export function useWidgetOperations(id: string) {
20
+ const operations = useSlotOperations(id);
21
+ const [widgetOperations, setWidgetOperations] = useState<WidgetOperation[]>([]);
22
+
23
+ useEffect(() => {
24
+ const filteredOperations = operations.filter((operation): operation is WidgetOperation => {
25
+ return isWidgetOperation(operation) && isSlotOperationConditionSatisfied(operation);
26
+ });
27
+
28
+ setWidgetOperations(filteredOperations);
29
+ }, [operations]);
30
+
31
+ return widgetOperations;
32
+ }
33
+
34
+ export function useSortedWidgetOperations(id: string) {
35
+ const operations = useWidgetOperations(id);
36
+ const [sortedOperations, setSortedOperations] = useState<WidgetOperation[]>([]);
37
+
38
+ useEffect(() => {
39
+ // This sorts widget operations in an order that guarantees that any 'related' widgets
40
+ // needed by relatively positioned operations (INSERT_AFTER, INSERT_BEFORE) should already exist
41
+ // by the time the relative operations are evaluated. It means the declaration order of
42
+ // operations in SiteConfig does not prevent an operation from interacting with a widget that was
43
+ // declared 'later'.
44
+ const sortedOperations = operations.sort((a: WidgetOperation, b: WidgetOperation) => {
45
+ // If both operations are widget operations, there are special sorting rules.
46
+ const aAbsolute = isWidgetAbsoluteOperation(a);
47
+ const bAbsolute = isWidgetAbsoluteOperation(b);
48
+ if (aAbsolute && bAbsolute) {
49
+ return 0;
50
+ } else if (aAbsolute) {
51
+ return -1;
52
+ } else if (bAbsolute) {
53
+ return 1;
54
+ } else if (isWidgetRelativeOperation(a) && isWidgetRelativeOperation(b)) {
55
+ if (a.id === b.relatedId) {
56
+ return -1;
57
+ } else if (b.id === a.relatedId) {
58
+ return 1;
59
+ }
60
+ }
61
+
62
+ return 0;
63
+ });
64
+
65
+ setSortedOperations(sortedOperations);
66
+ }, [operations]);
67
+
68
+ return sortedOperations;
69
+ }
70
+
71
+ /**
72
+ * useWidgetOptions iterates through the slot's operations to find any that are "widget options"
73
+ * operations specific to this widget. It merges these into a single object and returns them -
74
+ * operations are merged in declaration order, meaning last one in wins. useWidgetOptions only
75
+ * triggers a re-render when the options change.
76
+ */
77
+ export function useWidgetOptions() {
78
+ const { slotId, widgetId } = useContext(WidgetContext);
79
+ return useWidgetOptionsForId(slotId, widgetId);
80
+ }
81
+
82
+ export function useWidgetOptionsForId(slotId: string, widgetId: string) {
83
+ const operations = useWidgetOperations(slotId);
84
+
85
+ const [options, setOptions] = useState<Record<string, unknown>>({});
86
+ useEffect(() => {
87
+ const findOptions = () => {
88
+ let nextOptions: Record<string, unknown> = {};
89
+ for (const operation of operations) {
90
+ if (isSlotOperationConditionSatisfied(operation)) {
91
+ if (isWidgetOptionsOperation(operation)) {
92
+ if (operation.relatedId === widgetId) {
93
+ nextOptions = { ...nextOptions, ...operation.options };
94
+ }
95
+ }
96
+ }
97
+ }
98
+ return nextOptions;
99
+ };
100
+
101
+ setOptions(findOptions());
102
+ }, [widgetId, operations]);
103
+
104
+ return options;
105
+ }
@@ -0,0 +1,16 @@
1
+ import { defineMessages } from '../../../i18n';
2
+
3
+ const messages = defineMessages({
4
+ loading: {
5
+ id: 'loading.message.text',
6
+ defaultMessage: 'Loading',
7
+ description: 'the feature is currently loading',
8
+ },
9
+ unexpectedError: {
10
+ id: 'unexpected.error.message.text',
11
+ defaultMessage: ' Oops! An error occurred. Please refresh the screen to try again.',
12
+ description: 'error message when an unexpected error occurs',
13
+ },
14
+ });
15
+
16
+ export default messages;