Skip to content

[DRAFT] feat! replace webpack by rsbuild for faster build #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: release
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions tutormfe/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
}

CORE_MFE_APPS: dict[str, MFE_ATTRS_TYPE] = {
# TODO restore vanilla repositories
"authn": {
"repository": "https://github.com/openedx/frontend-app-authn.git",
"port": 1999,
Expand All @@ -45,27 +46,35 @@
"port": 1997,
},
"communications": {
"repository": "https://github.com/openedx/frontend-app-communications.git",
"repository": "https://github.com/regisb/frontend-app-communications.git",
"version": "regisb/rsbuild",
# "repository": "https://github.com/openedx/frontend-app-communications.git",
"port": 1984,
},
"discussions": {
"repository": "https://github.com/openedx/frontend-app-discussions.git",
"port": 2002,
},
"gradebook": {
"repository": "https://github.com/openedx/frontend-app-gradebook.git",
"repository": "https://github.com/regisb/frontend-app-gradebook.git",
"version": "regisb/rsbuild",
# "repository": "https://github.com/openedx/frontend-app-gradebook.git",
"port": 1994,
},
"learner-dashboard": {
"repository": "https://github.com/openedx/frontend-app-learner-dashboard.git",
"repository": "https://github.com/regisb/frontend-app-learner-dashboard.git",
"version": "regisb/rsbuild",
# "repository": "https://github.com/openedx/frontend-app-learner-dashboard.git",
"port": 1996,
},
"learning": {
"repository": "https://github.com/openedx/frontend-app-learning.git",
"port": 2000,
},
"ora-grading": {
"repository": "https://github.com/openedx/frontend-app-ora-grading.git",
"repository": "https://github.com/regisb/frontend-app-ora-grading.git",
"version": "regisb/rsbuild",
# "repository": "https://github.com/openedx/frontend-app-ora-grading.git",
"port": 1993,
},
"profile": {
Expand Down
14 changes: 11 additions & 3 deletions tutormfe/templates/mfe/build/mfe/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,22 @@ RUN apt update \
# required for gifsicle, mozjpeg, and optipng (on arm)
autoconf libtool pkg-config zlib1g-dev \
# required for node-sass (on arm)
python g++ \
python3 g++ \
# required for image-webpack-loader (on arm)
libpng-dev \
# required for building node-canvas (on arm, for authoring)
# https://www.npmjs.com/package/canvas
libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev

RUN mkdir -p /openedx/app /openedx/env

# Install shared dependencies in /openedx/node_modules
WORKDIR /openedx/
RUN npm install @rsbuild/core @rsbuild/plugin-react @rsbuild/plugin-svgr @rsbuild/plugin-sass

# Install apps in /openedx/app
WORKDIR /openedx/app
ENV PATH=/openedx/app/node_modules/.bin:${PATH}
ENV PATH=/openedx/app/node_modules/.bin:/openedx/node_modules/.bin:${PATH}

{{ patch("mfe-dockerfile-base") }}

Expand Down Expand Up @@ -77,7 +83,9 @@ CMD ["/bin/bash", "-c", "npm run start --- --config ./webpack.dev-tutor.config.j
######## {{ app_name }} (production)
FROM {{ app_name }}-common AS {{ app_name }}-prod
ENV NODE_ENV=production
RUN npm run build
COPY rsbuild.config.ts /openedx/app/rsbuild.config.ts
{# Locked cache will prevent concurrent builds of MFE apps, to spare CPU usage #}
RUN --mount=type=cache,target=/openedx/app/node_modules/.cache,sharing=locked rsbuild build
{{ patch("mfe-dockerfile-post-npm-build") }}
{{ patch("mfe-dockerfile-post-npm-build-{}".format(app_name)) }}
{% endfor %}
Expand Down
160 changes: 160 additions & 0 deletions tutormfe/templates/mfe/build/mfe/rsbuild.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Install dependencies:
//
// npm install --no-save @rsbuild/core @rsbuild/plugin-react @rsbuild/plugin-svgr @rsbuild/plugin-sass @rsdoctor/rspack-plugin
//
// Build with:
//
// rsbuild build
//
// Develop with:
//
// rsbuild
//
// Troubleshoot with:
//
// RSDOCTOR=true rsbuild build

import path from 'path';
import fs from 'fs';
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import { pluginSvgr } from '@rsbuild/plugin-svgr';
import { pluginSass } from '@rsbuild/plugin-sass';
import { loadEnv } from '@rsbuild/core';

// Load environment variables
// https://rsbuild.dev/api/javascript-api/core#loadenv
const currentDir = process.cwd();
const parsedEnv = loadEnv({ mode: 'production' }).parsed;
const parsedEnvDev = loadEnv({ mode: 'development' }).parsed;
const appName = parsedEnv.APP_ID || path.basename(currentDir).replace('frontend-app-', '');
const publicPath = parsedEnv.PUBLIC_PATH || '/' + appName + '/';
const dev = parsedEnv.NODE_ENV === 'development';
const prod = !dev;
const isArm64 = (process.arch === 'arm64');
parsedEnv.APP_ID = appName;
parsedEnv.PUBLIC_PATH = publicPath;
parsedEnv.MFE_CONFIG_API_URL = prod ? '/api/mfe_config/v1' : 'http://local.openedx.io:8000/api/mfe_config/v1';

// Load env.config.(js|jsx)
var envConfigPath = '';
try {
await import('@openedx/frontend-plugin-framework');
if (fs.existsSync(path.resolve(currentDir, './env.config.jsx'))) {
envConfigPath = path.resolve(currentDir, './env.config.jsx');
} else if (fs.existsSync(path.resolve(currentDir, './env.config.js'))) {
envConfigPath = path.resolve(currentDir, './env.config.js');
}
} catch {
// FPF is not available, we don't try to load env.config.js
}

const config = defineConfig({
html: {
template: './public/index.html',
},
output: {
// Flat directory
// TODO do we want a flat directory?
// distPath: {
// js: '',
// css: '',
// },
sourceMap: {
js: prod ? 'source-map' : 'eval',
css: true,
}
},
performance: {
// We enable caching everywhere except on amd64, even if it's unnecessary in
// some places. The negative impact on performance should be minor.
// The reason we disable it on arm64 is that build is failing on this platform
// https://github.com/web-infra-dev/rspack/issues/10118
buildCache: isArm64 ? false : true,
},
server: {
base: publicPath,
// Redirect all get requests to index.html
// https://rsbuild.dev/config/server/history-api-fallback
historyApiFallback: true,
port: parseInt(parsedEnvDev.PORT),
},
source: {
define: {
// This is explicitly recommended against, but this is how MFEs are built
// https://rsbuild.dev/guide/advanced/env-vars#processenv-replacement
'process.env': JSON.stringify(parsedEnv),
},
transformImport: [
// https://rsbuild.dev/config/source/transform-import#import-lodash-on-demand
{
libraryName: 'lodash',
customName: 'lodash/{' + '{ member }' + '}',
},
{
libraryName: '@fortawesome/free-brands-svg-icons',
customName: '@fortawesome/free-brands-svg-icons/{' + '{ member }' + '}',
transformToDefaultImport: false,
},
{
libraryName: '@fortawesome/free-regular-svg-icons',
customName: '@fortawesome/free-regular-svg-icons/{' + '{ member }' + '}',
transformToDefaultImport: false,
},
{
libraryName: '@fortawesome/free-solid-svg-icons',
customName: '@fortawesome/free-solid-svg-icons/{' + '{ member }' + '}',
transformToDefaultImport: false,
},
],
},
plugins: [
// https://rsbuild.dev/plugins/list/plugin-react
pluginReact(),
// https://rsbuild.dev/plugins/list/plugin-svgr#mixedimport
pluginSvgr({
mixedImport: true,
svgrOptions: {
exportType: 'named',
},
}),
// https://rsbuild.dev/plugins/list/plugin-sass
pluginSass({
sassLoaderOptions: {
sassOptions: {
silenceDeprecations: ['abs-percent', 'color-functions', 'import', 'mixed-decls', 'global-builtin', 'legacy-js-api'],
},
},
}),
],
resolve: {
alias: {
'env.config': envConfigPath || false,
}
}
});

///////////////////////
// MFE-specific changes
///////////////////////

// This is horrible but we don't have a choice
if (appName == 'authoring') {
config.resolve!.alias!['CourseAuthoring'] = path.resolve(currentDir, 'src/');
} else if (appName == 'communications') {
// config.resolve!.alias![''] = './src';
config.resolve!.alias!['@src'] = path.resolve(currentDir, 'src');
} else if (appName == 'gradebook') {
// config.resolve!.alias![''] = './src';
config.resolve!.alias!['@src'] = path.resolve(currentDir, 'src');
} else if (appName == 'learner-dashboard') {
// config.resolve!.alias![''] = './src';
config.resolve!.alias!['@src'] = path.resolve(currentDir, 'src');
} else if (appName === 'learning') {
config.resolve!.alias!['@src'] = path.resolve(currentDir, 'src');
} else if (appName == 'ora-grading') {
// config.resolve!.alias![''] = './src';
config.resolve!.alias!['@src'] = path.resolve(currentDir, 'src');
}

export default config;