fix: enable eslint & run eslint:fix (#43)

This commit is contained in:
Wesley Luyten
2025-10-08 17:52:02 -05:00
committed by GitHub
parent 39e862a487
commit 5cb93a14a7
115 changed files with 1166 additions and 1176 deletions
+10 -13
View File
@@ -1,7 +1,4 @@
import {
type SupportedFramework,
type SupportedStyle,
} from '@/types/docs';
import type { SupportedFramework, SupportedStyle } from '@/types/docs';
import { findFirstGuide } from '@/utils/docs/sidebar';
/**
@@ -15,16 +12,16 @@ import { findFirstGuide } from '@/utils/docs/sidebar';
* @throws Error if no guide is available for the given framework/style
*/
export function getDocsRedirectUrl<F extends SupportedFramework>(
framework: F,
style: SupportedStyle<F>,
framework: F,
style: SupportedStyle<F>,
): string {
const firstGuide = findFirstGuide(framework, style);
const firstGuide = findFirstGuide(framework, style);
if (!firstGuide) {
throw new Error(
`No guide available for framework "${framework}" and style "${style}"`,
);
}
if (!firstGuide) {
throw new Error(
`No guide available for framework "${framework}" and style "${style}"`,
);
}
return `/docs/framework/${framework}/style/${style}/${firstGuide}/`;
return `/docs/framework/${framework}/style/${style}/${firstGuide}/`;
}
+92 -91
View File
@@ -1,14 +1,12 @@
import {
type SupportedFramework,
type SupportedStyle,
type AnySupportedStyle,
getAvailableStyles,
type Guide,
type Section,
type Sidebar,
isSection,
} from '@/types/docs';
import type { AnySupportedStyle, Guide, Section, Sidebar, SupportedFramework, SupportedStyle } from '@/types/docs';
import { sidebar } from '@/config/docs/sidebar';
import {
getAvailableStyles,
isSection,
} from '@/types/docs';
/**
* Check if an item (Guide or Section) should be shown based on framework and style.
@@ -21,14 +19,14 @@ import { sidebar } from '@/config/docs/sidebar';
* @returns true if the item should be visible
*/
function isItemVisible(
item: Guide | Section,
framework: SupportedFramework,
style: AnySupportedStyle,
item: Guide | Section,
framework: SupportedFramework,
style: AnySupportedStyle,
): boolean {
const frameworkMatch =
!item.frameworks || item.frameworks.includes(framework);
const styleMatch = !item.styles || item.styles.includes(style);
return frameworkMatch && styleMatch;
const frameworkMatch
= !item.frameworks || item.frameworks.includes(framework);
const styleMatch = !item.styles || item.styles.includes(style);
return frameworkMatch && styleMatch;
}
/**
@@ -43,31 +41,31 @@ function isItemVisible(
* @returns A new filtered sidebar with only visible content
*/
export function filterSidebar(
framework: SupportedFramework,
style: AnySupportedStyle,
sidebarToFilter: Sidebar = sidebar,
framework: SupportedFramework,
style: AnySupportedStyle,
sidebarToFilter: Sidebar = sidebar,
): Sidebar {
return sidebarToFilter
.filter((item) => isItemVisible(item, framework, style))
.map((item) => {
if (isSection(item)) {
const filteredContents = filterSidebar(framework, style, item.contents);
return {
...item,
contents: filteredContents,
};
}
// It's a Guide, return as-is
return item;
})
.filter((item) => {
// Remove sections with no contents after filtering
if (isSection(item)) {
return item.contents.length > 0;
}
// Keep all guides
return true;
});
return sidebarToFilter
.filter(item => isItemVisible(item, framework, style))
.map((item) => {
if (isSection(item)) {
const filteredContents = filterSidebar(framework, style, item.contents);
return {
...item,
contents: filteredContents,
};
}
// It's a Guide, return as-is
return item;
})
.filter((item) => {
// Remove sections with no contents after filtering
if (isSection(item)) {
return item.contents.length > 0;
}
// Keep all guides
return true;
});
}
/**
@@ -81,26 +79,27 @@ export function filterSidebar(
* @returns The slug of the first visible guide, or null if none found
*/
export function findFirstGuide(
framework: SupportedFramework,
style: AnySupportedStyle,
sidebarToSearch: Sidebar = sidebar,
framework: SupportedFramework,
style: AnySupportedStyle,
sidebarToSearch: Sidebar = sidebar,
): string | null {
for (const item of sidebarToSearch) {
if (!isItemVisible(item, framework, style)) {
continue;
}
for (const item of sidebarToSearch) {
if (!isItemVisible(item, framework, style)) {
continue;
}
if (isSection(item)) {
// Recursively search section contents
const guide = findFirstGuide(framework, style, item.contents);
if (guide) return guide;
} else {
// It's a Guide, return its slug
return item.slug;
}
}
if (isSection(item)) {
// Recursively search section contents
const guide = findFirstGuide(framework, style, item.contents);
if (guide) return guide;
}
else {
// It's a Guide, return its slug
return item.slug;
}
}
return null;
return null;
}
/**
@@ -113,19 +112,20 @@ export function findFirstGuide(
* @returns An array of all guide slugs found in the sidebar
*/
export function getAllGuideSlugs(sidebarToExtract: Sidebar = sidebar): string[] {
const slugs: string[] = [];
const slugs: string[] = [];
for (const item of sidebarToExtract) {
if (isSection(item)) {
// Recursively get slugs from section contents
slugs.push(...getAllGuideSlugs(item.contents));
} else {
// It's a Guide, add its slug
slugs.push(item.slug);
}
}
for (const item of sidebarToExtract) {
if (isSection(item)) {
// Recursively get slugs from section contents
slugs.push(...getAllGuideSlugs(item.contents));
}
else {
// It's a Guide, add its slug
slugs.push(item.slug);
}
}
return slugs;
return slugs;
}
/**
@@ -136,20 +136,21 @@ export function getAllGuideSlugs(sidebarToExtract: Sidebar = sidebar): string[]
* @returns The guide object if found, null otherwise
*/
export function findGuideBySlug(
slug: string,
sidebarToSearch: Sidebar = sidebar,
slug: string,
sidebarToSearch: Sidebar = sidebar,
): Guide | null {
for (const item of sidebarToSearch) {
if (isSection(item)) {
// Recursively search section contents
const guide = findGuideBySlug(slug, item.contents);
if (guide) return guide;
} else if (item.slug === slug) {
// Found the guide
return item;
}
}
return null;
for (const item of sidebarToSearch) {
if (isSection(item)) {
// Recursively search section contents
const guide = findGuideBySlug(slug, item.contents);
if (guide) return guide;
}
else if (item.slug === slug) {
// Found the guide
return item;
}
}
return null;
}
/**
@@ -162,16 +163,16 @@ export function findGuideBySlug(
* @returns Array of valid styles for this guide in this framework
*/
export function getValidStylesForGuide<F extends SupportedFramework>(
guide: Guide,
framework: F,
guide: Guide,
framework: F,
): readonly SupportedStyle<F>[] {
const frameworkStyles = getAvailableStyles(framework);
const frameworkStyles = getAvailableStyles(framework);
// If guide has no style restrictions, all framework styles are valid
if (!guide.styles) {
return frameworkStyles;
}
// If guide has no style restrictions, all framework styles are valid
if (!guide.styles) {
return frameworkStyles;
}
// Return intersection of framework styles and guide styles
return frameworkStyles.filter((s) => guide.styles!.includes(s));
// Return intersection of framework styles and guide styles
return frameworkStyles.filter(s => guide.styles!.includes(s));
}
+42 -41
View File
@@ -1,16 +1,17 @@
import { glob, type ParseDataOptions } from 'astro/loaders';
import type { ParseDataOptions } from 'astro/loaders';
import { glob } from 'astro/loaders';
/**
* Parser function that runs before Astro's schema validation.
* Receives the entry and the original filename (before generateId transforms it).
*/
type Parser = <TData extends Record<string, unknown>>(
options: ParseDataOptions<TData>,
originalEntry: string,
options: ParseDataOptions<TData>,
originalEntry: string,
) => Promise<ParseDataOptions<TData>>;
type GlobWithParserOptions = Parameters<typeof glob>[0] & {
parser: Parser;
parser: Parser;
};
/**
@@ -36,46 +37,46 @@ type GlobWithParserOptions = Parameters<typeof glob>[0] & {
* ```
*/
export function globWithParser({
parser,
generateId,
...globOptions
parser,
generateId,
...globOptions
}: GlobWithParserOptions) {
/**
* Store mapping of transformed IDs to original entry filenames.
* Created per-invocation to avoid memory leaks across builds.
* This is needed because generateId transforms the entry name (e.g., removes date prefix),
* but we need access to the original filename in the parser (e.g., to extract date from filename).
*/
const entryMap = new Map<string, string>();
/**
* Store mapping of transformed IDs to original entry filenames.
* Created per-invocation to avoid memory leaks across builds.
* This is needed because generateId transforms the entry name (e.g., removes date prefix),
* but we need access to the original filename in the parser (e.g., to extract date from filename).
*/
const entryMap = new Map<string, string>();
// Wrap generateId to capture the original entry name before transformation
// This allows us to maintain a mapping from the transformed ID back to the original filename
const wrappedGenerateId = generateId
? (ctx: Parameters<NonNullable<typeof generateId>>[0]) => {
const newId = generateId(ctx);
// Store mapping: transformed ID -> original filename
entryMap.set(newId, ctx.entry);
return newId;
}
: undefined;
// Wrap generateId to capture the original entry name before transformation
// This allows us to maintain a mapping from the transformed ID back to the original filename
const wrappedGenerateId = generateId
? (ctx: Parameters<NonNullable<typeof generateId>>[0]) => {
const newId = generateId(ctx);
// Store mapping: transformed ID -> original filename
entryMap.set(newId, ctx.entry);
return newId;
}
: undefined;
// Create the base glob loader with our wrapped generateId
const loader = glob({ ...globOptions, generateId: wrappedGenerateId });
const originalLoad = loader.load;
// Create the base glob loader with our wrapped generateId
const loader = glob({ ...globOptions, generateId: wrappedGenerateId });
const originalLoad = loader.load;
// Intercept the load function to inject our custom parser
// This allows us to provide both the transformed entry and original filename to the parser
loader.load = async ({ parseData, ...rest }) => {
return originalLoad({
parseData: async (entry) => {
// Retrieve the original filename from our map, falling back to entry.id if not found
const originalEntry = entryMap.get(entry.id) || entry.id;
// Call user's parser with both the transformed entry and original filename
return parseData(await parser(entry, originalEntry));
},
...rest,
});
};
// Intercept the load function to inject our custom parser
// This allows us to provide both the transformed entry and original filename to the parser
loader.load = async ({ parseData, ...rest }) => {
return originalLoad({
parseData: async (entry) => {
// Retrieve the original filename from our map, falling back to entry.id if not found
const originalEntry = entryMap.get(entry.id) || entry.id;
// Call user's parser with both the transformed entry and original filename
return parseData(await parser(entry, originalEntry));
},
...rest,
});
};
return loader;
return loader;
}