Skip to content

Commit cfd77df

Browse files
committed
build: added ESLint configuration
1 parent 02f18a1 commit cfd77df

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1809
-1371
lines changed

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ It is built using HTML5, CSS, TypeScript, and PHP and supports various databases
6363

6464
## Coding Standards
6565

66-
- Use PSR-12 coding standards for PHP code.
66+
- Use PER Coding Style 3.0 for PHP code.
6767
- Use TypeScript coding standards for TypeScript code.
6868
- Use HTML5 and CSS3 standards for frontend code.
6969
- Use semicolons at the end of each statement.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This is a log of major user-visible changes in each phpMyFAQ release.
2525
- added support for .env files (Thorsten)
2626
- added support for OpenSearch (Thorsten)
2727
- added support for Mago (Thorsten)
28+
- added ESLint configuration (Thorsten)
2829
- added experimental support for FrankenPHP (Thorsten)
2930
- added experimental support for LDAP group support (Thorsten)
3031
- added experimental MCP Server (Thorsten)

docs/development.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ To run the Jest-based tests, you can use the following command:
300300

301301
The following coding standards are used in phpMyFAQ:
302302

303-
- PHP: [PER Coding Style 2.0](https://www.php-fig.org/per/coding-style/)
303+
- PHP: [PER Coding Style 3.0](https://www.php-fig.org/per/coding-style/)
304+
- TypeScript with ESLint recommendations
304305

305306
### 8.5.8 Rebase your Patch
306307

eslint.config.mjs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @ts-check
2+
import eslint from '@eslint/js';
3+
import { defineConfig, globalIgnores } from 'eslint/config';
4+
import tseslint from 'typescript-eslint';
5+
6+
const ignoresConfig = globalIgnores([
7+
'babel.config.cjs',
8+
'commitlint.config.cjs',
9+
'coverage/*',
10+
'html-coverage/*',
11+
'node_modules/*',
12+
'phpmyfaq/assets/public/*',
13+
'phpmyfaq/src/libs/*',
14+
'volumes/*',
15+
]);
16+
17+
export default defineConfig([
18+
{
19+
extends: [ignoresConfig, eslint.configs.recommended, tseslint.configs.recommended],
20+
},
21+
]);

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"build": "vite build",
2323
"build:watch": "vite build --watch",
2424
"build:prod": "vite build",
25+
"eslint": "eslint .",
2526
"lint": "prettier --check .",
2627
"lint:fix": "prettier --write .",
2728
"prepare": "husky",
@@ -51,6 +52,7 @@
5152
"@babel/preset-env": "^7.28.5",
5253
"@commitlint/cli": "^20.2.0",
5354
"@commitlint/config-conventional": "^20.2.0",
55+
"@eslint/js": "^9.39.2",
5456
"@types/bootstrap": "^5.2.10",
5557
"@types/highlightjs": "^9.12.6",
5658
"@types/jsdom": "^27.0.0",
@@ -60,6 +62,7 @@
6062
"@vitest/coverage-istanbul": "4.0.16",
6163
"@vitest/coverage-v8": "4.0.16",
6264
"autoprefixer": "^10.4.23",
65+
"eslint": "^9.39.2",
6366
"husky": "^9.1.7",
6467
"jsdom": "^27.3.0",
6568
"postcss": "^8.5.6",
@@ -68,6 +71,7 @@
6871
"sass": "^1.97.1",
6972
"sigmund": "^1.0.1",
7073
"typescript": "^5.9.3",
74+
"typescript-eslint": "^8.50.0",
7175
"vite": "^7.3.0",
7276
"vite-plugin-compression": "^0.5.1",
7377
"vite-plugin-html": "^3.2.2",

phpmyfaq/admin/assets/js/configuration.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
* @since 2022-02-19
1818
*/
1919

20+
/* global window, document, fetch, performance */
21+
2022
/**
2123
* Generates a UUID Version 4 compatible universally unique identifier.
2224
* @returns {string} The generated UUID.
@@ -29,7 +31,7 @@ const generateUUID = () => {
2931
}
3032

3133
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
32-
const random = (date + Math.random() * 16) % 16 | 0;
34+
const random = ((date + Math.random() * 16) % 16) | 0;
3335
date = Math.floor(date / 16);
3436
return (char === 'x' ? random : (random & 0x3) | 0x8).toString(16);
3537
});
@@ -38,7 +40,7 @@ const generateUUID = () => {
3840
/**
3941
* Sends a test email to the admin.
4042
*/
41-
const handleSendTestMail = async () => {
43+
export const handleSendTestMail = async () => {
4244
const button = document.getElementById('btn-phpmyfaq-mail-sendTestEmail');
4345
if (button) {
4446
const csrf = document.querySelector('#pmf-csrf-token').value;
@@ -54,7 +56,9 @@ const handleSendTestMail = async () => {
5456
});
5557

5658
if (!response.ok) {
57-
throw new Error('Network response was not ok');
59+
const result = await response.json();
60+
displayResult(button, '👎 ' + (result.error || 'Network response was not ok'));
61+
return;
5862
}
5963

6064
const result = await response.json();
@@ -79,7 +83,7 @@ const displayResult = (button, message) => {
7983
/**
8084
* Generates an API token if the input field is empty.
8185
*/
82-
const generateApiToken = () => {
86+
export const generateApiToken = () => {
8387
const buttonGenerateApiToken = document.getElementById('pmf-generate-api-token');
8488
const inputConfigurationApiToken = document.getElementById('edit[api.apiClientToken]');
8589

phpmyfaq/admin/assets/src/api/attachment.ts

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,37 @@
1616
import { Response } from '../interfaces';
1717

1818
export const deleteAttachments = async (attachmentId: string, csrfToken: string): Promise<Response> => {
19-
try {
20-
const response = await fetch('./api/content/attachments', {
21-
method: 'DELETE',
22-
headers: {
23-
Accept: 'application/json, text/plain, */*',
24-
'Content-Type': 'application/json',
25-
},
26-
body: JSON.stringify({ attId: attachmentId, csrf: csrfToken }),
27-
});
19+
const response = await fetch('./api/content/attachments', {
20+
method: 'DELETE',
21+
headers: {
22+
Accept: 'application/json, text/plain, */*',
23+
'Content-Type': 'application/json',
24+
},
25+
body: JSON.stringify({ attId: attachmentId, csrf: csrfToken }),
26+
});
2827

29-
return await response.json();
30-
} catch (error) {
31-
throw error;
32-
}
28+
return await response.json();
3329
};
3430

3531
export const refreshAttachments = async (attachmentId: string, csrfToken: string): Promise<Response> => {
36-
try {
37-
const response = await fetch('./api/content/attachments/refresh', {
38-
method: 'POST',
39-
headers: {
40-
Accept: 'application/json, text/plain, */*',
41-
'Content-Type': 'application/json',
42-
},
43-
body: JSON.stringify({ attId: attachmentId, csrf: csrfToken }),
44-
});
32+
const response = await fetch('./api/content/attachments/refresh', {
33+
method: 'POST',
34+
headers: {
35+
Accept: 'application/json, text/plain, */*',
36+
'Content-Type': 'application/json',
37+
},
38+
body: JSON.stringify({ attId: attachmentId, csrf: csrfToken }),
39+
});
4540

46-
return await response.json();
47-
} catch (error) {
48-
throw error;
49-
}
41+
return await response.json();
5042
};
5143

5244
export const uploadAttachments = async (formData: FormData): Promise<Response> => {
53-
try {
54-
const response = await fetch('./api/content/attachments/upload', {
55-
method: 'POST',
56-
cache: 'no-cache',
57-
body: formData,
58-
});
45+
const response = await fetch('./api/content/attachments/upload', {
46+
method: 'POST',
47+
cache: 'no-cache',
48+
body: formData,
49+
});
5950

60-
return await response.json();
61-
} catch (error) {
62-
throw error;
63-
}
51+
return await response.json();
6452
};

phpmyfaq/admin/assets/src/api/category.ts

Lines changed: 41 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,73 +15,61 @@
1515
import { CategoryTranslations, Response } from '../interfaces';
1616

1717
export const fetchCategoryTranslations = async (categoryId: string): Promise<CategoryTranslations> => {
18-
try {
19-
const response = await fetch(`./api/category/translations/${categoryId}`, {
20-
method: 'GET',
21-
cache: 'no-cache',
22-
headers: {
23-
'Content-Type': 'application/json',
24-
},
25-
redirect: 'follow',
26-
referrerPolicy: 'no-referrer',
27-
});
18+
const response = await fetch(`./api/category/translations/${categoryId}`, {
19+
method: 'GET',
20+
cache: 'no-cache',
21+
headers: {
22+
'Content-Type': 'application/json',
23+
},
24+
redirect: 'follow',
25+
referrerPolicy: 'no-referrer',
26+
});
2827

29-
return await response.json();
30-
} catch (error) {
31-
throw error;
32-
}
28+
return await response.json();
3329
};
3430

3531
export const deleteCategory = async (
3632
categoryId: string,
3733
language: string,
3834
csrfToken: string
3935
): Promise<Response | undefined> => {
40-
try {
41-
const response = await fetch(`./api/category/delete`, {
42-
method: 'DELETE',
43-
cache: 'no-cache',
44-
headers: {
45-
'Content-Type': 'application/json',
46-
},
47-
body: JSON.stringify({
48-
categoryId: categoryId,
49-
language: language,
50-
csrfToken: csrfToken,
51-
}),
52-
redirect: 'follow',
53-
referrerPolicy: 'no-referrer',
54-
});
36+
const response = await fetch(`./api/category/delete`, {
37+
method: 'DELETE',
38+
cache: 'no-cache',
39+
headers: {
40+
'Content-Type': 'application/json',
41+
},
42+
body: JSON.stringify({
43+
categoryId: categoryId,
44+
language: language,
45+
csrfToken: csrfToken,
46+
}),
47+
redirect: 'follow',
48+
referrerPolicy: 'no-referrer',
49+
});
5550

56-
return await response.json();
57-
} catch (error) {
58-
throw error;
59-
}
51+
return await response.json();
6052
};
6153

6254
export const setCategoryTree = async (
63-
categoryTree: any,
55+
categoryTree: unknown,
6456
categoryId: string,
6557
csrfToken: string
6658
): Promise<Response | undefined> => {
67-
try {
68-
const response = await fetch('./api/category/update-order', {
69-
method: 'POST',
70-
cache: 'no-cache',
71-
headers: {
72-
'Content-Type': 'application/json',
73-
},
74-
body: JSON.stringify({
75-
categoryTree: categoryTree,
76-
categoryId: categoryId,
77-
csrfToken: csrfToken,
78-
}),
79-
redirect: 'follow',
80-
referrerPolicy: 'no-referrer',
81-
});
59+
const response = await fetch('./api/category/update-order', {
60+
method: 'POST',
61+
cache: 'no-cache',
62+
headers: {
63+
'Content-Type': 'application/json',
64+
},
65+
body: JSON.stringify({
66+
categoryTree: categoryTree,
67+
categoryId: categoryId,
68+
csrfToken: csrfToken,
69+
}),
70+
redirect: 'follow',
71+
referrerPolicy: 'no-referrer',
72+
});
8273

83-
return await response.json();
84-
} catch (error) {
85-
throw error;
86-
}
74+
return await response.json();
8775
};

0 commit comments

Comments
 (0)