@humanspeak/svelte-markdown
Version:
Fast, customizable markdown renderer for Svelte with built-in caching, TypeScript support, and Svelte 5 runes
127 lines (126 loc) • 4.19 kB
JavaScript
/**
* Creates a set of filter utility functions for renderer maps.
* This factory generates three functions: buildUnsupported, allowOnly, and excludeOnly.
*
* Used to eliminate code duplication between unsupportedRenderers.ts and unsupportedHtmlRenderers.ts.
*
* @template TKey - The string literal type for valid keys
* @template TResult - The result map type (e.g., Partial<Renderers> or HtmlRenderers)
*
* @param keys - Array of valid keys for this renderer type
* @param unsupportedComponent - The component to use for unsupported/disabled renderers
* @param defaultsMap - Map of keys to their default component implementations
*
* @returns Object containing buildUnsupported, allowOnly, and excludeOnly functions
*
* @example
* ```typescript
* import { createFilterUtilities } from './createFilterUtilities'
*
* type MyKey = 'foo' | 'bar' | 'baz'
* const keys: readonly MyKey[] = ['foo', 'bar', 'baz'] as const
* const UnsupportedComponent = () => null
* const defaults = { foo: FooComponent, bar: BarComponent, baz: BazComponent }
*
* const { buildUnsupported, allowOnly, excludeOnly } = createFilterUtilities<MyKey, Record<MyKey, Component>>(
* keys,
* UnsupportedComponent,
* defaults
* )
*
* // Block all renderers
* const allUnsupported = buildUnsupported()
*
* // Allow only 'foo' and 'bar', block 'baz'
* const allowList = allowOnly(['foo', 'bar'])
*
* // Block only 'baz', allow others with defaults
* const denyList = excludeOnly(['baz'])
* ```
*/
export const createFilterUtilities = (keys, unsupportedComponent, defaultsMap) => {
/**
* Checks if a key is valid for this renderer type.
*/
const hasKey = (key) => keys.includes(key);
/**
* Builds a map where every key is set to the unsupported component.
* Useful for starting with a "deny all" approach.
*/
const buildUnsupported = () => {
const result = {};
for (const key of keys) {
;
result[key] = unsupportedComponent;
}
return result;
};
/**
* Produces a renderer map that allows only the specified keys.
* All non-listed keys are set to the unsupported component.
*
* Each entry can be either:
* - A key string (to use the default component for that key)
* - A tuple [key, component] to specify a custom component
*/
const allowOnly = (allowed) => {
const result = buildUnsupported();
for (const entry of allowed) {
if (Array.isArray(entry)) {
const [key, component] = entry;
if (hasKey(key)) {
;
result[key] = component;
}
}
else {
const key = entry;
if (hasKey(key)) {
;
result[key] = defaultsMap[key];
}
}
}
return result;
};
/**
* Produces a renderer map that excludes only the specified keys.
* Excluded keys are set to the unsupported component.
* All other keys use the default components.
*
* Optionally, specific non-excluded keys can be overridden with custom components.
* Exclusions take precedence over overrides.
*/
const excludeOnly = (excluded, overrides) => {
const result = {};
// Start with all defaults
for (const key of keys) {
;
result[key] = defaultsMap[key];
}
// Mark excluded keys as unsupported
for (const key of excluded) {
if (hasKey(key)) {
;
result[key] = unsupportedComponent;
}
}
// Apply overrides (exclusions take precedence)
if (overrides) {
for (const [key, component] of overrides) {
if (excluded.includes(key))
continue;
if (hasKey(key)) {
;
result[key] = component;
}
}
}
return result;
};
return {
buildUnsupported,
allowOnly,
excludeOnly
};
};