@openedx/frontend-base 1.0.0-alpha.0 → 1.0.0-alpha.2
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.
- package/config/types.js +0 -2
- package/config/webpack/plugins/html-webpack-new-relic-plugin/test/HtmlWebpackNewRelicPlugin.test.js +66 -0
- package/package.json +8 -1
- package/runtime/__mocks__/file.js +1 -0
- package/runtime/__mocks__/svg.js +1 -0
- package/runtime/__mocks__/universal-cookie.js +6 -0
- package/runtime/analytics/interface.test.js +242 -0
- package/runtime/auth/AxiosJwtAuthService.test.jsx +1076 -0
- package/runtime/auth/interceptors/createRetryInterceptor.test.js +23 -0
- package/runtime/config/getExternalLinkUrl.test.js +76 -0
- package/runtime/i18n/lib.test.js +230 -0
- package/runtime/initialize.async.function.config.test.js +43 -0
- package/runtime/initialize.const.config.test.js +41 -0
- package/runtime/initialize.function.config.test.js +41 -0
- package/runtime/initialize.test.js +356 -0
- package/runtime/logging/NewRelicLoggingService.test.js +214 -0
- package/runtime/react/AuthenticatedPageRoute.test.jsx +135 -0
- package/runtime/react/ErrorBoundary.test.jsx +83 -0
- package/runtime/react/SiteProvider.test.jsx +66 -0
- package/runtime/react/hooks.test.jsx +104 -0
- package/runtime/routing/utils.test.ts +7 -0
- package/runtime/scripts/GoogleAnalyticsLoader.test.ts +77 -0
- package/runtime/site.config.test.tsx +33 -0
- package/runtime/slots/Slot.test.tsx +40 -0
- package/runtime/slots/layout/DefaultSlotLayout.test.tsx +31 -0
- package/runtime/slots/layout/hooks.test.tsx +178 -0
- package/runtime/slots/layout/utils.test.ts +67 -0
- package/runtime/slots/types.ts +1 -0
- package/runtime/slots/utils.test.ts +64 -0
- package/runtime/slots/utils.ts +28 -9
- package/runtime/testing/initializeMockApp.test.ts +66 -0
- package/runtime/utils.test.js +116 -0
- package/shell/Logo.test.tsx +32 -0
- package/shell/__mocks__/file.js +1 -0
- package/shell/__mocks__/svg.js +1 -0
- package/shell/__mocks__/universal-cookie.js +6 -0
- package/shell/app.ts +14 -0
- package/shell/dev/devHome/app.ts +2 -2
- package/shell/dev/slotShowcase/app.tsx +9 -9
- package/shell/header/app.tsx +3 -3
- package/shell/router/createRouter.test.tsx +50 -0
- package/shell/router/getAppRoutes.test.tsx +59 -0
- package/shell/site.config.dev.tsx +3 -3
- package/shell/site.config.test.tsx +16 -0
- package/tools/dist/cli/intl-imports.test.js +146 -0
- package/tools/dist/cli/openedx.js +1 -15
- package/tools/dist/cli/utils/printUsage.js +0 -9
- package/tools/dist/types.js +0 -2
- package/tools/dist/webpack/plugins/html-webpack-new-relic-plugin/test/HtmlWebpackNewRelicPlugin.test.js +66 -0
- package/types.ts +1 -1
- package/eslint.config.js +0 -18
- package/frontend-base.d.ts +0 -8
- package/jest.config.js +0 -7
- package/openedx-frontend-base.tgz +0 -0
- package/test-site/app.d.ts +0 -15
- package/test-site/dist/176.436443549ebb858db483.js +0 -2
- package/test-site/dist/176.436443549ebb858db483.js.map +0 -1
- package/test-site/dist/362.536eff787d2380fe246c.js +0 -2
- package/test-site/dist/362.536eff787d2380fe246c.js.map +0 -1
- package/test-site/dist/653.486966b108d224551296.js +0 -2
- package/test-site/dist/653.486966b108d224551296.js.map +0 -1
- package/test-site/dist/74e025d3fe9a7b7f8503054e2563b353.jpg +0 -0
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js +0 -3
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.LICENSE.txt +0 -106
- package/test-site/dist/806.323cf6496ad0a7fe73a7.js.map +0 -1
- package/test-site/dist/95ec738c0b7faac5b5c9126794446bbd.svg +0 -4
- package/test-site/dist/app.612058b36c74787759ac.css +0 -61
- package/test-site/dist/app.612058b36c74787759ac.css.map +0 -1
- package/test-site/dist/app.612058b36c74787759ac.js +0 -2
- package/test-site/dist/app.612058b36c74787759ac.js.map +0 -1
- package/test-site/dist/cb28cdb1468c915e27e5cec9af64f22f.svg +0 -1
- package/test-site/dist/index.html +0 -1
- package/test-site/dist/report.html +0 -39
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js +0 -2
- package/test-site/dist/runtime.c7aeaf7b967496cb076f.js.map +0 -1
- package/test-site/eslint.config.js +0 -12
- package/test-site/package-lock.json +0 -19226
- package/test-site/package.json +0 -29
- package/test-site/public/index.html +0 -10
- package/test-site/site.config.build.tsx +0 -27
- package/test-site/site.config.dev.tsx +0 -27
- package/test-site/src/authenticated-page/AuthenticatedPage.tsx +0 -18
- package/test-site/src/authenticated-page/i18n/index.ts +0 -27
- package/test-site/src/authenticated-page/index.tsx +0 -28
- package/test-site/src/example-page/ExamplePage.tsx +0 -79
- package/test-site/src/example-page/Image.tsx +0 -11
- package/test-site/src/example-page/ParagonPreview.jsx +0 -66
- package/test-site/src/example-page/apple.jpg +0 -0
- package/test-site/src/example-page/apple.svg +0 -1
- package/test-site/src/example-page/index.ts +0 -16
- package/test-site/src/i18n/README.md +0 -3
- package/test-site/src/i18n/messages/frontend-app-sample/ar.json +0 -4
- package/test-site/src/i18n/messages/frontend-app-sample/eo.json +0 -1
- package/test-site/src/i18n/messages/frontend-app-sample/es_419.json +0 -4
- package/test-site/src/i18n/messages/frontend-component-emptylangs/ar.json +0 -1
- package/test-site/src/i18n/messages/frontend-component-singlelang/ar.json +0 -3
- package/test-site/src/iframe-widget/IframeWidget.tsx +0 -14
- package/test-site/src/iframe-widget/index.ts +0 -16
- package/test-site/src/index.tsx +0 -3
- package/test-site/src/messages.js +0 -11
- package/test-site/src/site.scss +0 -11
- package/test-site/tsconfig.json +0 -14
- package/tools/babel/babel.config.js +0 -27
- package/tools/babel.config.js +0 -3
- package/tools/cli/README.md +0 -29
- package/tools/cli/commands/pack.ts +0 -9
- package/tools/cli/commands/release.ts +0 -27
- package/tools/cli/commands/serve.ts +0 -43
- package/tools/cli/intl-imports.ts +0 -274
- package/tools/cli/openedx.ts +0 -101
- package/tools/cli/transifex-utils.ts +0 -75
- package/tools/cli/utils/ensureConfigFilenameOption.ts +0 -40
- package/tools/cli/utils/formatter.ts +0 -10
- package/tools/cli/utils/getResolvedConfigPath.ts +0 -23
- package/tools/cli/utils/prettyPrintTitle.ts +0 -15
- package/tools/cli/utils/printUsage.ts +0 -53
- package/tools/config-helpers/createConfig.ts +0 -8
- package/tools/config-helpers/createLintConfig.ts +0 -14
- package/tools/config-helpers/getBaseConfig.ts +0 -11
- package/tools/defaultConfigPaths.ts +0 -30
- package/tools/dist/cli/commands/pack.js +0 -14
- package/tools/dist/cli/commands/release.js +0 -28
- package/tools/eslint/base.eslint.config.js +0 -124
- package/tools/eslint/modules.d.ts +0 -5
- package/tools/eslint.config.js +0 -15
- package/tools/index.ts +0 -3
- package/tools/jest/jest.config.js +0 -30
- package/tools/jest.config.js +0 -19
- package/tools/tsconfig.json +0 -24
- package/tools/types.ts +0 -21
- package/tools/typescript/tsconfig.json +0 -32
- package/tools/webpack/common-config/README.md +0 -15
- package/tools/webpack/common-config/all/getCodeRules.ts +0 -51
- package/tools/webpack/common-config/all/getFileLoaderRules.ts +0 -23
- package/tools/webpack/common-config/all/getIgnoreWarnings.ts +0 -13
- package/tools/webpack/common-config/all/getImageMinimizer.ts +0 -26
- package/tools/webpack/common-config/all/getStylesheetRule.ts +0 -111
- package/tools/webpack/common-config/dev/getDevServer.ts +0 -35
- package/tools/webpack/common-config/index.ts +0 -6
- package/tools/webpack/common-config/site/getHtmlWebpackPlugin.ts +0 -11
- package/tools/webpack/modules.d.ts +0 -6
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/HtmlWebpackNewRelicPlugin.ts +0 -102
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/LICENSE +0 -21
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/README.md +0 -7
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/index.js +0 -3
- package/tools/webpack/plugins/html-webpack-new-relic-plugin/test/fixtures/entry.js +0 -1
- package/tools/webpack/plugins/paragon-webpack-plugin/ParagonWebpackPlugin.ts +0 -134
- package/tools/webpack/plugins/paragon-webpack-plugin/index.ts +0 -3
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/assetUtils.ts +0 -71
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/htmlUtils.ts +0 -72
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/index.ts +0 -6
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/paragonStylesheetUtils.ts +0 -131
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/scriptUtils.ts +0 -144
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/stylesheetUtils.ts +0 -106
- package/tools/webpack/plugins/paragon-webpack-plugin/utils/tagUtils.ts +0 -54
- package/tools/webpack/types.ts +0 -69
- package/tools/webpack/utils/getLocalAliases.ts +0 -65
- package/tools/webpack/utils/getPublicPath.ts +0 -3
- package/tools/webpack/utils/getResolvedSiteConfigPath.ts +0 -28
- package/tools/webpack/utils/paragonUtils.ts +0 -152
- package/tools/webpack/webpack.config.build.ts +0 -93
- package/tools/webpack/webpack.config.dev.shell.ts +0 -122
- package/tools/webpack/webpack.config.dev.ts +0 -90
- package/tsconfig.json +0 -23
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import * as parse5 from 'parse5';
|
|
2
|
-
import { sources } from 'webpack';
|
|
3
|
-
|
|
4
|
-
import { ParagonScriptContents } from '../../../types';
|
|
5
|
-
import { getDescendantByTag, minifyScript } from './tagUtils';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Finds the insertion point for a script in an HTML document.
|
|
9
|
-
*
|
|
10
|
-
* @param {Object} options - The options object.
|
|
11
|
-
* @param {Object} options.document - The parsed HTML document.
|
|
12
|
-
* @param {string} options.originalSource - The original source code of the HTML document.
|
|
13
|
-
* @throws {Error} If the body element is missing in the HTML document.
|
|
14
|
-
* @return {number} The insertion point for the script in the HTML document.
|
|
15
|
-
*/
|
|
16
|
-
function findScriptInsertionPoint({ document, originalSource }: { document: any, originalSource: string }) {
|
|
17
|
-
const bodyElement = getDescendantByTag(document, 'body');
|
|
18
|
-
if (!bodyElement) {
|
|
19
|
-
throw new Error('Missing body element in index.html.');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// determine script insertion point
|
|
23
|
-
if (bodyElement.sourceCodeLocation?.endTag) {
|
|
24
|
-
return bodyElement.sourceCodeLocation.endTag.startOffset;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// less accurate fallback
|
|
28
|
-
return originalSource.indexOf('</body>');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Inserts the given script contents into the HTML document and returns a new source with the modified content.
|
|
33
|
-
*
|
|
34
|
-
* @param {Object} options - The options object.
|
|
35
|
-
* @param {string} options.originalSource - The original HTML source.
|
|
36
|
-
* @param {Object} options.scriptContents - The contents of the script to be inserted.
|
|
37
|
-
* @return {sources.ReplaceSource} The new source with the modified HTML content.
|
|
38
|
-
*/
|
|
39
|
-
export function insertScriptContentsIntoDocument({
|
|
40
|
-
originalSource,
|
|
41
|
-
scriptContents,
|
|
42
|
-
}: { originalSource: string, scriptContents: ParagonScriptContents }) {
|
|
43
|
-
// parse file as html document
|
|
44
|
-
const document = parse5.parse(originalSource, {
|
|
45
|
-
sourceCodeLocationInfo: true,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// find the body element
|
|
49
|
-
const scriptInsertionPoint = findScriptInsertionPoint({
|
|
50
|
-
document,
|
|
51
|
-
originalSource,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// create Paragon script to inject into the HTML document
|
|
55
|
-
const paragonScript = `<script type="text/javascript">var PARAGON_THEME = ${JSON.stringify(scriptContents, null, 2)};</script>`;
|
|
56
|
-
|
|
57
|
-
// insert the Paragon script into the HTML document
|
|
58
|
-
const newSource = new sources.ReplaceSource(
|
|
59
|
-
new sources.RawSource(originalSource),
|
|
60
|
-
'index.html',
|
|
61
|
-
);
|
|
62
|
-
newSource.insert(scriptInsertionPoint, minifyScript(paragonScript));
|
|
63
|
-
return newSource;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Creates an object with the provided version, defaults, coreCssAsset, and themeVariantCssAssets
|
|
68
|
-
* and returns it. The returned object has the following structure:
|
|
69
|
-
* {
|
|
70
|
-
* version: The provided version,
|
|
71
|
-
* themeUrls: {
|
|
72
|
-
* core: The provided coreCssAsset,
|
|
73
|
-
* variants: The provided themeVariantCssAssets,
|
|
74
|
-
* defaults: The provided defaults
|
|
75
|
-
* }
|
|
76
|
-
* }
|
|
77
|
-
*
|
|
78
|
-
* @param {Object} options - The options object.
|
|
79
|
-
* @param {string} options.version - The version to be added to the returned object.
|
|
80
|
-
* @param {Object} options.defaults - The defaults to be added to the returned object.
|
|
81
|
-
* @param {Object} options.coreCssAsset - The coreCssAsset to be added to the returned object.
|
|
82
|
-
* @param {Object} options.themeVariantCssAssets - The themeVariantCssAssets to be added to the returned object.
|
|
83
|
-
* @return {Object} The object with the provided version, defaults, coreCssAsset, and themeVariantCssAssets.
|
|
84
|
-
*/
|
|
85
|
-
export function addToScriptContents({
|
|
86
|
-
version,
|
|
87
|
-
defaults,
|
|
88
|
-
coreCssAsset,
|
|
89
|
-
themeVariantCssAssets,
|
|
90
|
-
}: {
|
|
91
|
-
defaults: any,
|
|
92
|
-
coreCssAsset: any,
|
|
93
|
-
themeVariantCssAssets: any,
|
|
94
|
-
version: string,
|
|
95
|
-
}) {
|
|
96
|
-
return {
|
|
97
|
-
version,
|
|
98
|
-
themeUrls: {
|
|
99
|
-
core: coreCssAsset,
|
|
100
|
-
variants: themeVariantCssAssets,
|
|
101
|
-
defaults,
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Generates the script contents object based on the provided assets and versions.
|
|
108
|
-
*
|
|
109
|
-
* @param {Object} options - The options object.
|
|
110
|
-
* @param {Object} options.paragonCoreCssAsset - The asset for the Paragon core CSS.
|
|
111
|
-
* @param {Object} options.paragonThemeVariantCssAssets - The assets for the Paragon theme variants.
|
|
112
|
-
* @param {Object} options.brandCoreCssAsset - The asset for the brand core CSS.
|
|
113
|
-
* @param {Object} options.brandThemeVariantCssAssets - The assets for the brand theme variants.
|
|
114
|
-
* @param {Object} options.paragonThemeCss - The Paragon theme CSS.
|
|
115
|
-
* @param {string} options.paragonVersion - The version of the Paragon theme.
|
|
116
|
-
* @param {Object} options.brandThemeCss - The brand theme CSS.
|
|
117
|
-
* @param {string} options.brandVersion - The version of the brand theme.
|
|
118
|
-
* @return {Object} The script contents object.
|
|
119
|
-
*/
|
|
120
|
-
export function generateScriptContents({
|
|
121
|
-
paragonCoreCssAsset,
|
|
122
|
-
paragonThemeVariantCssAssets,
|
|
123
|
-
brandCoreCssAsset,
|
|
124
|
-
brandThemeVariantCssAssets,
|
|
125
|
-
paragonThemeCss,
|
|
126
|
-
paragonVersion,
|
|
127
|
-
brandThemeCss,
|
|
128
|
-
brandVersion,
|
|
129
|
-
}: { paragonCoreCssAsset: any, paragonThemeVariantCssAssets: any, brandCoreCssAsset: any, brandThemeVariantCssAssets: any, paragonThemeCss: any, paragonVersion: string, brandThemeCss: any, brandVersion: string }) {
|
|
130
|
-
const scriptContents: ParagonScriptContents = {};
|
|
131
|
-
scriptContents.paragon = addToScriptContents({
|
|
132
|
-
version: paragonVersion,
|
|
133
|
-
coreCssAsset: paragonCoreCssAsset,
|
|
134
|
-
themeVariantCssAssets: paragonThemeVariantCssAssets,
|
|
135
|
-
defaults: paragonThemeCss?.defaults,
|
|
136
|
-
});
|
|
137
|
-
scriptContents.brand = addToScriptContents({
|
|
138
|
-
version: brandVersion,
|
|
139
|
-
coreCssAsset: brandCoreCssAsset,
|
|
140
|
-
themeVariantCssAssets: brandThemeVariantCssAssets,
|
|
141
|
-
defaults: brandThemeCss?.defaults,
|
|
142
|
-
});
|
|
143
|
-
return scriptContents;
|
|
144
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import * as parse5 from 'parse5';
|
|
2
|
-
import { sources } from 'webpack';
|
|
3
|
-
|
|
4
|
-
import { getDescendantByTag } from './tagUtils';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Finds the insertion point for a stylesheet in an HTML document.
|
|
8
|
-
*
|
|
9
|
-
* @param {Object} options - The options object.
|
|
10
|
-
* @param {Object} options.document - The parsed HTML document.
|
|
11
|
-
* @param {string} options.source - The original source code of the HTML document.
|
|
12
|
-
* @throws {Error} If the head element is missing in the HTML document.
|
|
13
|
-
* @return {number} The insertion point for the stylesheet in the HTML document.
|
|
14
|
-
*/
|
|
15
|
-
export function findStylesheetInsertionPoint({ document, source }: { document: Document, source: string }) {
|
|
16
|
-
const headElement = getDescendantByTag(document, 'head');
|
|
17
|
-
if (!headElement) {
|
|
18
|
-
throw new Error('Missing head element in index.html.');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// determine script insertion point
|
|
22
|
-
if (headElement.sourceCodeLocation?.startTag) {
|
|
23
|
-
return headElement.sourceCodeLocation.startTag.endOffset;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// less accurate fallback
|
|
27
|
-
const headTagString = '<head>';
|
|
28
|
-
const headTagIndex = source.indexOf(headTagString);
|
|
29
|
-
return headTagIndex + headTagString.length;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Inserts stylesheets into an HTML document.
|
|
34
|
-
*
|
|
35
|
-
* @param {object} options - The options for inserting stylesheets.
|
|
36
|
-
* @param {string} options.source - The HTML source code.
|
|
37
|
-
* @param {object} options.urls - The URLs of the stylesheets to be inserted.
|
|
38
|
-
* @param {string} options.urls.default - The URL of the default stylesheet.
|
|
39
|
-
* @param {string} options.urls.brandOverride - The URL of the brand override stylesheet.
|
|
40
|
-
* @return {object|undefined} The new source code with the stylesheets inserted.
|
|
41
|
-
*/
|
|
42
|
-
export function insertStylesheetsIntoDocument({
|
|
43
|
-
source,
|
|
44
|
-
urls,
|
|
45
|
-
}: { source: string, urls: any }) {
|
|
46
|
-
// parse file as html document
|
|
47
|
-
const document = parse5.parse(source, {
|
|
48
|
-
sourceCodeLocationInfo: true,
|
|
49
|
-
});
|
|
50
|
-
if (!getDescendantByTag(document, 'head')) {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const newSource = new sources.ReplaceSource(
|
|
55
|
-
new sources.RawSource(source),
|
|
56
|
-
'index.html',
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// insert the brand overrides styles into the HTML document
|
|
60
|
-
const stylesheetInsertionPoint = findStylesheetInsertionPoint({
|
|
61
|
-
// @ts-expect-error Typescript complains this document instance is missing properties. Is parse5.parse not returning a valid Document instance?
|
|
62
|
-
document,
|
|
63
|
-
// @ts-expect-error We're passing a ReplaceSource here, when we expect a string in the function.
|
|
64
|
-
source: newSource,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Creates a new stylesheet link element.
|
|
69
|
-
*
|
|
70
|
-
* @param {string} url - The URL of the stylesheet.
|
|
71
|
-
* @return {string} The HTML code for the stylesheet link element.
|
|
72
|
-
*/
|
|
73
|
-
function createNewStylesheet(url: string) {
|
|
74
|
-
const baseLink = `<link
|
|
75
|
-
type="text/css"
|
|
76
|
-
rel="preload"
|
|
77
|
-
as="style"
|
|
78
|
-
href="${url}"
|
|
79
|
-
onload="this.rel='stylesheet';"
|
|
80
|
-
onerror="this.remove();"
|
|
81
|
-
/>`;
|
|
82
|
-
return baseLink;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (urls.default) {
|
|
86
|
-
// @ts-expect-error getDescendantByTag requires two parameters.
|
|
87
|
-
const existingDefaultLink = getDescendantByTag(`link[href='${urls.default}']`);
|
|
88
|
-
if (!existingDefaultLink) {
|
|
89
|
-
// create link to inject into the HTML document
|
|
90
|
-
const stylesheetLink = createNewStylesheet(urls.default);
|
|
91
|
-
newSource.insert(stylesheetInsertionPoint, stylesheetLink);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (urls.brandOverride) {
|
|
96
|
-
// @ts-expect-error getDescendantByTag requires two parameters.
|
|
97
|
-
const existingBrandLink = getDescendantByTag(`link[href='${urls.brandOverride}']`);
|
|
98
|
-
if (!existingBrandLink) {
|
|
99
|
-
// create link to inject into the HTML document
|
|
100
|
-
const stylesheetLink = createNewStylesheet(urls.brandOverride);
|
|
101
|
-
newSource.insert(stylesheetInsertionPoint, stylesheetLink);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return newSource;
|
|
106
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recursively searches for a descendant node with the specified tag name.
|
|
3
|
-
*
|
|
4
|
-
* @param {Object} node - The root node to start the search from.
|
|
5
|
-
* @param {string} tag - The tag name to search for.
|
|
6
|
-
* @return {Object|null} The first descendant node with the specified tag name, or null if not found.
|
|
7
|
-
*/
|
|
8
|
-
export function getDescendantByTag(node: any, tag: string): any {
|
|
9
|
-
for (let i = 0; i < node.childNodes?.length; i++) {
|
|
10
|
-
if (node.childNodes[i].tagName === tag) {
|
|
11
|
-
return node.childNodes[i];
|
|
12
|
-
}
|
|
13
|
-
const result = getDescendantByTag(node.childNodes[i], tag);
|
|
14
|
-
if (result) {
|
|
15
|
-
return result;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Replaces a wildcard keyword in a URL with a local version.
|
|
23
|
-
*
|
|
24
|
-
* @param {Object} options - The options object.
|
|
25
|
-
* @param {string} options.url - The URL to substitute the keyword in.
|
|
26
|
-
* @param {string} options.wildcardKeyword - The wildcard keyword to replace.
|
|
27
|
-
* @param {string} options.localVersion - The local version to substitute the keyword with.
|
|
28
|
-
* @return {string} The URL with the wildcard keyword substituted with the local version,
|
|
29
|
-
* or the original URL if no substitution is needed.
|
|
30
|
-
*/
|
|
31
|
-
export function handleVersionSubstitution({ url, wildcardKeyword, localVersion }: { url: string | undefined, wildcardKeyword: string, localVersion: string }) {
|
|
32
|
-
if (!url || !url.includes(wildcardKeyword) || !localVersion) {
|
|
33
|
-
return url;
|
|
34
|
-
}
|
|
35
|
-
return url.replaceAll(wildcardKeyword, localVersion);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Minifies a script by removing unnecessary whitespace and line breaks.
|
|
40
|
-
*
|
|
41
|
-
* @param {string} script - The script to be minified.
|
|
42
|
-
* @return {string} The minified script.
|
|
43
|
-
*/
|
|
44
|
-
export function minifyScript(script: string) {
|
|
45
|
-
return script
|
|
46
|
-
.replace(/>[\r\n ]+</g, '><')
|
|
47
|
-
.replace(/(<.*?>)|\s+/g, (m, $1) => {
|
|
48
|
-
if ($1) {
|
|
49
|
-
return $1;
|
|
50
|
-
}
|
|
51
|
-
return ' ';
|
|
52
|
-
})
|
|
53
|
-
.trim();
|
|
54
|
-
}
|
package/tools/webpack/types.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The metadata about the core Paragon theme CSS
|
|
3
|
-
*/
|
|
4
|
-
export interface ParagonThemeCssAsset {
|
|
5
|
-
filePath: string,
|
|
6
|
-
entryName: string,
|
|
7
|
-
outputChunkName: string,
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ParagonThemeVariantCssAsset {
|
|
11
|
-
filePath: string,
|
|
12
|
-
entryName: string,
|
|
13
|
-
outputChunkName: string,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ParagonThemeCss {
|
|
17
|
-
core: ParagonThemeCssAsset | undefined,
|
|
18
|
-
variants: Record<string, ParagonThemeVariantCssAsset>,
|
|
19
|
-
defaults: object,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// This isn't perfect, as it implies having neither url nor urls would be valid. Feel free to improve it so that one is required.
|
|
23
|
-
export interface ParagonThemeUrlsVariant {
|
|
24
|
-
url?: string,
|
|
25
|
-
urls?: Record<string, string | undefined>,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface ParagonThemeUrls {
|
|
29
|
-
core: ParagonThemeUrlsVariant,
|
|
30
|
-
defaults: Record<string, string>,
|
|
31
|
-
variants: Record<string, ParagonThemeUrlsVariant>,
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface ParagonThemeUrlsFile {
|
|
35
|
-
themeUrls: {
|
|
36
|
-
core: {
|
|
37
|
-
paths: {
|
|
38
|
-
default: string,
|
|
39
|
-
minified: string,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
defaults: Record<string, string>,
|
|
43
|
-
variants: Record<string, {
|
|
44
|
-
paths: {
|
|
45
|
-
default: string,
|
|
46
|
-
minified: string,
|
|
47
|
-
},
|
|
48
|
-
}>,
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface ParagonScriptContents {
|
|
53
|
-
paragon?: {
|
|
54
|
-
version: string,
|
|
55
|
-
themeUrls: {
|
|
56
|
-
core: any,
|
|
57
|
-
variants: any,
|
|
58
|
-
defaults: any,
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
brand?: {
|
|
62
|
-
version: string,
|
|
63
|
-
themeUrls: {
|
|
64
|
-
core: any,
|
|
65
|
-
variants: any,
|
|
66
|
-
defaults: any,
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
This function reads in a 'module.config.js' file if it exists and uses its contents to define
|
|
6
|
-
a set of webpack resolve.alias aliases for doing local development of application dependencies.
|
|
7
|
-
It reads the package.json file of the dependency to determine if it has any peer dependencies, and
|
|
8
|
-
then forces those peer dependencies to be resolved with the application's version. Primarily, this
|
|
9
|
-
is useful for making sure there's only one version of those dependencies loaded at once, which is a
|
|
10
|
-
problem with both react and react-intl.
|
|
11
|
-
|
|
12
|
-
The module.config.js file should have the form:
|
|
13
|
-
|
|
14
|
-
{
|
|
15
|
-
localModules: [
|
|
16
|
-
{ moduleName: 'nameOfPackage', dir: '../path/to/repo', dist: '/path/to/dist/in/repo' },
|
|
17
|
-
... others...
|
|
18
|
-
],
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Some working examples, as of the time of this writing:
|
|
22
|
-
|
|
23
|
-
{ moduleName: '@openedx/paragon/scss', dir: '../paragon', dist: 'scss' }
|
|
24
|
-
{ moduleName: '@openedx/paragon', dir: '../paragon', dist: 'dist' }
|
|
25
|
-
{ moduleName: '@openedx/frontend-base', dir: '../frontend-base', dist: 'dist' }
|
|
26
|
-
|
|
27
|
-
*/
|
|
28
|
-
export default function getLocalAliases() {
|
|
29
|
-
const aliases: Record<string, string> = {};
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const moduleConfigPath = path.resolve(process.cwd(), 'module.config.js');
|
|
33
|
-
if (!fs.existsSync(moduleConfigPath)) {
|
|
34
|
-
console.log('No local module configuration file found. This is fine.\n');
|
|
35
|
-
return aliases;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const { localModules } = require(moduleConfigPath);
|
|
39
|
-
|
|
40
|
-
let allPeerDependencies: string[] = [];
|
|
41
|
-
const excludedPeerPackages: string[] = [];
|
|
42
|
-
if (localModules.length > 0) {
|
|
43
|
-
console.info('Resolving modules from local directories via module.config.js.');
|
|
44
|
-
}
|
|
45
|
-
localModules.forEach(({ moduleName, dir, dist = '' }: { moduleName: string, dir: string, dist?: string }) => {
|
|
46
|
-
console.info(`Using local version of ${moduleName} from ${dir}/${dist}.`);
|
|
47
|
-
|
|
48
|
-
const { peerDependencies = {}, name } = require(path.resolve(process.cwd(), dir, 'package.json'));
|
|
49
|
-
allPeerDependencies = allPeerDependencies.concat(Object.keys(peerDependencies));
|
|
50
|
-
aliases[moduleName] = path.resolve(process.cwd(), dir, dist);
|
|
51
|
-
excludedPeerPackages.push(name);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
allPeerDependencies = allPeerDependencies.filter((dep) => !excludedPeerPackages.includes(dep));
|
|
55
|
-
|
|
56
|
-
allPeerDependencies.forEach((dep) => {
|
|
57
|
-
aliases[dep] = path.resolve(process.cwd(), 'node_modules', dep);
|
|
58
|
-
});
|
|
59
|
-
} catch (e) {
|
|
60
|
-
console.error(e);
|
|
61
|
-
console.error('Error in module.config.js parsing. module.config.js will be ignored.');
|
|
62
|
-
return {};
|
|
63
|
-
}
|
|
64
|
-
return aliases;
|
|
65
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
export default function getResolvedSiteConfigPath(defaultFilename: string) {
|
|
5
|
-
const siteConfigPath = process.env.SITE_CONFIG_PATH;
|
|
6
|
-
|
|
7
|
-
if (siteConfigPath !== undefined) {
|
|
8
|
-
// We assume siteConfigPath is a relative path.
|
|
9
|
-
const absolutePath = path.resolve(process.cwd(), siteConfigPath);
|
|
10
|
-
if (fs.existsSync(absolutePath)) {
|
|
11
|
-
// If it's a valid absolute path when joined with the current working directory, return that.
|
|
12
|
-
return absolutePath;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
// Otherwise, return our default site config file path.
|
|
16
|
-
const defaultPath = path.resolve(process.cwd(), defaultFilename);
|
|
17
|
-
if (fs.existsSync(defaultPath)) {
|
|
18
|
-
return defaultPath;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// They supplied something but we can't figure out what it is. Exit with an error.
|
|
22
|
-
if (siteConfigPath !== undefined) {
|
|
23
|
-
console.error(`Invalid site config path (${siteConfigPath} specified as an environment variable. Aborting.`);
|
|
24
|
-
} else {
|
|
25
|
-
console.error(`Default site config file (${defaultPath}) does not exist. Aborting.`);
|
|
26
|
-
}
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { Chunk } from 'webpack';
|
|
4
|
-
import { ParagonThemeCss, ParagonThemeUrlsFile } from '../types';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Retrieves the name of the brand package from the given directory.
|
|
8
|
-
*
|
|
9
|
-
* @param {string} dir - The directory path containing the package.json file.
|
|
10
|
-
* @return {string} The name of the brand package, or an empty string if not found.
|
|
11
|
-
*/
|
|
12
|
-
function getBrandPackageName(dir: string) {
|
|
13
|
-
const appDependencies = JSON.parse(fs.readFileSync(path.resolve(dir, 'package.json'), 'utf-8')).dependencies;
|
|
14
|
-
return Object.keys(appDependencies).find((key) => /@(open)?edx\/brand/.exec(key)) ?? '';
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Attempts to extract the Paragon version from the `node_modules` of
|
|
19
|
-
* the consuming application.
|
|
20
|
-
*
|
|
21
|
-
* @param {string} dir Path to directory containing `node_modules`.
|
|
22
|
-
* @returns {string} Paragon dependency version of the consuming application
|
|
23
|
-
*/
|
|
24
|
-
export function getParagonVersion(dir: string, { isBrandOverride = false } = {}) {
|
|
25
|
-
const npmPackageName = isBrandOverride ? getBrandPackageName(dir) : '@openedx/paragon';
|
|
26
|
-
const pathToPackageJson = `${dir}/node_modules/${npmPackageName}/package.json`;
|
|
27
|
-
if (!fs.existsSync(pathToPackageJson)) {
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
return JSON.parse(fs.readFileSync(pathToPackageJson, 'utf-8')).version;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Attempts to extract the Paragon theme CSS from the locally installed `@openedx/paragon` package.
|
|
35
|
-
* @param {string} dir Path to directory containing `node_modules`.
|
|
36
|
-
* @param {boolean} isBrandOverride
|
|
37
|
-
* @returns {ParagonThemeCss}
|
|
38
|
-
*/
|
|
39
|
-
export function getParagonThemeCss(dir: string, { isBrandOverride = false } = {}): ParagonThemeCss | undefined {
|
|
40
|
-
const npmPackageName = isBrandOverride ? getBrandPackageName(dir) : '@openedx/paragon';
|
|
41
|
-
const pathToParagonThemeOutput = path.resolve(dir, 'node_modules', npmPackageName, 'dist', 'theme-urls.json');
|
|
42
|
-
|
|
43
|
-
if (!fs.existsSync(pathToParagonThemeOutput)) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
const paragonConfig: ParagonThemeUrlsFile = JSON.parse(fs.readFileSync(pathToParagonThemeOutput, 'utf-8'));
|
|
47
|
-
const {
|
|
48
|
-
core: themeCore,
|
|
49
|
-
variants: themeVariants,
|
|
50
|
-
defaults,
|
|
51
|
-
} = paragonConfig?.themeUrls || {};
|
|
52
|
-
|
|
53
|
-
const pathToCoreCss = path.resolve(dir, 'node_modules', npmPackageName, 'dist', themeCore.paths.minified);
|
|
54
|
-
const coreCssExists = fs.existsSync(pathToCoreCss);
|
|
55
|
-
|
|
56
|
-
const themeVariantResults = Object.entries(themeVariants || {}).reduce((themeVariantAcc, [themeVariant, value]) => {
|
|
57
|
-
const themeVariantCssDefault = path.resolve(dir, 'node_modules', npmPackageName, 'dist', value.paths.default);
|
|
58
|
-
const themeVariantCssMinified = path.resolve(dir, 'node_modules', npmPackageName, 'dist', value.paths.minified);
|
|
59
|
-
|
|
60
|
-
if (!fs.existsSync(themeVariantCssDefault) && !fs.existsSync(themeVariantCssMinified)) {
|
|
61
|
-
return themeVariantAcc;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return ({
|
|
65
|
-
...themeVariantAcc,
|
|
66
|
-
[themeVariant]: {
|
|
67
|
-
filePath: themeVariantCssMinified,
|
|
68
|
-
entryName: isBrandOverride ? `brand.theme.variants.${themeVariant}` : `paragon.theme.variants.${themeVariant}`,
|
|
69
|
-
outputChunkName: isBrandOverride ? `brand-theme-variants-${themeVariant}` : `paragon-theme-variants-${themeVariant}`,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}, {});
|
|
73
|
-
|
|
74
|
-
if (!coreCssExists || Object.keys(themeVariantResults).length === 0) {
|
|
75
|
-
return undefined;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const coreResult = {
|
|
79
|
-
filePath: path.resolve(dir, pathToCoreCss),
|
|
80
|
-
entryName: isBrandOverride ? 'brand.theme.core' : 'paragon.theme.core',
|
|
81
|
-
outputChunkName: isBrandOverride ? 'brand-theme-core' : 'paragon-theme-core',
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
core: fs.existsSync(pathToCoreCss) ? coreResult : undefined,
|
|
86
|
-
variants: themeVariantResults,
|
|
87
|
-
defaults,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* @typedef CacheGroup
|
|
93
|
-
* @property {string} type The type of cache group.
|
|
94
|
-
* @property {string|function} name The name of the cache group.
|
|
95
|
-
* @property {function} chunks A function that returns true if the chunk should be included in the cache group.
|
|
96
|
-
* @property {boolean} enforce If true, this cache group will be created even if it conflicts with default cache groups.
|
|
97
|
-
*/
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* @param {ParagonThemeCss} paragonThemeCss The Paragon theme CSS metadata.
|
|
101
|
-
* @returns {Object.<string, CacheGroup>} The cache groups for the Paragon theme CSS.
|
|
102
|
-
*/
|
|
103
|
-
export function getParagonCacheGroups(paragonThemeCss: ParagonThemeCss | undefined) {
|
|
104
|
-
if (!paragonThemeCss) {
|
|
105
|
-
return {};
|
|
106
|
-
}
|
|
107
|
-
const cacheGroups: Record<string, { type: string, name: string, chunks: (chunk: Chunk) => boolean, enforce: boolean }> = {};
|
|
108
|
-
|
|
109
|
-
if (paragonThemeCss.core !== undefined) {
|
|
110
|
-
const { core } = paragonThemeCss;
|
|
111
|
-
cacheGroups[paragonThemeCss.core.outputChunkName] = {
|
|
112
|
-
type: 'css/mini-extract',
|
|
113
|
-
name: paragonThemeCss.core.outputChunkName,
|
|
114
|
-
chunks: (chunk: Chunk) => chunk.name === core.entryName,
|
|
115
|
-
enforce: true,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
Object.values(paragonThemeCss.variants).forEach(({ entryName, outputChunkName }) => {
|
|
120
|
-
cacheGroups[outputChunkName] = {
|
|
121
|
-
type: 'css/mini-extract',
|
|
122
|
-
name: outputChunkName,
|
|
123
|
-
chunks: (chunk: Chunk) => chunk.name === entryName,
|
|
124
|
-
enforce: true,
|
|
125
|
-
};
|
|
126
|
-
});
|
|
127
|
-
return cacheGroups;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @param {ParagonThemeCss} paragonThemeCss The Paragon theme CSS metadata.
|
|
132
|
-
* @returns {Object.<string, string>} The entry points for the Paragon theme CSS. Example: ```
|
|
133
|
-
* {
|
|
134
|
-
* "paragon.theme.core": "/path/to/node_modules/@openedx/paragon/dist/core.min.css",
|
|
135
|
-
* "paragon.theme.variants.light": "/path/to/node_modules/@openedx/paragon/dist/light.min.css"
|
|
136
|
-
* }
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
export function getParagonEntryPoints(paragonThemeCss: ParagonThemeCss | undefined) {
|
|
140
|
-
if (!paragonThemeCss) {
|
|
141
|
-
return {};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const entryPoints: Record<string, string> = {};
|
|
145
|
-
if (paragonThemeCss.core !== undefined) {
|
|
146
|
-
entryPoints[paragonThemeCss.core.entryName] = path.resolve(process.cwd(), paragonThemeCss.core.filePath);
|
|
147
|
-
}
|
|
148
|
-
Object.values(paragonThemeCss.variants).forEach(({ filePath, entryName }) => {
|
|
149
|
-
entryPoints[entryName] = path.resolve(process.cwd(), filePath);
|
|
150
|
-
});
|
|
151
|
-
return entryPoints;
|
|
152
|
-
}
|