Skip to content

Commit a60b2c4

Browse files
authored
perf: Cache HTTP requests across all pages (#174)
* perf: Cache HTTP requests across all pages * perf: Always consume `Response.body`
1 parent 4ae5119 commit a60b2c4

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

src/no-dead-link.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,12 @@ const createCheckAliveURL = (ruleOptions: Options, resolvePath: (path: string, b
184184
// and see what kind of redirect is occurring
185185
redirect: "manual" as RequestRedirect
186186
};
187+
188+
// Declare the variable outside the try-catch block to ensure the body is always consumed in `finally`.
189+
let res: Response | null = null;
190+
187191
try {
188-
const res = await fetchWithDefaults(uri, opts);
192+
res = await fetchWithDefaults(uri, opts);
189193
const errorResult = {
190194
ok: false,
191195
redirected: true,
@@ -214,6 +218,7 @@ const createCheckAliveURL = (ruleOptions: Options, resolvePath: (path: string, b
214218
const finalRes = await fetchWithDefaults(redirectedUrl, { ...opts, redirect: "follow" });
215219
const url = URL.parse(uri);
216220
const hash = url?.hash || null;
221+
await finalRes.body?.cancel();
217222
return {
218223
ok: finalRes.ok,
219224
redirected: true,
@@ -264,6 +269,10 @@ const createCheckAliveURL = (ruleOptions: Options, resolvePath: (path: string, b
264269
ok: false,
265270
message: ex.message
266271
};
272+
} finally {
273+
if (res && !res.body?.locked) {
274+
await res.body?.cancel();
275+
}
267276
}
268277
};
269278
};
@@ -289,6 +298,11 @@ async function isAliveLocalFile(filePath: string): Promise<AliveFunctionReturn>
289298
}
290299
}
291300

301+
const memorizedIsAliveURIByOptions = new Map<
302+
string,
303+
ReturnType<typeof pMemoize<ReturnType<typeof createCheckAliveURL>>>
304+
>();
305+
292306
const reporter: TextlintRuleReporter<Options> = (context, options) => {
293307
const { Syntax, getSource, report, RuleError, fixer, getFilePath, locator } = context;
294308
const helper = new RuleHelper(context);
@@ -307,10 +321,21 @@ const reporter: TextlintRuleReporter<Options> = (context, options) => {
307321
}
308322
}
309323
};
310-
const isAliveURI = createCheckAliveURL(ruleOptions, resolvePath);
311-
const memorizedIsAliveURI = pMemoize(isAliveURI, {
312-
maxAge: ruleOptions.linkMaxAge
313-
});
324+
const memorizedIsAliveURI = (() => {
325+
const memoOptionsKey = JSON.stringify(ruleOptions);
326+
const func = memorizedIsAliveURIByOptions.get(memoOptionsKey);
327+
if (!func) {
328+
const isAliveURI = createCheckAliveURL(ruleOptions, resolvePath);
329+
const func = pMemoize(isAliveURI, {
330+
maxAge: ruleOptions.linkMaxAge
331+
});
332+
memorizedIsAliveURIByOptions.set(memoOptionsKey, func);
333+
return func;
334+
}
335+
336+
return func;
337+
})();
338+
314339
/**
315340
* Checks a given URI's availability and report if it is dead.
316341
* @param {TextLintNode} node TextLintNode the URI belongs to.

0 commit comments

Comments
 (0)