Skip to content

Commit 3d136f8

Browse files
committed
Standardized lint rules
1 parent 7ef9417 commit 3d136f8

File tree

68 files changed

+399
-694
lines changed

Some content is hidden

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

68 files changed

+399
-694
lines changed

.prettierrc

Lines changed: 0 additions & 13 deletions
This file was deleted.

eslint.config.js

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import unusedImportsPlugin from "eslint-plugin-unused-imports";
55

66
export default [
77
{
8-
ignores: ["node_modules/**", "dist/**", "**/*.js", "**/*.d.ts", "tools/**", "coverage/**", "layer/**"]
8+
ignores: ["node_modules/", "dist/", "build/", "coverage/", "layer/", "*.config.js"]
99
},
1010
{
1111
files: ["src/**/*.ts"],
@@ -22,37 +22,63 @@ export default [
2222
"unused-imports": unusedImportsPlugin
2323
},
2424
rules: {
25-
// Unused imports and variables (auto-fixable)
25+
// --- Code quality ---
26+
"prefer-const": "error",
27+
"@typescript-eslint/no-explicit-any": "off",
28+
"@typescript-eslint/no-unused-vars": ["warn", {
29+
args: "all",
30+
argsIgnorePattern: "^_",
31+
varsIgnorePattern: "^_"
32+
}],
2633
"unused-imports/no-unused-imports": "error",
27-
"unused-imports/no-unused-vars": [
28-
"error",
29-
{
30-
vars: "all",
31-
varsIgnorePattern: "^_|^start$|^result$|^app$|^interfaces$",
32-
args: "after-used",
33-
argsIgnorePattern: "^_|^req$|^res$|^au$|^ex$|^e$|^bind$|^next$|^report$|^config$",
34-
caughtErrors: "all",
35-
caughtErrorsIgnorePattern: "^_"
36-
}
37-
],
3834

39-
// TypeScript-specific rules (matching MembershipApi)
40-
"@typescript-eslint/no-unused-vars": "off", // Turn off in favor of unused-imports plugin
41-
"@typescript-eslint/no-explicit-any": "off",
42-
"@typescript-eslint/explicit-function-return-type": "off",
43-
"@typescript-eslint/no-require-imports": "error",
44-
"@typescript-eslint/no-inferrable-types": "off",
35+
// --- Formatting (ESLint is the sole formatter — no Prettier) ---
36+
"no-trailing-spaces": "error",
37+
"eol-last": ["error", "always"],
38+
"quotes": ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
39+
"semi": ["error", "always"],
40+
"comma-dangle": ["error", "never"],
41+
"indent": ["warn", 2, { SwitchCase: 1 }],
42+
"comma-spacing": ["error", { before: false, after: true }],
43+
"key-spacing": ["error", { beforeColon: false, afterColon: true, mode: "strict" }],
44+
"keyword-spacing": ["error", { before: true, after: true }],
45+
"space-infix-ops": "error",
46+
"no-multi-spaces": ["error", { ignoreEOLComments: true }],
47+
"block-spacing": ["error", "always"],
4548

46-
// General rules (matching MembershipApi)
47-
"prefer-const": "error",
48-
"no-unused-vars": "off", // Turn off base rule since we use unused-imports plugin
49-
indent: "off", // Delegate indentation to Prettier for consistency with VS Code
49+
// --- Compact / single-line formatting ---
50+
"brace-style": ["error", "1tbs", { allowSingleLine: true }],
51+
curly: ["error", "multi-line"],
52+
"nonblock-statement-body-position": ["error", "beside"],
5053

51-
// Code style (enforced by Prettier, but useful for linting)
52-
semi: ["error", "always"],
53-
quotes: ["error", "double", { "avoidEscape": false }],
54-
"comma-dangle": ["error", "never"],
55-
"max-len": ["off"],
54+
// Objects
55+
"object-curly-spacing": ["error", "always"],
56+
"object-curly-newline": ["error", {
57+
ObjectExpression: { multiline: true },
58+
ObjectPattern: { multiline: true },
59+
ImportDeclaration: { multiline: true },
60+
ExportDeclaration: { multiline: true }
61+
}],
62+
"object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
63+
64+
// Arrays
65+
"array-bracket-spacing": ["error", "never"],
66+
"array-bracket-newline": ["error", { multiline: true, minItems: 8 }],
67+
"array-element-newline": ["error", { ArrayExpression: "consistent", ArrayPattern: { minItems: 8 } }],
68+
69+
// Functions
70+
"function-paren-newline": ["error", "consistent"],
71+
"function-call-argument-newline": ["error", "consistent"],
72+
73+
// Generous line length
74+
"max-len": ["warn", {
75+
code: 250,
76+
ignoreStrings: true,
77+
ignoreTemplateLiterals: true,
78+
ignoreComments: true,
79+
ignoreUrls: true,
80+
ignoreRegExpLiterals: true
81+
}],
5682

5783
// Module separation rules for monolith
5884
"import/no-restricted-paths": [
@@ -62,38 +88,32 @@ export default [
6288
{
6389
target: "./src/modules/attendance/**/*",
6490
from: "./src/modules/!(attendance)/**/*",
65-
message:
66-
"Attendance module should not import directly from other modules. Use shared interfaces or dependency injection."
91+
message: "Attendance module should not import directly from other modules. Use shared interfaces or dependency injection."
6792
},
6893
{
6994
target: "./src/modules/content/**/*",
7095
from: "./src/modules/!(content)/**/*",
71-
message:
72-
"Content module should not import directly from other modules. Use shared interfaces or dependency injection."
96+
message: "Content module should not import directly from other modules. Use shared interfaces or dependency injection."
7397
},
7498
{
7599
target: "./src/modules/doing/**/*",
76100
from: "./src/modules/!(doing)/**/*",
77-
message:
78-
"Doing module should not import directly from other modules. Use shared interfaces or dependency injection."
101+
message: "Doing module should not import directly from other modules. Use shared interfaces or dependency injection."
79102
},
80103
{
81104
target: "./src/modules/giving/**/*",
82105
from: "./src/modules/!(giving)/**/*",
83-
message:
84-
"Giving module should not import directly from other modules. Use shared interfaces or dependency injection."
106+
message: "Giving module should not import directly from other modules. Use shared interfaces or dependency injection."
85107
},
86108
{
87109
target: "./src/modules/membership/**/*",
88110
from: "./src/modules/!(membership)/**/*",
89-
message:
90-
"Membership module should not import directly from other modules. Use shared interfaces or dependency injection."
111+
message: "Membership module should not import directly from other modules. Use shared interfaces or dependency injection."
91112
},
92113
{
93114
target: "./src/modules/messaging/**/*",
94115
from: "./src/modules/!(messaging)/**/*",
95-
message:
96-
"Messaging module should not import directly from other modules. Use shared interfaces or dependency injection."
116+
message: "Messaging module should not import directly from other modules. Use shared interfaces or dependency injection."
97117
}
98118
]
99119
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"reset-db": "tsx tools/initdb.ts --reset",
2323
"dev": "tsx watch src/index.ts",
2424
"clean": "rimraf dist",
25-
"lint": "prettier --write src/**/*.ts && eslint . --fix",
25+
"lint": "eslint --fix src/",
2626
"lint:check": "eslint .",
2727
"tsc": "tsc",
2828
"copy-assets": "copyfiles -u 1 src/**/*.html src/**/*.css dist/",

src/app.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ export const createApp = async () => {
8383
} catch {
8484
req.body = {};
8585
}
86-
}
87-
// Handle Buffer-like objects
88-
else if (req.body && req.body.type === "Buffer" && Array.isArray(req.body.data)) {
86+
} else if (req.body && req.body.type === "Buffer" && Array.isArray(req.body.data)) {
8987
try {
9088
const bodyString = Buffer.from(req.body.data).toString("utf8");
9189
// Keep raw body for webhook endpoints, parse JSON for others
@@ -97,9 +95,7 @@ export const createApp = async () => {
9795
} catch {
9896
req.body = {};
9997
}
100-
}
101-
// Handle string JSON bodies
102-
else if (typeof req.body === "string" && req.body.length > 0) {
98+
} else if (typeof req.body === "string" && req.body.length > 0) {
10399
try {
104100
// Keep raw body for webhook endpoints, parse JSON for others
105101
if (!isWebhookEndpoint && contentType.includes("application/json")) {
@@ -108,9 +104,7 @@ export const createApp = async () => {
108104
} catch {
109105
// Silently ignore JSON parse errors
110106
}
111-
}
112-
// If no body was provided, ensure body is set to prevent parsing attempts
113-
else if (!req.body) {
107+
} else if (!req.body) {
114108
req.body = {};
115109
}
116110

src/lambda/socket-handler.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,9 @@ export const handleSocket = async (event: APIGatewayProxyEvent, context: Context
4545
// Run within messaging module context
4646
return await TypedDB.runWithContext("messaging", async () => {
4747
switch (eventType) {
48-
case "CONNECT":
49-
return await handleConnect(event, context);
50-
case "DISCONNECT":
51-
return await handleDisconnect(event, context);
52-
case "MESSAGE":
53-
return await handleMessage(event, context);
48+
case "CONNECT": return await handleConnect(event, context);
49+
case "DISCONNECT": return await handleDisconnect(event, context);
50+
case "MESSAGE": return await handleMessage(event, context);
5451
default:
5552
console.log("Unknown eventType:", eventType);
5653
return { statusCode: 400, body: "Unknown event type" };

src/modules/content/controllers/BibleController.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ export class BibleController extends ContentBaseController {
9494
public async getVerses(
9595
@requestParam("translationKey") translationKey: string,
9696
@requestParam("chapterKey") chapterKey: string,
97-
req: express.Request<{}, {}, null>,
98-
res: express.Response
97+
req: express.Request<{}, {}, null>,
98+
res: express.Response
9999
): Promise<any> {
100100
return this.actionWrapperAnon(req, res, async () => {
101101
let result = await this.repos.bibleVerse.loadByChapter(translationKey, chapterKey);
@@ -114,8 +114,8 @@ export class BibleController extends ContentBaseController {
114114
@requestParam("translationKey") translationKey: string,
115115
@requestParam("startVerseKey") startVerseKey: string,
116116
@requestParam("endVerseKey") endVerseKey: string,
117-
req: express.Request<{}, {}, null>,
118-
res: express.Response
117+
req: express.Request<{}, {}, null>,
118+
res: express.Response
119119
): Promise<any> {
120120
return this.actionWrapperAnon(req, res, async () => {
121121
const canCache = !this.noCache.includes(translationKey);
@@ -146,11 +146,7 @@ export class BibleController extends ContentBaseController {
146146
public async getAvailableTranslations(@requestParam("source") source: string, req: express.Request<{}, {}, null>, res: express.Response): Promise<any> {
147147
return this.actionWrapperAnon(req, res, async () => {
148148
const available = await BibleSourceFactory.getAvailableTranslations(source);
149-
return available.map((t: BibleTranslation) => ({
150-
abbreviation: t.abbreviation,
151-
name: t.name,
152-
language: t.language
153-
}));
149+
return available.map((t: BibleTranslation) => ({ abbreviation: t.abbreviation, name: t.name, language: t.language }));
154150
});
155151
}
156152

src/modules/content/controllers/CuratedEventController.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export class CuratedEventController extends ContentBaseController {
2222
public async getPublicForCuratedCalendar(
2323
@requestParam("churchId") churchId: string,
2424
@requestParam("curatedCalendarId") curatedCalendarId: string,
25-
req: express.Request<{}, {}, null>,
26-
res: express.Response
25+
req: express.Request<{}, {}, null>,
26+
res: express.Response
2727
): Promise<any> {
2828
return this.actionWrapperAnon(req, res, async () => {
2929
const result = await this.repos.curatedEvent.loadForEvents(curatedCalendarId, churchId);
@@ -93,8 +93,8 @@ export class CuratedEventController extends ContentBaseController {
9393
public async deleteByEventId(
9494
@requestParam("curatedCalendarId") curatedCalendarId: string,
9595
@requestParam("eventId") eventId: string,
96-
req: express.Request<{}, {}, null>,
97-
res: express.Response
96+
req: express.Request<{}, {}, null>,
97+
res: express.Response
9898
): Promise<any> {
9999
return this.actionWrapper(req, res, async (au) => {
100100
if (!au.checkAccess(Permissions.content.edit)) return this.json({}, 401);
@@ -109,8 +109,8 @@ export class CuratedEventController extends ContentBaseController {
109109
public async deleteByGroupId(
110110
@requestParam("curatedCalendarId") curatedCalendarId: string,
111111
@requestParam("groupId") groupId: string,
112-
req: express.Request<{}, {}, null>,
113-
res: express.Response
112+
req: express.Request<{}, {}, null>,
113+
res: express.Response
114114
): Promise<any> {
115115
return this.actionWrapper(req, res, async (au) => {
116116
if (!au.checkAccess(Permissions.content.edit)) return this.json({}, 401);

src/modules/content/controllers/FileController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class FileController extends ContentBaseController {
6161
if (totalBytes?.size > 100000000) return this.json({}, 401);
6262
else {
6363
let key = "/" + au.churchId + "/files/" + req.body.fileName;
64-
if (req.body.contentId) key = "/" + au.churchId + "/files/" + req.body.contentType + "/" + req.body.contentId + "/" + req.body.fileName;
64+
if (req.body.contentId) key = "/" + au.churchId + "/files/" + req.body.contentType + "/" + req.body.contentId + "/" + req.body.fileName;
6565
const result = Environment.fileStore === "S3" ? await AwsHelper.S3PresignedUrl(key) : {};
6666
return result;
6767
}

src/modules/content/controllers/LinkController.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,14 @@ export class LinkController extends ContentCrudController {
3232
const visibility = link.visibility || "everyone";
3333

3434
switch (visibility) {
35-
case "everyone":
36-
return true;
37-
38-
case "visitors":
39-
return !!au.personId;
40-
35+
case "everyone": return true;
36+
case "visitors": return !!au.personId;
4137
case "members": {
4238
const status = au.membershipStatus?.toLowerCase();
4339
return status === "member" || status === "staff";
4440
}
45-
46-
case "staff":
47-
return au.membershipStatus?.toLowerCase() === "staff";
48-
49-
case "team":
50-
// Pass through to client - client will check group tags
51-
return true;
52-
41+
case "staff": return au.membershipStatus?.toLowerCase() === "staff";
42+
case "team": return true; // Pass through to client - client will check group tags
5343
case "groups": {
5444
if (!link.groupIds) return false;
5545
try {
@@ -60,9 +50,7 @@ export class LinkController extends ContentCrudController {
6050
return false;
6151
}
6252
}
63-
64-
default:
65-
return true;
53+
default: return true;
6654
}
6755
}
6856

src/modules/content/controllers/PlaylistController.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class PlaylistController extends ContentCrudController {
3535
base64Photo = p.thumbnail;
3636
p.thumbnail = "";
3737
}
38-
if (p.churchId === au.churchId)
38+
if (p.churchId === au.churchId) {
3939
promises.push(
4040
this.repos.playlist.save(p).then(async (playlist: Playlist) => {
4141
if (base64Photo) {
@@ -45,6 +45,7 @@ export class PlaylistController extends ContentCrudController {
4545
return playlist;
4646
})
4747
);
48+
}
4849
}
4950

5051
playlists = await Promise.all(promises);

0 commit comments

Comments
 (0)