Skip to content

Commit 6ebadca

Browse files
filipchristiansenix-56h
authored andcommitted
chore(pre-commit) / refactor(templates): add ESLint pre-commit hook + refactor result.jinja (coderamp-labs#379)
* chore(pre-commit) / refactor(templates): add ESLint hook + refactor `result.jinja` * Add `eslint` to `pre-commit` hooks * `getFileName` + `toggleFile` moved to `utils.js` * Run linter
1 parent 3a4d014 commit 6ebadca

File tree

9 files changed

+244
-109
lines changed

9 files changed

+244
-109
lines changed

.pre-commit-config.yaml

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,82 @@ repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
33
rev: v5.0.0
44
hooks:
5-
# Files
65
- id: check-added-large-files
7-
description: "Prevent large files from being committed."
8-
args: ["--maxkb=10000"]
6+
description: 'Prevent large files from being committed.'
7+
args: ['--maxkb=10000']
8+
99
- id: check-case-conflict
10-
description: "Check for files that would conflict in case-insensitive filesystems."
10+
description: 'Check for files that would conflict in case-insensitive filesystems.'
11+
1112
- id: fix-byte-order-marker
12-
description: "Remove utf-8 byte order marker."
13+
description: 'Remove utf-8 byte order marker.'
14+
1315
- id: mixed-line-ending
14-
description: "Replace mixed line ending."
16+
description: 'Replace mixed line ending.'
1517

16-
# Links
1718
- id: destroyed-symlinks
18-
description: "Detect symlinks which are changed to regular files with a content of a path which that symlink was pointing to."
19+
description: 'Detect symlinks which are changed to regular files with a content of a path which that symlink was pointing to.'
1920

20-
# File files for parseable syntax: python
2121
- id: check-ast
22+
description: 'Check for parseable syntax.'
2223

23-
# File and line endings
2424
- id: end-of-file-fixer
25-
description: "Ensure that a file is either empty, or ends with one newline."
25+
description: 'Ensure that a file is either empty, or ends with one newline.'
26+
2627
- id: trailing-whitespace
27-
description: "Trim trailing whitespace."
28+
description: 'Trim trailing whitespace.'
2829

29-
# Python
3030
- id: check-docstring-first
31-
description: "Check a common error of defining a docstring after code."
31+
description: 'Check a common error of defining a docstring after code.'
32+
3233
- id: requirements-txt-fixer
33-
description: "Sort entries in requirements.txt."
34+
description: 'Sort entries in requirements.txt.'
3435

3536
- repo: https://github.com/MarcoGorelli/absolufy-imports
3637
rev: v0.3.1
3738
hooks:
3839
- id: absolufy-imports
39-
description: "Automatically convert relative imports to absolute. (Use `args: [--never]` to revert.)"
40+
description: 'Automatically convert relative imports to absolute. (Use `args: [--never]` to revert.)'
4041

4142
- repo: https://github.com/asottile/pyupgrade
4243
rev: v3.20.0
4344
hooks:
4445
- id: pyupgrade
45-
description: "Automatically upgrade syntax for newer versions."
46+
description: 'Automatically upgrade syntax for newer versions.'
4647
args: [--py3-plus, --py36-plus]
4748

4849
- repo: https://github.com/pre-commit/pygrep-hooks
4950
rev: v1.10.0
5051
hooks:
5152
- id: python-check-blanket-noqa
52-
description: "Enforce that `noqa` annotations always occur with specific codes. Sample annotations: `# noqa: F401`, `# noqa: F401,W203`."
53+
description: 'Enforce that `# noqa` annotations always occur with specific codes.'
54+
5355
- id: python-check-blanket-type-ignore
54-
description: "Enforce that `# type: ignore` annotations always occur with specific codes. Sample annotations: `# type: ignore[attr-defined]`, `# type: ignore[attr-defined, name-defined]`."
56+
description: 'Enforce that `# type: ignore` annotations always occur with specific codes.'
57+
5558
- id: python-use-type-annotations
56-
description: "Enforce that python3.6+ type annotations are used instead of type comments."
59+
description: 'Enforce that python3.6+ type annotations are used instead of type comments.'
5760

5861
- repo: https://github.com/PyCQA/isort
5962
rev: 6.0.1
6063
hooks:
6164
- id: isort
62-
description: "Sort imports alphabetically, and automatically separated into sections and by type."
65+
description: 'Sort imports alphabetically, and automatically separated into sections and by type.'
6366

67+
- repo: https://github.com/pre-commit/mirrors-eslint
68+
rev: v9.30.1
69+
hooks:
70+
- id: eslint
71+
description: 'Lint javascript files.'
72+
files: \.js$
73+
args: [--max-warnings=0, --fix]
74+
additional_dependencies:
75+
[
76+
'eslint@9.30.1',
77+
'@eslint/js@9.30.1',
78+
'eslint-plugin-import@2.32.0',
79+
'globals@16.3.0',
80+
]
6481

6582
- repo: https://github.com/djlint/djLint
6683
rev: v1.36.4
@@ -71,11 +88,11 @@ repos:
7188
rev: v0.45.0
7289
hooks:
7390
- id: markdownlint
74-
description: "Lint markdown files."
75-
args: ["--disable=line-length"]
91+
description: 'Lint markdown files.'
92+
args: ['--disable=line-length']
7693

7794
- repo: https://github.com/astral-sh/ruff-pre-commit
78-
rev: v0.12.1
95+
rev: v0.12.2
7996
hooks:
8097
- id: ruff-check
8198
- id: ruff-format
@@ -97,17 +114,19 @@ repos:
97114
additional_dependencies:
98115
[
99116
click>=8.0.0,
100-
"fastapi[standard]>=0.109.1",
117+
'fastapi[standard]>=0.109.1',
118+
httpx,
119+
pathspec>=0.12.1,
101120
pydantic,
102121
pytest-asyncio,
103122
pytest-mock,
104123
python-dotenv,
105124
slowapi,
106125
starlette>=0.40.0,
107-
tiktoken,
108-
pathspec,
126+
tiktoken>=0.7.0,
109127
uvicorn>=0.11.7,
110128
]
129+
111130
- id: pylint
112131
name: pylint for tests
113132
files: ^tests/
@@ -116,15 +135,16 @@ repos:
116135
additional_dependencies:
117136
[
118137
click>=8.0.0,
119-
"fastapi[standard]>=0.109.1",
138+
'fastapi[standard]>=0.109.1',
139+
httpx,
140+
pathspec>=0.12.1,
120141
pydantic,
121142
pytest-asyncio,
122143
pytest-mock,
123144
python-dotenv,
124145
slowapi,
125146
starlette>=0.40.0,
126-
tiktoken,
127-
pathspec,
147+
tiktoken>=0.7.0,
128148
uvicorn>=0.11.7,
129149
]
130150

eslint.config.cjs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const js = require('@eslint/js');
2+
const globals = require('globals');
3+
const importPlugin = require('eslint-plugin-import');
4+
5+
module.exports = [
6+
js.configs.recommended,
7+
8+
{
9+
files: ['src/static/js/**/*.js'],
10+
11+
languageOptions: {
12+
parserOptions: { ecmaVersion: 2021, sourceType: 'module' },
13+
globals: {
14+
...globals.browser,
15+
changePattern: 'readonly',
16+
copyFullDigest: 'readonly',
17+
copyText: 'readonly',
18+
downloadFullDigest: 'readonly',
19+
handleSubmit: 'readonly',
20+
posthog: 'readonly',
21+
submitExample: 'readonly',
22+
toggleAccessSettings: 'readonly',
23+
toggleFile: 'readonly',
24+
},
25+
},
26+
27+
plugins: { import: importPlugin },
28+
29+
rules: {
30+
// Import hygiene (eslint-plugin-import)
31+
'import/no-extraneous-dependencies': 'error',
32+
'import/no-unresolved': 'error',
33+
'import/order': ['warn', { alphabetize: { order: 'asc' } }],
34+
35+
// Safety & bug-catchers
36+
'consistent-return': 'error',
37+
'default-case': 'error',
38+
'no-implicit-globals': 'error',
39+
'no-shadow': 'error',
40+
41+
// Maintainability / complexity
42+
complexity: ['warn', 10],
43+
'max-depth': ['warn', 4],
44+
'max-lines': ['warn', 500],
45+
'max-params': ['warn', 5],
46+
47+
// Stylistic consistency (auto-fixable)
48+
'arrow-parens': ['error', 'always'],
49+
curly: ['error', 'all'],
50+
indent: ['error', 4, { SwitchCase: 2 }],
51+
'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 2 }],
52+
'no-multi-spaces': 'error',
53+
'object-shorthand': ['error', 'always'],
54+
'padding-line-between-statements': [
55+
'warn',
56+
{ blankLine: 'always', prev: '*', next: 'return' },
57+
{ blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' },
58+
{ blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] },
59+
],
60+
'quote-props': ['error', 'consistent-as-needed'],
61+
quotes: ['error', 'single', { avoidEscape: true }],
62+
semi: 'error',
63+
64+
// Modern / performance tips
65+
'arrow-body-style': ['warn', 'as-needed'],
66+
'prefer-arrow-callback': 'error',
67+
'prefer-exponentiation-operator': 'error',
68+
'prefer-numeric-literals': 'error',
69+
'prefer-object-has-own': 'warn',
70+
'prefer-object-spread': 'error',
71+
'prefer-template': 'error',
72+
},
73+
},
74+
];
Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
<style type="text/tailwindcss">
2-
@layer components {
3-
.badge-new { @apply inline-block -rotate-6 -translate-y-1 mx-1 px-1 bg-[#FE4A60] border border-gray-900 text-white text-[10px] font-bold shadow-[2px_2px_0_0_rgba(0,0,0,1)]; }
4-
.landing-page-title { @apply inline-block w-full relative text-center text-4xl sm:text-5xl md:text-6xl lg:text-7xl sm:pt-20 lg:pt-5 font-bold tracking-tighter; }
5-
.intro-text { @apply text-center text-gray-600 text-lg max-w-2xl mx-auto; }
6-
.sparkle-red { @apply absolute flex-shrink-0 h-auto w-14 sm:w-20 md:w-24 p-2 left-0 lg:ml-32 -translate-x-2 md:translate-x-10 lg:-translate-x-full -translate-y-4 sm:-translate-y-8 md:-translate-y-0 lg:-translate-y-10; }
7-
.sparkle-green { @apply absolute flex-shrink-0 right-0 bottom-0 w-10 sm:w-16 lg:w-20 -translate-x-10 lg:-translate-x-12 translate-y-4 sm:translate-y-10 md:translate-y-2 lg:translate-y-4; }
8-
.pattern-select { @apply min-w-max appearance-none pr-6 pl-2 py-2 bg-[#e6e8eb] border-r-[3px] border-gray-900 cursor-pointer focus:outline-none; }
2+
@layer components {
3+
.badge-new {
4+
@apply inline-block -rotate-6 -translate-y-1 mx-1 px-1 bg-[#FE4A60] border border-gray-900 text-white text-[10px] font-bold shadow-[2px_2px_0_0_rgba(0,0,0,1)];
95
}
6+
.landing-page-title {
7+
@apply inline-block w-full relative text-center text-4xl sm:text-5xl md:text-6xl lg:text-7xl sm:pt-20 lg:pt-5 font-bold tracking-tighter;
8+
}
9+
.intro-text {
10+
@apply text-center text-gray-600 text-lg max-w-2xl mx-auto;
11+
}
12+
.sparkle-red {
13+
@apply absolute flex-shrink-0 h-auto w-14 sm:w-20 md:w-24 p-2 left-0 lg:ml-32 -translate-x-2 md:translate-x-10 lg:-translate-x-full -translate-y-4 sm:-translate-y-8 md:-translate-y-0 lg:-translate-y-10;
14+
}
15+
.sparkle-green {
16+
@apply absolute flex-shrink-0 right-0 bottom-0 w-10 sm:w-16 lg:w-20 -translate-x-10 lg:-translate-x-12 translate-y-4 sm:translate-y-10 md:translate-y-2 lg:translate-y-4;
17+
}
18+
.pattern-select {
19+
@apply min-w-max appearance-none pr-6 pl-2 py-2 bg-[#e6e8eb] border-r-[3px] border-gray-900 cursor-pointer focus:outline-none;
20+
}
21+
}
1022

11-
@layer utilities {
12-
.no-drag { @apply pointer-events-none select-none; -webkit-user-drag: none; }
13-
.link-bounce { @apply transition-transform hover:-translate-y-0.5; }
23+
@layer utilities {
24+
.no-drag {
25+
@apply pointer-events-none select-none;
26+
-webkit-user-drag: none;
27+
}
28+
.link-bounce {
29+
@apply transition-transform hover:-translate-y-0.5;
1430
}
31+
}
1532
</style>

src/static/js/git.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
function waitForStars() {
22
return new Promise((resolve) => {
33
const check = () => {
4-
const stars = document.getElementById("github-stars");
5-
if (stars && stars.textContent !== "0") resolve();
6-
else setTimeout(check, 10);
4+
const stars = document.getElementById('github-stars');
5+
6+
if (stars && stars.textContent !== '0') {resolve();}
7+
else {setTimeout(check, 10);}
78
};
9+
810
check();
911
});
1012
}
1113

12-
document.addEventListener("DOMContentLoaded", () => {
13-
const urlInput = document.getElementById("input_text");
14-
const form = document.getElementById("ingestForm");
14+
document.addEventListener('DOMContentLoaded', () => {
15+
const urlInput = document.getElementById('input_text');
16+
const form = document.getElementById('ingestForm');
1517

1618
if (urlInput && urlInput.value.trim() && form) {
17-
// Wait for stars to be loaded before submitting
19+
// Wait for stars to be loaded before submitting
1820
waitForStars().then(() => {
19-
const submitEvent = new SubmitEvent("submit", {
21+
const submitEvent = new SubmitEvent('submit', {
2022
cancelable: true,
2123
bubbles: true
2224
});
23-
Object.defineProperty(submitEvent, "target", {
25+
26+
Object.defineProperty(submitEvent, 'target', {
2427
value: form,
2528
enumerable: true
2629
});

src/static/js/git_form.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@ function changePattern() {
1414

1515
// Show/hide the Personal-Access-Token section when the "Private repository" checkbox is toggled.
1616
function toggleAccessSettings() {
17-
const container = document.getElementById("accessSettingsContainer");
18-
const examples = document.getElementById("exampleRepositories");
19-
const show = document.getElementById('showAccessSettings')?.checked;
20-
container?.classList.toggle("hidden", !show);
21-
examples?.classList.toggle("lg:mt-0", show);
17+
const container = document.getElementById('accessSettingsContainer');
18+
const examples = document.getElementById('exampleRepositories');
19+
const show = document.getElementById('showAccessSettings')?.checked;
20+
21+
container?.classList.toggle('hidden', !show);
22+
examples?.classList.toggle('lg:mt-0', show);
2223
}
2324

2425

2526

2627
document.addEventListener("DOMContentLoaded", () => {
2728
toggleAccessSettings();
2829
changePattern();
29-
});
30+
});
3031

3132

3233
// Make them available to existing inline attributes

src/static/js/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
function submitExample(repoName) {
2-
const input = document.getElementById("input_text");
2+
const input = document.getElementById('input_text');
3+
34
if (input) {
45
input.value = repoName;
56
input.focus();

src/static/js/navbar.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
// Fetch GitHub stars
22
function formatStarCount(count) {
3-
if (count >= 1000) return (count / 1000).toFixed(1) + 'k';
3+
if (count >= 1000) {return `${ (count / 1000).toFixed(1) }k`;}
4+
45
return count.toString();
5-
}
6+
}
67

7-
async function fetchGitHubStars() {
8+
async function fetchGitHubStars() {
89
try {
9-
const res = await fetch('https://api.github.com/repos/cyclotruc/gitingest');
10-
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
11-
const data = await res.json();
12-
document.getElementById('github-stars').textContent =
10+
const res = await fetch('https://api.github.com/repos/cyclotruc/gitingest');
11+
12+
if (!res.ok) {throw new Error(`${res.status} ${res.statusText}`);}
13+
const data = await res.json();
14+
15+
document.getElementById('github-stars').textContent =
1316
formatStarCount(data.stargazers_count);
1417
} catch (err) {
15-
console.error('Error fetching GitHub stars:', err);
16-
const el = document.getElementById('github-stars').parentElement;
17-
if (el) el.style.display = 'none';
18+
console.error('Error fetching GitHub stars:', err);
19+
const el = document.getElementById('github-stars').parentElement;
20+
21+
if (el) {el.style.display = 'none';}
1822
}
19-
}
23+
}
2024

21-
// auto-run when script loads
22-
fetchGitHubStars();
25+
// auto-run when script loads
26+
fetchGitHubStars();

0 commit comments

Comments
 (0)