diff --git a/.eslintrc.js b/.eslintrc.js index 4c2043012828bd16438eb4f36472ad48460eb6e4..3bdc1f231b3a75f10ae3ba0687e8c649d7218082 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,7 +9,6 @@ module.exports = { "vue/multi-word-component-names": "off", }, env: { - browser: true, - node: true, + es2021: true, }, }; diff --git a/aleksis/core/management/commands/webpack_bundle.py b/aleksis/core/management/commands/vite_bundle.py similarity index 51% rename from aleksis/core/management/commands/webpack_bundle.py rename to aleksis/core/management/commands/vite_bundle.py index ee38566a8cda8587c41aacf036eca1d7da16cd04..07beab7eac7db25c9c4c6f8f7156d7b3180edd40 100644 --- a/aleksis/core/management/commands/webpack_bundle.py +++ b/aleksis/core/management/commands/vite_bundle.py @@ -11,25 +11,29 @@ from ...util.frontend_helpers import get_apps_with_assets class Command(BaseYarnCommand): - help = "Create webpack bundles for AlekSIS" # noqa + help = "Create Vite bundles for AlekSIS" # noqa def handle(self, *args, **options): super(Command, self).handle(*args, **options) - # Write webpack entrypoints for all apps - assets = { - app: {"dependOn": "core", "import": os.path.join(path, "index")} + # Inject settings into Vite + vite_values = { + "static_url": settings.STATIC_URL, + } + # Write rollup entrypoints for all apps + vite_values["entrypoints"] = { + app: os.path.join(path, "index") for app, path in get_apps_with_assets().items() } - assets["core"] = os.path.join(settings.BASE_DIR, "aleksis", "core", "assets", "index") - with open(os.path.join(settings.NODE_MODULES_ROOT, "webpack-entrypoints.json"), "w") as out: - json.dump(assets, out) + vite_values["entrypoints"]["core"] = os.path.join(settings.BASE_DIR, "aleksis", "core", "assets", "index") + with open(os.path.join(settings.NODE_MODULES_ROOT, "django-vite-values.json"), "w") as out: + json.dump(vite_values, out) # Install Node dependencies yarn_adapter.install(settings.YARN_INSTALLED_APPS) - # Run webpack - config_path = os.path.join(settings.BASE_DIR, "aleksis", "core", "webpack.config.js") + # Run Vite + config_path = os.path.join(settings.BASE_DIR, "aleksis", "core", "vite.config.js") shutil.copy(config_path, settings.NODE_MODULES_ROOT) mode = "development" if settings.DEBUG else "production" - yarn_adapter.call_yarn(["run", "webpack", f"--mode={mode}"]) + yarn_adapter.call_yarn(["run", "vite", "build", "-m", mode, "-l", "info", "-d"]) diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index daafab787c5112d393e5e547e7ba0638149efd64..78bd718a01f76fb4c5e985f6f55ee31a5a2f12ff 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -97,7 +97,7 @@ INSTALLED_APPS = [ "sass_processor", "django_any_js", "django_yarnpkg", - "webpack_loader", + "django_vite", "django_tables2", "maintenance_mode", "menu_generator", @@ -572,22 +572,16 @@ YARN_INSTALLED_APPS = [ "vue-apollo@^3.1.0", "vuetify@^2.6.7", "vue-router@^3.5.2", - "css-loader@^6.7.1", - "sass-loader@^13.0", - "vue-loader@^15.0.0", - "vue-style-loader@^4.1.3", - "vue-template-compiler@^2.7.7", - "webpack@^5.73.0", - "webpack-bundle-tracker@^1.6.0", - "webpack-cli@^4.10.0", + "vite@^4.0.1", + "@vitejs/plugin-vue2@^2.2.0", + "@rollup/plugin-node-resolve@^15.0.1", + "@rollup/plugin-graphql@^2.0.2", "vue-i18n@8", "eslint@^8.26.0", "eslint-plugin-vue@^9.7.0", - "eslint-webpack-plugin@^3.2.0", "eslint-config-prettier@^8.5.0", "stylelint@^14.14.0", "stylelint-config-standard@^29.0.0", - "stylelint-webpack-plugin@^3.3.0", "stylelint-config-prettier@^9.0.3", ] @@ -596,17 +590,12 @@ merge_app_settings("YARN_INSTALLED_APPS", YARN_INSTALLED_APPS, True) JS_URL = _settings.get("js_assets.url", STATIC_URL) JS_ROOT = _settings.get("js_assets.root", os.path.join(NODE_MODULES_ROOT, "node_modules")) -WEBPACK_LOADER = { - "DEFAULT": { - "CACHE": not DEBUG, - "STATS_FILE": os.path.join(NODE_MODULES_ROOT, "webpack-stats.json"), - "BUNDLE_DIR_NAME": "", - "POLL_INTERVAL": 0.1, - "IGNORE": [r".+\.hot-update.js", r".+\.map"], - } -} +DJANGO_VITE_ASSETS_PATH = os.path.join(NODE_MODULES_ROOT, "vite_bundles") +DJANGO_VITE_DEV_MODE = DEBUG +DJANGO_VITE_DEV_SERVER_PORT = 5173 + STATICFILES_DIRS = ( - os.path.join(NODE_MODULES_ROOT, "webpack_bundles"), + DJANGO_VITE_ASSETS_PATH, JS_ROOT, ) diff --git a/aleksis/core/templates/core/vue_base.html b/aleksis/core/templates/core/vue_base.html index 261c3bb81e9fd24b33726afe369b239546e31faf..f741ef151be2ed6523fc8c6ee45a64b919484bf1 100644 --- a/aleksis/core/templates/core/vue_base.html +++ b/aleksis/core/templates/core/vue_base.html @@ -1,7 +1,7 @@ {# -*- engine:django -*- #} {% load i18n menu_generator static sass_tags any_js rules html_helpers %} -{% load render_bundle from webpack_loader %} +{% load django_vite %} {% get_current_language as LANGUAGE_CODE %} {% get_available_languages as LANGUAGES %} @@ -53,6 +53,8 @@ <script type="text/javascript" src="{% url 'config.js' %}"></script> {% include_js "iconify" %} + {% vite_hmr_client %} + {% block extra_head %}{% endblock %} </head> <body {% if no_menu %}class="without-menu"{% endif %}> @@ -222,7 +224,7 @@ {{ request.site.preferences.theme__primary|json_script:"primary-color" }} {{ request.site.preferences.theme__secondary|json_script:"secondary-color" }} <script type="text/javascript" src="{% static 'js/search.js' %}"></script> -{% render_bundle 'core' %} +{% vite_asset 'core' %} {% block extra_body %}{% endblock %} </body> </html> diff --git a/aleksis/core/vite.config.js b/aleksis/core/vite.config.js new file mode 100644 index 0000000000000000000000000000000000000000..b290bb1c7cd4158f89dfc4d1fde598725847c935 --- /dev/null +++ b/aleksis/core/vite.config.js @@ -0,0 +1,22 @@ +const fs = require("fs"); +const path = require("path"); + +import { defineConfig } from 'vite'; +import vue from "@vitejs/plugin-vue2"; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import graphql from '@rollup/plugin-graphql'; + +const django_values = JSON.parse(fs.readFileSync("./django-vite-values.json")); + +export default defineConfig({ + base: django_values.static_url, + build: { + outDir: path.resolve("./vite_bundles/"), + manifest: true, + rollupOptions: { + input: django_values.entrypoints, + plugins: [nodeResolve({modulePaths: [path.resolve("./node_modules")]}), graphql()], + }, + }, + plugins: [vue()], +}); diff --git a/aleksis/core/webpack.config.js b/aleksis/core/webpack.config.js deleted file mode 100644 index 8f327cbe55a6dba6b7ae5f104a89f4da46ddf711..0000000000000000000000000000000000000000 --- a/aleksis/core/webpack.config.js +++ /dev/null @@ -1,97 +0,0 @@ -const fs = require("fs"); -const path = require("path"); -const webpack = require("webpack"); -const BundleTracker = require("webpack-bundle-tracker"); -const { VueLoaderPlugin } = require("vue-loader"); -const ESLintPlugin = require("eslint-webpack-plugin"); -const StyleLintPlugin = require("stylelint-webpack-plugin"); - -module.exports = { - context: __dirname, - entry: JSON.parse(fs.readFileSync("./webpack-entrypoints.json")), - output: { - path: path.resolve("./webpack_bundles/"), - filename: "[name]-[hash].js", - chunkFilename: "[id]-[chunkhash].js", - }, - plugins: [ - new BundleTracker({ filename: "./webpack-stats.json" }), - new VueLoaderPlugin(), - new ESLintPlugin({ - extensions: ["js", "vue"], - }), - new StyleLintPlugin({ - files: ["assets/**/*.{vue,htm,html,css,sss,less,scss,sass}"], - }), - ], - module: { - rules: [ - { - test: /\.vue$/, - use: { - loader: "vue-loader", - options: { - transpileOptions: { - transforms: { - dangerousTaggedTemplateString: true, - }, - }, - }, - }, - }, - { - test: /\.(css)$/, - use: ["vue-style-loader", "css-loader"], - }, - { - test: /\.scss$/, - use: [ - "vue-style-loader", - "css-loader", - { - loader: "sass-loader", - options: { - sassOptions: { - indentedSyntax: false, - }, - }, - }, - ], - }, - { - test: /\.(graphql|gql)$/, - exclude: /node_modules/, - loader: "graphql-tag/loader", - }, - ], - }, - optimization: { - runtimeChunk: "single", - splitChunks: { - chunks: "all", - maxInitialRequests: Infinity, - minSize: 0, - cacheGroups: { - vendor: { - test: /[\\/]node_modules[\\/]/, - name(module) { - // get the name. E.g. node_modules/packageName/not/this/part.js - // or node_modules/packageName - const packageName = module.context.match( - /[\\/]node_modules[\\/](.*?)([\\/]|$)/ - )[1]; - - // npm package names are URL-safe, but some servers don't like @ symbols - return `npm.${packageName.replace("@", "")}`; - }, - }, - }, - }, - }, - resolve: { - modules: [path.resolve("./node_modules")], - alias: { - vue$: "vue/dist/vue.esm.js", - }, - }, -}; diff --git a/pyproject.toml b/pyproject.toml index c9a5f25b70fabae18809434113aeed5cdef4a1d7..0ab9b866ddfe0cf79255cb41de68747973452c0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,8 +128,8 @@ django-ical = "^1.8.3" django-iconify = "^0.3" customidenticon = "^0.1.5" graphene-django = "^3.0.0" -django-webpack-loader = "^1.6.0" selenium = "^4.4.3" +django-vite = "^2.0.2" [tool.poetry.extras] ldap = ["django-auth-ldap"]