Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
919677b544 | ||
|
|
32f0f33975 | ||
|
|
0662177f40 | ||
|
|
f266bba04c | ||
|
|
20e7bb09bb | ||
|
|
da7e055d77 | ||
|
|
bee2e54b87 | ||
|
|
1b179f55d1 | ||
|
|
edef77f101 | ||
|
|
e4f1a6100a | ||
|
|
b3869b97f7 | ||
|
|
cd339d491d | ||
|
|
b66cbb3eb6 | ||
|
|
cd2fdae725 | ||
|
|
51b4d9b42a | ||
|
|
112857de00 | ||
|
|
3317d2b148 | ||
|
|
be67c13914 |
32
.env
Normal file
32
.env
Normal file
@@ -0,0 +1,32 @@
|
||||
NODE_ENV=null
|
||||
ACCESS_TOKEN_COOKIE_NAME=null
|
||||
BASE_URL=null
|
||||
CREDENTIALS_BASE_URL=null
|
||||
CSRF_TOKEN_API_PATH=null
|
||||
ECOMMERCE_BASE_URL=null
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME=null
|
||||
LMS_BASE_URL=null
|
||||
LOGIN_URL=null
|
||||
LOGOUT_URL=null
|
||||
MARKETING_SITE_BASE_URL=null
|
||||
ORDER_HISTORY_URL=null
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT=null
|
||||
SEGMENT_KEY=null
|
||||
SITE_NAME=null
|
||||
USER_INFO_COOKIE_NAME=null
|
||||
APPLE_APP_STORE_URL=null
|
||||
CONTACT_URL=null
|
||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM=null
|
||||
ENTERPRISE_MARKETING_URL=null
|
||||
ENTERPRISE_MARKETING_UTM_CAMPAIGN=null
|
||||
ENTERPRISE_MARKETING_UTM_SOURCE=null
|
||||
FACEBOOK_URL=null
|
||||
GOOGLE_PLAY_URL=null
|
||||
LINKED_IN_URL=null
|
||||
OPEN_SOURCE_URL=null
|
||||
PRIVACY_POLICY_URL=null
|
||||
REDDIT_URL=null
|
||||
SUPPORT_URL=null
|
||||
TERMS_OF_SERVICE_URL=null
|
||||
TWITTER_URL=null
|
||||
YOU_TUBE_URL=null
|
||||
33
.env.development
Normal file
33
.env.development
Normal file
@@ -0,0 +1,33 @@
|
||||
NODE_ENV='development'
|
||||
PORT=1995
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
BASE_URL='localhost:1995'
|
||||
CREDENTIALS_BASE_URL='http://localhost:18150'
|
||||
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
ECOMMERCE_BASE_URL='http://localhost:18130'
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||
LMS_BASE_URL='http://localhost:18000'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/login'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
|
||||
SEGMENT_KEY=null
|
||||
SITE_NAME='edX'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
APPLE_APP_STORE_URL='https://www.apple.com/ios/app-store/'
|
||||
CONTACT_URL='http://localhost:18000/contact'
|
||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM='Footer'
|
||||
ENTERPRISE_MARKETING_URL='http://example.com'
|
||||
ENTERPRISE_MARKETING_UTM_CAMPAIGN='my_campaign'
|
||||
ENTERPRISE_MARKETING_UTM_SOURCE='edX profile'
|
||||
FACEBOOK_URL='https://www.facebook.com'
|
||||
GOOGLE_PLAY_URL='https://play.google.com/store'
|
||||
LINKED_IN_URL='https://www.linkedin.com'
|
||||
OPEN_SOURCE_URL='http://localhost:18000/openedx'
|
||||
PRIVACY_POLICY_URL='http://localhost:18000/privacy-policy'
|
||||
REDDIT_URL='https://www.reddit.com'
|
||||
SUPPORT_URL='http://localhost:18000/support'
|
||||
TERMS_OF_SERVICE_URL='http://localhost:18000/terms-of-service'
|
||||
TWITTER_URL='https://twitter.com'
|
||||
YOU_TUBE_URL='https://www.youtube.com'
|
||||
15
.env.test
Normal file
15
.env.test
Normal file
@@ -0,0 +1,15 @@
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
BASE_URL='localhost:1995'
|
||||
CREDENTIALS_BASE_URL='http://localhost:18150'
|
||||
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
ECOMMERCE_BASE_URL='http://localhost:18130'
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||
LMS_BASE_URL='http://localhost:18000'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/login'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
ORDER_HISTORY_URL='localhost:1996/orders'
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
|
||||
SEGMENT_KEY=null
|
||||
SITE_NAME='edX'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
@@ -2,3 +2,4 @@ coverage/*
|
||||
dist/
|
||||
node_modules/
|
||||
__mocks__/
|
||||
__snapshots__/
|
||||
|
||||
34
.eslintrc
34
.eslintrc
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"extends": "eslint-config-edx",
|
||||
"parser": "babel-eslint",
|
||||
"rules": {
|
||||
"import/no-extraneous-dependencies": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": [
|
||||
"webpack/*.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.test.js"
|
||||
]
|
||||
}
|
||||
],
|
||||
// https://github.com/evcohen/eslint-plugin-jsx-a11y/issues/340#issuecomment-338424908
|
||||
"jsx-a11y/anchor-is-valid": [ "error", {
|
||||
"components": [ "Link" ],
|
||||
"specialLink": [ "to" ]
|
||||
}],
|
||||
"jsx-a11y/label-has-for": [ 2, {
|
||||
"components": [ "label" ],
|
||||
"required": {
|
||||
"some": [ "nesting", "id" ]
|
||||
},
|
||||
"allowChildren": false
|
||||
}]
|
||||
},
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
"globals": {
|
||||
"newrelic": false
|
||||
}
|
||||
}
|
||||
3
.eslintrc.js
Normal file
3
.eslintrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const { createConfig } = require('@edx/frontend-build');
|
||||
|
||||
module.exports = createConfig('eslint');
|
||||
@@ -1,11 +1,9 @@
|
||||
.tx
|
||||
coverage
|
||||
dist
|
||||
footer
|
||||
node_modules
|
||||
public
|
||||
src
|
||||
webpack
|
||||
.dockerignore
|
||||
.eslintignore
|
||||
.eslintrc
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
node_js: 12
|
||||
before_install:
|
||||
- npm install -g npm@latest
|
||||
- npm install -g npm@6
|
||||
install:
|
||||
- npm ci
|
||||
script:
|
||||
@@ -13,7 +13,7 @@ script:
|
||||
- npm run npm-build
|
||||
- npm run is-es5
|
||||
after_success:
|
||||
- npm run semantic-release
|
||||
- npx semantic-release
|
||||
- codecov
|
||||
env:
|
||||
global:
|
||||
|
||||
2
Makefile
2
Makefile
@@ -51,7 +51,7 @@ validate-no-uncommitted-package-lock-changes:
|
||||
|
||||
npm-build:
|
||||
rm -rf ./npm-dist
|
||||
./node_modules/.bin/babel src/profile --out-dir npm-dist --source-maps --ignore **/*.test.jsx,**/*.test.js,**/setupTest.js --copy-files
|
||||
./node_modules/.bin/fedx-scripts babel src/profile --out-dir npm-dist --source-maps --ignore **/*.test.jsx,**/*.test.js,**/setupTest.js --copy-files
|
||||
@# --copy-files will bring in everything else that wasn't processed by babel. Remove what we don't want.
|
||||
@find npm-dist -name '*.test.js*' -delete
|
||||
@rm -rf ./npm-dist/__mocks__
|
||||
|
||||
60
README.rst
60
README.rst
@@ -3,22 +3,58 @@
|
||||
frontend-app-profile
|
||||
====================
|
||||
|
||||
Please tag **@edx/arch-fed** on any PRs or issues.
|
||||
This is a micro-frontend application responsible for the display and updating of user profiles. Please tag **@edx/arch-fed** on any PRs or issues.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
When a user views their own profile, they're given fields to edit their full name, location, primary spoken language, education, social links, and bio. Each field also has a dropdown to select the visibility of that field - i.e., whether it can be viewed by other learners.
|
||||
|
||||
React app for user account management.
|
||||
When a user views someone else's profile, they see all those fields that that user set as public.
|
||||
|
||||
Important Note
|
||||
--------------
|
||||
----------
|
||||
|
||||
The production Webpack configuration for this repo uses `Purgecss <https://www.purgecss.com/>`_
|
||||
to remove unused CSS from the production css file. In webpack/webpack.prod.config.js the Purgecss
|
||||
plugin is configured to scan directories to determine what css selectors should remain. Currently
|
||||
the src/ directory is scanned along with all @edx/frontend-component* node modules and paragon.
|
||||
If you add and use a component in this repo that relies on HTML classes or ids for styling you
|
||||
must add it to the Purgecss configuration or it will be unstyled in the production build.
|
||||
Development
|
||||
-----------
|
||||
|
||||
Start Devstack
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
To use this application `devstack <https://github.com/edx/devstack>`__ must be running and you must be logged into it.
|
||||
|
||||
- Start devstack
|
||||
- Log in (http://localhost:18000/login)
|
||||
|
||||
Start the development server
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In this project, install requirements and start the development server by running:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
npm install
|
||||
npm start # The server will run on port 1995
|
||||
|
||||
Once the dev server is up visit http://localhost:1995/u/staff.
|
||||
|
||||
----------
|
||||
|
||||
Configuration and Deployment
|
||||
----------------------------
|
||||
|
||||
This MFE is configured via node environment variables supplied at build time. See the .env file for the list of required environment variables. Example build syntax with a single environment variable:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
NODE_ENV=production ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload' npm run build
|
||||
|
||||
|
||||
For more information see the document: `Micro-frontend applications in Open
|
||||
edX <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/micro-frontends-in-open-edx.html>`__.
|
||||
|
||||
----------
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
The production Webpack configuration for this repo uses `Purgecss <https://www.purgecss.com/>`__ to remove unused CSS from the production css file. In ``webpack.prod.config.js`` the Purgecss plugin is configured to scan directories to determine what css selectors should remain. Currently the src/ directory is scanned along with all ``@edx/frontend-component*`` node modules and ``@edx/paragon``. **If you add and use a component in this repo that relies on HTML classes or ids for styling you must add it to the Purgecss configuration or it will be unstyled in the production build.**
|
||||
|
||||
.. |Build Status| image:: https://api.travis-ci.org/edx/frontend-app-profile.svg?branch=master
|
||||
:target: https://travis-ci.org/edx/frontend-app-profile
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
[
|
||||
'transform-imports',
|
||||
{
|
||||
'@fortawesome/free-brands-svg-icons': {
|
||||
transform: '@fortawesome/free-brands-svg-icons/${member}',
|
||||
skipDefaultConversion: true,
|
||||
},
|
||||
'@fortawesome/free-regular-svg-icons': {
|
||||
transform: '@fortawesome/free-regular-svg-icons/${member}',
|
||||
skipDefaultConversion: true,
|
||||
},
|
||||
'@fortawesome/free-solid-svg-icons': {
|
||||
transform: '@fortawesome/free-solid-svg-icons/${member}',
|
||||
skipDefaultConversion: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
env: {
|
||||
i18n: {
|
||||
plugins: [
|
||||
[
|
||||
'react-intl',
|
||||
{
|
||||
messagesDir: './temp/babel-plugin-react-intl',
|
||||
moduleSourceName: '@edx/frontend-i18n',
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
3
commitlint.config.js
Normal file
3
commitlint.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-angular'],
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
2. Build time customization using NPM aliases
|
||||
---------------------------------------------
|
||||
|
||||
Status
|
||||
------
|
||||
|
||||
Accepted
|
||||
|
||||
Context
|
||||
-------
|
||||
|
||||
Frontend applications created throughout FY2019 contain hardcoded edX brand specific elements
|
||||
such as the site's header, footer, logo, visual style, and navigational links. This enabled
|
||||
teams to move more quickly in efforts to adopt a micro-frontend architecture to enable more
|
||||
rapid UI innovation in the future. There was no easy path for these new applications to be
|
||||
incorporated into the Open edX platform where they need to be branded properly.
|
||||
|
||||
Decision
|
||||
--------
|
||||
|
||||
In order to make frontend applications brand agnostic, we will split branded elements into
|
||||
npm packages such as ``frontend-component-header``. Frontend applications will expect to
|
||||
find components or other exports according to a defined interface. Package interfaces will
|
||||
be defined in the README for each npm package repository.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
// exports React component as default and a 'messages' object for i18n
|
||||
import Header, { messages } from '@edx/frontend-component-header';
|
||||
|
||||
To build a frontend application for a specific brand (edX or other Open edX implementation) we
|
||||
will leverage npm aliases to override these npm packages with branded packages that implement the
|
||||
same interface before build. For example, ``frontend-component-header`` will be overriden with
|
||||
``frontend-component-header-edx``. This is done using the
|
||||
`npm alias syntax introduced in version 6.9.0`_.
|
||||
|
||||
We install aliases using the syntax below:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# npm install <package-name>@<type>:<branded-package>
|
||||
|
||||
# npm package
|
||||
npm install @edx/frontend-component-header@npm:@edx/frontend-component-header-edx@latest
|
||||
|
||||
# git repository
|
||||
npm install @edx/frontend-component-header@git:https://github.com/edx/frontend-component-header-edx.git
|
||||
|
||||
# local folder
|
||||
npm install @edx/frontend-component-header@file:../path/to/local/module/during/build
|
||||
|
||||
After installing overrides using npm aliases, we build the project normally:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
npm run build
|
||||
|
||||
Using this mechanism branded packages are substituted for the default unbranded packages at build
|
||||
and deployment time.
|
||||
|
||||
.. _npm alias syntax introduced in version 6.9.0: https://github.com/npm/rfcs/blob/latest/implemented/0001-package-aliases.md
|
||||
|
||||
Consequences
|
||||
------------
|
||||
|
||||
One drawback of this process is the inability to automatically test substituted packages before
|
||||
deployment. This is a risk we are willing to accept until it becomes an issue.
|
||||
|
||||
edX has built a deployment pipeline that will read configuration for npm packages to override
|
||||
and alias. This code is open source but heavily catered to edX's specific deployment needs.
|
||||
The Open edX community will need to collaborate on a simpler, less opinionated build and
|
||||
deployment process for micro-frontend applications.
|
||||
@@ -1,99 +0,0 @@
|
||||
import React from 'react';
|
||||
import SiteFooter from '@edx/frontend-component-footer';
|
||||
import { sendTrackEvent } from '@edx/frontend-analytics';
|
||||
import { App, validateConfig } from '@edx/frontend-base';
|
||||
import {
|
||||
faFacebookSquare,
|
||||
faTwitterSquare,
|
||||
faYoutubeSquare,
|
||||
faLinkedin,
|
||||
faRedditSquare,
|
||||
} from '@fortawesome/free-brands-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
|
||||
import FooterLogo from './edx-footer.png';
|
||||
|
||||
const config = {
|
||||
APPLE_APP_STORE_URL: process.env.APPLE_APP_STORE_URL,
|
||||
CONTACT_URL: process.env.CONTACT_URL,
|
||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM: process.env.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
|
||||
ENTERPRISE_MARKETING_URL: process.env.ENTERPRISE_MARKETING_URL,
|
||||
ENTERPRISE_MARKETING_UTM_CAMPAIGN: process.env.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
|
||||
ENTERPRISE_MARKETING_UTM_SOURCE: process.env.ENTERPRISE_MARKETING_UTM_SOURCE,
|
||||
FACEBOOK_URL: process.env.FACEBOOK_URL,
|
||||
GOOGLE_PLAY_URL: process.env.GOOGLE_PLAY_URL,
|
||||
LINKED_IN_URL: process.env.LINKED_IN_URL,
|
||||
OPEN_SOURCE_URL: process.env.OPEN_SOURCE_URL,
|
||||
PRIVACY_POLICY_URL: process.env.PRIVACY_POLICY_URL,
|
||||
REDDIT_URL: process.env.REDDIT_URL,
|
||||
SUPPORT_URL: process.env.SUPPORT_URL,
|
||||
TERMS_OF_SERVICE_URL: process.env.TERMS_OF_SERVICE_URL,
|
||||
TWITTER_URL: process.env.TWITTER_URL,
|
||||
YOU_TUBE_URL: process.env.YOU_TUBE_URL,
|
||||
};
|
||||
|
||||
App.requireConfig(['SITE_NAME', 'MARKETING_SITE_BASE_URL'], 'ProfileFooter');
|
||||
validateConfig(config, 'ProfileFooter');
|
||||
|
||||
export default function ProfileFooter() {
|
||||
const socialLinks = [
|
||||
{
|
||||
title: 'Facebook',
|
||||
url: config.FACEBOOK_URL,
|
||||
icon: <FontAwesomeIcon icon={faFacebookSquare} className="social-icon" size="2x" />,
|
||||
screenReaderText: 'Like edX on Facebook',
|
||||
},
|
||||
{
|
||||
title: 'Twitter',
|
||||
url: config.TWITTER_URL,
|
||||
icon: <FontAwesomeIcon icon={faTwitterSquare} className="social-icon" size="2x" />,
|
||||
screenReaderText: 'Follow edX on Twitter',
|
||||
},
|
||||
{
|
||||
title: 'Youtube',
|
||||
url: config.YOU_TUBE_URL,
|
||||
icon: <FontAwesomeIcon icon={faYoutubeSquare} className="social-icon" size="2x" />,
|
||||
screenReaderText: 'Subscribe to the edX YouTube channel',
|
||||
},
|
||||
{
|
||||
title: 'LinkedIn',
|
||||
url: config.LINKED_IN_URL,
|
||||
icon: <FontAwesomeIcon icon={faLinkedin} className="social-icon" size="2x" />,
|
||||
screenReaderText: 'Follow edX on LinkedIn',
|
||||
},
|
||||
{
|
||||
title: 'Reddit',
|
||||
url: config.REDDIT_URL,
|
||||
icon: <FontAwesomeIcon icon={faRedditSquare} className="social-icon" size="2x" />,
|
||||
screenReaderText: 'Subscribe to the edX subreddit',
|
||||
},
|
||||
];
|
||||
|
||||
const enterpriseMarketingLinkData = {
|
||||
url: config.ENTERPRISE_MARKETING_URL,
|
||||
queryParams: {
|
||||
utm_campaign: config.ENTERPRISE_MARKETING_UTM_CAMPAIGN,
|
||||
utm_source: config.ENTERPRISE_MARKETING_UTM_SOURCE,
|
||||
utm_medium: config.ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<SiteFooter
|
||||
appleAppStoreUrl={config.APPLE_APP_STORE_URL}
|
||||
contactUrl={config.CONTACT_URL}
|
||||
enterpriseMarketingLink={enterpriseMarketingLinkData}
|
||||
googlePlayUrl={config.GOOGLE_PLAY_URL}
|
||||
handleAllTrackEvents={sendTrackEvent}
|
||||
marketingSiteBaseUrl={App.config.MARKETING_SITE_BASE_URL}
|
||||
openSourceUrl={config.OPEN_SOURCE_URL}
|
||||
privacyPolicyUrl={config.PRIVACY_POLICY_URL}
|
||||
siteLogo={FooterLogo}
|
||||
siteName={App.config.SITE_NAME}
|
||||
socialLinks={socialLinks}
|
||||
supportUrl={config.SUPPORT_URL}
|
||||
termsOfServiceUrl={config.TERMS_OF_SERVICE_URL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
7
jest.config.js
Normal file
7
jest.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const { createConfig } = require('@edx/frontend-build');
|
||||
|
||||
module.exports = createConfig('jest', {
|
||||
setupFiles: [
|
||||
'<rootDir>/src/setupTest.js',
|
||||
],
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
# This file describes this Open edX repo, as described in OEP-2:
|
||||
# http://open-edx-proposals.readthedocs.io/en/latest/oeps/oep-0002.html#specification
|
||||
|
||||
nick: acct
|
||||
nick: prof
|
||||
oeps: {}
|
||||
owner: edx/arch-team
|
||||
|
||||
18773
package-lock.json
generated
18773
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
125
package.json
125
package.json
@@ -1,25 +1,23 @@
|
||||
{
|
||||
"name": "@edx/frontend-app-profile",
|
||||
"version": "1.0.0-semantically-released",
|
||||
"description": "User profile micro-frontend for edX",
|
||||
"description": "User profile micro-frontend for Open edX",
|
||||
"author": "edX",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "npm-dist/index.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/edx/frontend-app-profile.git"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"ie 11"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production BABEL_ENV=production webpack --config=webpack/webpack.prod.config.js",
|
||||
"build": "fedx-scripts webpack",
|
||||
"npm-build": "make npm-build",
|
||||
"lint": "eslint --ext .js --ext .jsx .",
|
||||
"i18n_extract": "BABEL_ENV=i18n babel src --quiet > /dev/null",
|
||||
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
|
||||
"is-es5": "es-check es5 ./dist/*.js",
|
||||
"semantic-release": "semantic-release",
|
||||
"snapshot": "jest --updateSnapshot",
|
||||
"start": "NODE_ENV=development BABEL_ENV=development webpack-dev-server --config=webpack/webpack.dev.config.js --progress",
|
||||
"test": "jest --coverage --passWithNoTests"
|
||||
"lint": "fedx-scripts eslint",
|
||||
"snapshot": "fedx-scripts jest --updateSnapshot",
|
||||
"start": "fedx-scripts webpack-dev-server --progress",
|
||||
"test": "fedx-scripts jest --coverage --passWithNoTests"
|
||||
},
|
||||
"files": [
|
||||
"/npm-dist"
|
||||
@@ -30,22 +28,23 @@
|
||||
"commit-msg": "commitlint -e $GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/edx/frontend-app-profile.git"
|
||||
},
|
||||
"author": "edX",
|
||||
"license": "AGPL-3.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/edx/frontend-app-profile/issues"
|
||||
},
|
||||
"homepage": "https://github.com/edx/frontend-app-profile#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"ie 11"
|
||||
],
|
||||
"dependencies": {
|
||||
"@edx/frontend-analytics": "3.0.0",
|
||||
"@edx/frontend-auth": "7.0.1",
|
||||
"@edx/frontend-base": "2.1.4",
|
||||
"@edx/frontend-component-footer": "6.0.2",
|
||||
"@edx/frontend-component-header": "1.1.2",
|
||||
"@edx/frontend-base": "4.1.0",
|
||||
"@edx/frontend-component-footer": "9.0.0",
|
||||
"@edx/frontend-component-header": "1.1.4",
|
||||
"@edx/frontend-i18n": "3.0.2",
|
||||
"@edx/frontend-logging": "3.0.1",
|
||||
"@edx/paragon": "7.1.3",
|
||||
@@ -58,7 +57,7 @@
|
||||
"classnames": "2.2.6",
|
||||
"email-prop-type": "1.1.7",
|
||||
"font-awesome": "4.7.0",
|
||||
"form-urlencoded": "3.0.0",
|
||||
"form-urlencoded": "3.0.2",
|
||||
"history": "4.7.2",
|
||||
"lodash.camelcase": "4.3.0",
|
||||
"lodash.get": "4.4.2",
|
||||
@@ -82,82 +81,20 @@
|
||||
"universal-cookie": "3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.6.0",
|
||||
"@babel/core": "7.6.0",
|
||||
"@babel/plugin-proposal-class-properties": "7.5.5",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.5.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "7.2.0",
|
||||
"@babel/preset-env": "7.6.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@svgr/webpack": "4.3.2",
|
||||
"autoprefixer": "9.6.1",
|
||||
"axios-mock-adapter": "1.16.0",
|
||||
"babel-eslint": "10.0.3",
|
||||
"babel-jest": "24.9.0",
|
||||
"babel-loader": "8.0.6",
|
||||
"babel-plugin-react-intl": "4.1.16",
|
||||
"babel-plugin-transform-imports": "2.0.0",
|
||||
"clean-webpack-plugin": "0.1.19",
|
||||
"@commitlint/cli": "8.2.0",
|
||||
"@commitlint/config-angular": "8.2.0",
|
||||
"@commitlint/prompt": "8.2.0",
|
||||
"@commitlint/prompt-cli": "8.2.0",
|
||||
"@edx/frontend-build": "1.2.2",
|
||||
"codecov": "3.1.0",
|
||||
"copy-webpack-plugin": "4.6.0",
|
||||
"css-loader": "3.2.0",
|
||||
"cssnano": "4.1.10",
|
||||
"enzyme": "3.10.0",
|
||||
"enzyme-adapter-react-16": "1.14.0",
|
||||
"es-check": "5.0.0",
|
||||
"eslint-config-edx": "4.0.4",
|
||||
"fetch-mock": "6.5.2",
|
||||
"file-loader": "1.1.11",
|
||||
"glob": "7.1.3",
|
||||
"html-webpack-harddisk-plugin": "0.2.0",
|
||||
"html-webpack-new-relic-plugin": "1.1.0",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "0.14.3",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"image-webpack-loader": "4.6.0",
|
||||
"jest": "24.9.0",
|
||||
"mini-css-extract-plugin": "0.4.5",
|
||||
"new-relic-source-map-webpack-plugin": "1.2.0",
|
||||
"node-sass": "4.12.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"postcss-rtl": "1.3.3",
|
||||
"purgecss-webpack-plugin": "1.5.0",
|
||||
"react-dev-utils": "9.0.3",
|
||||
"husky": "3.0.9",
|
||||
"purgecss-webpack-plugin": "1.6.0",
|
||||
"react-test-renderer": "16.9.0",
|
||||
"reactifex": "1.1.1",
|
||||
"redux-mock-store": "1.5.3",
|
||||
"resolve-url-loader": "3.1.0",
|
||||
"sass-loader": "6.0.7",
|
||||
"semantic-release": "15.13.24",
|
||||
"source-map-loader": "0.2.4",
|
||||
"style-loader": "0.20.3",
|
||||
"url-loader": "1.1.2",
|
||||
"webpack": "4.29.2",
|
||||
"webpack-bundle-analyzer": "3.3.2",
|
||||
"webpack-cli": "3.2.3",
|
||||
"webpack-dev-server": "3.1.14",
|
||||
"webpack-merge": "4.2.1"
|
||||
},
|
||||
"jest": {
|
||||
"rootDir": "./src",
|
||||
"testURL": "http://localhost/",
|
||||
"setupFiles": [
|
||||
"./setupTest.js"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.svg": "<rootDir>/__mocks__/svgrMock.js",
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
|
||||
"\\.(css|scss)$": "identity-obj-proxy"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.{js,jsx}"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"setupTest.js",
|
||||
"index.jsx"
|
||||
],
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!((@edx/paragon)|(@edx/frontend-base))/).*/"
|
||||
]
|
||||
"redux-mock-store": "1.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700" rel="stylesheet">
|
||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
],
|
||||
"patch": {
|
||||
"automerge": true
|
||||
},
|
||||
"rebaseStalePrs": true
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = 'test-file-stub';
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = { ReactComponent: 'IconMock' };
|
||||
BIN
src/assets/favicon.ico
Normal file
BIN
src/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
33
src/data/configureStore.js
Normal file
33
src/data/configureStore.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { App } from '@edx/frontend-base';
|
||||
import { applyMiddleware, createStore, compose } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
|
||||
import createRootReducer from './reducers';
|
||||
import rootSaga from './sagas';
|
||||
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
|
||||
function composeMiddleware() {
|
||||
if (App.config.ENVIRONMENT === 'development') {
|
||||
const loggerMiddleware = createLogger({
|
||||
collapsed: true,
|
||||
});
|
||||
return composeWithDevTools(applyMiddleware(thunkMiddleware, sagaMiddleware, loggerMiddleware));
|
||||
}
|
||||
|
||||
return compose(applyMiddleware(thunkMiddleware, sagaMiddleware));
|
||||
}
|
||||
|
||||
export default function configureStore(initialState = {}) {
|
||||
const store = createStore(
|
||||
createRootReducer(),
|
||||
initialState,
|
||||
composeMiddleware(),
|
||||
);
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
return store;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import { userAccount } from '@edx/frontend-auth';
|
||||
|
||||
import { reducer as profilePage } from './profile';
|
||||
import { reducer as profilePage } from '../profile';
|
||||
|
||||
const createRootReducer = () =>
|
||||
combineReducers({
|
||||
@@ -1,6 +1,6 @@
|
||||
import { all } from 'redux-saga/effects';
|
||||
|
||||
import { saga as profileSaga } from './profile';
|
||||
import { saga as profileSaga } from '../profile';
|
||||
|
||||
export default function* rootSaga() {
|
||||
yield all([
|
||||
@@ -1,18 +1,19 @@
|
||||
import 'babel-polyfill';
|
||||
|
||||
import { App, AppProvider, APP_ERROR, APP_READY, ErrorPage } from '@edx/frontend-base';
|
||||
import { NewRelicLoggingService } from '@edx/frontend-logging';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
|
||||
import Header, { messages as headerMessages } from '@edx/frontend-component-header';
|
||||
import Footer from '../footer/Footer';
|
||||
import Footer, { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||
|
||||
import appMessages from './i18n';
|
||||
import './index.scss';
|
||||
import { ProfilePage, NotFoundPage } from './profile';
|
||||
import configureStore from './store';
|
||||
import configureStore from './data/configureStore';
|
||||
|
||||
import './index.scss';
|
||||
import './assets/favicon.ico';
|
||||
|
||||
App.subscribe(APP_READY, () => {
|
||||
ReactDOM.render(
|
||||
@@ -35,4 +36,10 @@ App.subscribe(APP_ERROR, (error) => {
|
||||
ReactDOM.render(<ErrorPage message={error.message} />, document.getElementById('root'));
|
||||
});
|
||||
|
||||
App.initialize({ messages: [appMessages, headerMessages], loggingService: NewRelicLoggingService });
|
||||
App.initialize({
|
||||
messages: [
|
||||
appMessages,
|
||||
headerMessages,
|
||||
footerMessages,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
|
||||
@import './profile/index.scss';
|
||||
|
||||
@import "~@edx/frontend-component-header/src/index";
|
||||
@import "~@edx/frontend-component-footer/src/lib/scss/site-footer";
|
||||
@import "~@edx/frontend-component-header/dist/index";
|
||||
@import "~@edx/frontend-component-footer/dist/footer";
|
||||
|
||||
@@ -38,7 +38,7 @@ import { profilePageSelector } from './data/selectors';
|
||||
// i18n
|
||||
import messages from './ProfilePage.messages';
|
||||
|
||||
App.requireConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
|
||||
App.ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
|
||||
|
||||
class ProfilePage extends React.Component {
|
||||
constructor(props, context) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { App } from '@edx/frontend-base';
|
||||
import { logApiClientError } from '@edx/frontend-logging';
|
||||
import { camelCaseObject, convertKeyNames, snakeCaseObject } from '../utils';
|
||||
|
||||
const { LMS_BASE_URL } = App.requireConfig(['LMS_BASE_URL'], 'Profile API service');
|
||||
App.ensureConfig(['LMS_BASE_URL'], 'Profile API service');
|
||||
|
||||
function processAccountData(data) {
|
||||
return camelCaseObject(data);
|
||||
@@ -20,7 +20,7 @@ function processAndThrowError(error, errorDataProcessor) {
|
||||
|
||||
// GET ACCOUNT
|
||||
export async function getAccount(username) {
|
||||
const { data } = await App.apiClient.get(`${LMS_BASE_URL}/api/user/v1/accounts/${username}`);
|
||||
const { data } = await App.apiClient.get(`${App.config.LMS_BASE_URL}/api/user/v1/accounts/${username}`);
|
||||
|
||||
// Process response data
|
||||
return processAccountData(data);
|
||||
@@ -31,7 +31,7 @@ export async function patchProfile(username, params) {
|
||||
const processedParams = snakeCaseObject(params);
|
||||
|
||||
const { data } = await App.apiClient
|
||||
.patch(`${LMS_BASE_URL}/api/user/v1/accounts/${username}`, processedParams, {
|
||||
.patch(`${App.config.LMS_BASE_URL}/api/user/v1/accounts/${username}`, processedParams, {
|
||||
headers: {
|
||||
'Content-Type': 'application/merge-patch+json',
|
||||
},
|
||||
@@ -49,7 +49,7 @@ export async function patchProfile(username, params) {
|
||||
export async function postProfilePhoto(username, formData) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { data } = await App.apiClient.post(
|
||||
`${LMS_BASE_URL}/api/user/v1/accounts/${username}/image`,
|
||||
`${App.config.LMS_BASE_URL}/api/user/v1/accounts/${username}/image`,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
@@ -73,7 +73,7 @@ export async function postProfilePhoto(username, formData) {
|
||||
|
||||
export async function deleteProfilePhoto(username) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { data } = await App.apiClient.delete(`${LMS_BASE_URL}/api/user/v1/accounts/${username}/image`);
|
||||
const { data } = await App.apiClient.delete(`${App.config.LMS_BASE_URL}/api/user/v1/accounts/${username}/image`);
|
||||
|
||||
// TODO: Someday in the future the POST photo endpoint
|
||||
// will return the new values. At that time we should
|
||||
@@ -86,7 +86,7 @@ export async function deleteProfilePhoto(username) {
|
||||
|
||||
// GET PREFERENCES
|
||||
export async function getPreferences(username) {
|
||||
const { data } = await App.apiClient.get(`${LMS_BASE_URL}/api/user/v1/preferences/${username}`);
|
||||
const { data } = await App.apiClient.get(`${App.config.LMS_BASE_URL}/api/user/v1/preferences/${username}`);
|
||||
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
@@ -106,7 +106,7 @@ export async function patchPreferences(username, params) {
|
||||
visibility_time_zone: 'visibility.time_zone',
|
||||
});
|
||||
|
||||
await App.apiClient.patch(`${LMS_BASE_URL}/api/user/v1/preferences/${username}`, processedParams, {
|
||||
await App.apiClient.patch(`${App.config.LMS_BASE_URL}/api/user/v1/preferences/${username}`, processedParams, {
|
||||
headers: { 'Content-Type': 'application/merge-patch+json' },
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ function transformCertificateData(data) {
|
||||
cert.download_url.search(/http[s]?:\/\//) !== 0;
|
||||
|
||||
const downloadUrl = urlIsPath ?
|
||||
`${LMS_BASE_URL}${cert.download_url}` :
|
||||
`${App.config.LMS_BASE_URL}${cert.download_url}` :
|
||||
cert.download_url;
|
||||
|
||||
transformedData.push({
|
||||
@@ -137,7 +137,7 @@ function transformCertificateData(data) {
|
||||
}
|
||||
|
||||
export async function getCourseCertificates(username) {
|
||||
const url = `${LMS_BASE_URL}/api/certificates/v0/certificates/${username}/`;
|
||||
const url = `${App.config.LMS_BASE_URL}/api/certificates/v0/certificates/${username}/`;
|
||||
try {
|
||||
const { data } = await App.apiClient.get(url);
|
||||
return transformCertificateData(data);
|
||||
|
||||
@@ -1,25 +1,4 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import Enzyme from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
import 'babel-polyfill';
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
|
||||
// These configuration values are usually set in webpack's EnvironmentPlugin however
|
||||
// Jest does not use webpack so we need to set these so for testing
|
||||
process.env.ACCESS_TOKEN_COOKIE_NAME = 'edx-jwt-cookie-header-payload';
|
||||
process.env.BASE_URL = 'localhost:1995';
|
||||
process.env.CREDENTIALS_BASE_URL = 'http://localhost:18150';
|
||||
process.env.CSRF_COOKIE_NAME = 'csrftoken';
|
||||
process.env.CSRF_TOKEN_API_PATH = '/csrf/api/v1/token';
|
||||
process.env.ECOMMERCE_BASE_URL = 'http://localhost:18130';
|
||||
process.env.LANGUAGE_PREFERENCE_COOKIE_NAME = 'openedx-language-preference';
|
||||
process.env.LMS_BASE_URL = 'http://localhost:18000';
|
||||
process.env.LOGIN_URL = 'http://localhost:18000/login';
|
||||
process.env.LOGOUT_URL = 'http://localhost:18000/login';
|
||||
process.env.MARKETING_SITE_BASE_URL = 'http://localhost:18000';
|
||||
process.env.ORDER_HISTORY_URL = 'localhost:1996/orders';
|
||||
process.env.REFRESH_ACCESS_TOKEN_ENDPOINT = 'http://localhost:18000/login_refresh';
|
||||
process.env.SEGMENT_KEY = null;
|
||||
process.env.SITE_NAME = 'edX';
|
||||
process.env.USER_INFO_COOKIE_NAME = 'edx-user-info';
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { applyMiddleware, createStore } from 'redux';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';
|
||||
import { createLogger } from 'redux-logger';
|
||||
|
||||
import createRootReducer from '../reducers';
|
||||
import rootSaga from '../sagas';
|
||||
|
||||
export default function configureStore(initialState = {}) {
|
||||
const loggerMiddleware = createLogger({
|
||||
collapsed: true,
|
||||
});
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
|
||||
const store = createStore(
|
||||
createRootReducer(),
|
||||
initialState,
|
||||
composeWithDevTools(applyMiddleware(thunkMiddleware, sagaMiddleware, loggerMiddleware)), // eslint-disable-line
|
||||
);
|
||||
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
return store;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { applyMiddleware, createStore, compose } from 'redux';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
|
||||
import createRootReducer from '../reducers';
|
||||
import rootSaga from '../sagas';
|
||||
|
||||
export default function configureStore(initialState = {}) {
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
|
||||
const store = createStore(
|
||||
createRootReducer(),
|
||||
initialState,
|
||||
compose(applyMiddleware(thunkMiddleware, sagaMiddleware)),
|
||||
);
|
||||
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
return store;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import configureStoreProd from './configureStore.prod';
|
||||
import configureStoreDev from './configureStore.dev';
|
||||
|
||||
export default function configureStore(state, env) {
|
||||
if (env === 'production') {
|
||||
return configureStoreProd(state);
|
||||
}
|
||||
return configureStoreDev(state);
|
||||
}
|
||||
21
webpack.prod.config.js
Normal file
21
webpack.prod.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const glob = require('glob');
|
||||
const PurgecssPlugin = require('purgecss-webpack-plugin');
|
||||
const { createConfig } = require('@edx/frontend-build');
|
||||
|
||||
module.exports = createConfig('webpack-prod', {
|
||||
plugins: [
|
||||
// Scan files for class names and ids and remove unused css
|
||||
new PurgecssPlugin({
|
||||
paths: [].concat(
|
||||
// Scan files in this app
|
||||
glob.sync('src/**/*', { nodir: true }),
|
||||
// Scan files in any edx frontend-component
|
||||
glob.sync('node_modules/@edx/frontend-component*/**/*', { nodir: true }),
|
||||
// Scan files in paragon
|
||||
glob.sync('node_modules/@edx/paragon/**/*', { nodir: true }),
|
||||
),
|
||||
// Protect react-css-transition class names
|
||||
whitelistPatterns: [/-enter/, /-appear/, /-exit/],
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
// This is the common Webpack config. The dev and prod Webpack configs both
|
||||
// inherit config defined here.
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
app: path.resolve(__dirname, '../src/index.jsx'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
publicPath: '/',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx'],
|
||||
},
|
||||
};
|
||||
@@ -1,173 +0,0 @@
|
||||
// This is the dev Webpack config. All settings here should prefer a fast build
|
||||
// time at the expense of creating larger, unoptimized bundles.
|
||||
|
||||
const Merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const PostCssRtlPlugin = require('postcss-rtl');
|
||||
|
||||
const commonConfig = require('./webpack.common.config.js');
|
||||
|
||||
module.exports = Merge.smart(commonConfig, {
|
||||
mode: 'development',
|
||||
devtool: 'eval-source-map',
|
||||
entry: {
|
||||
// enable react's custom hot dev client so we get errors reported in the browser
|
||||
hot: require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
app: path.resolve(__dirname, '../src/index.jsx'),
|
||||
},
|
||||
module: {
|
||||
// Specify file-by-file rules to Webpack. Some file-types need a particular kind of loader.
|
||||
rules: [
|
||||
// The babel-loader transforms newer ES2015+ syntax to older ES5 for older browsers.
|
||||
// Babel is configured with the .babelrc file at the root of the project.
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
include: [
|
||||
path.resolve(__dirname, '../src'),
|
||||
path.resolve(__dirname, '../footer'),
|
||||
path.resolve(__dirname, '../header'),
|
||||
],
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
// Caches result of loader to the filesystem. Future builds will attempt to read from the
|
||||
// cache to avoid needing to run the expensive recompilation process on each run.
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
// We are not extracting CSS from the javascript bundles in development because extracting
|
||||
// prevents hot-reloading from working, it increases build time, and we don't care about
|
||||
// flash-of-unstyled-content issues in development.
|
||||
{
|
||||
test: /(.scss|.css)$/,
|
||||
use: [
|
||||
'style-loader', // creates style nodes from JS strings
|
||||
{
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
plugins: () => [PostCssRtlPlugin()],
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'resolve-url-loader',
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader', // compiles Sass to CSS
|
||||
options: {
|
||||
sourceMap: true,
|
||||
includePaths: [
|
||||
path.join(__dirname, '../node_modules'),
|
||||
path.join(__dirname, '../src'),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /.svg$/,
|
||||
issuer: {
|
||||
test: /\.jsx?$/,
|
||||
},
|
||||
loader: '@svgr/webpack',
|
||||
},
|
||||
// Webpack, by default, uses the url-loader for images and fonts that are required/included by
|
||||
// files it processes, which just base64 encodes them and inlines them in the javascript
|
||||
// bundles. This makes the javascript bundles ginormous and defeats caching so we will use the
|
||||
// file-loader instead to copy the files directly to the output directory.
|
||||
{
|
||||
test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
use: [
|
||||
'file-loader',
|
||||
{
|
||||
loader: 'image-webpack-loader',
|
||||
options: {
|
||||
optimizationlevel: 7,
|
||||
mozjpeg: {
|
||||
progressive: true,
|
||||
},
|
||||
gifsicle: {
|
||||
interlaced: false,
|
||||
},
|
||||
pngquant: {
|
||||
quality: '65-90',
|
||||
speed: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Specify additional processing or side-effects done on the Webpack output bundles as a whole.
|
||||
plugins: [
|
||||
// Generates an HTML file in the output directory.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true, // Appends script tags linking to the webpack bundles at the end of the body
|
||||
template: path.resolve(__dirname, '../public/index.html'),
|
||||
}),
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: 'development',
|
||||
|
||||
// App configuration
|
||||
ACCESS_TOKEN_COOKIE_NAME: 'edx-jwt-cookie-header-payload',
|
||||
BASE_URL: 'localhost:1995',
|
||||
CREDENTIALS_BASE_URL: 'http://localhost:18150',
|
||||
CSRF_COOKIE_NAME: 'csrftoken',
|
||||
CSRF_TOKEN_API_PATH: '/csrf/api/v1/token',
|
||||
ECOMMERCE_BASE_URL: 'http://localhost:18130',
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference',
|
||||
LMS_BASE_URL: 'http://localhost:18000',
|
||||
LOGIN_URL: 'http://localhost:18000/login',
|
||||
LOGOUT_URL: 'http://localhost:18000/login',
|
||||
MARKETING_SITE_BASE_URL: 'http://localhost:18000',
|
||||
ORDER_HISTORY_URL: 'localhost:1996/orders',
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://localhost:18000/login_refresh',
|
||||
SEGMENT_KEY: null,
|
||||
SITE_NAME: 'edX',
|
||||
USER_INFO_COOKIE_NAME: 'edx-user-info',
|
||||
|
||||
// ProfileFooter configuration
|
||||
APPLE_APP_STORE_URL: 'https://www.apple.com/ios/app-store/',
|
||||
CONTACT_URL: 'http://localhost:18000/contact',
|
||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM: 'Footer',
|
||||
ENTERPRISE_MARKETING_URL: 'http://example.com',
|
||||
ENTERPRISE_MARKETING_UTM_CAMPAIGN: 'my_campaign',
|
||||
ENTERPRISE_MARKETING_UTM_SOURCE: 'edX profile',
|
||||
FACEBOOK_URL: 'https://www.facebook.com',
|
||||
GOOGLE_PLAY_URL: 'https://play.google.com/store',
|
||||
LINKED_IN_URL: 'https://www.linkedin.com',
|
||||
OPEN_SOURCE_URL: 'http://localhost:18000/openedx',
|
||||
PRIVACY_POLICY_URL: 'http://localhost:18000/privacy-policy',
|
||||
REDDIT_URL: 'https://www.reddit.com',
|
||||
SUPPORT_URL: 'http://localhost:18000/support',
|
||||
TERMS_OF_SERVICE_URL: 'http://localhost:18000/terms-of-service',
|
||||
TWITTER_URL: 'https://twitter.com',
|
||||
YOU_TUBE_URL: 'https://www.youtube.com',
|
||||
}),
|
||||
// when the --hot option is not passed in as part of the command
|
||||
// the HotModuleReplacementPlugin has to be specified in the Webpack configuration
|
||||
// https://webpack.js.org/configuration/dev-server/#devserver-hot
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
],
|
||||
// This configures webpack-dev-server which serves bundles from memory and provides live
|
||||
// reloading.
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
port: 1995,
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
inline: true,
|
||||
publicPath: '/',
|
||||
},
|
||||
});
|
||||
@@ -1,222 +0,0 @@
|
||||
// This is the prod Webpack config. All settings here should prefer smaller,
|
||||
// optimized bundles at the expense of a longer build time.
|
||||
const Merge = require('webpack-merge');
|
||||
const commonConfig = require('./webpack.common.config.js');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const webpack = require('webpack');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
const HtmlWebpackNewRelicPlugin = require('html-webpack-new-relic-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const PurgecssPlugin = require('purgecss-webpack-plugin');
|
||||
const NewRelicSourceMapPlugin = require('new-relic-source-map-webpack-plugin');
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // eslint-disable-line prefer-destructuring
|
||||
const PostCssRtlPlugin = require('postcss-rtl');
|
||||
const PostCssAutoprefixerPlugin = require('autoprefixer');
|
||||
const CssNano = require('cssnano');
|
||||
|
||||
module.exports = Merge.smart(commonConfig, {
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
filename: '[name].[chunkhash].js',
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
},
|
||||
module: {
|
||||
// Specify file-by-file rules to Webpack. Some file-types need a particular kind of loader.
|
||||
rules: [
|
||||
// The babel-loader transforms newer ES2015+ syntax to older ES5 for older browsers.
|
||||
// Babel is configured with the .babelrc file at the root of the project.
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
include: [
|
||||
path.resolve(__dirname, '../src'),
|
||||
path.resolve(__dirname, '../footer'),
|
||||
path.resolve(__dirname, '../header'),
|
||||
],
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ['source-map-loader'],
|
||||
enforce: 'pre',
|
||||
},
|
||||
// Webpack, by default, includes all CSS in the javascript bundles. Unfortunately, that means:
|
||||
// a) The CSS won't be cached by browsers separately (a javascript change will force CSS
|
||||
// re-download). b) Since CSS is applied asyncronously, it causes an ugly
|
||||
// flash-of-unstyled-content.
|
||||
//
|
||||
// To avoid these problems, we extract the CSS from the bundles into separate CSS files that
|
||||
// can be included as <link> tags in the HTML <head> manually.
|
||||
//
|
||||
// We will not do this in development because it prevents hot-reloading from working and it
|
||||
// increases build time.
|
||||
{
|
||||
test: /(.scss|.css)$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: 'css-loader', // translates CSS into CommonJS
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
plugins: () => [
|
||||
PostCssRtlPlugin(),
|
||||
PostCssAutoprefixerPlugin({ grid: true }),
|
||||
CssNano(),
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'resolve-url-loader',
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader', // compiles Sass to CSS
|
||||
options: {
|
||||
sourceMap: true,
|
||||
includePaths: [
|
||||
path.join(__dirname, '../node_modules'),
|
||||
path.join(__dirname, '../src'),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /.svg$/,
|
||||
issuer: {
|
||||
test: /\.jsx?$/,
|
||||
},
|
||||
loader: '@svgr/webpack',
|
||||
},
|
||||
// Webpack, by default, uses the url-loader for images and fonts that are required/included by
|
||||
// files it processes, which just base64 encodes them and inlines them in the javascript
|
||||
// bundles. This makes the javascript bundles ginormous and defeats caching so we will use the
|
||||
// file-loader instead to copy the files directly to the output directory.
|
||||
{
|
||||
test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif|ico)(\?v=\d+\.\d+\.\d+)?$/,
|
||||
use: [
|
||||
'file-loader',
|
||||
{
|
||||
loader: 'image-webpack-loader',
|
||||
options: {
|
||||
optimizationlevel: 7,
|
||||
mozjpeg: {
|
||||
progressive: true,
|
||||
},
|
||||
gifsicle: {
|
||||
interlaced: false,
|
||||
},
|
||||
pngquant: {
|
||||
quality: '65-90',
|
||||
speed: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// New in Webpack 4. Replaces CommonChunksPlugin. Extract common modules among all chunks to one
|
||||
// common chunk and extract the Webpack runtime to a single runtime chunk.
|
||||
optimization: {
|
||||
runtimeChunk: 'single',
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
// Specify additional processing or side-effects done on the Webpack output bundles as a whole.
|
||||
plugins: [
|
||||
// Cleans the dist directory before each build
|
||||
new CleanWebpackPlugin(['dist'], {
|
||||
root: path.join(__dirname, '../'),
|
||||
}),
|
||||
// Writes the extracted CSS from each entry to a file in the output directory.
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[chunkhash].css',
|
||||
}),
|
||||
// Scan files for class names and ids and remove unused css
|
||||
new PurgecssPlugin({
|
||||
paths: [].concat(
|
||||
// Scan files in this app
|
||||
glob.sync(`${path.resolve(__dirname, '../src')}/**/*`, { nodir: true }),
|
||||
// Scan files in any edx frontend-component
|
||||
glob.sync(`${path.resolve(__dirname, '../node_modules/@edx/frontend-component')}*/**/*`, { nodir: true }),
|
||||
// Scan files in paragon
|
||||
glob.sync(`${path.resolve(__dirname, '../node_modules/@edx/paragon')}/**/*`, { nodir: true }),
|
||||
),
|
||||
// Protect react-css-transition class names
|
||||
whitelistPatterns: [/-enter/, /-appear/, /-exit/],
|
||||
}),
|
||||
// Generates an HTML file in the output directory.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true, // Appends script tags linking to the webpack bundles at the end of the body
|
||||
template: path.resolve(__dirname, '../public/index.html'),
|
||||
}),
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: 'production',
|
||||
|
||||
// App configuration
|
||||
ACCESS_TOKEN_COOKIE_NAME: null,
|
||||
BASE_URL: null,
|
||||
CREDENTIALS_BASE_URL: null,
|
||||
CSRF_COOKIE_NAME: 'csrftoken',
|
||||
CSRF_TOKEN_API_PATH: null,
|
||||
ECOMMERCE_BASE_URL: null,
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME: null,
|
||||
LMS_BASE_URL: null,
|
||||
LOGIN_URL: null,
|
||||
LOGOUT_URL: null,
|
||||
MARKETING_SITE_BASE_URL: null,
|
||||
ORDER_HISTORY_URL: null,
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT: null,
|
||||
SEGMENT_KEY: null,
|
||||
SITE_NAME: null,
|
||||
USER_INFO_COOKIE_NAME: null,
|
||||
|
||||
// ProfileFooter configuration
|
||||
APPLE_APP_STORE_URL: null,
|
||||
CONTACT_URL: null,
|
||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM: null,
|
||||
ENTERPRISE_MARKETING_URL: null,
|
||||
ENTERPRISE_MARKETING_UTM_CAMPAIGN: null,
|
||||
ENTERPRISE_MARKETING_UTM_SOURCE: null,
|
||||
FACEBOOK_URL: null,
|
||||
GOOGLE_PLAY_URL: null,
|
||||
LINKED_IN_URL: null,
|
||||
OPEN_SOURCE_URL: null,
|
||||
PRIVACY_POLICY_URL: null,
|
||||
REDDIT_URL: null,
|
||||
SUPPORT_URL: null,
|
||||
TERMS_OF_SERVICE_URL: null,
|
||||
TWITTER_URL: null,
|
||||
YOU_TUBE_URL: null,
|
||||
}),
|
||||
new HtmlWebpackNewRelicPlugin({
|
||||
// This plugin fixes an issue where the newrelic script will break if
|
||||
// not added directly to the HTML.
|
||||
// We use non empty strings as defaults here to prevent errors for empty configs
|
||||
license: process.env.NEW_RELIC_LICENSE_KEY || 'fake_app',
|
||||
applicationID: process.env.NEW_RELIC_APP_ID || 'fake_license',
|
||||
}),
|
||||
new NewRelicSourceMapPlugin({
|
||||
applicationId: process.env.NEW_RELIC_APP_ID,
|
||||
nrAdminKey: process.env.NEW_RELIC_ADMIN_KEY,
|
||||
staticAssetUrl: process.env.BASE_URL,
|
||||
noop: typeof process.env.NEW_RELIC_ADMIN_KEY === 'undefined', // upload source maps in prod builds only
|
||||
}),
|
||||
new BundleAnalyzerPlugin({
|
||||
analyzerMode: 'static',
|
||||
openAnalyzer: false,
|
||||
}),
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user