mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix: enable eslint & run eslint:fix (#43)
This commit is contained in:
@@ -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}/`;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user