Webpack Configuration
Webpack is a JavaScript module bundler that transforms and packages your front-end code into optimized files that browsers can execute efficiently.
Why Webpack?
Modern front-end development involves:
- Multiple JavaScript files that depend on each other
- npm packages from the JavaScript ecosystem
- React components requiring JSX transformation
- Code splitting for optimal page loading
Webpack handles all of this:
| What Webpack Does | Why It Matters |
|---|---|
| Bundling — Combines many files into one or a few bundles | Reduces HTTP requests, improves load times |
| Transpilation — Converts JSX, modern JS to browser-compatible code | Lets you use modern syntax and React |
| npm integration — Installs and bundles npm packages | Access to thousands of open-source libraries |
| Code splitting — Creates separate bundles per entry point | Users only download code needed for the pages they visit |
| Minification — Removes whitespace, compresses code | Smaller file sizes, faster delivery |
Webpack performs code splitting by analyzing each entry point separately. Only the dependencies explicitly required by an entry point (directly or transitively) are included in its resulting bundle. This keeps bundles small and ensures users load only what’s needed for the specific page they’re visiting.
How It Works in NRP Repositories
NRP repositories relies on two main Python libraries that brings Webpack support for Python/Flask:
| Library | Purpose |
|---|---|
| invenio-assets | Provides CLI commands and manages the build process |
| flask-webpackext | Reads Webpack’s output files (manifest.json) and makes them available in Jinja templates via the webpack variable |
Each Python package or module in your repository could contribute to the resulting UI through a webpack.py file. Configurations from these files are merged into a single unified Webpack project:
How the UI Build Process Works
NRP repositories use Flask-WebpackExt combined with a build process managed through ./run.sh commands to build the UI assets.
Build Process Flow
-
Discovery: During
./run.sh invenio webpack install, Flask-WebpackExt discovers all installed Python packages that have registeredwebpack.pyfiles -
Configuration Collection: Each package’s
webpack.pymodule declares:- Webpack entry points (JS bundles to be built)
- Path aliases for importing modules
- NPM dependencies required by the package
- Additional webpack configuration extensions
-
Dependency Aggregation: All NPM dependencies from discovered packages are aggregated into a single
package.jsonat the build directory -
Installation:
npm installruns to install all dependencies needed by all UI packages intonode_modules/ -
Bundling:
./run.sh invenio webpack buildruns webpack to process all entry points and generate optimized bundles (JS/CSS) along with amanifest.jsonfile -
Runtime: Jinja templates read
manifest.jsonand use thewebpackvariable to include the bundled assets
Available Commands
| Command | Purpose |
|---|---|
./run.sh invenio webpack install | Install npm dependencies and set up build environment |
./run.sh invenio webpack build | Build JS/CSS bundles and generate manifest |
./run.sh invenio webpack clean | Clean built assets |
When developing with ./run.sh run, the webpack build and install process happens automatically. You only need to run these commands explicitly when:
- Adding new webpack entry points
- Adding a new
webpack.pymodule - Manually forcing a rebuild
Key Benefits
This architecture means you don’t need to:
- Manually manage NPM dependencies for each UI module
- Configure bundling separately for each package
- Worry about dependency conflicts between modules
All webpack configurations from all packages are combined and built together as a single project.
Registering webpack.py
Each webpack.py file must be explicitly registered as a Python entry point in your package’s pyproject.toml file. The invenio-assets build system discovers webpack configurations through the invenio_assets.webpack entry point group.
# pyproject.toml - registering webpack configurations
[project.entry-points."invenio_assets.webpack"]
my_theme = "ui.mytheme.webpack:theme"The format is "<entry-point-id>" = "<module.path>:<variable>":
| Part | Example | Description |
|---|---|---|
| Entry point ID | my_theme | A unique identifier for this webpack configuration |
| Module path | ui.mytheme.webpack | Python module path (folders separated by dots) |
| Variable | theme | The name of the WebpackThemeBundle variable to import |
After adding or modifying entry points in pyproject.toml, reinstall the package for the changes to take effect: uv pip install -e .
webpack.py Structure
A webpack.py file defines a WebpackThemeBundle instance that configures:
- Entry points — JavaScript entry files for bundling
- Aliases — Short names for importing modules
- Dependencies — npm packages (
dependenciesanddevDependencies) required by your code - Theme — Which UI theme base to use (typically
semantic-ui)
WebpackThemeBundle
WebpackThemeBundle is the class that defines a webpack theme configuration. It can be placed in any webpack.py file.
Parameters
| Parameter | Type | Description |
|---|---|---|
import_name | string | Typically __name__ - the Python module name |
static_folder | string | Base directory for static files (usually ".") |
default | string | Default theme name (e.g., "semantic-ui") |
themes | dict | Theme configuration with entry points, aliases, dependencies |
Example
from invenio_assets.webpack import WebpackThemeBundle
theme = WebpackThemeBundle(
__name__, # Python module name
".", # Static folder path (relative to this file)
default="semantic-ui", # Default theme to use
themes={
"semantic-ui": dict(
entry={
# JavaScript entry points to bundle
"welcome_banner": "./js/WelcomeBanner/index.js",
"recent_items": "./js/RecentItems/index.js",
},
aliases={
# webpack resolver aliases for clean imports
"@js/features": "./js/features",
"@js/components": "./js/components",
},
# npm packages required by this bundle
dependencies={
"lodash": "^4.17.21",
"axios": "^1.6.0",
},
devDependencies={
"jest": "^29.0.0",
},
)
}
)Entry Points
An entry point is the main JavaScript file that webpack processes to create a bundle:
// ui/components/semantic-ui/js/WelcomeBanner/index.js
import ReactDOM from "react-dom";
import { WelcomeBanner } from "./WelcomeBanner";
function renderWelcomeBanner() {
const element = document.getElementById("welcome-banner");
if (element) {
ReactDOM.render(<WelcomeBanner />, element);
}
}
renderWelcomeBanner();The entry point key becomes the bundle name for inclusion in templates: webpack['welcome_banner.js'].
Entry Points from oarepo-ui
The oarepo-ui package provides pre-configured entry points:
| Entry Point | Purpose |
|---|---|
oarepo_ui | Core utilities |
oarepo_ui_search | Search app components |
oarepo_ui_forms | Form components and contexts |
oarepo_ui_theme | Theme-related JavaScript |
oarepo_ui_components | Reusable UI components |
{%- block javascript %}
{{ webpack['oarepo_ui_search.js'] }}
{%- endblock %}Including Bundles in Templates
Once configured, include the bundle in your Jinja or JinjaX templates. See Templating for more details on template inheritance and including scripts:
{%- block javascript %}
{{ super() }} <!-- Include parent JavaScript -->
{{ webpack['welcome_banner.js'] }} <!-- Your custom bundle -->
{%- endblock %}Aliases
Aliases provide convenient shortcuts for importing modules:
"aliases": {
"@js/features": "./js/features",
"@js/components": "./js/components",
}Then use in your code:
// Instead of:
import MyComponent from '../features/my-component';
// You can use:
import MyComponent from '@js/features/my-component';npm Dependencies
Add npm packages through the dependencies or devDependencies keys within the theme dict:
theme = WebpackThemeBundle(
__name__,
".",
themes={
"semantic-ui": dict(
entry={},
aliases={},
# Production dependencies
dependencies={
"axios": "^1.6.0",
"moment": "^2.29.0",
},
# Development dependencies
devDependencies={
"jest": "^29.0.0",
"typescript": "^5.0.0",
},
)
},
)After modifying dependencies, run:
invenio webpack installRebuilding Assets
After modifying entry points, aliases, or packages:
Full Rebuild
invenio webpack clean create
invenio webpack installDevelopment Mode (Watch Changes)
For development with automatic reloading, see Run frontend assets builder.
Troubleshooting
Bundle Not Loading
- Check the entry point path is relative to
webpack.py - Verify the file exists at the path
- Rebuild with
./run.sh invenio webpack clean create install - Ensure your package is installed (
pip install -e .) after modifying entry points
Import Errors
# Correct: path relative to webpack.py location
"entry": {
"my_feature": "./js/MyFeature/index.js", # ✓
}# Incorrect: absolute path
"entry": {
"my_feature": "/js/MyFeature/index.js", # ✗
}