@openedx/frontend-base 1.0.0-alpha.0 → 1.0.0-alpha.10

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 (232) hide show
  1. package/config/eslint/base.eslint.config.js +1 -1
  2. package/config/jest/jest.config.js +1 -0
  3. package/config/types.js +0 -2
  4. package/config/webpack/common-config/all/getStylesheetRule.js +1 -1
  5. package/config/webpack/plugins/html-webpack-new-relic-plugin/test/HtmlWebpackNewRelicPlugin.test.js +66 -0
  6. package/config/webpack/webpack.config.build.js +1 -11
  7. package/config/webpack/webpack.config.dev.js +5 -11
  8. package/config/webpack/webpack.config.dev.shell.js +5 -11
  9. package/package.json +11 -3
  10. package/runtime/__mocks__/file.js +1 -0
  11. package/runtime/__mocks__/svg.js +1 -0
  12. package/runtime/__mocks__/universal-cookie.js +6 -0
  13. package/runtime/analytics/interface.test.js +242 -0
  14. package/runtime/auth/AxiosJwtAuthService.test.jsx +1076 -0
  15. package/runtime/auth/interceptors/createRetryInterceptor.test.js +23 -0
  16. package/runtime/config/getExternalLinkUrl.test.js +76 -0
  17. package/runtime/config/index.ts +2 -3
  18. package/runtime/i18n/lib.test.js +230 -0
  19. package/runtime/index.ts +5 -0
  20. package/runtime/initialize.async.function.config.test.js +43 -0
  21. package/runtime/initialize.const.config.test.js +41 -0
  22. package/runtime/initialize.function.config.test.js +41 -0
  23. package/runtime/initialize.test.js +356 -0
  24. package/runtime/jest.config.js +1 -0
  25. package/runtime/logging/NewRelicLoggingService.test.js +214 -0
  26. package/runtime/react/AuthenticatedPageRoute.test.jsx +135 -0
  27. package/runtime/react/ErrorBoundary.test.jsx +83 -0
  28. package/runtime/react/SiteProvider.test.jsx +66 -0
  29. package/runtime/react/SiteProvider.tsx +26 -3
  30. package/runtime/react/constants.ts +3 -0
  31. package/runtime/react/hooks/index.ts +8 -0
  32. package/runtime/react/hooks/theme/index.ts +2 -0
  33. package/runtime/react/hooks/theme/useTheme.test.ts +221 -0
  34. package/runtime/react/hooks/theme/useTheme.ts +179 -0
  35. package/runtime/react/hooks/theme/useThemeConfig.test.ts +107 -0
  36. package/runtime/react/hooks/theme/useThemeConfig.ts +34 -0
  37. package/runtime/react/hooks/theme/useThemeCore.test.ts +65 -0
  38. package/runtime/react/hooks/theme/useThemeCore.ts +52 -0
  39. package/runtime/react/hooks/theme/useThemeVariants.test.ts +97 -0
  40. package/runtime/react/hooks/theme/useThemeVariants.ts +116 -0
  41. package/runtime/react/hooks/theme/useTrackColorSchemeChoice.test.ts +54 -0
  42. package/runtime/react/hooks/theme/useTrackColorSchemeChoice.ts +30 -0
  43. package/runtime/react/hooks/theme/utils.ts +11 -0
  44. package/runtime/react/hooks/useActiveRoles.ts +15 -0
  45. package/runtime/react/hooks/useActiveRouteRoleWatcher.ts +31 -0
  46. package/runtime/react/hooks/useAppConfig.ts +9 -0
  47. package/runtime/react/hooks/useAuthenticatedUser.test.tsx +41 -0
  48. package/runtime/react/hooks/useAuthenticatedUser.ts +9 -0
  49. package/runtime/react/hooks/useSiteConfig.test.tsx +13 -0
  50. package/runtime/react/hooks/useSiteConfig.ts +9 -0
  51. package/runtime/react/hooks/useSiteEvent.ts +24 -0
  52. package/runtime/react/reducers.ts +40 -0
  53. package/runtime/routing/utils.test.ts +7 -0
  54. package/runtime/scripts/GoogleAnalyticsLoader.test.ts +77 -0
  55. package/runtime/setupTest.js +0 -35
  56. package/runtime/site.config.test.tsx +33 -0
  57. package/runtime/slots/Slot.test.tsx +40 -0
  58. package/runtime/slots/layout/DefaultSlotLayout.test.tsx +31 -0
  59. package/runtime/slots/layout/hooks.test.tsx +178 -0
  60. package/runtime/slots/layout/utils.test.ts +67 -0
  61. package/runtime/slots/types.ts +1 -0
  62. package/runtime/slots/utils.test.ts +64 -0
  63. package/runtime/slots/utils.ts +28 -9
  64. package/runtime/slots/widget/iframe/hooks.ts +1 -1
  65. package/runtime/testing/initializeMockApp.test.ts +66 -0
  66. package/runtime/testing/initializeMockApp.ts +5 -0
  67. package/runtime/utils.test.js +116 -0
  68. package/shell/Logo.test.tsx +32 -0
  69. package/shell/__mocks__/file.js +1 -0
  70. package/shell/__mocks__/svg.js +1 -0
  71. package/shell/__mocks__/universal-cookie.js +6 -0
  72. package/shell/app.scss +2 -1
  73. package/shell/app.ts +14 -0
  74. package/shell/dev/devHome/app.ts +2 -2
  75. package/shell/dev/slotShowcase/app.tsx +9 -9
  76. package/shell/header/app.tsx +3 -3
  77. package/shell/jest.config.js +1 -0
  78. package/shell/router/createRouter.test.tsx +50 -0
  79. package/shell/router/getAppRoutes.test.tsx +59 -0
  80. package/shell/setupTest.js +0 -35
  81. package/shell/site.config.dev.tsx +3 -3
  82. package/shell/site.config.test.tsx +16 -0
  83. package/shell/site.tsx +1 -1
  84. package/tools/dist/cli/intl-imports.test.js +146 -0
  85. package/tools/dist/cli/openedx.js +1 -15
  86. package/tools/dist/cli/utils/printUsage.js +0 -9
  87. package/tools/dist/eslint/base.eslint.config.js +1 -1
  88. package/tools/dist/jest/jest.config.js +1 -0
  89. package/tools/dist/types.js +0 -2
  90. package/tools/dist/webpack/common-config/all/getStylesheetRule.js +1 -1
  91. package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/HtmlWebpackNewRelicPlugin.test.js +66 -0
  92. package/tools/dist/webpack/webpack.config.build.js +1 -11
  93. package/tools/dist/webpack/webpack.config.dev.js +5 -11
  94. package/tools/dist/webpack/webpack.config.dev.shell.js +5 -11
  95. package/types.ts +21 -1
  96. package/config/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +0 -108
  97. package/config/webpack/plugins/paragon-webpack-plugin/index.js +0 -7
  98. package/config/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +0 -64
  99. package/config/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +0 -53
  100. package/config/webpack/plugins/paragon-webpack-plugin/utils/index.js +0 -9
  101. package/config/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +0 -114
  102. package/config/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +0 -146
  103. package/config/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +0 -126
  104. package/config/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +0 -57
  105. package/config/webpack/types.js +0 -2
  106. package/config/webpack/utils/paragonUtils.js +0 -138
  107. package/eslint.config.js +0 -18
  108. package/frontend-base.d.ts +0 -8
  109. package/jest.config.js +0 -7
  110. package/openedx-frontend-base.tgz +0 -0
  111. package/runtime/react/hooks.ts +0 -106
  112. package/test-site/app.d.ts +0 -15
  113. package/test-site/dist/176.436443549ebb858db483.js +0 -2
  114. package/test-site/dist/176.436443549ebb858db483.js.map +0 -1
  115. package/test-site/dist/362.536eff787d2380fe246c.js +0 -2
  116. package/test-site/dist/362.536eff787d2380fe246c.js.map +0 -1
  117. package/test-site/dist/653.486966b108d224551296.js +0 -2
  118. package/test-site/dist/653.486966b108d224551296.js.map +0 -1
  119. package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
  120. package/test-site/dist/806.323cf6496ad0a7fe73a7.js +0 -3
  121. package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +0 -106
  122. package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +0 -1
  123. package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +0 -4
  124. package/test-site/dist/app.612058b36c74787759ac.css +0 -61
  125. package/test-site/dist/app.612058b36c74787759ac.css.map +0 -1
  126. package/test-site/dist/app.612058b36c74787759ac.js +0 -2
  127. package/test-site/dist/app.612058b36c74787759ac.js.map +0 -1
  128. package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +0 -1
  129. package/test-site/dist/index.html +0 -1
  130. package/test-site/dist/report.html +0 -39
  131. package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +0 -2
  132. package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +0 -1
  133. package/test-site/eslint.config.js +0 -12
  134. package/test-site/package-lock.json +0 -19226
  135. package/test-site/package.json +0 -29
  136. package/test-site/public/index.html +0 -10
  137. package/test-site/site.config.build.tsx +0 -27
  138. package/test-site/site.config.dev.tsx +0 -27
  139. package/test-site/src/authenticated-page/AuthenticatedPage.tsx +0 -18
  140. package/test-site/src/authenticated-page/i18n/index.ts +0 -27
  141. package/test-site/src/authenticated-page/index.tsx +0 -28
  142. package/test-site/src/example-page/ExamplePage.tsx +0 -79
  143. package/test-site/src/example-page/Image.tsx +0 -11
  144. package/test-site/src/example-page/ParagonPreview.jsx +0 -66
  145. package/test-site/src/example-page/apple.jpg +0 -0
  146. package/test-site/src/example-page/apple.svg +0 -1
  147. package/test-site/src/example-page/index.ts +0 -16
  148. package/test-site/src/i18n/README.md +0 -3
  149. package/test-site/src/i18n/messages/frontend-app-sample/ar.json +0 -4
  150. package/test-site/src/i18n/messages/frontend-app-sample/eo.json +0 -1
  151. package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +0 -4
  152. package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +0 -1
  153. package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +0 -3
  154. package/test-site/src/iframe-widget/IframeWidget.tsx +0 -14
  155. package/test-site/src/iframe-widget/index.ts +0 -16
  156. package/test-site/src/index.tsx +0 -3
  157. package/test-site/src/messages.js +0 -11
  158. package/test-site/src/site.scss +0 -11
  159. package/test-site/tsconfig.json +0 -14
  160. package/tools/babel/babel.config.js +0 -27
  161. package/tools/babel.config.js +0 -3
  162. package/tools/cli/README.md +0 -29
  163. package/tools/cli/commands/pack.ts +0 -9
  164. package/tools/cli/commands/release.ts +0 -27
  165. package/tools/cli/commands/serve.ts +0 -43
  166. package/tools/cli/intl-imports.ts +0 -274
  167. package/tools/cli/openedx.ts +0 -101
  168. package/tools/cli/transifex-utils.ts +0 -75
  169. package/tools/cli/utils/ensureConfigFilenameOption.ts +0 -40
  170. package/tools/cli/utils/formatter.ts +0 -10
  171. package/tools/cli/utils/getResolvedConfigPath.ts +0 -23
  172. package/tools/cli/utils/prettyPrintTitle.ts +0 -15
  173. package/tools/cli/utils/printUsage.ts +0 -53
  174. package/tools/config-helpers/createConfig.ts +0 -8
  175. package/tools/config-helpers/createLintConfig.ts +0 -14
  176. package/tools/config-helpers/getBaseConfig.ts +0 -11
  177. package/tools/defaultConfigPaths.ts +0 -30
  178. package/tools/dist/cli/commands/pack.js +0 -14
  179. package/tools/dist/cli/commands/release.js +0 -28
  180. package/tools/dist/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.js +0 -108
  181. package/tools/dist/webpack/plugins/paragon-webpack-plugin/index.js +0 -7
  182. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.js +0 -64
  183. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.js +0 -53
  184. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/index.js +0 -9
  185. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.js +0 -114
  186. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.js +0 -146
  187. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.js +0 -126
  188. package/tools/dist/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.js +0 -57
  189. package/tools/dist/webpack/types.js +0 -2
  190. package/tools/dist/webpack/utils/paragonUtils.js +0 -138
  191. package/tools/eslint/base.eslint.config.js +0 -124
  192. package/tools/eslint/modules.d.ts +0 -5
  193. package/tools/eslint.config.js +0 -15
  194. package/tools/index.ts +0 -3
  195. package/tools/jest/jest.config.js +0 -30
  196. package/tools/jest.config.js +0 -19
  197. package/tools/tsconfig.json +0 -24
  198. package/tools/types.ts +0 -21
  199. package/tools/typescript/tsconfig.json +0 -32
  200. package/tools/webpack/common-config/README.md +0 -15
  201. package/tools/webpack/common-config/all/getCodeRules.ts +0 -51
  202. package/tools/webpack/common-config/all/getFileLoaderRules.ts +0 -23
  203. package/tools/webpack/common-config/all/getIgnoreWarnings.ts +0 -13
  204. package/tools/webpack/common-config/all/getImageMinimizer.ts +0 -26
  205. package/tools/webpack/common-config/all/getStylesheetRule.ts +0 -111
  206. package/tools/webpack/common-config/dev/getDevServer.ts +0 -35
  207. package/tools/webpack/common-config/index.ts +0 -6
  208. package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +0 -11
  209. package/tools/webpack/modules.d.ts +0 -6
  210. package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +0 -102
  211. package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +0 -21
  212. package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +0 -7
  213. package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +0 -3
  214. package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +0 -1
  215. package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +0 -134
  216. package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +0 -3
  217. package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +0 -71
  218. package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +0 -72
  219. package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +0 -6
  220. package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +0 -131
  221. package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +0 -144
  222. package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +0 -106
  223. package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +0 -54
  224. package/tools/webpack/types.ts +0 -69
  225. package/tools/webpack/utils/getLocalAliases.ts +0 -65
  226. package/tools/webpack/utils/getPublicPath.ts +0 -3
  227. package/tools/webpack/utils/getResolvedSiteConfigPath.ts +0 -28
  228. package/tools/webpack/utils/paragonUtils.ts +0 -152
  229. package/tools/webpack/webpack.config.build.ts +0 -93
  230. package/tools/webpack/webpack.config.dev.shell.ts +0 -122
  231. package/tools/webpack/webpack.config.dev.ts +0 -90
  232. package/tsconfig.json +0 -23
