*** Wartungsfenster jeden ersten Mittwoch vormittag im Monat ***

Skip to content
Snippets Groups Projects
Commit f49fd5b0 authored by Moser, Maximilian's avatar Moser, Maximilian
Browse files

Add project files for an rspack-based build

* also add `.npmrc` which will likely be needed for PNPM support
* this is the extent for our PNPM support right now, as further changes
  in `invenio-cli` are required
* this is based on work from Alex (CERN) and Christoph (TU Graz)
parent 2e9187d8
No related branches found
No related tags found
1 merge request!176Add project files for an `rspack`-based build
# required for pnpm to work with webpack
# https://github.com/webpack/webpack/issues/5087#issuecomment-1589749984
node-linker=hoisted
shamefully-hoist=true
/*
* This file is part of Invenio.
* Copyright (C) 2017-2018 CERN.
* Copyright (C) 2022-2023 Graz University of Technology.
* Copyright (C) 2023-2025 TU Wien.
*
* Invenio is free software; you can redistribute it and/or modify it
* under the terms of the MIT License; see LICENSE file for more details.
*/
// https://birtles.blog/2024/08/14/lessons-learned-switching-to-rspack/
const BundleTracker = require("webpack-bundle-tracker");
const config = require("./config");
const path = require("path");
// Use rspack
const rspack = require("@rspack/core");
// Load aliases from config and resolve their full path
let aliases = {};
if (config.aliases) {
aliases = Object.fromEntries(
Object.entries(config.aliases).map(([alias, alias_path]) => [
alias,
path.resolve(config.build.context, alias_path),
]),
);
}
// Create copy patterns from config
let copyPatterns = [];
if (config.copy) {
for (const copy of config.copy) {
const copyPattern = {
from: path.resolve(__dirname, copy.from),
to: path.resolve(__dirname, copy.to),
};
copyPatterns.push(copyPattern);
}
}
const prod = process.env.NODE_ENV === "production";
const webpackConfig = {
mode: process.env.NODE_ENV,
entry: config.entry,
context: config.build.context,
stats: {
//preset: 'verbose',
warnings: true,
errors: true,
errorsCount: true,
errorStack: true,
errorDetails: true,
children: true,
},
resolve: {
extensions: ["*", ".js", ".jsx"],
symlinks: false,
alias: aliases,
fallback: {
zlib: require.resolve("browserify-zlib"),
stream: require.resolve("stream-browserify"),
https: require.resolve("https-browserify"),
http: require.resolve("stream-http"),
url: false,
assert: false,
},
},
output: {
clean: true, // replaces CleanWebpackPlugin
path: config.build.assetsPath,
filename: "js/[name].[chunkhash].js",
chunkFilename: "js/[id].[chunkhash].js",
publicPath: config.build.assetsURL,
},
optimization: {
minimizer: [
new rspack.SwcJsMinimizerRspackPlugin({
compress: {
ecma: 5,
// warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending further investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: true,
},
}),
// would be nice, but not workable at the moment, no idea why
new rspack.LightningCssMinimizerRspackPlugin({
minimizerOptions: {
targets: [
"last 2 Chrome versions",
"Firefox ESR",
"last 2 Safari versions",
],
},
}),
],
splitChunks: {
chunks: "all",
},
// Extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated.
runtimeChunk: {
name: "manifest",
},
},
module: {
rules: [
{
test: require.resolve("jquery"),
use: [
{
loader: "expose-loader",
options: {
exposes: ["$", "jQuery"],
},
},
],
},
{
test: /\.(js|jsx)$/,
exclude: [/node_modules/, /@babel(?:\/|\\{1,2})runtime/],
loader: "builtin:swc-loader",
options: {
jsc: {
parser: {
syntax: "ecmascript",
jsx: true,
},
externalHelpers: true,
transform: {
react: {
development: !prod,
useBuiltins: true,
},
},
},
env: {
targets: "Chrome >= 48",
},
},
},
{
test: /\.(scss|css)$/,
use: [
rspack.CssExtractRspackPlugin.loader,
"css-loader",
"sass-loader",
],
},
{
test: /\.(less)$/,
use: [
rspack.CssExtractRspackPlugin.loader,
"css-loader",
"less-loader",
],
},
// Rspack
// Inline images smaller than 10k
{
test: /\.(avif|webp|png|jpe?g|gif|svg)(\?.*)?$/,
type: "asset/resource",
},
// no mimetype for ".cur" in mimetype database, specify it with `generator`
{
test: /\.(cur)(\?.*)?$/,
type: "asset/inline",
generator: {
dataUrl: {
encoding: "base64",
mimetype: "image/x-icon",
},
},
},
// Inline webfonts smaller than 10k
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: "asset/resource",
generator: {
filename: "fonts/[name].[contenthash:7].[ext]",
},
},
],
},
devtool:
process.env.NODE_ENV === "production" ? "source-map" : "inline-source-map",
plugins: [
new rspack.DefinePlugin({
"process.env": process.env.NODE_ENV,
}),
new rspack.CssExtractRspackPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "css/[name].[contenthash].css",
chunkFilename: "css/[name].[contenthash].css",
}),
// Copying relevant CSS files as TinyMCE tries to import css files from the dist/js folder of static files
new rspack.CopyRspackPlugin({
patterns: copyPatterns,
}),
// Automatically inject jquery
new rspack.ProvidePlugin({
jQuery: "jquery",
$: "jquery",
jquery: "jquery",
"window.jQuery": "jquery",
}),
// Write manifest file which Python will read.
new BundleTracker({
path: config.build.assetsPath,
filename: path.join(config.build.assetsPath, "manifest.json"),
publicPath: config.build.assetsURL,
}),
],
performance: { hints: false },
watchOptions: {
followSymlinks: true,
},
experiments: {
css: false,
},
devServer: {
hot: true, // Enable Hot Module Replacement (HMR)
liveReload: true, // Enable live reload
},
};
if (process.env.npm_config_report) {
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
module.exports = webpackConfig;
{
"name": "invenio-assets",
"version": "2.0.0",
"description": "Invenio assets",
"author": "CERN <info@inveniosoftware.org>",
"private": true,
"scripts": {
"start": "NODE_PRESERVE_SYMLINKS=1 rspack --mode development --watch --config ./build/rspack.config.js",
"build": "NODE_PRESERVE_SYMLINKS=1 rspack --mode production --config ./build/rspack.config.js",
"postinstall": "patch-package"
},
"dependencies": {
"tinymce": "^6.7.2"
},
"devDependencies": {
"@rspack/cli": ">1.0.0",
"@rspack/core": ">1.0.0",
"@rspack/dev-server": ">1.0.0",
"@swc/core": "^1.6.13",
"@swc/helpers": "^0.5.11",
"sass-loader": "^16.0.0",
"swc-loader": "^0.2.6",
"@babel/core": "^7.18.0",
"@babel/eslint-parser": "^7.18.0",
"@babel/plugin-proposal-class-properties": "^7.18.0",
"@babel/plugin-transform-runtime": "^7.18.0",
"@babel/preset-env": "^7.18.0",
"@babel/preset-react": "^7.18.0",
"@babel/register": "^7.18.0",
"@babel/runtime": "^7.18.0",
"@inveniosoftware/eslint-config-invenio": "^2.0.0",
"ajv": "^8.12.0",
"autoprefixer": "^10.4.0",
"babel-loader": "^9.0.0",
"browserify-zlib": "^0.2.0",
"chalk": "^5.0.0",
"css-loader": "^6.0.0",
"eslint-config-react-app": "^7.0.1",
"eslint-friendly-formatter": "^4.0.1",
"eslint-webpack-plugin": "^2.5.0",
"eventsource-polyfill": "^0.9.0",
"expose-loader": "^4.0.0",
"file-loader": "^6.0.0",
"function-bind": "^1.1.0",
"https-browserify": "^1.0.0",
"less": "^4.0.0",
"less-loader": "^11.0.0",
"ora": "^6.0.0",
"patch-package": "^6.5.0",
"postcss-flexbugs-fixes": "^5.0.0",
"postcss-loader": "^7.0.0",
"postcss-preset-env": "^8.0.0",
"postcss-safe-parser": "^6.0.0",
"prettier": "^2.7.0",
"rimraf": "^4.0.0",
"sass": "^1.50.0",
"stream-browserify": "^3.0.0",
"stream-http": "^3.2.0",
"style-loader": "^3.0.0",
"url-loader": "^4.1.0",
"webpack-bundle-analyzer": "^4.0.0",
"webpack-bundle-tracker": "^1.0.0"
},
"engines": {
"node": ">=18",
"npm": ">=8"
}
}
......@@ -97,7 +97,7 @@ OAUTHCLIENT_LOGIN_USER_TEMPLATE = "invenio_theme_tuw/overrides/login_user.html"
# ================
# See https://flask-webpackext.readthedocs.io/en/latest/configuration.html
WEBPACKEXT_PROJECT = "invenio_theme_tuw.webpack:project"
WEBPACKEXT_PROJECT = "invenio_theme_tuw.webpack:rspack_project"
APP_RDM_DETAIL_SIDE_BAR_TEMPLATES = [
"invenio_app_rdm/records/details/side_bar/manage_menu.html",
......
......@@ -7,10 +7,20 @@
"""JS/CSS Webpack bundles for TU Wien theme."""
from flask_webpackext import WebpackBundleProject
from flask_webpackext import WebpackBundleProject as WebpackBundleProjectBase
from invenio_assets.webpack import WebpackThemeBundle
from pywebpack import bundles_from_entry_point
class WebpackBundleProject(WebpackBundleProjectBase):
"""Flask webpack bundle project."""
def __init__(self, import_name, base_package_json="package.json", **kwargs):
"""Constructor."""
super().__init__(import_name, **kwargs)
self._package_json_source_path = base_package_json
# our override for the frontend build project:
# we override the build configuration (based on the one provided in `invenio-assets`)
# in order to customize its behaviour, e.g. allowing the collection of '*.webp' images.
......@@ -27,8 +37,17 @@ from pywebpack import bundles_from_entry_point
# * project_folder: the sub-directory in which to look for the `package.json`
# * config_path: where to put npm's `config.json` (inside the `project_folder`)
# * bundles: the bundles that should be used for building the frontend
project = WebpackBundleProject(
webpack_project = WebpackBundleProject(
import_name=__name__,
project_folder="build_project",
config_path="build/config.json",
bundles=bundles_from_entry_point("invenio_assets.webpack"),
)
project = webpack_project
rspack_project = WebpackBundleProject(
import_name=__name__,
base_package_json="rspack-package.json",
project_folder="build_project",
config_path="build/config.json",
bundles=bundles_from_entry_point("invenio_assets.webpack"),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment