chore(lint): manual cleanup batch 1/3 (groups 1-5)
- group 1 (catalog.service): rename categoryId→categoryIdInput param + local categoryId; init pl24Categories with Awaited type; fix img alt + scroll buttons - group 2 (useButtonType): add type=button to 12 buttons (demo.tsx, index.tsx) - group 3 (noSvgWithoutTitle): add role+aria-label to 13 decorative svgs - group 4 (noAssignInExpressions): convert 13 while((m=regex.exec())) → for...of matchAll (pl24-ford-legacy + emex.service) - group 5 (useExhaustiveDependencies): correct deps in __root user identification, subscription onboarding, model-list-columns; biome-ignore for legitimate single-trigger effects Lint count: 769 → 218 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -652,7 +652,7 @@ export class CatalogService {
|
||||
|
||||
if (dbCategories.length === 0 && effectiveCatalogPath) {
|
||||
try {
|
||||
let pl24Categories;
|
||||
let pl24Categories: Awaited<ReturnType<PL24Service["fetchMainGroups"]>>;
|
||||
|
||||
if (vehicle.architecture === "LEGACY_PSA") {
|
||||
// PSA vehicles: parse family/salesType from catalogPath, mode/upds from metadata
|
||||
@@ -742,7 +742,7 @@ export class CatalogService {
|
||||
*/
|
||||
async getCategoryWithParts(
|
||||
catalogVehicleId: string,
|
||||
categoryId: string,
|
||||
categoryIdInput: string,
|
||||
userId: string,
|
||||
body = "_all_",
|
||||
engine = "_all_",
|
||||
@@ -762,21 +762,27 @@ export class CatalogService {
|
||||
|
||||
// PSA variant trees return PL24 codes (e.g. "_FCT0100") as IDs instead of UUIDs.
|
||||
// Detect and resolve to DB UUID via externalId lookup.
|
||||
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(categoryId);
|
||||
const isUuid =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(categoryIdInput);
|
||||
let categoryRow: typeof categories.$inferSelect | undefined;
|
||||
|
||||
if (isUuid) {
|
||||
[categoryRow] = await this.db
|
||||
.select()
|
||||
.from(categories)
|
||||
.where(eq(categories.id, categoryId))
|
||||
.where(eq(categories.id, categoryIdInput))
|
||||
.limit(1);
|
||||
} else {
|
||||
// PSA external code — look up by externalId within this catalog vehicle
|
||||
[categoryRow] = await this.db
|
||||
.select()
|
||||
.from(categories)
|
||||
.where(and(eq(categories.externalId, categoryId), eq(categories.catalogVehicleId, catalogVehicleId)))
|
||||
.where(
|
||||
and(
|
||||
eq(categories.externalId, categoryIdInput),
|
||||
eq(categories.catalogVehicleId, catalogVehicleId),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
}
|
||||
|
||||
@@ -784,7 +790,7 @@ export class CatalogService {
|
||||
|
||||
// Normalize to DB UUID for all downstream queries
|
||||
const category = categoryRow;
|
||||
categoryId = category.id;
|
||||
const categoryId = category.id;
|
||||
|
||||
const linkPath = category.linkPath;
|
||||
|
||||
|
||||
@@ -256,8 +256,7 @@ export class EmexService {
|
||||
const linkRx = /href="(Vehicle\.aspx\?[^"]+)">([^<]+)<\/a>/g;
|
||||
const seen = new Set<string>();
|
||||
const vehicles: EmexHttpVehicle[] = [];
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = linkRx.exec(html)) !== null) {
|
||||
for (const m of html.matchAll(linkRx)) {
|
||||
const href = m[1].replace(/&/g, "&");
|
||||
if (seen.has(href)) continue;
|
||||
seen.add(href);
|
||||
@@ -291,8 +290,7 @@ export class EmexService {
|
||||
const catRx = /href="(QuickDetails\.aspx\?[^"]+)">([^<]+)<\/a>/g;
|
||||
const seen = new Set<string>();
|
||||
const cats: EmexHttpCategory[] = [];
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = catRx.exec(html)) !== null) {
|
||||
for (const m of html.matchAll(catRx)) {
|
||||
const href = m[1].replace(/&/g, "&");
|
||||
const name = m[2].trim();
|
||||
if (name.length < 2 || seen.has(href)) continue;
|
||||
|
||||
@@ -2185,8 +2185,7 @@ export class PL24FordLegacyService {
|
||||
// Scope rows contain jsonUrl="json-main-groups.action?...&scope=_FCT0001&..." and a name cell
|
||||
const trRegex =
|
||||
/<tr[^>]+jsonUrl="(json-main-groups\.action[^"]+)"[^>]*>[\s\S]*?<td[^>]*>([^<]+)<\/td>/g;
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = trRegex.exec(html)) !== null) {
|
||||
for (const m of html.matchAll(trRegex)) {
|
||||
const jsonUrl = m[1];
|
||||
const name = m[2].trim();
|
||||
const scopeMatch = jsonUrl.match(/[?&]scope=([^&]+)/);
|
||||
@@ -2843,8 +2842,7 @@ export class PL24FordLegacyService {
|
||||
// These are server-rendered (not JS-injected) — the most reliable source.
|
||||
const trRegex = /<tr[^>]*\bmodelFamily="([^"]+)"[^>]*\bmodelFamilyName="([^"]+)"/g;
|
||||
const fromHtmlAttrs: Array<{ id: string; name: string }> = [];
|
||||
let m: RegExpExecArray | null;
|
||||
while ((m = trRegex.exec(html)) !== null) {
|
||||
for (const m of html.matchAll(trRegex)) {
|
||||
fromHtmlAttrs.push({ id: m[1], name: m[2] });
|
||||
}
|
||||
if (fromHtmlAttrs.length > 0) {
|
||||
@@ -2875,7 +2873,7 @@ export class PL24FordLegacyService {
|
||||
if (selectMatch) {
|
||||
const optionRegex = /<option[^>]*value=["']([^"']+)["'][^>]*>([\s\S]*?)<\/option>/gi;
|
||||
const options: Array<{ id: string; name: string }> = [];
|
||||
while ((m = optionRegex.exec(selectMatch[1])) !== null) {
|
||||
for (const m of selectMatch[1].matchAll(optionRegex)) {
|
||||
const id = m[1].trim();
|
||||
const name = m[2].replace(/<[^>]+>/g, "").trim();
|
||||
if (id && id !== "" && id !== "0" && name) {
|
||||
@@ -2894,8 +2892,7 @@ export class PL24FordLegacyService {
|
||||
"gi",
|
||||
);
|
||||
const modes = new Map<string, string>();
|
||||
let modeMatch: RegExpExecArray | null;
|
||||
while ((modeMatch = modeRegex.exec(html)) !== null) {
|
||||
for (const modeMatch of html.matchAll(modeRegex)) {
|
||||
const mode = modeMatch[1];
|
||||
if (mode && !modes.has(mode)) {
|
||||
modes.set(mode, mode);
|
||||
@@ -3279,8 +3276,7 @@ export class PL24FordLegacyService {
|
||||
// Try to extract any text from <td> elements, including those with nested tags
|
||||
let tdName = "";
|
||||
const tdPattern = /<td[^>]*>([\s\S]*?)<\/td>/g;
|
||||
let tdMatch2: RegExpExecArray | null;
|
||||
while ((tdMatch2 = tdPattern.exec(segment)) !== null) {
|
||||
for (const tdMatch2 of segment.matchAll(tdPattern)) {
|
||||
const text = tdMatch2[1]
|
||||
.replace(/<[^>]+>/g, " ")
|
||||
.replace(/ /g, " ")
|
||||
@@ -3331,8 +3327,7 @@ export class PL24FordLegacyService {
|
||||
// Fallback: <a href="...group...action"> links
|
||||
if (groups.length === 0) {
|
||||
const linkRegex = /<a[^>]+href="([^"]*group[^"]*\.action[^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = linkRegex.exec(html)) !== null) {
|
||||
for (const match of html.matchAll(linkRegex)) {
|
||||
const url = match[1];
|
||||
const name = match[2].replace(/<[^>]+>/g, "").trim();
|
||||
if (!name || seen.has(url)) continue;
|
||||
@@ -3508,8 +3503,7 @@ export class PL24FordLegacyService {
|
||||
} else {
|
||||
// Extract text from first non-empty <td> (strip inner tags)
|
||||
const tdPattern = /<td[^>]*>([\s\S]*?)<\/td>/g;
|
||||
let tdMatch: RegExpExecArray | null;
|
||||
while ((tdMatch = tdPattern.exec(segment)) !== null) {
|
||||
for (const tdMatch of segment.matchAll(tdPattern)) {
|
||||
const text = tdMatch[1]
|
||||
.replace(/<[^>]+>/g, " ")
|
||||
.replace(/ /g, " ")
|
||||
@@ -3568,9 +3562,7 @@ export class PL24FordLegacyService {
|
||||
extractLinks(html: string, pattern: RegExp): { href: string; text: string }[] {
|
||||
const linkRegex = /<a\s+[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi;
|
||||
const results: { href: string; text: string }[] = [];
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = linkRegex.exec(html)) !== null) {
|
||||
for (const match of html.matchAll(linkRegex)) {
|
||||
const href = match[1];
|
||||
const text = match[2].replace(/<[^>]+>/g, "").trim();
|
||||
if (pattern.test(href) && text) {
|
||||
@@ -3596,22 +3588,19 @@ export class PL24FordLegacyService {
|
||||
// Extract header cells
|
||||
const headerRegex = /<th[^>]*>([\s\S]*?)<\/th>/gi;
|
||||
const headers: string[] = [];
|
||||
let hMatch: RegExpExecArray | null;
|
||||
while ((hMatch = headerRegex.exec(tableHtml)) !== null) {
|
||||
for (const hMatch of tableHtml.matchAll(headerRegex)) {
|
||||
headers.push(hMatch[1].replace(/<[^>]+>/g, "").trim());
|
||||
}
|
||||
|
||||
// Extract rows
|
||||
const rowRegex = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
|
||||
const rows: Record<string, string>[] = [];
|
||||
let rMatch: RegExpExecArray | null;
|
||||
let rowIndex = 0;
|
||||
|
||||
while ((rMatch = rowRegex.exec(tableHtml)) !== null) {
|
||||
for (const rMatch of tableHtml.matchAll(rowRegex)) {
|
||||
const cellRegex = /<td[^>]*>([\s\S]*?)<\/td>/gi;
|
||||
const cells: string[] = [];
|
||||
let cMatch: RegExpExecArray | null;
|
||||
while ((cMatch = cellRegex.exec(rMatch[1])) !== null) {
|
||||
for (const cMatch of rMatch[1].matchAll(cellRegex)) {
|
||||
cells.push(cMatch[1].replace(/<[^>]+>/g, "").trim());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user