@@ -43,13 +43,13 @@ function TakesPropsViaContext() {
43
43
  }
44
44
 
45
45
  const app: App = {
46
- appId: 'org.openedx.frontend.app.dev.slotShowcase',
46
+ appId: 'org.openedx.frontend.app.slotShowcase',
47
47
  routes: [{
48
- id: 'org.openedx.frontend.route.dev.slotShowcase',
48
+ id: 'org.openedx.frontend.route.slotShowcase',
49
49
  path: '/slots',
50
50
  Component: SlotShowcasePage,
51
51
  handle: {
52
- role: 'slotShowcase',
52
+ role: 'org.openedx.frontend.role.slotShowcase',
53
53
  }
54
54
  }],
55
55
  slots: [
@@ -314,7 +314,7 @@ const app: App = {
314
314
  title: 'Courses (modified)',
315
315
  },
316
316
  condition: {
317
- active: ['slotShowcase'],
317
+ active: ['org.openedx.frontend.role.slotShowcase'],
318
318
  }
319
319
  },
320
320
  {
@@ -324,7 +324,7 @@ const app: App = {
324
324
  relatedId: 'org.openedx.frontend.widget.slotShowcase.headerLink3',
325
325
  element: (<LinkMenuItem label="Link After 3" url="#" variant="navLink" />),
326
326
  condition: {
327
- active: ['slotShowcase'],
327
+ active: ['org.openedx.frontend.role.slotShowcase'],
328
328
  }
329
329
  },
330
330
  {
@@ -335,7 +335,7 @@ const app: App = {
335
335
  <NavDropdownMenuSlot id="org.openedx.frontend.slot.header.primaryLinksDropdown.v1" label="Resources" />
336
336
  ),
337
337
  condition: {
338
- active: ['slotShowcase']
338
+ active: ['org.openedx.frontend.role.slotShowcase']
339
339
  }
340
340
  },
341
341
  {
@@ -346,7 +346,7 @@ const app: App = {
346
346
  <LinkMenuItem label="Resource 1" url="#" variant="dropdownItem" />
347
347
  ),
348
348
  condition: {
349
- active: ['slotShowcase'],
349
+ active: ['org.openedx.frontend.role.slotShowcase'],
350
350
  }
351
351
  },
352
352
  {
@@ -355,7 +355,7 @@ const app: App = {
355
355
  op: WidgetOperationTypes.APPEND,
356
356
  element: (<LinkMenuItem label="Link 3" url="#" variant="navLink" />),
357
357
  condition: {
358
- active: ['slotShowcase'],
358
+ active: ['org.openedx.frontend.role.slotShowcase'],
359
359
  }
360
360
  },
361
361
  {
@@ -364,7 +364,7 @@ const app: App = {
364
364
  op: WidgetOperationTypes.APPEND,
365
365
  element: (<LinkMenuItem label="Link 4" url="#" variant="navLink" />),
366
366
  condition: {
367
- active: ['slotShowcase'],
367
+ active: ['org.openedx.frontend.role.slotShowcase'],
368
368
  }
369
369
  },
370
370
  ]
@@ -66,7 +66,7 @@ const config: App = {
66
66
  element: (
67
67
  <ProfileLinkMenuItem
68
68
  label={messages['header.user.menu.profile']}
69
- role="profile"
69
+ role="org.openedx.frontend.role.profile"
70
70
  variant="dropdownItem"
71
71
  />
72
72
  )
@@ -78,7 +78,7 @@ const config: App = {
78
78
  element: (
79
79
  <LinkMenuItem
80
80
  label={messages['header.user.menu.account']}
81
- role="account"
81
+ role="org.openedx.frontend.role.account"
82
82
  variant="dropdownItem"
83
83
  />
84
84
  )
@@ -90,7 +90,7 @@ const config: App = {
90
90
  element: (
91
91
  <LinkMenuItem
92
92
  label={messages['header.user.menu.logout']}
93
- role="logout"
93
+ role="org.openedx.frontend.role.logout"
94
94
  variant="dropdownItem"
95
95
  />
96
96
  )
@@ -7,6 +7,7 @@ module.exports = {
7
7
  '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/file.js',
8
8
  '\\.(css|scss)$': require.resolve('identity-obj-proxy'),
9
9
  'site.config': '<rootDir>/site.config.test.tsx',
10
+ '^@src/(.*)$': '<rootDir>/src/$1',
10
11
  },
11
12
  testEnvironment: 'jsdom',
12
13
  testEnvironmentOptions: {
@@ -0,0 +1,50 @@
1
+ import { createBrowserRouter } from 'react-router-dom';
2
+ import { getBasename } from '../../runtime/initialize';
3
+ import Shell from '../Shell';
4
+ import createRouter from './createRouter';
5
+ import getAppRoutes from './getAppRoutes';
6
+
7
+ jest.mock('react-router-dom', () => ({
8
+ createBrowserRouter: jest.fn(),
9
+ }));
10
+
11
+ jest.mock('../../runtime/initialize', () => ({
12
+ getBasename: jest.fn(),
13
+ }));
14
+
15
+ jest.mock('../Shell', () => jest.fn());
16
+ jest.mock('./getAppRoutes', () => jest.fn());
17
+
18
+ describe('createRouter', () => {
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ });
22
+
23
+ it('should create a router with the correct configuration', () => {
24
+ const mockRoutes = [{ path: '/', element: <div>Home</div> }];
25
+ const mockBasename = '/base';
26
+
27
+ (getAppRoutes as jest.Mock).mockReturnValue(mockRoutes);
28
+ (getBasename as jest.Mock).mockReturnValue(mockBasename);
29
+ (createBrowserRouter as jest.Mock).mockReturnValue('fake router value');
30
+
31
+ const result = createRouter();
32
+
33
+ expect(getAppRoutes).toHaveBeenCalled();
34
+ expect(getBasename).toHaveBeenCalled();
35
+ expect(createBrowserRouter).toHaveBeenCalledWith(
36
+ [
37
+ {
38
+ Component: Shell,
39
+ children: mockRoutes,
40
+ },
41
+ ],
42
+ {
43
+ basename: mockBasename,
44
+ }
45
+ );
46
+
47
+ // Just proving we actually return the router from createBrowserRouter.
48
+ expect(result).toEqual('fake router value');
49
+ });
50
+ });
@@ -0,0 +1,59 @@
1
+ import { RouteObject } from 'react-router';
2
+ import { getSiteConfig } from '../../runtime';
3
+ import getAppRoutes from './getAppRoutes';
4
+
5
+ jest.mock('../../runtime', () => ({
6
+ getSiteConfig: jest.fn(),
7
+ }));
8
+
9
+ describe('getAppRoutes', () => {
10
+ it('should return an empty array when no apps are configured', () => {
11
+ (getSiteConfig as jest.Mock).mockReturnValue({ apps: [] });
12
+
13
+ const routes = getAppRoutes();
14
+
15
+ expect(routes).toEqual([]);
16
+ });
17
+
18
+ it('should flatten and return routes from configured apps', () => {
19
+ const mockApps = [
20
+ {
21
+ routes: [
22
+ { path: '/page1', element: <div>Page 1</div> },
23
+ { path: '/page2', element: <div>Page 2</div> }
24
+ ]
25
+ },
26
+ {
27
+ routes: [
28
+ { path: '/page3', element: <div>Page 3</div> }
29
+ ]
30
+ },
31
+ ];
32
+
33
+ (getSiteConfig as jest.Mock).mockReturnValue({ apps: mockApps });
34
+
35
+ const routes = getAppRoutes();
36
+
37
+ expect(routes).toEqual([
38
+ { path: '/page1', element: <div>Page 1</div> },
39
+ { path: '/page2', element: <div>Page 2</div> },
40
+ { path: '/page3', element: <div>Page 3</div> }
41
+ ]);
42
+ });
43
+
44
+ it('should ignore apps without routes', () => {
45
+ const mockRoutes: RouteObject[] = [
46
+ { path: '/page1', element: <div>Page 1</div> },
47
+ ];
48
+ const mockApps = [
49
+ { routes: mockRoutes },
50
+ { slots: [] },
51
+ ];
52
+
53
+ (getSiteConfig as jest.Mock).mockReturnValue({ apps: mockApps });
54
+
55
+ const routes = getAppRoutes();
56
+
57
+ expect(routes).toEqual(mockRoutes);
58
+ });
59
+ });
@@ -11,38 +11,3 @@ jest.mock('universal-cookie', () => {
11
11
  });
12
12
 
13
13
  mergeSiteConfig(siteConfig);
14
-
15
- global.PARAGON_THEME = {
16
- paragon: {
17
- version: '1.0.0',
18
- themeUrls: {
19
- core: {
20
- fileName: 'core.min.css',
21
- },
22
- defaults: {
23
- light: 'light',
24
- },
25
- variants: {
26
- light: {
27
- fileName: 'light.min.css',
28
- },
29
- },
30
- },
31
- },
32
- brand: {
33
- version: '1.0.0',
34
- themeUrls: {
35
- core: {
36
- fileName: 'core.min.css',
37
- },
38
- defaults: {
39
- light: 'light',
40
- },
41
- variants: {
42
- light: {
43
- fileName: 'light.min.css',
44
- },
45
- },
46
- },
47
- },
48
- };
@@ -18,15 +18,15 @@ const siteConfig: SiteConfig = {
18
18
 
19
19
  externalRoutes: [
20
20
  {
21
- role: 'profile',
21
+ role: 'org.openedx.frontend.role.profile',
22
22
  url: 'http://apps.local.openedx.io:1995/profile/'
23
23
  },
24
24
  {
25
- role: 'account',
25
+ role: 'org.openedx.frontend.role.account',
26
26
  url: 'http://apps.local.openedx.io:1997/account/'
27
27
  },
28
28
  {
29
- role: 'logout',
29
+ role: 'org.openedx.frontend.role.logout',
30
30
  url: 'http://local.openedx.io:8000/logout'
31
31
  },
32
32
  ],
@@ -0,0 +1,16 @@
1
+ import { EnvironmentTypes, SiteConfig } from '../types';
2
+
3
+ const siteConfig: SiteConfig = {
4
+ siteId: 'test',
5
+ siteName: 'Open edX',
6
+ baseUrl: 'http://localhost:8080',
7
+ lmsBaseUrl: 'http://localhost:18000',
8
+ loginUrl: 'http://localhost:18000/login',
9
+ logoutUrl: 'http://localhost:18000/logout',
10
+
11
+ environment: EnvironmentTypes.TEST,
12
+ apps: [],
13
+ segmentKey: 'segment_whoa',
14
+ };
15
+
16
+ export default siteConfig;
package/shell/site.tsx CHANGED
@@ -25,7 +25,7 @@ subscribe(SITE_READY, async () => {
25
25
  root.render(
26
26
  <StrictMode>
27
27
  <QueryClientProvider client={queryClient}>
28
- <RouterProvider router={router} />,
28
+ <RouterProvider router={router} />
29
29
  </QueryClientProvider>
30
30
  </StrictMode>
31
31
  );
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ // Tests for the intl-imports.js command line.
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const path_1 = __importDefault(require("path"));
8
+ const intl_imports_1 = require("./intl-imports");
9
+ const sampleAppDirectory = path_1.default.join(__dirname, '../../test-site');
10
+ // History for `process.stdout.write` mock calls.
11
+ const logHistory = {
12
+ log: [],
13
+ latest: '',
14
+ };
15
+ // History for `fs.writeFileSync` mock calls.
16
+ const writeFileHistory = {
17
+ log: [],
18
+ latest: null,
19
+ };
20
+ // Mock for process.stdout.write
21
+ const log = (text) => {
22
+ logHistory.latest = text;
23
+ logHistory.log.push(text);
24
+ };
25
+ // Mock for fs.writeFileSync
26
+ const writeFileSync = (filename, content) => {
27
+ const entry = { filename, content };
28
+ writeFileHistory.latest = entry;
29
+ writeFileHistory.log.push(entry);
30
+ };
31
+ // Main with mocked output
32
+ const main = (...directories) => (0, intl_imports_1.main)({
33
+ directories,
34
+ log,
35
+ writeFileSync,
36
+ pwd: sampleAppDirectory,
37
+ });
38
+ // Clean up mock histories
39
+ afterEach(() => {
40
+ logHistory.log = [];
41
+ logHistory.latest = null;
42
+ writeFileHistory.log = [];
43
+ writeFileHistory.latest = null;
44
+ });
45
+ describe('help document', () => {
46
+ it('should print help for --help', () => {
47
+ main('--help');
48
+ expect(logHistory.latest).toMatch('Script to generate the src/i18n/index.js');
49
+ });
50
+ it('should print help for -h', () => {
51
+ main('-h');
52
+ expect(logHistory.latest).toMatch('Script to generate the src/i18n/index.js');
53
+ });
54
+ });
55
+ describe('error validation', () => {
56
+ it('expects a list of directories', () => {
57
+ main();
58
+ expect(logHistory.log.join('\n')).toMatch('Script to generate the src/i18n/index.js'); // Print help error
59
+ expect(logHistory.latest).toMatch('Error: A list of directories is required'); // Print error message
60
+ });
61
+ it('expects a directory with a relative path of "src/i18n"', () => {
62
+ (0, intl_imports_1.main)({
63
+ directories: ['frontend-app-example'],
64
+ log,
65
+ writeFileSync,
66
+ pwd: path_1.default.join(__dirname), // __dirname === `scripts` which has no sub-dir `src/i18n`
67
+ });
68
+ expect(logHistory.log.join('\n')).toMatch('Script to generate the src/i18n/index.js'); // Print help on error
69
+ expect(logHistory.latest).toMatch('Error: src/i18n directory was not found.'); // Print error message
70
+ });
71
+ });
72
+ describe('generated files', () => {
73
+ it('writes a correct src/i18n/index.js file', () => {
74
+ main('frontend-component-singlelang', 'frontend-component-nolangs', 'frontend-component-emptylangs', 'frontend-app-sample');
75
+ const mainFileActualContent = writeFileHistory.log.find((file) => {
76
+ return file.filename.endsWith('src/i18n/index.js');
77
+ })?.content;
78
+ const mainFileExpectedContent = `// This file is generated by the openedx/frontend-base's "intl-import.js" script.
79
+ //
80
+ // Refer to the i18n documents in https://docs.openedx.org/en/latest/developers/references/i18n.html to update
81
+ // the file and use the Micro-frontend i18n pattern in new repositories.
82
+ //
83
+
84
+ import messagesFromFrontendComponentSinglelang from './messages/frontend-component-singlelang';
85
+ // Skipped import due to missing 'frontend-component-nolangs/index.js' likely due to empty translations..
86
+ // Skipped import due to missing 'frontend-component-emptylangs/index.js' likely due to empty translations..
87
+ import messagesFromFrontendAppSample from './messages/frontend-app-sample';
88
+
89
+ export default [
90
+ messagesFromFrontendComponentSinglelang,
91
+ messagesFromFrontendAppSample,
92
+ ];
93
+ `;
94
+ expect(mainFileActualContent).toEqual(mainFileExpectedContent);
95
+ });
96
+ it('writes a correct frontend-component-singlelang/index.js file', () => {
97
+ main('frontend-component-singlelang', 'frontend-component-nolangs', 'frontend-component-emptylangs', 'frontend-app-sample');
98
+ const mainFileActualContent = writeFileHistory.log.find(file => file.filename.endsWith('frontend-component-singlelang/index.js'))?.content;
99
+ const singleLangExpectedContent = `// This file is generated by the openedx/frontend-base's "intl-import.js" script.
100
+ //
101
+ // Refer to the i18n documents in https://docs.openedx.org/en/latest/developers/references/i18n.html to update
102
+ // the file and use the Micro-frontend i18n pattern in new repositories.
103
+ //
104
+
105
+ import messagesOfArLanguage from './ar.json';
106
+
107
+ export default {
108
+ 'ar': messagesOfArLanguage,
109
+ };
110
+ `;
111
+ expect(mainFileActualContent).toEqual(singleLangExpectedContent);
112
+ });
113
+ it('writes a correct frontend-app-sample/index.js file', () => {
114
+ main('frontend-component-singlelang', 'frontend-component-nolangs', 'frontend-component-emptylangs', 'frontend-app-sample');
115
+ const mainFileActualContent = writeFileHistory.log.find(file => file.filename.endsWith('frontend-app-sample/index.js'))?.content;
116
+ const singleLangExpectedContent = `// This file is generated by the openedx/frontend-base's "intl-import.js" script.
117
+ //
118
+ // Refer to the i18n documents in https://docs.openedx.org/en/latest/developers/references/i18n.html to update
119
+ // the file and use the Micro-frontend i18n pattern in new repositories.
120
+ //
121
+
122
+ import messagesOfArLanguage from './ar.json';
123
+ // Note: Skipped empty 'eo.json' messages file.
124
+ import messagesOfEs419Language from './es_419.json';
125
+
126
+ export default {
127
+ 'ar': messagesOfArLanguage,
128
+ 'es-419': messagesOfEs419Language,
129
+ };
130
+ `;
131
+ expect(mainFileActualContent).toEqual(singleLangExpectedContent);
132
+ });
133
+ });
134
+ describe('list of generated index.js files', () => {
135
+ it('writes only non-empty languages in addition to the main file', () => {
136
+ main('frontend-component-singlelang', 'frontend-component-nolangs', 'frontend-component-emptylangs', 'frontend-app-sample');
137
+ const writtenFiles = writeFileHistory.log
138
+ .map(file => file.filename)
139
+ .map(file => path_1.default.relative(sampleAppDirectory, file));
140
+ expect(writtenFiles).toEqual([
141
+ 'src/i18n/messages/frontend-component-singlelang/index.js',
142
+ 'src/i18n/messages/frontend-app-sample/index.js',
143
+ 'src/i18n/index.js',
144
+ ]);
145
+ });
146
+ });
@@ -8,8 +8,6 @@ const chalk_1 = __importDefault(require("chalk"));
8
8
  const fs_1 = require("fs");
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const types_1 = require("../types");
11
- const pack_1 = __importDefault(require("./commands/pack"));
12
- const release_1 = __importDefault(require("./commands/release"));
13
11
  const ensureConfigFilenameOption_1 = require("./utils/ensureConfigFilenameOption");
14
12
  const prettyPrintTitle_1 = __importDefault(require("./utils/prettyPrintTitle"));
15
13
  const printUsage_1 = __importDefault(require("./utils/printUsage"));
@@ -26,18 +24,6 @@ else if ((0, fs_1.existsSync)(path_1.default.resolve(__dirname, '../../../packag
26
24
  }
27
25
  (0, prettyPrintTitle_1.default)(`Open edX CLI v${version}`);
28
26
  switch (commandName) {
29
- case types_1.CommandTypes.RELEASE:
30
- (0, release_1.default)();
31
- break;
32
- case types_1.CommandTypes.PACK:
33
- if (process.argv[2] === undefined) {
34
- console.log(chalk_1.default.red(`${chalk_1.default.bold.red(commandName)} command usage: specify a peer folder where the command should install the package:
35
-
36
- npm run pack my-project`));
37
- process.exit(1);
38
- }
39
- (0, pack_1.default)();
40
- break;
41
27
  case types_1.CommandTypes.LINT:
42
28
  (0, ensureConfigFilenameOption_1.ensureConfigFilenameOption)(types_1.ConfigTypes.LINT, ['-c', '--config']);
43
29
  require('.bin/eslint');
@@ -78,7 +64,7 @@ switch (commandName) {
78
64
  srcFoldersString = `{${srcFoldersString}}`;
79
65
  }
80
66
  process.argv = process.argv.concat([
81
- '--format', 'node_modules/@openedx/frontend-base/dist/tools/cli/utils/formatter.js',
67
+ '--format', 'node_modules/@openedx/frontend-base/tools/dist/cli/utils/formatter.js',
82
68
  '--ignore', `${srcFoldersString}/**/*.json`,
83
69
  '--ignore', `${srcFoldersString}/**/*.d.ts`,
84
70
  '--out-file', './temp/formatjs/Default.messages.json',
@@ -11,15 +11,6 @@ function printUsage() {
11
11
  console.log('openedx <command> <options>\n');
12
12
  console.groupEnd();
13
13
  console.log('Commands:\n');
14
- console.group();
15
- console.log(`${chalk_1.default.bold('release')}\n`);
16
- console.group();
17
- console.log(`Compile source code for release as a library. Compiled code is put into the dist folder.\n`);
18
- console.groupEnd();
19
- console.log(`${chalk_1.default.bold('pack')} <peer folder>\n`);
20
- console.group();
21
- 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`);
22
- console.groupEnd();
23
14
  console.log(`${chalk_1.default.bold('lint')} <eslint options>\n`);
24
15
  console.group();
25
16
  console.log(`Runs ESLint on the source code. Requires an ${chalk_1.default.bold('eslint.config.js')} file.\n`);
@@ -31,7 +31,6 @@ module.exports = tseslint.config(eslint.configs.recommended, ...tseslint.configs
31
31
  ...globals.browser,
32
32
  ...globals.node,
33
33
  ...globals.jest,
34
- PARAGON_THEME: 'readonly',
35
34
  newrelic: 'readonly',
36
35
  },
37
36
  },
@@ -79,6 +78,7 @@ module.exports = tseslint.config(eslint.configs.recommended, ...tseslint.configs
79
78
  caughtErrors: 'none',
80
79
  }],
81
80
  '@typescript-eslint/no-empty-function': 'off',
81
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
82
82
  '@stylistic/semi': ['error', 'always', { omitLastInOneLineBlock: true, omitLastInOneLineClassBody: true }],
83
83
  '@stylistic/quotes': ['error', 'single', {
84
84
  avoidEscape: true,
@@ -9,6 +9,7 @@ module.exports = {
9
9
  moduleNameMapper: {
10
10
  '\\.(css|scss)$': require.resolve('identity-obj-proxy'),
11
11
  'site.config': path.resolve(process.cwd(), './site.config.test.tsx'),
12
+ '^@src/(.*)$': '<rootDir>/src/$1',
12
13
  },
13
14
  collectCoverageFrom: [
14
15
  'src/**/*.{js,jsx,ts,tsx}',
@@ -12,8 +12,6 @@ var ConfigTypes;
12
12
  })(ConfigTypes || (exports.ConfigTypes = ConfigTypes = {}));
13
13
  var CommandTypes;
14
14
  (function (CommandTypes) {
15
- CommandTypes["RELEASE"] = "release";
16
- CommandTypes["PACK"] = "pack";
17
15
  CommandTypes["LINT"] = "lint";
18
16
  CommandTypes["TEST"] = "test";
19
17
  CommandTypes["BUILD"] = "build";
@@ -87,7 +87,7 @@ function getStyleUseConfig(mode) {
87
87
  ],
88
88
  // Silences compiler deprecation warnings. They mostly come from bootstrap and/or paragon.
89
89
  quietDeps: true,
90
- silenceDeprecations: ['abs-percent', 'color-functions', 'import', 'mixed-decls', 'global-builtin', 'legacy-js-api'],
90
+ silenceDeprecations: ['abs-percent', 'color-functions', 'import', 'global-builtin', 'legacy-js-api'],
91
91
  },
92
92
  },
93
93
  },
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const html_webpack_plugin_1 = __importDefault(require("html-webpack-plugin"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const webpack_1 = __importDefault(require("webpack"));
10
+ const HtmlWebpackNewRelicPlugin_1 = __importDefault(require("../HtmlWebpackNewRelicPlugin"));
11
+ const OUTPUT_DIR = path_1.default.join(__dirname, './dist');
12
+ describe('HtmlWebpackNewRelicPlugin', () => {
13
+ const testPluginOptions = {
14
+ accountID: '121212',
15
+ agentID: '343434',
16
+ trustKey: '565656',
17
+ licenseKey: '123456',
18
+ applicationID: '654321',
19
+ };
20
+ afterEach(() => {
21
+ if (fs_1.default.existsSync(OUTPUT_DIR)) {
22
+ fs_1.default.rmSync(OUTPUT_DIR, { recursive: true, force: true });
23
+ }
24
+ });
25
+ it('should append new relic script to body', done => {
26
+ (0, webpack_1.default)({
27
+ entry: path_1.default.resolve(__dirname, 'fixtures', 'entry.js'),
28
+ output: {
29
+ path: path_1.default.resolve(__dirname, './dist'),
30
+ },
31
+ plugins: [
32
+ new html_webpack_plugin_1.default(),
33
+ new HtmlWebpackNewRelicPlugin_1.default(testPluginOptions),
34
+ ],
35
+ }, (err) => {
36
+ const htmlFile = path_1.default.resolve(OUTPUT_DIR, 'index.html');
37
+ expect(err).toBeNull();
38
+ expect(fs_1.default.existsSync(htmlFile)).toBe(true);
39
+ const file = fs_1.default.readFileSync(path_1.default.resolve(OUTPUT_DIR, htmlFile), { encoding: 'utf-8' }, (error, data) => data.toString());
40
+ Object.entries(testPluginOptions).forEach(([optionName, optionValue]) => {
41
+ expect(file.indexOf(`${optionName}:"${optionValue}"`)).toBeGreaterThan(-1);
42
+ });
43
+ done();
44
+ });
45
+ });
46
+ describe('when its missing configuration variables', () => {
47
+ function testMissingOption(missingOptionName) {
48
+ it(`should throw error if ${missingOptionName} is missing`, done => {
49
+ const compiler = (0, webpack_1.default)({
50
+ entry: path_1.default.resolve(__dirname, 'fixtures', 'entry.js'),
51
+ output: {
52
+ path: path_1.default.resolve(__dirname, '../dist'),
53
+ },
54
+ plugins: [new html_webpack_plugin_1.default()],
55
+ });
56
+ const optionsMissingOne = { ...testPluginOptions };
57
+ delete optionsMissingOne[missingOptionName];
58
+ expect(() => compiler.options.plugins.push(new HtmlWebpackNewRelicPlugin_1.default(optionsMissingOne))).toThrow(`${missingOptionName} argument is required`);
59
+ done();
60
+ });
61
+ }
62
+ Object.keys(testPluginOptions).forEach((key) => {
63
+ testMissingOption(key);
64
+ });
65
+ });
66
+ });
@@ -9,13 +9,9 @@ const path_1 = __importDefault(require("path"));
9
9
  const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
10
10
  const webpack_remove_empty_scripts_1 = __importDefault(require("webpack-remove-empty-scripts"));
11
11
  const common_config_1 = require("./common-config");
12
- const ParagonWebpackPlugin_1 = __importDefault(require("./plugins/paragon-webpack-plugin/ParagonWebpackPlugin"));
13
12
  const getLocalAliases_1 = __importDefault(require("./utils/getLocalAliases"));
14
13
  const getPublicPath_1 = __importDefault(require("./utils/getPublicPath"));
15
14
  const getResolvedSiteConfigPath_1 = __importDefault(require("./utils/getResolvedSiteConfigPath"));
16
- const paragonUtils_1 = require("./utils/paragonUtils");
17
- const paragonThemeCss = (0, paragonUtils_1.getParagonThemeCss)(process.cwd());
18
- const brandThemeCss = (0, paragonUtils_1.getParagonThemeCss)(process.cwd(), { isBrandOverride: true });
19
15
  const aliases = (0, getLocalAliases_1.default)();
20
16
  const resolvedSiteConfigPath = (0, getResolvedSiteConfigPath_1.default)('site.config.build.tsx');
21
17
  const config = {
@@ -23,8 +19,6 @@ const config = {
23
19
  devtool: 'source-map',
24
20
  entry: {
25
21
  app: path_1.default.resolve(process.cwd(), 'node_modules/@openedx/frontend-base/shell/site'),
26
- ...(0, paragonUtils_1.getParagonEntryPoints)(paragonThemeCss),
27
- ...(0, paragonUtils_1.getParagonEntryPoints)(brandThemeCss),
28
22
  },
29
23
  output: {
30
24
  filename: '[name].[chunkhash].js',
@@ -36,6 +30,7 @@ const config = {
36
30
  alias: {
37
31
  ...aliases,
38
32
  'site.config': resolvedSiteConfigPath,
33
+ '@src': path_1.default.resolve(process.cwd(), 'src'),
39
34
  },
40
35
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
41
36
  },
@@ -51,10 +46,6 @@ const config = {
51
46
  runtimeChunk: 'single',
52
47
  splitChunks: {
53
48
  chunks: 'all',
54
- cacheGroups: {
55
- ...(0, paragonUtils_1.getParagonCacheGroups)(paragonThemeCss),
56
- ...(0, paragonUtils_1.getParagonCacheGroups)(brandThemeCss),
57
- },
58
49
  },
59
50
  minimizer: (0, common_config_1.getImageMinimizer)(),
60
51
  },
@@ -65,7 +56,6 @@ const config = {
65
56
  // See: https://www.npmjs.com/package/webpack-remove-empty-scripts#usage-with-mini-css-extract-plugin
66
57
  new webpack_remove_empty_scripts_1.default(),
67
58
  // Writes the extracted CSS from each entry to a file in the output directory.
68
- new ParagonWebpackPlugin_1.default(),
69
59
  new mini_css_extract_plugin_1.default({
70
60
  filename: '[name].[chunkhash].css',
71
61
  }),