mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 06:03:20 +00:00
Passage de webpack à vite.dev
This commit is contained in:
parent
7b41051d0d
commit
8a8851847c
@ -1,15 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"edge": "17",
|
||||
"firefox": "60",
|
||||
"chrome": "67",
|
||||
"safari": "11.1"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<title>{% trans %}Slideshow{% endtrans %}</title>
|
||||
<link href="{{ static('css/slideshow.scss') }}" rel="stylesheet" type="text/css" />
|
||||
<script src="{{ static('bundled/jquery-index.js') }}"></script>
|
||||
<script type="module" src="{{ static('bundled/jquery-index.js') }}"></script>
|
||||
<script src="{{ static('com/js/slideshow.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -1 +1 @@
|
||||
require("@fortawesome/fontawesome-free/css/all.css");
|
||||
import "@fortawesome/fontawesome-free/css/all.css";
|
||||
|
@ -1 +1,3 @@
|
||||
window.htmx = require("htmx.org");
|
||||
import htmx from "htmx.org";
|
||||
|
||||
Object.assign(window, { htmx });
|
||||
|
25
core/static/bundled/jquery-index.js
vendored
25
core/static/bundled/jquery-index.js
vendored
@ -1,25 +0,0 @@
|
||||
import $ from "jquery";
|
||||
import "jquery.shorten/src/jquery.shorten.min.js";
|
||||
|
||||
// We ship jquery-ui with jquery because when standalone with webpack
|
||||
// JQuery is also included in the jquery-ui package. We do gain space by doing this
|
||||
// We require jquery-ui components manually and not in a loop
|
||||
// Otherwise it increases the output files by a x2 factor !
|
||||
require("jquery-ui/ui/widgets/accordion.js");
|
||||
require("jquery-ui/ui/widgets/autocomplete.js");
|
||||
require("jquery-ui/ui/widgets/button.js");
|
||||
require("jquery-ui/ui/widgets/dialog.js");
|
||||
require("jquery-ui/ui/widgets/tabs.js");
|
||||
|
||||
require("jquery-ui/themes/base/all.css");
|
||||
|
||||
/**
|
||||
* Simple wrapper to solve shorten not being able on legacy pages
|
||||
* @param {string} selector to be passed to jQuery
|
||||
* @param {Object} options object to pass to the shorten function
|
||||
**/
|
||||
function shorten(selector, options) {
|
||||
$(selector).shorten(options);
|
||||
}
|
||||
|
||||
window.shorten = shorten;
|
2
core/static/bundled/jquery-ui-index.js
vendored
Normal file
2
core/static/bundled/jquery-ui-index.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// This is only used to import jquery-ui css files
|
||||
import "jquery-ui/themes/base/all.css";
|
@ -14,18 +14,19 @@
|
||||
|
||||
{% block jquery_css %}
|
||||
{# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
|
||||
<link rel="stylesheet" href="{{ static('bundled/jquery-index.css') }}">
|
||||
<link rel="stylesheet" href="{{ static('bundled/jquery-ui-index.css') }}">
|
||||
{% endblock %}
|
||||
<link rel="preload" as="style" href="{{ static('bundled/fontawesome-index.css') }}" onload="this.onload=null;this.rel='stylesheet'">
|
||||
<noscript><link rel="stylesheet" href="{{ static('bundled/fontawesome-index.css') }}"></noscript>
|
||||
|
||||
<script src="{{ url('javascript-catalog') }}"></script>
|
||||
<script src={{ static("bundled/core/components/include-index.ts") }}></script>
|
||||
<script src="{{ static('bundled/alpine-index.js') }}" defer></script>
|
||||
<script src="{{ static('bundled/htmx-index.js') }}" defer></script>
|
||||
<script type="module" src={{ static("bundled/core/components/include-index.ts") }}></script>
|
||||
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script>
|
||||
<script type="module" src="{{ static('bundled/htmx-index.js') }}"></script>
|
||||
|
||||
<!-- Jquery declared here to be accessible in every django widgets -->
|
||||
<script src="{{ static('bundled/jquery-index.js') }}"></script>
|
||||
<!-- Put here to always have access to those functions on django widgets -->
|
||||
<script src="{{ static('bundled/vendored/jquery.min.js') }}"></script>
|
||||
<script src="{{ static('bundled/vendored/jquery-ui.min.js') }}"></script>
|
||||
<script src="{{ static('core/js/script.js') }}"></script>
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
{%- endblock -%}
|
||||
|
||||
{% block additional_js %}
|
||||
<script src="{{ static("bundled/user/family-graph-index.js") }}" defer></script>
|
||||
<script type="module" src="{{ static("bundled/user/family-graph-index.js") }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
|
@ -5,7 +5,7 @@
|
||||
{%- endblock -%}
|
||||
|
||||
{% block additional_js %}
|
||||
<script src="{{ static('bundled/user/pictures-index.js') }}" defer></script>
|
||||
<script type="module" src="{{ static('bundled/user/pictures-index.js') }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% for js in statics.js %}
|
||||
<script-once src="{{ js }}" defer></script-once>
|
||||
<script-once type="module" src="{{ js }}" defer></script-once>
|
||||
{% endfor %}
|
||||
{% for css in statics.css %}
|
||||
<link-once rel="stylesheet" type="text/css" href="{{ css }}" defer></link-once>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<script-once src="{{ statics.js }}" defer></script-once>
|
||||
<script-once type="module" src="{{ statics.js }}" defer></script-once>
|
||||
<link-once rel="stylesheet" type="text/css" href="{{ statics.css }}" defer></link-once>
|
||||
|
||||
<markdown-input name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% if widget.value %}{{ widget.value }}{% endif %}</markdown-input>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<script-once src="{{ statics.js }}" defer></script-once>
|
||||
<script-once type="module" src="{{ statics.js }}" defer></script-once>
|
||||
<link-once rel="stylesheet" type="text/css" href="{{ statics.css }}" defer></link-once>
|
||||
|
||||
<span>
|
||||
|
@ -10,6 +10,6 @@ class MarkdownInput(Textarea):
|
||||
|
||||
context["statics"] = {
|
||||
"js": staticfiles_storage.url("bundled/core/components/easymde-index.ts"),
|
||||
"css": staticfiles_storage.url("bundled/core/components/easymde-index.css"),
|
||||
"css": staticfiles_storage.url("bundled/easymde-index.css"),
|
||||
}
|
||||
return context
|
||||
|
@ -22,7 +22,7 @@ class AutoCompleteSelectMixin:
|
||||
"bundled/core/components/ajax-select-index.ts",
|
||||
]
|
||||
css = [
|
||||
"bundled/core/components/ajax-select-index.css",
|
||||
"bundled/ajax-select-index.css",
|
||||
"core/components/ajax-select.scss",
|
||||
]
|
||||
|
||||
|
@ -401,24 +401,16 @@ Npm possède, tout comme Poetry, la capacité de locker les dépendances au moye
|
||||
|
||||
Nous l'utilisons ici pour gérer les dépendances JavaScript. Celle-ci sont déclarées dans le fichier `package.json` situé à la racine du projet.
|
||||
|
||||
### Webpack
|
||||
### Vite
|
||||
|
||||
[Utiliser webpack](https://webpack.js.org/concepts/)
|
||||
[Utiliser vite](https://vite.dev)
|
||||
|
||||
Webpack est un bundler de fichiers static. Il nous sert ici à mettre à disposition les dépendances frontend gérées par npm.
|
||||
Vite est un bundler de fichiers static. Il nous sert ici à mettre à disposition les dépendances frontend gérées par npm.
|
||||
|
||||
Il sert également à intégrer les autres outils JavaScript au workflow du Sith de manière transparente.
|
||||
|
||||
Webpack a été choisi pour sa versatilité et sa popularité. C'est un des plus anciens bundler et il est là pour rester.
|
||||
Vite a été choisi pour sa versatilité et sa popularité. Il est moderne et très rapide avec un fort soutien de la communauté.
|
||||
|
||||
Le logiciel se configure au moyen du fichier `webpack.config.js` à la racine du projet.
|
||||
Il intègre aussi tout le nécessaire pour la rétro-compatibilité et le Typescript.
|
||||
|
||||
### Babel
|
||||
|
||||
[Babel](https://babeljs.io/)
|
||||
|
||||
Babel est un outil qui offre la promesse de convertir le code JavaScript moderne en code JavaScript plus ancien sans action de la part du développeur. Il permet de ne pas se soucier de la compatibilité avec les navigateurs et de coder comme si on était toujours sur la dernière version du langage.
|
||||
|
||||
Babel est intégré dans Webpack et tout code bundlé par celui-ci est automatiquement converti.
|
||||
|
||||
Le logiciel se configure au moyen du fichier `babel.config.json` à la racine du projet.
|
||||
Le logiciel se configure au moyen du fichier `vite.config.mts` à la racine du projet.
|
||||
|
@ -11,7 +11,7 @@
|
||||
{% block additional_js %}
|
||||
{# This script contains the code to perform requests to manipulate the
|
||||
user basket without having to reload the page #}
|
||||
<script src="{{ static('bundled/eboutic/eboutic-index.ts') }}"></script>
|
||||
<script type="module" src="{{ static('bundled/eboutic/eboutic-index.ts') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_css %}
|
||||
|
@ -9,6 +9,10 @@
|
||||
<link rel="stylesheet" href="{{ static('election/css/election.scss') }}">
|
||||
{%- endblock %}
|
||||
|
||||
{% block additional_css %}
|
||||
<script src="{{ static('bundled/vendored/jquery.shorten.min.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3 class="election__title">{{ election.title }}</h3>
|
||||
<p class="election__description">{{ election.description }}</p>
|
||||
@ -197,12 +201,12 @@
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
shorten('.role_description', {
|
||||
$('.role_description').shorten({
|
||||
moreText: "{% trans %}Show more{% endtrans %}",
|
||||
lessText: "{% trans %}Show less{% endtrans %}",
|
||||
showChars: 50
|
||||
});
|
||||
shorten('.candidate_program', {
|
||||
$('.candidate_program').shorten({
|
||||
moreText: "{% trans %}Show more{% endtrans %}",
|
||||
lessText: "{% trans %}Show less{% endtrans %}",
|
||||
showChars: 200
|
||||
|
@ -5,7 +5,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_js %}
|
||||
<script src="{{ static('bundled/galaxy/galaxy-index.js') }}" defer></script>
|
||||
<script type="module" src="{{ static('bundled/galaxy/galaxy-index.js') }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
4020
package-lock.json
generated
4020
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@ -4,11 +4,11 @@
|
||||
"description": "Le web Sith de l'AE",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"compile": "webpack --mode production",
|
||||
"compile-dev": "webpack --mode development",
|
||||
"serve": "webpack --mode development --watch",
|
||||
"analyse-dev": "webpack --config webpack.analyze.config.js --mode development",
|
||||
"analyse-prod": "webpack --config webpack.analyze.config.js --mode production",
|
||||
"compile": "vite build --mode production",
|
||||
"compile-dev": "vite build --mode development",
|
||||
"serve": "vite build --mode development --watch",
|
||||
"analyse-dev": "vite-bundle-visualizer --mode development",
|
||||
"analyse-prod": "vite-bundle-visualizer --mode production",
|
||||
"check": "biome check --write"
|
||||
},
|
||||
"keywords": [],
|
||||
@ -25,20 +25,12 @@
|
||||
"@babel/preset-env": "^7.25.4",
|
||||
"@biomejs/biome": "1.9.3",
|
||||
"@hey-api/openapi-ts": "^0.53.8",
|
||||
"@rollup/plugin-inject": "^5.0.5",
|
||||
"@types/alpinejs": "^3.13.10",
|
||||
"@types/jquery": "^3.5.31",
|
||||
"babel-loader": "^9.2.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
"expose-loader": "^5.0.0",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"source-map-loader": "^5.0.0",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
"vite": "^5.4.11",
|
||||
"vite-bundle-visualizer": "^1.2.1",
|
||||
"vite-plugin-static-copy": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
|
@ -10,7 +10,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_js %}
|
||||
<script src="{{ static('bundled/pedagogy/guide-index.js') }}" defer></script>
|
||||
<script type="module" src="{{ static('bundled/pedagogy/guide-index.js') }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
|
@ -6,7 +6,7 @@
|
||||
{%- endblock -%}
|
||||
|
||||
{%- block additional_js -%}
|
||||
<script src="{{ static('bundled/sas/album-index.js') }}" defer></script>
|
||||
<script type="module" src="{{ static('bundled/sas/album-index.js') }}" defer></script>
|
||||
{%- endblock -%}
|
||||
|
||||
{% block title %}
|
||||
|
@ -1,14 +1,14 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{%- block additional_css -%}
|
||||
<link defer rel="stylesheet" href="{{ static('bundled/core/components/ajax-select-index.css') }}">
|
||||
<link defer rel="stylesheet" href="{{ static('bundled/ajax-select-index.css') }}">
|
||||
<link defer rel="stylesheet" href="{{ static('core/components/ajax-select.scss') }}">
|
||||
<link defer rel="stylesheet" href="{{ static('sas/css/picture.scss') }}">
|
||||
{%- endblock -%}
|
||||
|
||||
{%- block additional_js -%}
|
||||
<script defer src="{{ static('bundled/core/components/ajax-select-index.ts') }}"></script>
|
||||
<script defer src="{{ static("bundled/sas/viewer-index.ts") }}"></script>
|
||||
<script type="module" defer src="{{ static('bundled/core/components/ajax-select-index.ts') }}"></script>
|
||||
<script type="module" defer src="{{ static("bundled/sas/viewer-index.ts") }}"></script>
|
||||
{%- endblock -%}
|
||||
|
||||
{% block title %}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<script src="{{ static('bundled/subscription/stats-index.ts') }}" defer></script>
|
||||
<script type="module" src="{{ static('bundled/subscription/stats-index.ts') }}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -10,6 +10,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": ["jquery", "alpinejs"],
|
||||
"paths": {
|
||||
"#openapi": ["./staticfiles/generated/openapi/index.ts"],
|
||||
|
85
vite.config.mts
Normal file
85
vite.config.mts
Normal file
@ -0,0 +1,85 @@
|
||||
// biome-ignore lint/correctness/noNodejsModules: this is backend side
|
||||
import { parse, resolve } from "node:path";
|
||||
import inject from "@rollup/plugin-inject";
|
||||
import { glob } from "glob";
|
||||
import type { AliasOptions, UserConfig } from "vite";
|
||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||
import tsconfig from "./tsconfig.json";
|
||||
|
||||
const outDir = resolve(__dirname, "./staticfiles/generated/bundled");
|
||||
const vendored = resolve(outDir, "vendored");
|
||||
const nodeModules = resolve(__dirname, "node_modules");
|
||||
|
||||
function getAliases(): AliasOptions {
|
||||
const aliases: AliasOptions = {};
|
||||
for (const [key, value] of Object.entries(tsconfig.compilerOptions.paths)) {
|
||||
aliases[key] = resolve(__dirname, value[0]);
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
type IndexPath = { [find: string]: string };
|
||||
|
||||
// biome-ignore lint/style/noDefaultExport: this is recommended by documentation
|
||||
export default {
|
||||
base: "/static/bundled/",
|
||||
appType: "custom",
|
||||
build: {
|
||||
outDir: outDir,
|
||||
modulePreload: false, // would require `import 'vite/modulepreload-polyfill'` to always be injected
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
input: glob
|
||||
.sync("./!(static)/static/bundled/**/*?(-)index.?(m)[j|t]s?(x)")
|
||||
.reduce((obj: IndexPath, el) => {
|
||||
// We include the path inside the bundled folder in the name
|
||||
let relativePath: string[] = [];
|
||||
const fullPath = parse(el);
|
||||
for (const dir of fullPath.dir.split("/").reverse()) {
|
||||
if (dir === "bundled") {
|
||||
break;
|
||||
}
|
||||
relativePath.push(dir);
|
||||
}
|
||||
// We collected folders in reverse order, we put them back in the original order
|
||||
relativePath = relativePath.reverse();
|
||||
relativePath.push(fullPath.name);
|
||||
obj[relativePath.join("/")] = `./${el}`;
|
||||
return obj;
|
||||
}, {}),
|
||||
output: {
|
||||
entryFileNames: "[name].js",
|
||||
assetFileNames: "[name].[ext]",
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: getAliases(),
|
||||
},
|
||||
|
||||
plugins: [
|
||||
inject({
|
||||
// biome-ignore lint/style/useNamingConvention: that's how it's called
|
||||
Alpine: "alpinejs",
|
||||
}),
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: resolve(nodeModules, "jquery/dist/jquery.min.js"),
|
||||
dest: vendored,
|
||||
},
|
||||
{
|
||||
src: resolve(nodeModules, "jquery-ui/dist/jquery-ui.min.js"),
|
||||
dest: vendored,
|
||||
},
|
||||
{
|
||||
src: resolve(nodeModules, "jquery.shorten/src/jquery.shorten.min.js"),
|
||||
dest: vendored,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
optimizeDeps: {
|
||||
include: ["jquery"],
|
||||
},
|
||||
} satisfies UserConfig;
|
@ -1,7 +0,0 @@
|
||||
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
|
||||
const config = require("./webpack.config.js");
|
||||
|
||||
module.exports = {
|
||||
...config,
|
||||
plugins: [...config.plugins, new BundleAnalyzerPlugin()],
|
||||
};
|
@ -1,111 +0,0 @@
|
||||
const glob = require("glob");
|
||||
// biome-ignore lint/correctness/noNodejsModules: this is backend side
|
||||
const path = require("node:path");
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
entry: glob
|
||||
.sync("./!(static)/static/bundled/**/*?(-)index.[j|t]s?(x)")
|
||||
.reduce((obj, el) => {
|
||||
// We include the path inside the bundled folder in the name
|
||||
let relativePath = [];
|
||||
const fullPath = path.parse(el);
|
||||
for (const dir of fullPath.dir.split("/").reverse()) {
|
||||
if (dir === "bundled") {
|
||||
break;
|
||||
}
|
||||
relativePath.push(dir);
|
||||
}
|
||||
// We collected folders in reverse order, we put them back in the original order
|
||||
relativePath = relativePath.reverse();
|
||||
relativePath.push(fullPath.name);
|
||||
obj[relativePath.join("/")] = `./${el}`;
|
||||
return obj;
|
||||
}, {}),
|
||||
cache: {
|
||||
type: "filesystem", // This reduces typescript compilation time like crazy when you restart the server
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(__dirname, "./staticfiles/generated/bundled"),
|
||||
clean: true,
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".tsx", ".ts", ".js"],
|
||||
},
|
||||
plugins: [new MiniCssExtractPlugin()],
|
||||
optimization: {
|
||||
minimizer: [
|
||||
"...",
|
||||
new CssMinimizerPlugin({
|
||||
parallel: true,
|
||||
}),
|
||||
new TerserPlugin({
|
||||
parallel: true,
|
||||
terserOptions: {
|
||||
mangle: true,
|
||||
compress: {
|
||||
// biome-ignore lint/style/useNamingConvention: this is how the underlying library wants it
|
||||
drop_console: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
sideEffects: true,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(jpe?g|png|gif)$/i,
|
||||
type: "asset/resource",
|
||||
},
|
||||
{
|
||||
test: /\.m?js$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
presets: ["@babel/preset-env"],
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
enforce: "pre",
|
||||
use: ["source-map-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: "ts-loader",
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: require.resolve("jquery"),
|
||||
loader: "expose-loader",
|
||||
options: {
|
||||
exposes: [
|
||||
{
|
||||
globalName: ["$"],
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
globalName: ["jQuery"],
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
globalName: ["window.jQuery"],
|
||||
override: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user