Skip to content

Commit da0cc40

Browse files
Add lint rule to check for raw fetch calls.
1 parent 62c276c commit da0cc40

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @fileoverview ESLint rule to disallow raw fetch() calls.
3+
* Use authFetch from useAuthenticatedFetch.ts instead.
4+
*/
5+
6+
/** @type {import('eslint').Rule.RuleModule} */
7+
export default {
8+
meta: {
9+
type: "problem",
10+
docs: {
11+
description: "Disallow raw fetch() calls. Use authFetch from useAuthenticatedFetch.ts instead.",
12+
},
13+
messages: {
14+
noRawFetch: "Do not use raw fetch(). Use authFetch from '@/composables/useAuthenticatedFetch' instead.",
15+
},
16+
schema: [],
17+
},
18+
create(context) {
19+
const sourceCode = context.sourceCode || context.getSourceCode();
20+
21+
return {
22+
CallExpression(node) {
23+
// Check if it's a call to `fetch`
24+
if (node.callee.type === "Identifier" && node.callee.name === "fetch") {
25+
// Check if this file is the useAuthenticatedFetch composable itself
26+
const filename = context.filename || context.getFilename();
27+
if (filename.includes("useAuthenticatedFetch")) {
28+
return; // Allow fetch in the wrapper file itself
29+
}
30+
31+
// Check if `fetch` is a local variable/parameter (not the global)
32+
const scope = sourceCode.getScope(node);
33+
const variable = scope.references.find((ref) => ref.identifier === node.callee);
34+
if (variable && variable.resolved && variable.resolved.defs.length > 0) {
35+
return; // It's a local variable, not the global fetch
36+
}
37+
38+
context.report({
39+
node,
40+
messageId: "noRawFetch",
41+
});
42+
}
43+
},
44+
};
45+
},
46+
};

src/Frontend/eslint.config.mjs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import tseslint from "typescript-eslint";
44
import pluginVue from "eslint-plugin-vue";
55
import pluginPromise from "eslint-plugin-promise";
66
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
7+
import noRawFetch from "./eslint-rules/no-raw-fetch.js";
8+
9+
const localPlugin = {
10+
rules: {
11+
"no-raw-fetch": noRawFetch,
12+
},
13+
};
714

815
export default tseslint.config(
916
{
@@ -12,6 +19,9 @@ export default tseslint.config(
1219
{
1320
files: ["**/*.{js,mjs,ts,vue}"],
1421
languageOptions: { globals: globals.browser, ecmaVersion: "latest", parserOptions: { parser: tseslint.parser } },
22+
plugins: {
23+
local: localPlugin,
24+
},
1525
extends: [pluginJs.configs.recommended, ...tseslint.configs.recommended, ...pluginVue.configs["flat/essential"], pluginPromise.configs["flat/recommended"], eslintPluginPrettierRecommended],
1626
rules: {
1727
"no-duplicate-imports": "error",
@@ -24,6 +34,7 @@ export default tseslint.config(
2434
"prefer-const": "error",
2535
eqeqeq: ["error", "smart"],
2636
"no-throw-literal": "warn",
37+
"local/no-raw-fetch": "error",
2738
},
2839
}
2940
);

src/Frontend/src/composables/autoRefresh.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { watch, ref, shallowReadonly, type WatchStopHandle } from "vue";
22
import { useCounter, useDocumentVisibility, useTimeoutPoll } from "@vueuse/core";
33

4-
export default function useFetchWithAutoRefresh(name: string, fetch: () => Promise<void>, intervalMs: number) {
4+
export default function useFetchWithAutoRefresh(name: string, fetchFn: () => Promise<void>, intervalMs: number) {
55
let watchStop: WatchStopHandle | null = null;
66
const { count, inc, dec, reset } = useCounter(0);
77
const interval = ref(intervalMs);
@@ -11,7 +11,7 @@ export default function useFetchWithAutoRefresh(name: string, fetch: () => Promi
1111
return;
1212
}
1313
isRefreshing.value = true;
14-
await fetch();
14+
await fetchFn();
1515
isRefreshing.value = false;
1616
};
1717
const { pause, resume } = useTimeoutPoll(

src/Frontend/src/stores/EnvironmentAndVersionsStore.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ export const useEnvironmentAndVersionsStore = defineStore("EnvironmentAndVersion
115115

116116
async function getData(url: string, authenticated = false) {
117117
try {
118-
const response = await (authenticated ? authFetch(url) : fetch(url));
118+
// eslint-disable-next-line local/no-raw-fetch
119+
const response = await (authenticated ? authFetch(url) : fetch(url)); // this needs to be an unauthenticated call
119120
return (await response.json()) as unknown as Release[];
120121
} catch (e) {
121122
console.log(e);

0 commit comments

Comments
 (0)