@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
@@ -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}',
package/config/types.js CHANGED
@@ -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
  }),
@@ -9,20 +9,14 @@ const mini_css_extract_plugin_1 = __importDefault(require("mini-css-extract-plug
9
9
  const path_1 = __importDefault(require("path"));
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.dev.tsx');
21
17
  const config = {
22
18
  entry: {
23
19
  app: path_1.default.resolve(process.cwd(), 'node_modules/@openedx/frontend-base/shell/site'),
24
- ...(0, paragonUtils_1.getParagonEntryPoints)(paragonThemeCss),
25
- ...(0, paragonUtils_1.getParagonEntryPoints)(brandThemeCss),
26
20
  },
27
21
  output: {
28
22
  path: path_1.default.resolve(process.cwd(), './dist'),
@@ -32,6 +26,7 @@ const config = {
32
26
  alias: {
33
27
  ...aliases,
34
28
  'site.config': resolvedSiteConfigPath,
29
+ '@src': path_1.default.resolve(process.cwd(), 'src'),
35
30
  },
36
31
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
37
32
  },
@@ -47,10 +42,6 @@ const config = {
47
42
  optimization: {
48
43
  splitChunks: {
49
44
  chunks: 'all',
50
- cacheGroups: {
51
- ...(0, paragonUtils_1.getParagonCacheGroups)(paragonThemeCss),
52
- ...(0, paragonUtils_1.getParagonCacheGroups)(brandThemeCss),
53
- },
54
45
  },
55
46
  minimizer: (0, common_config_1.getImageMinimizer)(),
56
47
  },
@@ -60,7 +51,6 @@ const config = {
60
51
  // This helps to clean up the final bundle application
61
52
  // See: https://www.npmjs.com/package/webpack-remove-empty-scripts#usage-with-mini-css-extract-plugin
62
53
  new webpack_remove_empty_scripts_1.default(),
63
- new ParagonWebpackPlugin_1.default(),
64
54
  // Writes the extracted CSS from each entry to a file in the output directory.
65
55
  new mini_css_extract_plugin_1.default({
66
56
  filename: '[name].css',
@@ -72,5 +62,9 @@ const config = {
72
62
  // This configures webpack-dev-server which serves bundles from memory and provides live
73
63
  // reloading.
74
64
  devServer: (0, common_config_1.getDevServer)(),
65
+ // Limit the number of watched files to avoid `inotify` resource starvation.
66
+ watchOptions: {
67
+ ignored: /node_modules/
68
+ }
75
69
  };
76
70
  exports.default = config;
@@ -11,21 +11,15 @@ const path_1 = __importDefault(require("path"));
11
11
  const react_refresh_typescript_1 = __importDefault(require("react-refresh-typescript"));
12
12
  const webpack_remove_empty_scripts_1 = __importDefault(require("webpack-remove-empty-scripts"));
13
13
  const common_config_1 = require("./common-config");
14
- const ParagonWebpackPlugin_1 = __importDefault(require("./plugins/paragon-webpack-plugin/ParagonWebpackPlugin"));
15
14
  const html_webpack_plugin_1 = __importDefault(require("html-webpack-plugin"));
16
15
  const getLocalAliases_1 = __importDefault(require("./utils/getLocalAliases"));
17
16
  const getPublicPath_1 = __importDefault(require("./utils/getPublicPath"));
18
17
  const getResolvedSiteConfigPath_1 = __importDefault(require("./utils/getResolvedSiteConfigPath"));
19
- const paragonUtils_1 = require("./utils/paragonUtils");
20
- const paragonThemeCss = (0, paragonUtils_1.getParagonThemeCss)(process.cwd());
21
- const brandThemeCss = (0, paragonUtils_1.getParagonThemeCss)(process.cwd(), { isBrandOverride: true });
22
18
  const aliases = (0, getLocalAliases_1.default)();
23
19
  const resolvedSiteConfigPath = (0, getResolvedSiteConfigPath_1.default)('shell/site.config.dev.tsx');
24
20
  const config = {
25
21
  entry: {
26
22
  app: path_1.default.resolve(process.cwd(), 'shell/site'),
27
- ...(0, paragonUtils_1.getParagonEntryPoints)(paragonThemeCss),
28
- ...(0, paragonUtils_1.getParagonEntryPoints)(brandThemeCss),
29
23
  },
30
24
  output: {
31
25
  path: path_1.default.resolve(process.cwd(), './dist'),
@@ -35,6 +29,7 @@ const config = {
35
29
  alias: {
36
30
  ...aliases,
37
31
  'site.config': resolvedSiteConfigPath,
32
+ '@src': path_1.default.resolve(process.cwd(), 'src'),
38
33
  },
39
34
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
40
35
  },
@@ -77,10 +72,6 @@ const config = {
77
72
  optimization: {
78
73
  splitChunks: {
79
74
  chunks: 'all',
80
- cacheGroups: {
81
- ...(0, paragonUtils_1.getParagonCacheGroups)(paragonThemeCss),
82
- ...(0, paragonUtils_1.getParagonCacheGroups)(brandThemeCss),
83
- },
84
75
  },
85
76
  minimizer: (0, common_config_1.getImageMinimizer)(),
86
77
  },
@@ -90,7 +81,6 @@ const config = {
90
81
  // This helps to clean up the final bundle application
91
82
  // See: https://www.npmjs.com/package/webpack-remove-empty-scripts#usage-with-mini-css-extract-plugin
92
83
  new webpack_remove_empty_scripts_1.default(),
93
- new ParagonWebpackPlugin_1.default(),
94
84
  // Writes the extracted CSS from each entry to a file in the output directory.
95
85
  new mini_css_extract_plugin_1.default({
96
86
  filename: '[name].css',
@@ -106,5 +96,9 @@ const config = {
106
96
  // This configures webpack-dev-server which serves bundles from memory and provides live
107
97
  // reloading.
108
98
  devServer: (0, common_config_1.getDevServer)(),
99
+ // Limit the number of watched files to avoid `inotify` resource starvation.
100
+ watchOptions: {
101
+ ignored: /node_modules/
102
+ }
109
103
  };
110
104
  exports.default = config;
package/package.json CHANGED
@@ -1,11 +1,18 @@
1
1
  {
2
2
  "name": "@openedx/frontend-base",
3
- "version": "1.0.0-alpha.0",
3
+ "version": "1.0.0-alpha.10",
4
4
  "description": "Build tools, setup and config for frontend apps",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "main": "index.ts",
9
+ "files": [
10
+ "/config",
11
+ "/runtime",
12
+ "/tools/dist",
13
+ "/shell",
14
+ "/types.ts"
15
+ ],
9
16
  "bin": {
10
17
  "intl-imports.js": "tools/dist/cli/scripts/intl-imports.js",
11
18
  "openedx": "tools/dist/cli/openedx.js",
@@ -79,6 +86,7 @@
79
86
  "image-minimizer-webpack-plugin": "3.8.3",
80
87
  "jest": "^29.7.0",
81
88
  "jest-environment-jsdom": "^29.7.0",
89
+ "jest-localstorage-mock": "^2.4.26",
82
90
  "jwt-decode": "^3.1.2",
83
91
  "localforage": "^1.10.0",
84
92
  "localforage-memoryStorageDriver": "^0.9.2",
@@ -94,7 +102,7 @@
94
102
  "postcss-rtlcss": "^5.5.0",
95
103
  "prop-types": "^15.8.1",
96
104
  "react-dev-utils": "12.0.1",
97
- "react-focus-on": "^3.9.4",
105
+ "react-focus-on": "<3.10.0",
98
106
  "react-intl": "^6.6.6",
99
107
  "react-refresh": "0.16.0",
100
108
  "react-refresh-typescript": "^2.0.9",
@@ -139,7 +147,7 @@
139
147
  "nodemon": "^3.1.4"
140
148
  },
141
149
  "peerDependencies": {
142
- "@openedx/paragon": "^22.20.2",
150
+ "@openedx/paragon": "^23.4.5",
143
151
  "@tanstack/react-query": "^5.81.2",
144
152
  "react": "^18.3.1",
145
153
  "react-dom": "^18.3.1",
@@ -0,0 +1 @@
1
+ export default 'FileMock';
@@ -0,0 +1 @@
1
+ export default 'SvgURL';
@@ -0,0 +1,6 @@
1
+ const mockCookiesImplementation = {
2
+ get: jest.fn(),
3
+ remove: jest.fn(),
4
+ };
5
+
6
+ module.exports = () => mockCookiesImplementation;
@@ -0,0 +1,242 @@
1
+ import {
2
+ configureAnalytics,
3
+ identifyAnonymousUser,
4
+ identifyAuthenticatedUser,
5
+ sendPageEvent,
6
+ sendTrackEvent,
7
+ sendTrackingLogEvent,
8
+ SegmentAnalyticsService,
9
+ } from './index';
10
+
11
+ const eventType = 'test.event';
12
+ const eventData = {
13
+ testShallow: 'test-shallow',
14
+ testObject: {
15
+ testDeep: 'test-deep',
16
+ },
17
+ };
18
+ const testUserId = 99;
19
+ const mockLoggingService = {
20
+ logError: jest.fn(),
21
+ logInfo: jest.fn(),
22
+ };
23
+ // The actual ApiClient is a function, so while this feels weird, it accurately models the real
24
+ // thing.
25
+ const mockAuthApiClient = () => {
26
+ };
27
+ mockAuthApiClient.post = jest.fn().mockResolvedValue(undefined);
28
+
29
+ // SegmentAnalyticsService inserts a script before the first script element
30
+ // in the document. Add one here.
31
+ document.body.innerHTML = '<script id="stub" />';
32
+
33
+ describe('Analytics', () => {
34
+ let service;
35
+ let userIdFunction;
36
+
37
+ beforeEach(() => {
38
+ window.analytics = [];
39
+ });
40
+
41
+ function afterConfigure() {
42
+ mockLoggingService.logError.mockReset();
43
+ mockAuthApiClient.post.mockReset();
44
+ mockAuthApiClient.post.mockResolvedValue(undefined);
45
+ window.analytics.identify = jest.fn();
46
+ window.analytics.page = jest.fn();
47
+ window.analytics.track = jest.fn();
48
+ window.analytics.reset = jest.fn();
49
+ userIdFunction = jest.fn();
50
+ window.analytics.user = jest.fn(() => ({
51
+ id: userIdFunction,
52
+ }));
53
+ window.analytics.ready = jest.fn((callback) => {
54
+ if (callback) {
55
+ callback();
56
+ }
57
+ });
58
+ }
59
+
60
+ describe('with valid segmentKey', () => {
61
+ beforeEach(() => {
62
+ service = configureAnalytics(SegmentAnalyticsService, {
63
+ loggingService: mockLoggingService,
64
+ httpClient: mockAuthApiClient,
65
+ config: {
66
+ lmsBaseUrl: 'https://example.com',
67
+ segmentKey: 'test-key',
68
+ },
69
+ });
70
+
71
+ afterConfigure();
72
+
73
+ expect(global.analytics.invoked).toBe(true);
74
+ expect(service.segmentInitialized).toBe(true);
75
+ });
76
+
77
+ describe('analytics sendTrackingLogEvent', () => {
78
+ it('posts expected data when successful', () => {
79
+ expect.assertions(6);
80
+ return sendTrackingLogEvent(eventType, eventData)
81
+ .then(() => {
82
+ expect(mockAuthApiClient.post.mock.calls.length).toEqual(1);
83
+ expect(mockAuthApiClient.post.mock.calls[0][0]).toEqual('https://example.com/event');
84
+ const expectedData = 'event_type=test.event&event=%7B%22test_shallow%22%3A%22test-shallow%22%2C%22test_object%22%3A%7B%22test_deep%22%3A%22test-deep%22%7D%7D&page=http%3A%2F%2Flocalhost%2F';
85
+ expect(mockAuthApiClient.post.mock.calls[0][1]).toEqual(expectedData);
86
+ const config = mockAuthApiClient.post.mock.calls[0][2];
87
+ expect(config.headers['Content-Type']).toEqual('application/x-www-form-urlencoded');
88
+ });
89
+ });
90
+
91
+ it('calls loggingService.logError on error', () => {
92
+ mockAuthApiClient.post.mockRejectedValue('test-error');
93
+ expect.assertions(4);
94
+ return sendTrackingLogEvent(eventType, eventData)
95
+ .then(() => {
96
+ expect(mockLoggingService.logError.mock.calls.length).toBe(1);
97
+ expect(mockLoggingService.logError).toBeCalledWith('test-error');
98
+ });
99
+ });
100
+ });
101
+
102
+ describe('analytics identifyAuthenticatedUser', () => {
103
+ it('calls Segment identify on success', () => {
104
+ const testTraits = { anything: 'Yay!' };
105
+ identifyAuthenticatedUser(testUserId, testTraits);
106
+
107
+ expect(window.analytics.identify.mock.calls.length).toBe(1);
108
+ expect(window.analytics.identify).toBeCalledWith(testUserId, testTraits);
109
+ });
110
+
111
+ it('throws error if userId is not supplied', () => {
112
+ expect(() => identifyAuthenticatedUser(null))
113
+ .toThrowError(new Error('UserId is required for identifyAuthenticatedUser.'));
114
+ });
115
+ });
116
+
117
+ describe('analytics identifyAnonymousUser', () => {
118
+ it('does not call segment reset for no previous segment user', () => {
119
+ window.analytics.user = () => ({ id: () => null });
120
+ const testTraits = { anything: 'Yay!' };
121
+ identifyAnonymousUser(testTraits);
122
+
123
+ expect(window.analytics.reset.mock.calls.length).toBe(0);
124
+ });
125
+
126
+ it('calls segment reset for a previous segment user', () => {
127
+ window.analytics.user = () => ({ id: () => 7 });
128
+ const testTraits = { anything: 'Yay!' };
129
+ identifyAnonymousUser(testTraits);
130
+
131
+ expect(window.analytics.reset.mock.calls.length).toBe(1);
132
+ });
133
+ });
134
+
135
+ function testSendPageAfterIdentify(identifyFunction) {
136
+ identifyFunction();
137
+
138
+ const testCategory = 'test-category';
139
+ const testName = 'test-name';
140
+ const testProperties = { anything: 'Yay!' };
141
+ sendPageEvent(testCategory, testName, testProperties);
142
+
143
+ expect(window.analytics.page.mock.calls.length).toBe(1);
144
+ expect(window.analytics.page).toBeCalledWith(testCategory, testName, testProperties);
145
+ }
146
+
147
+ describe('analytics send Page event', () => {
148
+ it('calls Segment page on success after identifyAuthenticatedUser', () => {
149
+ const userId = 1;
150
+ testSendPageAfterIdentify(() => identifyAuthenticatedUser(userId));
151
+ });
152
+
153
+ it('calls Segment page on success after identifyAnonymousUser', () => {
154
+ testSendPageAfterIdentify(identifyAnonymousUser);
155
+ });
156
+
157
+ it('fails if page called with no identify', () => {
158
+ sendPageEvent();
159
+
160
+ expect(mockLoggingService.logError.mock.calls.length).toBe(1);
161
+ expect(mockLoggingService.logError).toBeCalledWith('Identify must be called before other tracking events.');
162
+ });
163
+ });
164
+
165
+ function testSendTrackEventAfterIdentify(identifyFunction) {
166
+ identifyFunction();
167
+
168
+ const testName = 'test-name';
169
+ const testProperties = { anything: 'Yay!' };
170
+ sendTrackEvent(testName, testProperties);
171
+
172
+ expect(window.analytics.track.mock.calls.length).toBe(1);
173
+ expect(window.analytics.track).toBeCalledWith(testName, testProperties);
174
+ }
175
+
176
+ describe('analytics send Track event', () => {
177
+ it('calls Segment track on success after identifyAuthenticatedUser', () => {
178
+ const userId = 1;
179
+ testSendTrackEventAfterIdentify(() => identifyAuthenticatedUser(userId));
180
+ });
181
+
182
+ it('calls Segment track on success after identifyAnonymousUser', () => {
183
+ testSendTrackEventAfterIdentify(identifyAnonymousUser);
184
+ });
185
+
186
+ it('fails if track called with no identify', () => {
187
+ sendTrackEvent();
188
+
189
+ expect(mockLoggingService.logError.mock.calls.length).toBe(1);
190
+ expect(mockLoggingService.logError).toBeCalledWith('Identify must be called before other tracking events.');
191
+ });
192
+ });
193
+ });
194
+
195
+ describe('with invalid segmentKey', () => {
196
+ beforeEach(() => {
197
+ service = configureAnalytics(SegmentAnalyticsService, {
198
+ loggingService: mockLoggingService,
199
+ httpClient: mockAuthApiClient,
200
+ config: {
201
+ lmsBaseUrl: 'https://example.com',
202
+ segmentKey: '',
203
+ },
204
+ });
205
+
206
+ afterConfigure();
207
+ });
208
+
209
+ it('should not initialize Segment analytics', () => {
210
+ expect(global.analytics.invoked).toBeFalsy();
211
+ expect(service.segmentInitialized).toBe(false);
212
+ });
213
+
214
+ it('should not call segment track in sendTrackEvent', () => {
215
+ sendTrackEvent();
216
+ expect(window.analytics.track).not.toHaveBeenCalled();
217
+ });
218
+
219
+ it('should not call segment page in sendPageEvent', () => {
220
+ sendPageEvent();
221
+ expect(window.analytics.page).not.toHaveBeenCalled();
222
+ });
223
+
224
+ it('should not call ready, user, reset, or identify in identifyAnonymousUser', () => {
225
+ identifyAnonymousUser();
226
+
227
+ expect(window.analytics.ready).not.toHaveBeenCalled();
228
+ expect(window.analytics.user).not.toHaveBeenCalled();
229
+ expect(userIdFunction).not.toHaveBeenCalled();
230
+ expect(window.analytics.reset).not.toHaveBeenCalled();
231
+ expect(window.analytics.identify).not.toHaveBeenCalled();
232
+ expect(service.hasIdentifyBeenCalled).toBe(false);
233
+ });
234
+
235
+ it('should not call identify in identifyAuthenticatedUser', () => {
236
+ identifyAuthenticatedUser(testUserId);
237
+
238
+ expect(window.analytics.identify).not.toHaveBeenCalled();
239
+ expect(service.hasIdentifyBeenCalled).toBe(false);
240
+ });
241
+ });
242
+ });