diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..d2cd3d3
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,56 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "jquery": true
+ },
+ "extends": [
+ "eslint:recommended"
+ ],
+ "ignorePatterns": ["dist/", "node_modules/"],
+ "rules": {
+ "block-scoped-var": "error",
+ "consistent-return": "error",
+ "curly": "error",
+ "default-case": "error",
+ "default-param-last": ["error"],
+ "dot-notation": "error",
+ "eqeqeq": "error",
+ "guard-for-in": "error",
+ "max-classes-per-file": "error",
+ "no-alert": "error",
+ "no-caller": "error",
+ "no-else-return": "error",
+ "no-empty-function": "error",
+ "no-floating-decimal": "error",
+ "no-implicit-coercion": "error",
+ "no-implicit-globals": "error",
+ "no-multi-spaces": "error",
+ "no-multi-str": "error",
+ "no-param-reassign": "error",
+ "no-return-assign": "error",
+ "no-return-await": "error",
+ "no-self-compare": "error",
+ "no-throw-literal": "error",
+ "no-useless-concat": "error",
+ "radix": ["error", "as-needed"],
+ "require-await": "error",
+ "yoda": "error",
+ "no-shadow": "off",
+ "prefer-destructuring": ["error", { "array": false, "object": true }],
+ "padding-line-between-statements": [
+ "error",
+ { "blankLine": "always", "prev": "import", "next": "export" },
+ { "blankLine": "always", "prev": "export", "next": "export" },
+ { "blankLine": "always", "prev": "*", "next": "return" }
+ ]
+ },
+ "parserOptions": {
+ "ecmaVersion": 6,
+ "sourceType": "script"
+ },
+ "globals": {
+ "bootstrap": false,
+ "moment": false
+ }
+}
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..d6245f5
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "tabWidth": 4,
+ "printWidth": 120,
+ "endOfLine": "auto"
+}
diff --git a/pyproject.toml b/pyproject.toml
index 155b59a..f525a3c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -46,3 +46,33 @@ python_files = [
"test_*.py",
"tests.py",
]
+
+
+###############################################################################
+# flake8 / flakeheaven
+###############################################################################
+[tool.flakeheaven]
+max_complexity = 10
+format = "grouped"
+
+# Base rules
+#############################
+[tool.flakeheaven.plugins]
+"*" = [
+ "+*",
+ "-E501", # long lines
+ "-E203", # conflict with black on PEP8 interpretation
+ "-W503", # deprecated rule: https://www.flake8rules.com/rules/W503.html
+]
+flake8-docstrings = [
+ "+*",
+ "-D1??", # missing docstring
+]
+
+# Exceptions
+#############################
+[tool.flakeheaven.exceptions."**/tests/*"]
+flake8-bandit = [
+ "+*",
+ "-S101", # Use of assert detected.
+]
diff --git a/src/redirect/admin.py b/src/redirect/admin.py
index af4afa5..dfc9a0e 100644
--- a/src/redirect/admin.py
+++ b/src/redirect/admin.py
@@ -42,7 +42,7 @@ class RedirectAdmin(admin.ModelAdmin):
def link(self, instance: Redirect) -> str:
url = instance.get_absolute_url()
url = self.request.build_absolute_uri(url)
- return mark_safe(f'link')
+ return mark_safe(f'link') # noqa: S308, S703
admin.site.register(RedirectUser, UserAdmin)
diff --git a/src/redirect/backends.py b/src/redirect/backends.py
index 5a51ca2..eae643d 100644
--- a/src/redirect/backends.py
+++ b/src/redirect/backends.py
@@ -1,4 +1,4 @@
-from django.contrib.auth.backends import BaseBackend, ModelBackend
+from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import AbstractUser
from redirect.models import Redirect
diff --git a/src/shortener/urls.py b/src/shortener/urls.py
index c2dae6c..0512274 100644
--- a/src/shortener/urls.py
+++ b/src/shortener/urls.py
@@ -1,4 +1,4 @@
-"""shortener URL Configuration
+"""shortener URL Configuration.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/