@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,274 @@
1
+ #!/usr/bin/env node
2
+
3
+ const scriptHelpDocument = `
4
+ NAME
5
+ intl-imports.js — Script to generate the src/i18n/index.js file that exports messages from all the languages for Micro-frontends.
6
+
7
+ SYNOPSIS
8
+ intl-imports.js [DIRECTORY ...]
9
+
10
+ DESCRIPTION
11
+ This script is intended to run after 'atlas' has pulled the files.
12
+
13
+ This expects to run inside a Micro-frontend root directory with the following structure:
14
+
15
+ frontend-app-learning $ tree src/i18n/
16
+ src/i18n/
17
+ ├── index.js
18
+ └── messages
19
+ ├── frontend-app-example
20
+ │ ├── ar.json
21
+ │ ├── es_419.json
22
+ │ └── zh_CN.json
23
+ ├── frontend-component-footer
24
+ │ ├── ar.json
25
+ │ ├── es_419.json
26
+ │ └── zh_CN.json
27
+ └── frontend-component-header (empty directory)
28
+
29
+
30
+
31
+ With the structure above it's expected to run with the following command in Makefile:
32
+
33
+
34
+ $ node_modules/.bin/intl-imports.js frontend-component-footer frontend-component-header frontend-app-example
35
+
36
+
37
+ It will generate two type of files:
38
+
39
+ - Main src/i18n/index.js which overrides the Micro-frontend provided with a sample output of:
40
+
41
+ """
42
+ import messagesFromFrontendComponentFooter from './messages/frontend-component-footer';
43
+ // Skipped import due to missing './messages/frontend-component-footer/index.js' likely due to empty translations.
44
+ import messagesFromFrontendAppExample from './messages/frontend-app-example';
45
+
46
+ export default [
47
+ messagesFromFrontendComponentFooter,
48
+ messagesFromFrontendAppExample,
49
+ ];
50
+ """
51
+
52
+ - Each sub-directory has src/i18n/messages/frontend-component-header/index.js which is imported by the main file.:
53
+
54
+ """
55
+ import messagesOfArLanguage from './ar.json';
56
+ import messagesOfDeLanguage from './de.json';
57
+ import messagesOfEs419Language from './es_419.json';
58
+ export default {
59
+ 'ar': messagesOfArLanguage,
60
+ 'de': messagesOfDeLanguage,
61
+ 'es-419': messagesOfEs419Language,
62
+ };
63
+ """
64
+ `;
65
+
66
+ import fs from 'fs';
67
+ import camelCase from 'lodash.camelcase';
68
+ import path from 'path';
69
+
70
+ const loggingPrefix = path.basename(`${__filename}`); // the name of this JS file
71
+
72
+ // Header note for generated src/i18n/index.js file
73
+ const filesCodeGeneratorNoticeHeader = `// This file is generated by the openedx/frontend-base's "intl-import.js" script.
74
+ //
75
+ // Refer to the i18n documents in https://docs.openedx.org/en/latest/developers/references/i18n.html to update
76
+ // the file and use the Micro-frontend i18n pattern in new repositories.
77
+ //
78
+ `;
79
+
80
+ /**
81
+ * Create frontend-app-example/index.js file with proper imports.
82
+ *
83
+ * @param directory - a directory name containing .json files from Transifex e.g. "frontend-app-example".
84
+ * @param log - Mockable process.stdout.write
85
+ * @param writeFileSync - Mockable fs.writeFileSync
86
+ * @param i18nDir - Path to `src/i18n` directory
87
+ *
88
+ * @return object - An object containing directory name and whether its "index.js" file was successfully written.
89
+ */
90
+ function generateSubdirectoryMessageFile({
91
+ directory,
92
+ log,
93
+ writeFileSync,
94
+ i18nDir,
95
+ }: {
96
+ directory: string,
97
+ log: (message: string) => void,
98
+ writeFileSync: (filename: string, content: string) => void,
99
+ i18nDir: string,
100
+
101
+ }) {
102
+ const importLines: string[] = [];
103
+ const messagesLines: string[] = [];
104
+ const counter = { nonEmptyLanguages: 0 };
105
+ const messagesDir = `${i18nDir}/messages`; // The directory of Micro-frontend i18n messages
106
+
107
+ try {
108
+ const files = fs.readdirSync(`${messagesDir}/${directory}`, { withFileTypes: true });
109
+ files.sort(); // Sorting ensures a consistent generated `index.js` order of imports cross-platforms.
110
+
111
+ const jsonFiles = files.filter(file => file.isFile() && file.name.endsWith('.json'));
112
+
113
+ if (!jsonFiles.length) {
114
+ log(`${loggingPrefix}: Not creating '${directory}/index.js' because no .json translation files were found.\n`);
115
+ return {
116
+ directory,
117
+ isWritten: false,
118
+ };
119
+ }
120
+
121
+ jsonFiles.forEach((file) => {
122
+ const filename = file.name;
123
+ // Gets `fr_CA` from `fr_CA.json`
124
+ const languageCode = filename.replace(/\.json$/, '');
125
+ // React-friendly language code fr_CA --> fr-ca
126
+ const reactIntlLanguageCode = languageCode.toLowerCase().replace(/_/g, '-');
127
+ // camelCase variable name
128
+ const messagesCamelCaseVar = camelCase(`messages_Of_${languageCode}_Language`);
129
+ const filePath = `${messagesDir}/${directory}/${filename}`;
130
+
131
+ try {
132
+ const entries = JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8' }));
133
+
134
+ if (!Object.keys(entries).length) {
135
+ importLines.push(`// Note: Skipped empty '${filename}' messages file.`);
136
+ return; // Skip the language
137
+ }
138
+ } catch (e) {
139
+ importLines.push(`// Error: unable to parse '${filename}' messages file.`);
140
+ log(`${loggingPrefix}: NOTICE: Skipping '${directory}/${filename}' due to error: ${e}.\n`);
141
+ return; // Skip the language
142
+ }
143
+
144
+ counter.nonEmptyLanguages += 1;
145
+ importLines.push(`import ${messagesCamelCaseVar} from './${filename}';`);
146
+ messagesLines.splice(1, 0, ` '${reactIntlLanguageCode}': ${messagesCamelCaseVar},`);
147
+ });
148
+
149
+ if (counter.nonEmptyLanguages) {
150
+ // See the help message above for sample output.
151
+ const messagesFileContent = [
152
+ filesCodeGeneratorNoticeHeader,
153
+ importLines.join('\n'),
154
+ '\nexport default {',
155
+ messagesLines.join('\n'),
156
+ '};\n',
157
+ ].join('\n');
158
+
159
+ writeFileSync(`${messagesDir}/${directory}/index.js`, messagesFileContent);
160
+ return {
161
+ directory,
162
+ isWritten: true,
163
+ };
164
+ }
165
+ log(`${loggingPrefix}: Skipping '${directory}' because no languages were found.\n`);
166
+ } catch (e) {
167
+ log(`${loggingPrefix}: NOTICE: Skipping '${directory}' due to error: ${e}.\n`);
168
+ }
169
+
170
+ return {
171
+ directory,
172
+ isWritten: false,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Create main `src/i18n/index.js` messages import file.
178
+ *
179
+ *
180
+ * @param processedDirectories - List of directories with a boolean flag whether its "index.js" file is written
181
+ * The format is "[\{ directory: "frontend-component-example", isWritten: false \}, ...]"
182
+ * @param log - Mockable process.stdout.write
183
+ * @param writeFileSync - Mockable fs.writeFileSync
184
+ * @param i18nDir` - Path to `src/i18n` directory
185
+ */
186
+ function generateMainMessagesFile({
187
+ processedDirectories,
188
+ log,
189
+ writeFileSync,
190
+ i18nDir,
191
+ }: {
192
+ processedDirectories: { directory: string, isWritten: boolean }[],
193
+ log: (message: string) => void,
194
+ writeFileSync: (filename: string, content: string) => void,
195
+ i18nDir: string,
196
+ }) {
197
+ const importLines: string[] = [];
198
+ const exportLines: string[] = [];
199
+
200
+ processedDirectories.forEach(processedDirectory => {
201
+ const { directory, isWritten } = processedDirectory;
202
+ if (isWritten) {
203
+ const moduleCamelCaseVariableName = camelCase(`messages_from_${directory}`);
204
+ importLines.push(`import ${moduleCamelCaseVariableName} from './messages/${directory}';`);
205
+ exportLines.push(` ${moduleCamelCaseVariableName},`);
206
+ } else {
207
+ const skipMessage = `Skipped import due to missing '${directory}/index.js' likely due to empty translations.`;
208
+ importLines.push(`// ${skipMessage}.`);
209
+ log(`${loggingPrefix}: ${skipMessage}\n`);
210
+ }
211
+ });
212
+
213
+ // See the help message above for sample output.
214
+ const indexFileContent = [
215
+ filesCodeGeneratorNoticeHeader,
216
+ importLines.join('\n'),
217
+ '\nexport default [',
218
+ exportLines.join('\n'),
219
+ '];\n',
220
+ ].join('\n');
221
+
222
+ writeFileSync(`${i18nDir}/index.js`, indexFileContent);
223
+ }
224
+
225
+ /*
226
+ * Main function of the file.
227
+ */
228
+ export function main({
229
+ directories,
230
+ log,
231
+ writeFileSync,
232
+ pwd,
233
+ }: {
234
+ directories: string | string[],
235
+ log: (message: string) => void,
236
+ writeFileSync: (filename: string, content: string) => void,
237
+ pwd: string,
238
+ }) {
239
+ const i18nDir = `${pwd}/src/i18n`; // The Micro-frontend i18n root directory
240
+
241
+ if (directories.includes('--help') || directories.includes('-h')) {
242
+ log(scriptHelpDocument);
243
+ } else if (!directories.length) {
244
+ log(scriptHelpDocument);
245
+ log(`${loggingPrefix}: Error: A list of directories is required.\n`);
246
+ } else if (!fs.existsSync(i18nDir) || !fs.lstatSync(i18nDir).isDirectory()) {
247
+ log(scriptHelpDocument);
248
+ log(`${loggingPrefix}: Error: src/i18n directory was not found.\n`);
249
+ } else {
250
+ // If we're here, we know that directories is an array, so cast it as one.
251
+ const processedDirectories = (directories as string[]).map((directory: string) => generateSubdirectoryMessageFile({
252
+ directory,
253
+ log,
254
+ writeFileSync,
255
+ i18nDir,
256
+ }));
257
+ generateMainMessagesFile({
258
+ processedDirectories,
259
+ log,
260
+ writeFileSync,
261
+ i18nDir,
262
+ });
263
+ }
264
+ }
265
+
266
+ if (require.main === module) {
267
+ // Run the main() function if called from the command line.
268
+ main({
269
+ directories: process.argv.slice(2),
270
+ log: text => process.stdout.write(text),
271
+ writeFileSync: fs.writeFileSync,
272
+ pwd: process.env.PWD ?? '.',
273
+ });
274
+ }
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk';
3
+
4
+ import { existsSync } from 'fs';
5
+ import path from 'path';
6
+ import { CommandTypes, ConfigTypes } from '../types';
7
+ import pack from './commands/pack';
8
+ import release from './commands/release';
9
+ import { ensureConfigFilenameOption } from './utils/ensureConfigFilenameOption';
10
+ import prettyPrintTitle from './utils/prettyPrintTitle';
11
+ import printUsage from './utils/printUsage';
12
+
13
+ // commandName is the third argument after node and 'openedx'
14
+ const commandName = process.argv[2];
15
+
16
+ // remove 'openedx' from process.argv to allow subcommands to read options properly
17
+ process.argv.splice(1, 1);
18
+
19
+ let version;
20
+
21
+ if (existsSync(path.resolve(__dirname, '../../package.json'))) {
22
+ version = require('../../package.json').version;
23
+ } else if (existsSync(path.resolve(__dirname, '../../../package.json'))) {
24
+ version = require('../../../package.json').version;
25
+ }
26
+
27
+ prettyPrintTitle(`Open edX CLI v${version}`);
28
+
29
+ switch (commandName) {
30
+ case CommandTypes.RELEASE:
31
+ release();
32
+ break;
33
+ case CommandTypes.PACK:
34
+ if (process.argv[2] === undefined) {
35
+ console.log(chalk.red(`${chalk.bold.red(commandName)} command usage: specify a peer folder where the command should install the package:
36
+
37
+ npm run pack my-project`));
38
+ process.exit(1);
39
+ }
40
+ pack();
41
+ break;
42
+ case CommandTypes.LINT:
43
+ ensureConfigFilenameOption(ConfigTypes.LINT, ['-c', '--config']);
44
+ require('.bin/eslint');
45
+ break;
46
+ case CommandTypes.TEST:
47
+ ensureConfigFilenameOption(ConfigTypes.TEST, ['-c', '--config']);
48
+ require('jest/bin/jest');
49
+ break;
50
+ case CommandTypes.BUILD:
51
+ ensureConfigFilenameOption(ConfigTypes.WEBPACK_BUILD, ['-c', '--config']);
52
+ require('webpack/bin/webpack');
53
+ break;
54
+ case CommandTypes.DEV:
55
+ ensureConfigFilenameOption(ConfigTypes.WEBPACK_DEV, ['-c', '--config']);
56
+ require('webpack-dev-server/bin/webpack-dev-server');
57
+ break;
58
+ case CommandTypes.DEV_SHELL:
59
+ ensureConfigFilenameOption(ConfigTypes.WEBPACK_DEV_SHELL, ['-c', '--config']);
60
+ require('webpack-dev-server/bin/webpack-dev-server');
61
+ break;
62
+ case CommandTypes.FORMAT_JS: {
63
+ /* The include option is used to specify which additional source folders to extract messages from.
64
+ * To extract more messages on other source folders use: --include=plugins --include=plugins2
65
+ * The intention use case is to allow extraction from the 'plugins' directory on 'frontend-app-authoring'.
66
+ * That plugins folder were kept outside the src folder to ensure they remain independent and
67
+ * can function without relying on the MFE environment's special features.
68
+ * This approach allows them to be packaged separately as NPM packages. */
69
+ const additionalSrcFolders = [] as string[];
70
+ process.argv.forEach((val, index) => {
71
+ if (val.startsWith('--include=')) {
72
+ additionalSrcFolders.push(val.split('=')[1]);
73
+ process.argv.splice(index, 1);
74
+ }
75
+ });
76
+ const srcFolders = ['src'].concat(additionalSrcFolders);
77
+ let srcFoldersString = srcFolders.join(',');
78
+ if (srcFolders.length > 1) {
79
+ srcFoldersString = `{${srcFoldersString}}`;
80
+ }
81
+ process.argv = process.argv.concat([
82
+ '--format', 'node_modules/@openedx/frontend-base/dist/tools/cli/utils/formatter.js',
83
+ '--ignore', `${srcFoldersString}/**/*.json`,
84
+ '--ignore', `${srcFoldersString}/**/*.d.ts`,
85
+ '--out-file', './temp/formatjs/Default.messages.json',
86
+ '--', `${srcFoldersString}/**/*.{j,t}s*`,
87
+ ]);
88
+ require('@formatjs/cli/bin/formatjs');
89
+ break;
90
+ }
91
+ case CommandTypes.SERVE:
92
+ require('./commands/serve');
93
+ break;
94
+ case CommandTypes.HELP:
95
+ printUsage();
96
+ break;
97
+ default:
98
+ console.log(`The command ${chalk.bold(commandName)} doesn't exist.`);
99
+ console.log('');
100
+ printUsage();
101
+ }
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import glob from 'glob';
5
+ import path from 'path';
6
+
7
+ /*
8
+ * See the Makefile for how the required hash file is downloaded from Transifex.
9
+ */
10
+
11
+ /*
12
+ * Expected input: a directory, possibly containing subdirectories, with .json files. Each .json
13
+ * file is an array of translation triplets (id, description, defaultMessage).
14
+ *
15
+ *
16
+ */
17
+ function gatherJson(dir: string) {
18
+ const ret: { id: string, description: string, defaultMessage: string }[] = [];
19
+ const files = glob.sync(`${dir}/**/*.json`);
20
+
21
+ files.forEach((filename) => {
22
+ const messages = JSON.parse(fs.readFileSync(filename, { encoding: 'utf8' }));
23
+ ret.push(...messages);
24
+ });
25
+ return ret;
26
+ }
27
+
28
+ // the hash file returns ids whose periods are "escaped" (sort of), like this:
29
+ // "key": "profile\\.sociallinks\\.social\\.links"
30
+ // so our regular messageIds won't match them out of the box
31
+ function escapeDots(messageId: string) {
32
+ return messageId.replace(/\./g, '\\.');
33
+ }
34
+
35
+ const jsonDir = process.argv[2];
36
+ const messageObjects = gatherJson(jsonDir);
37
+
38
+ if (messageObjects.length === 0) {
39
+ process.exitCode = 1;
40
+ throw new Error('Found no messages');
41
+ }
42
+
43
+ if (process.argv[3] === '--comments') { // prepare to handle the translator notes
44
+ const loggingPrefix = path.basename(`${__filename}`); // the name of this JS file
45
+ const bashScriptsPath = (
46
+ process.argv[4] && process.argv[4] === '--v3-scripts-path'
47
+ ? './node_modules/@edx/reactifex/bash_scripts'
48
+ : './node_modules/reactifex/bash_scripts');
49
+
50
+ const hashFile = `${bashScriptsPath}/hashmap.json`;
51
+ process.stdout.write(`${loggingPrefix}: reading hash file ${hashFile}\n`);
52
+ const messageInfo = JSON.parse(fs.readFileSync(hashFile, { encoding: 'utf8' }));
53
+
54
+ const outputFile = `${bashScriptsPath}/hashed_data.txt`;
55
+ process.stdout.write(`${loggingPrefix}: writing to output file ${outputFile}\n`);
56
+ fs.writeFileSync(outputFile, '');
57
+
58
+ messageObjects.forEach((message) => {
59
+ const transifexFormatId = escapeDots(message.id);
60
+
61
+ const info = messageInfo.find((mi: { key: string }) => mi.key === transifexFormatId);
62
+ if (info) {
63
+ fs.appendFileSync(outputFile, `${info.string_hash}|${message.description}\n`);
64
+ } else {
65
+ process.stdout.write(`${loggingPrefix}: string ${message.id} does not yet exist on transifex!\n`);
66
+ }
67
+ });
68
+ } else {
69
+ const output: Record<string, string> = {};
70
+
71
+ messageObjects.forEach((message) => {
72
+ output[message.id] = message.defaultMessage;
73
+ });
74
+ fs.writeFileSync(process.argv[3], JSON.stringify(output, null, 2));
75
+ }
@@ -0,0 +1,40 @@
1
+ import path from 'path';
2
+ import { ConfigTypes } from '../../types';
3
+ import getResolvedConfigPath from './getResolvedConfigPath';
4
+
5
+ export function ensureConfigFilenameOption(configType: ConfigTypes, keys: string[]) {
6
+ let configFileName = null;
7
+ let fileNameIndex = null;
8
+ for (const key of keys) {
9
+ const index = process.argv.indexOf(key);
10
+ if (index !== -1) {
11
+ fileNameIndex = index + 1;
12
+ if (process.argv.length > fileNameIndex) {
13
+ configFileName = process.argv[fileNameIndex];
14
+ } else {
15
+ console.error(`You must specify a config filename as the next argument when using the ${key} flag. Aborting.`);
16
+ process.exit(1);
17
+ }
18
+ }
19
+ }
20
+
21
+ let customConfigFilePath = null;
22
+ if (configFileName !== null) {
23
+ customConfigFilePath = path.resolve(process.cwd(), configFileName);
24
+ }
25
+
26
+ const resolvedConfigPath = getResolvedConfigPath(customConfigFilePath, configType);
27
+
28
+ if (resolvedConfigPath === null) {
29
+ console.error(`No valid config file was found. Did you forget to create a config file? You may specify a custom path with the ${keys.join(' or ')} flag(s).`);
30
+ process.exit(1);
31
+ }
32
+
33
+ // If there wasn't a config flag on the command line, add one.
34
+ if (fileNameIndex === null) {
35
+ process.argv.push(keys[0]);
36
+ process.argv.push(resolvedConfigPath);
37
+ } else {
38
+ process.argv[fileNameIndex] = resolvedConfigPath;
39
+ }
40
+ }
@@ -0,0 +1,10 @@
1
+ exports.format = (messages: Record<string, { defaultMessage: string, description: string }>) => {
2
+ const results = Object.entries(messages).map(([id, message]) => (
3
+ {
4
+ id,
5
+ defaultMessage: message.defaultMessage,
6
+ description: message.description,
7
+ }
8
+ ));
9
+ return results;
10
+ };
@@ -0,0 +1,23 @@
1
+ import fs from 'fs';
2
+ import { defaultConfigPaths } from '../../defaultConfigPaths';
3
+ import { ConfigTypes } from '../../types';
4
+
5
+ export default function getResolvedConfigPath(customConfigPath: string | null, configType: ConfigTypes) {
6
+ const configPaths = defaultConfigPaths[configType];
7
+
8
+ if (customConfigPath !== null) {
9
+ if (fs.existsSync(customConfigPath)) {
10
+ return customConfigPath;
11
+ } else {
12
+ console.error(`The custom config file at ${customConfigPath} does not exist. Aborting.`);
13
+ return null;
14
+ }
15
+ } else {
16
+ for (const configPath of configPaths) {
17
+ if (fs.existsSync(configPath)) {
18
+ return configPath;
19
+ }
20
+ }
21
+ }
22
+ return null;
23
+ }
@@ -0,0 +1,15 @@
1
+ import chalk from 'chalk';
2
+ import gradient from 'gradient-string';
3
+
4
+ export default function prettyPrintTitle(title: string) {
5
+ const openedxGradient = gradient(['#B82669', '#22358C']);
6
+
7
+ const borderedTitle = `█ ${title} █`;
8
+
9
+ const whitespace = Array.from({ length: borderedTitle.length }, () => '█').join('');
10
+
11
+ console.log(chalk.bgWhite(openedxGradient(whitespace)));
12
+ console.log(chalk.bold.bgWhite(openedxGradient(borderedTitle)));
13
+ console.log(chalk.bgWhite(openedxGradient(whitespace)));
14
+ console.log('');
15
+ }
@@ -0,0 +1,53 @@
1
+ import chalk from 'chalk';
2
+
3
+ export default function printUsage() {
4
+ console.log('CLI Usage:\n');
5
+
6
+ console.group();
7
+ console.log('openedx <command> <options>\n');
8
+ console.groupEnd();
9
+
10
+ console.log('Commands:\n');
11
+
12
+ console.group();
13
+ console.log(`${chalk.bold('release')}\n`);
14
+ console.group();
15
+ console.log(`Compile source code for release as a library. Compiled code is put into the dist folder.\n`);
16
+ console.groupEnd();
17
+
18
+ console.log(`${chalk.bold('pack')} <peer folder>\n`);
19
+ console.group();
20
+ console.log(`Package the dist folder as an NPM-compatible .tgz file suitable for use with npm install, then install it in the specified peer folder. The folder is assumed to be a peer of this repository, do not include a path.\n`);
21
+ console.groupEnd();
22
+
23
+ console.log(`${chalk.bold('lint')} <eslint options>\n`);
24
+ console.group();
25
+ console.log(`Runs ESLint on the source code. Requires an ${chalk.bold('eslint.config.js')} file.\n`);
26
+ console.groupEnd();
27
+
28
+ console.log(`${chalk.bold('test')} <jest options>\n`);
29
+ console.group();
30
+ console.log(`Runs Jest on the source code test suite. Requires a ${chalk.bold('jest.config.js')} file.\n`);
31
+ console.groupEnd();
32
+
33
+ console.log(`${chalk.bold('build')}\n`);
34
+ console.group();
35
+ console.log(`Compiles the source code for deployment as a frontend site. Requires a ${chalk.bold('site.config.build.tsx')} file.\n`);
36
+ console.groupEnd();
37
+
38
+ console.log(`${chalk.bold('dev')}\n`);
39
+ console.group();
40
+ console.log(`Compiles the source code and serves it in Webpack dev server as a frontend site. Requires a ${chalk.bold('site.config.dev.tsx')} file.\n`);
41
+ console.groupEnd();
42
+
43
+ console.log(`${chalk.bold('formatjs')}\n`);
44
+ console.group();
45
+ console.log(`Runs formatjs on the source code to extract internationalization messages.\n`);
46
+ console.groupEnd();
47
+
48
+ console.log(`${chalk.bold('serve')}\n`);
49
+ console.group();
50
+ console.log(`Serves the dist folder with an express server. Used to locally test production assets.\n`);
51
+ console.groupEnd();
52
+ console.groupEnd();
53
+ }
@@ -0,0 +1,8 @@
1
+ import { merge } from 'webpack-merge';
2
+ import { ConfigTypes } from '../types';
3
+ import getBaseConfig from './getBaseConfig';
4
+
5
+ export default function createConfig(configType: ConfigTypes, configFragment: any = {}) {
6
+ const baseConfig = getBaseConfig(configType);
7
+ return merge(baseConfig, configFragment);
8
+ };
@@ -0,0 +1,14 @@
1
+ import tseslint, { ConfigWithExtends } from 'typescript-eslint';
2
+ import { ConfigTypes } from '../types';
3
+ import getBaseConfig from './getBaseConfig';
4
+
5
+ export default function createLintConfig(...configs: ConfigWithExtends[]) {
6
+ const baseConfig = getBaseConfig(ConfigTypes.LINT);
7
+
8
+ return tseslint.config(
9
+ {
10
+ extends: baseConfig,
11
+ },
12
+ ...configs,
13
+ );
14
+ };
@@ -0,0 +1,11 @@
1
+ import { defaultConfigPaths } from '../defaultConfigPaths';
2
+ import { ConfigTypes } from '../types';
3
+
4
+ export default function getBaseConfig(configType: ConfigTypes) {
5
+ const configPaths = defaultConfigPaths[configType];
6
+ if (configPaths === undefined) {
7
+ throw new Error(`openedx: ${configType} is not a supported config type.`);
8
+ }
9
+
10
+ return require(require.resolve(configPaths[configPaths.length - 1]));
11
+ };
@@ -0,0 +1,30 @@
1
+ import path from 'path';
2
+ import { ConfigTypes } from './types';
3
+
4
+ // These config paths are tested in the order they're defined, so the last ones are the last fallback.
5
+ export const defaultConfigPaths = {
6
+ [ConfigTypes.BABEL]: [
7
+ path.resolve(process.cwd(), 'babel.config.js'),
8
+ path.resolve(__dirname, './babel/babel.config.js'),
9
+ ],
10
+ [ConfigTypes.WEBPACK_BUILD]: [
11
+ path.resolve(process.cwd(), 'webpack.config.build.js'),
12
+ path.resolve(__dirname, './webpack/webpack.config.build.js'),
13
+ ],
14
+ [ConfigTypes.WEBPACK_DEV]: [
15
+ path.resolve(process.cwd(), 'webpack.config.dev.js'),
16
+ path.resolve(__dirname, './webpack/webpack.config.dev.js'),
17
+ ],
18
+ [ConfigTypes.WEBPACK_DEV_SHELL]: [
19
+ path.resolve(process.cwd(), 'webpack.config.dev.shell.js'),
20
+ path.resolve(__dirname, './webpack/webpack.config.dev.shell.js'),
21
+ ],
22
+ [ConfigTypes.LINT]: [
23
+ path.resolve(process.cwd(), 'eslint.config.js'),
24
+ path.resolve(__dirname, './eslint/base.eslint.config.js'),
25
+ ],
26
+ [ConfigTypes.TEST]: [
27
+ path.resolve(process.cwd(), 'jest.config.js'),
28
+ path.resolve(__dirname, './jest/jest.config.js'),
29
+ ],
30
+ };