@humanspeak/svelte-markdown 0.8.13 → 0.8.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,168 +1,5 @@
1
1
  /**
2
- * Configuration options for cache initialization.
3
- *
4
- * @interface CacheOptions
5
- * @property {number} [maxSize] - Maximum number of entries the cache can hold before evicting oldest entries
6
- * @property {number} [ttl] - Time-to-live in milliseconds for cache entries before they expire
2
+ * Re-export MemoryCache from @humanspeak/memory-cache package.
3
+ * This provides a generic in-memory cache implementation with TTL and size-based eviction.
7
4
  */
8
- type CacheOptions = {
9
- maxSize?: number;
10
- ttl?: number;
11
- };
12
- /**
13
- * Generic in-memory cache implementation with TTL and size-based eviction.
14
- * Provides efficient caching for any type of data with automatic cleanup
15
- * of expired or excess entries.
16
- *
17
- * @class MemoryCache
18
- * @template T - The type of values being cached
19
- *
20
- * @example
21
- * ```typescript
22
- * // Create a cache for string values
23
- * const cache = new MemoryCache<string>({
24
- * maxSize: 100,
25
- * ttl: 5 * 60 * 1000 // 5 minutes
26
- * });
27
- *
28
- * // Store and retrieve values
29
- * cache.set('key', 'value');
30
- * const value = cache.get('key');
31
- * ```
32
- */
33
- export declare class MemoryCache<T> {
34
- private cache;
35
- private maxSize;
36
- private ttl;
37
- /**
38
- * Creates a new MemoryCache instance.
39
- *
40
- * @param {CacheOptions} options - Configuration options for the cache
41
- * @param {number} [options.maxSize=100] - Maximum number of entries (default: 100)
42
- * @param {number} [options.ttl=300000] - Time-to-live in milliseconds (default: 5 minutes)
43
- */
44
- constructor(options?: CacheOptions);
45
- /**
46
- * Retrieves a value from the cache if it exists and hasn't expired.
47
- *
48
- * @param {string} key - The key to look up
49
- * @returns {T | undefined} The cached value if found and valid, undefined otherwise
50
- *
51
- * @example
52
- * ```typescript
53
- * const cache = new MemoryCache<number>();
54
- * cache.set('counter', 42);
55
- * const value = cache.get('counter'); // Returns 42
56
- * const missing = cache.get('nonexistent'); // Returns undefined
57
- * ```
58
- */
59
- get(key: string): T | undefined;
60
- /**
61
- * Checks if a key exists in the cache (regardless of its value).
62
- * This is useful for distinguishing between cache misses and cached undefined values.
63
- *
64
- * @param {string} key - The key to check
65
- * @returns {boolean} True if the key exists in cache and hasn't expired, false otherwise
66
- */
67
- has(key: string): boolean;
68
- /**
69
- * Stores a value in the cache. If the cache is full, the oldest entry is removed.
70
- *
71
- * @param {string} key - The key under which to store the value
72
- * @param {T} value - The value to cache
73
- *
74
- * @example
75
- * ```typescript
76
- * const cache = new MemoryCache<string>();
77
- * cache.set('greeting', 'Hello, World!');
78
- * ```
79
- */
80
- set(key: string, value: T): void;
81
- /**
82
- * Removes a specific entry from the cache.
83
- *
84
- * @param {string} key - The key of the entry to remove
85
- * @returns {boolean} True if an element was removed, false if the key wasn't found
86
- *
87
- * @example
88
- * ```typescript
89
- * const cache = new MemoryCache<string>();
90
- * cache.set('key', 'value');
91
- * cache.delete('key'); // Returns true
92
- * cache.delete('nonexistent'); // Returns false
93
- * ```
94
- */
95
- delete(key: string): boolean;
96
- deleteAsync(key: string): Promise<boolean>;
97
- /**
98
- * Removes all entries from the cache.
99
- *
100
- * @example
101
- * ```typescript
102
- * const cache = new MemoryCache<string>();
103
- * cache.set('key1', 'value1');
104
- * cache.set('key2', 'value2');
105
- * cache.clear(); // Removes all entries
106
- * ```
107
- */
108
- clear(): void;
109
- /**
110
- * Removes all entries from the cache whose keys start with the given prefix.
111
- *
112
- * @param {string} prefix - The prefix to match against cache keys
113
- * @returns {number} Number of entries removed
114
- *
115
- * @example
116
- * ```typescript
117
- * const cache = new MemoryCache<string>();
118
- * cache.set('user:123:name', 'John');
119
- * cache.set('user:123:email', 'john@example.com');
120
- * cache.set('post:456', 'Hello World');
121
- *
122
- * const removed = cache.deleteByPrefix('user:123:'); // Returns 2
123
- * ```
124
- */
125
- deleteByPrefix(prefix: string): number;
126
- /**
127
- * Removes all entries from the cache whose keys match the given wildcard pattern.
128
- * Supports asterisk (*) wildcards for flexible pattern matching.
129
- *
130
- * @param {string} magicString - The wildcard pattern to match against cache keys (use * for wildcards)
131
- * @returns {number} Number of entries removed
132
- *
133
- * @example
134
- * ```typescript
135
- * const cache = new MemoryCache<string>();
136
- * cache.set('user:123:name', 'John');
137
- * cache.set('user:123:email', 'john@example.com');
138
- * cache.set('user:456:name', 'Jane');
139
- * cache.set('post:789', 'Hello World');
140
- *
141
- * const removed1 = cache.deleteByMagicString('user:123:*'); // Returns 2 (matches name and email)
142
- * const removed2 = cache.deleteByMagicString('user:*:name'); // Returns 1 (matches Jane's name)
143
- * const removed3 = cache.deleteByMagicString('post:*'); // Returns 1 (matches the post)
144
- * ```
145
- */
146
- deleteByMagicString(magicString: string): number;
147
- }
148
- /**
149
- * Cache decorator factory for method-level caching.
150
- * Provides a way to cache method results based on their arguments.
151
- *
152
- * @template T - The return type of the decorated method
153
- * @param {CacheOptions} options - Configuration options for the cache
154
- * @returns A method decorator that caches the results
155
- *
156
- * @example
157
- * ```typescript
158
- * class UserService {
159
- * @cached<User>({ ttl: 60000 })
160
- * async getUser(id: string): Promise<User> {
161
- * // Expensive operation
162
- * return await fetchUser(id);
163
- * }
164
- * }
165
- * ```
166
- */
167
- export declare function cached<T>(options?: CacheOptions): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
168
- export {};
5
+ export { MemoryCache } from '@humanspeak/memory-cache';
@@ -1,263 +1,5 @@
1
1
  /**
2
- * Special sentinel values to represent cached undefined/null values.
3
- * This allows us to distinguish between "not cached" and "cached undefined/null".
2
+ * Re-export MemoryCache from @humanspeak/memory-cache package.
3
+ * This provides a generic in-memory cache implementation with TTL and size-based eviction.
4
4
  */
5
- const CACHED_UNDEFINED = Symbol('CACHED_UNDEFINED');
6
- const CACHED_NULL = Symbol('CACHED_NULL');
7
- /**
8
- * Generic in-memory cache implementation with TTL and size-based eviction.
9
- * Provides efficient caching for any type of data with automatic cleanup
10
- * of expired or excess entries.
11
- *
12
- * @class MemoryCache
13
- * @template T - The type of values being cached
14
- *
15
- * @example
16
- * ```typescript
17
- * // Create a cache for string values
18
- * const cache = new MemoryCache<string>({
19
- * maxSize: 100,
20
- * ttl: 5 * 60 * 1000 // 5 minutes
21
- * });
22
- *
23
- * // Store and retrieve values
24
- * cache.set('key', 'value');
25
- * const value = cache.get('key');
26
- * ```
27
- */
28
- export class MemoryCache {
29
- cache = new Map();
30
- maxSize;
31
- ttl;
32
- /**
33
- * Creates a new MemoryCache instance.
34
- *
35
- * @param {CacheOptions} options - Configuration options for the cache
36
- * @param {number} [options.maxSize=100] - Maximum number of entries (default: 100)
37
- * @param {number} [options.ttl=300000] - Time-to-live in milliseconds (default: 5 minutes)
38
- */
39
- constructor(options = {}) {
40
- this.maxSize = options.maxSize ?? 100;
41
- this.ttl = options.ttl ?? 5 * 60 * 1000; // 5 minutes default
42
- }
43
- /**
44
- * Retrieves a value from the cache if it exists and hasn't expired.
45
- *
46
- * @param {string} key - The key to look up
47
- * @returns {T | undefined} The cached value if found and valid, undefined otherwise
48
- *
49
- * @example
50
- * ```typescript
51
- * const cache = new MemoryCache<number>();
52
- * cache.set('counter', 42);
53
- * const value = cache.get('counter'); // Returns 42
54
- * const missing = cache.get('nonexistent'); // Returns undefined
55
- * ```
56
- */
57
- get(key) {
58
- const entry = this.cache.get(key);
59
- if (!entry)
60
- return undefined;
61
- // Check if entry has expired (skip check if TTL is 0 or negative)
62
- if (this.ttl > 0 && Date.now() - entry.timestamp > this.ttl) {
63
- this.cache.delete(key);
64
- return undefined;
65
- }
66
- // Handle the special sentinel values for cached undefined/null
67
- if (entry.value === CACHED_UNDEFINED) {
68
- return undefined;
69
- }
70
- if (entry.value === CACHED_NULL) {
71
- return null;
72
- }
73
- return entry.value;
74
- }
75
- /**
76
- * Checks if a key exists in the cache (regardless of its value).
77
- * This is useful for distinguishing between cache misses and cached undefined values.
78
- *
79
- * @param {string} key - The key to check
80
- * @returns {boolean} True if the key exists in cache and hasn't expired, false otherwise
81
- */
82
- has(key) {
83
- const entry = this.cache.get(key);
84
- if (!entry)
85
- return false;
86
- // Check if entry has expired (skip check if TTL is 0 or negative)
87
- if (this.ttl > 0 && Date.now() - entry.timestamp > this.ttl) {
88
- this.cache.delete(key);
89
- return false;
90
- }
91
- return true;
92
- }
93
- /**
94
- * Stores a value in the cache. If the cache is full, the oldest entry is removed.
95
- *
96
- * @param {string} key - The key under which to store the value
97
- * @param {T} value - The value to cache
98
- *
99
- * @example
100
- * ```typescript
101
- * const cache = new MemoryCache<string>();
102
- * cache.set('greeting', 'Hello, World!');
103
- * ```
104
- */
105
- set(key, value) {
106
- // Remove oldest entry if cache is full (skip if maxSize is 0 or negative)
107
- if (this.maxSize > 0 && this.cache.size >= this.maxSize) {
108
- const oldestKey = this.cache.keys().next().value;
109
- if (oldestKey) {
110
- this.cache.delete(oldestKey);
111
- }
112
- }
113
- // Store undefined/null values as sentinels to distinguish from cache misses
114
- let valueToStore;
115
- if (value === undefined) {
116
- valueToStore = CACHED_UNDEFINED;
117
- }
118
- else if (value === null) {
119
- valueToStore = CACHED_NULL;
120
- }
121
- else {
122
- valueToStore = value;
123
- }
124
- this.cache.set(key, {
125
- value: valueToStore,
126
- timestamp: Date.now()
127
- });
128
- }
129
- /**
130
- * Removes a specific entry from the cache.
131
- *
132
- * @param {string} key - The key of the entry to remove
133
- * @returns {boolean} True if an element was removed, false if the key wasn't found
134
- *
135
- * @example
136
- * ```typescript
137
- * const cache = new MemoryCache<string>();
138
- * cache.set('key', 'value');
139
- * cache.delete('key'); // Returns true
140
- * cache.delete('nonexistent'); // Returns false
141
- * ```
142
- */
143
- delete(key) {
144
- return this.cache.delete(key);
145
- }
146
- async deleteAsync(key) {
147
- return Promise.resolve(this.cache.delete(key));
148
- }
149
- /**
150
- * Removes all entries from the cache.
151
- *
152
- * @example
153
- * ```typescript
154
- * const cache = new MemoryCache<string>();
155
- * cache.set('key1', 'value1');
156
- * cache.set('key2', 'value2');
157
- * cache.clear(); // Removes all entries
158
- * ```
159
- */
160
- clear() {
161
- this.cache.clear();
162
- }
163
- /**
164
- * Removes all entries from the cache whose keys start with the given prefix.
165
- *
166
- * @param {string} prefix - The prefix to match against cache keys
167
- * @returns {number} Number of entries removed
168
- *
169
- * @example
170
- * ```typescript
171
- * const cache = new MemoryCache<string>();
172
- * cache.set('user:123:name', 'John');
173
- * cache.set('user:123:email', 'john@example.com');
174
- * cache.set('post:456', 'Hello World');
175
- *
176
- * const removed = cache.deleteByPrefix('user:123:'); // Returns 2
177
- * ```
178
- */
179
- deleteByPrefix(prefix) {
180
- let count = 0;
181
- for (const key of this.cache.keys()) {
182
- if (key.startsWith(prefix)) {
183
- this.cache.delete(key);
184
- count++;
185
- }
186
- }
187
- return count;
188
- }
189
- /**
190
- * Removes all entries from the cache whose keys match the given wildcard pattern.
191
- * Supports asterisk (*) wildcards for flexible pattern matching.
192
- *
193
- * @param {string} magicString - The wildcard pattern to match against cache keys (use * for wildcards)
194
- * @returns {number} Number of entries removed
195
- *
196
- * @example
197
- * ```typescript
198
- * const cache = new MemoryCache<string>();
199
- * cache.set('user:123:name', 'John');
200
- * cache.set('user:123:email', 'john@example.com');
201
- * cache.set('user:456:name', 'Jane');
202
- * cache.set('post:789', 'Hello World');
203
- *
204
- * const removed1 = cache.deleteByMagicString('user:123:*'); // Returns 2 (matches name and email)
205
- * const removed2 = cache.deleteByMagicString('user:*:name'); // Returns 1 (matches Jane's name)
206
- * const removed3 = cache.deleteByMagicString('post:*'); // Returns 1 (matches the post)
207
- * ```
208
- */
209
- deleteByMagicString(magicString) {
210
- let count = 0;
211
- // Convert wildcard pattern to regex
212
- const escapedPattern = magicString
213
- .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
214
- .replace(/\*/g, '.*'); // Convert * to .*
215
- const regex = new RegExp(`^${escapedPattern}$`);
216
- for (const key of this.cache.keys()) {
217
- if (regex.test(key)) {
218
- this.cache.delete(key);
219
- count++;
220
- }
221
- }
222
- return count;
223
- }
224
- }
225
- /**
226
- * Cache decorator factory for method-level caching.
227
- * Provides a way to cache method results based on their arguments.
228
- *
229
- * @template T - The return type of the decorated method
230
- * @param {CacheOptions} options - Configuration options for the cache
231
- * @returns A method decorator that caches the results
232
- *
233
- * @example
234
- * ```typescript
235
- * class UserService {
236
- * @cached<User>({ ttl: 60000 })
237
- * async getUser(id: string): Promise<User> {
238
- * // Expensive operation
239
- * return await fetchUser(id);
240
- * }
241
- * }
242
- * ```
243
- */
244
- export function cached(options = {}) {
245
- const cache = new MemoryCache(options);
246
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
247
- return function (target, propertyKey, descriptor) {
248
- const originalMethod = descriptor.value;
249
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
250
- descriptor.value = function (...args) {
251
- const key = `${propertyKey}:${JSON.stringify(args)}`;
252
- // Use has() to check if key exists, then get() to retrieve value
253
- // This allows us to distinguish between cache miss and cached undefined
254
- if (cache.has(key)) {
255
- return cache.get(key);
256
- }
257
- const result = originalMethod.apply(this, args);
258
- cache.set(key, result);
259
- return result;
260
- };
261
- return descriptor;
262
- };
263
- }
5
+ export { MemoryCache } from '@humanspeak/memory-cache';
@@ -0,0 +1,52 @@
1
+ import type { Component } from 'svelte';
2
+ /**
3
+ * Generic component type for filter utilities.
4
+ * Allows Component, undefined, or null values.
5
+ */
6
+ type FilterComponent = Component<any, any, any> | undefined | null;
7
+ /**
8
+ * Creates a set of filter utility functions for renderer maps.
9
+ * This factory generates three functions: buildUnsupported, allowOnly, and excludeOnly.
10
+ *
11
+ * Used to eliminate code duplication between unsupportedRenderers.ts and unsupportedHtmlRenderers.ts.
12
+ *
13
+ * @template TKey - The string literal type for valid keys
14
+ * @template TResult - The result map type (e.g., Partial<Renderers> or HtmlRenderers)
15
+ *
16
+ * @param keys - Array of valid keys for this renderer type
17
+ * @param unsupportedComponent - The component to use for unsupported/disabled renderers
18
+ * @param defaultsMap - Map of keys to their default component implementations
19
+ *
20
+ * @returns Object containing buildUnsupported, allowOnly, and excludeOnly functions
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { createFilterUtilities } from './createFilterUtilities'
25
+ *
26
+ * type MyKey = 'foo' | 'bar' | 'baz'
27
+ * const keys: readonly MyKey[] = ['foo', 'bar', 'baz'] as const
28
+ * const UnsupportedComponent = () => null
29
+ * const defaults = { foo: FooComponent, bar: BarComponent, baz: BazComponent }
30
+ *
31
+ * const { buildUnsupported, allowOnly, excludeOnly } = createFilterUtilities<MyKey, Record<MyKey, Component>>(
32
+ * keys,
33
+ * UnsupportedComponent,
34
+ * defaults
35
+ * )
36
+ *
37
+ * // Block all renderers
38
+ * const allUnsupported = buildUnsupported()
39
+ *
40
+ * // Allow only 'foo' and 'bar', block 'baz'
41
+ * const allowList = allowOnly(['foo', 'bar'])
42
+ *
43
+ * // Block only 'baz', allow others with defaults
44
+ * const denyList = excludeOnly(['baz'])
45
+ * ```
46
+ */
47
+ export declare const createFilterUtilities: <TKey extends string, TResult extends Record<string, FilterComponent>>(keys: readonly TKey[], unsupportedComponent: FilterComponent, defaultsMap: Record<TKey, FilterComponent>) => {
48
+ buildUnsupported: () => TResult;
49
+ allowOnly: (_allowed: Array<TKey | [TKey, FilterComponent]>) => TResult;
50
+ excludeOnly: (_excluded: TKey[], _overrides?: Array<[TKey, FilterComponent]>) => TResult;
51
+ };
52
+ export {};
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Creates a set of filter utility functions for renderer maps.
3
+ * This factory generates three functions: buildUnsupported, allowOnly, and excludeOnly.
4
+ *
5
+ * Used to eliminate code duplication between unsupportedRenderers.ts and unsupportedHtmlRenderers.ts.
6
+ *
7
+ * @template TKey - The string literal type for valid keys
8
+ * @template TResult - The result map type (e.g., Partial<Renderers> or HtmlRenderers)
9
+ *
10
+ * @param keys - Array of valid keys for this renderer type
11
+ * @param unsupportedComponent - The component to use for unsupported/disabled renderers
12
+ * @param defaultsMap - Map of keys to their default component implementations
13
+ *
14
+ * @returns Object containing buildUnsupported, allowOnly, and excludeOnly functions
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { createFilterUtilities } from './createFilterUtilities'
19
+ *
20
+ * type MyKey = 'foo' | 'bar' | 'baz'
21
+ * const keys: readonly MyKey[] = ['foo', 'bar', 'baz'] as const
22
+ * const UnsupportedComponent = () => null
23
+ * const defaults = { foo: FooComponent, bar: BarComponent, baz: BazComponent }
24
+ *
25
+ * const { buildUnsupported, allowOnly, excludeOnly } = createFilterUtilities<MyKey, Record<MyKey, Component>>(
26
+ * keys,
27
+ * UnsupportedComponent,
28
+ * defaults
29
+ * )
30
+ *
31
+ * // Block all renderers
32
+ * const allUnsupported = buildUnsupported()
33
+ *
34
+ * // Allow only 'foo' and 'bar', block 'baz'
35
+ * const allowList = allowOnly(['foo', 'bar'])
36
+ *
37
+ * // Block only 'baz', allow others with defaults
38
+ * const denyList = excludeOnly(['baz'])
39
+ * ```
40
+ */
41
+ export const createFilterUtilities = (keys, unsupportedComponent, defaultsMap) => {
42
+ /**
43
+ * Checks if a key is valid for this renderer type.
44
+ */
45
+ const hasKey = (key) => keys.includes(key);
46
+ /**
47
+ * Builds a map where every key is set to the unsupported component.
48
+ * Useful for starting with a "deny all" approach.
49
+ */
50
+ const buildUnsupported = () => {
51
+ const result = {};
52
+ for (const key of keys) {
53
+ ;
54
+ result[key] = unsupportedComponent;
55
+ }
56
+ return result;
57
+ };
58
+ /**
59
+ * Produces a renderer map that allows only the specified keys.
60
+ * All non-listed keys are set to the unsupported component.
61
+ *
62
+ * Each entry can be either:
63
+ * - A key string (to use the default component for that key)
64
+ * - A tuple [key, component] to specify a custom component
65
+ */
66
+ const allowOnly = (allowed) => {
67
+ const result = buildUnsupported();
68
+ for (const entry of allowed) {
69
+ if (Array.isArray(entry)) {
70
+ const [key, component] = entry;
71
+ if (hasKey(key)) {
72
+ ;
73
+ result[key] = component;
74
+ }
75
+ }
76
+ else {
77
+ const key = entry;
78
+ if (hasKey(key)) {
79
+ ;
80
+ result[key] = defaultsMap[key];
81
+ }
82
+ }
83
+ }
84
+ return result;
85
+ };
86
+ /**
87
+ * Produces a renderer map that excludes only the specified keys.
88
+ * Excluded keys are set to the unsupported component.
89
+ * All other keys use the default components.
90
+ *
91
+ * Optionally, specific non-excluded keys can be overridden with custom components.
92
+ * Exclusions take precedence over overrides.
93
+ */
94
+ const excludeOnly = (excluded, overrides) => {
95
+ const result = {};
96
+ // Start with all defaults
97
+ for (const key of keys) {
98
+ ;
99
+ result[key] = defaultsMap[key];
100
+ }
101
+ // Mark excluded keys as unsupported
102
+ for (const key of excluded) {
103
+ if (hasKey(key)) {
104
+ ;
105
+ result[key] = unsupportedComponent;
106
+ }
107
+ }
108
+ // Apply overrides (exclusions take precedence)
109
+ if (overrides) {
110
+ for (const [key, component] of overrides) {
111
+ if (excluded.includes(key))
112
+ continue;
113
+ if (hasKey(key)) {
114
+ ;
115
+ result[key] = component;
116
+ }
117
+ }
118
+ }
119
+ return result;
120
+ };
121
+ return {
122
+ buildUnsupported,
123
+ allowOnly,
124
+ excludeOnly
125
+ };
126
+ };
@@ -39,6 +39,9 @@ export function parseAndCacheTokens(source, options, isInline) {
39
39
  const lexer = new Lexer(options);
40
40
  const parsedTokens = isInline ? lexer.inlineTokens(source) : lexer.lex(source);
41
41
  const cleanedTokens = shrinkHtmlTokens(parsedTokens);
42
+ if (typeof options.walkTokens === 'function') {
43
+ cleanedTokens.forEach(options.walkTokens);
44
+ }
42
45
  // Cache the cleaned tokens for next time
43
46
  tokenCache.setTokens(source, options, cleanedTokens);
44
47
  return cleanedTokens;
@@ -2,5 +2,5 @@ import Html from '../renderers/html/index.js';
2
2
  import { type Renderers } from './markdown-parser.js';
3
3
  export type RendererKey = Exclude<keyof Renderers, 'html'>;
4
4
  export declare const rendererKeysInternal: RendererKey[];
5
- export type HtmlKey = keyof typeof Html;
5
+ export type HtmlKey = keyof typeof Html & string;
6
6
  export declare const htmlRendererKeysInternal: HtmlKey[];
@@ -19,7 +19,7 @@ export declare const buildUnsupportedHTML: () => HtmlRenderers;
19
19
  * Produces an HTML renderer map that allows only the specified tags.
20
20
  * All non‑listed tags are set to `UnsupportedHTML`.
21
21
  *
22
- * Each entry can be either a tag name (to use the librarys default component for that tag),
22
+ * Each entry can be either a tag name (to use the library's default component for that tag),
23
23
  * or a tuple `[tag, component]` to specify a custom component for that tag.
24
24
  *
25
25
  * @function allowHtmlOnly
@@ -35,11 +35,11 @@ export declare const buildUnsupportedHTML: () => HtmlRenderers;
35
35
  * // Allow a custom component for div while allowing the default for a
36
36
  * const html = allowHtmlOnly([['div', MyDiv], 'a'])
37
37
  */
38
- export declare const allowHtmlOnly: (allowed: Array<HtmlKey | [HtmlKey, Component | null]>) => HtmlRenderers;
38
+ export declare const allowHtmlOnly: (_allowed: Array<HtmlKey | [HtmlKey, Component | null]>) => HtmlRenderers;
39
39
  /**
40
40
  * Produces an HTML renderer map that excludes only the specified tags.
41
41
  * Excluded tags are mapped to `UnsupportedHTML`, while all other tags use the
42
- * librarys default components. Optionally, you can override specific non‑excluded
42
+ * library's default components. Optionally, you can override specific non‑excluded
43
43
  * tags with custom components using `[tag, component]` tuples.
44
44
  *
45
45
  * Exclusions take precedence over overrides. If a tag is listed in `excluded`, an
@@ -59,4 +59,4 @@ export declare const allowHtmlOnly: (allowed: Array<HtmlKey | [HtmlKey, Componen
59
59
  * // Disable span; override 'a' to CustomA component
60
60
  * const html = excludeHtmlOnly(['span'], [['a', CustomA]])
61
61
  */
62
- export declare const excludeHtmlOnly: (excluded: HtmlKey[], overrides?: Array<[HtmlKey, Component | null]>) => HtmlRenderers;
62
+ export declare const excludeHtmlOnly: (_excluded: HtmlKey[], _overrides?: Array<[HtmlKey, Component | null]>) => HtmlRenderers;
@@ -1,5 +1,8 @@
1
1
  import Html, { UnsupportedHTML } from '../renderers/html/index.js';
2
2
  import { htmlRendererKeysInternal } from './rendererKeys.js';
3
+ import { createFilterUtilities } from './createFilterUtilities.js';
4
+ // Create filter utilities using the generic factory
5
+ const filterUtils = createFilterUtilities(htmlRendererKeysInternal, UnsupportedHTML, Html);
3
6
  /**
4
7
  * Builds a map of HTML renderers where every known HTML tag is mapped to `UnsupportedHTML`.
5
8
  * This is useful when you want to disable all built‑in HTML rendering and provide
@@ -13,18 +16,12 @@ import { htmlRendererKeysInternal } from './rendererKeys.js';
13
16
  * html: buildUnsupportedHTML()
14
17
  * }
15
18
  */
16
- export const buildUnsupportedHTML = () => {
17
- const result = {};
18
- for (const key of htmlRendererKeysInternal) {
19
- result[key] = UnsupportedHTML;
20
- }
21
- return result;
22
- };
19
+ export const buildUnsupportedHTML = filterUtils.buildUnsupported;
23
20
  /**
24
21
  * Produces an HTML renderer map that allows only the specified tags.
25
22
  * All non‑listed tags are set to `UnsupportedHTML`.
26
23
  *
27
- * Each entry can be either a tag name (to use the librarys default component for that tag),
24
+ * Each entry can be either a tag name (to use the library's default component for that tag),
28
25
  * or a tuple `[tag, component]` to specify a custom component for that tag.
29
26
  *
30
27
  * @function allowHtmlOnly
@@ -40,27 +37,11 @@ export const buildUnsupportedHTML = () => {
40
37
  * // Allow a custom component for div while allowing the default for a
41
38
  * const html = allowHtmlOnly([['div', MyDiv], 'a'])
42
39
  */
43
- export const allowHtmlOnly = (allowed) => {
44
- const result = buildUnsupportedHTML();
45
- for (const entry of allowed) {
46
- if (Array.isArray(entry)) {
47
- const [tag, component] = entry;
48
- // Only set if the tag exists in our Html map
49
- if (tag in Html)
50
- result[tag] = component;
51
- }
52
- else {
53
- const tag = entry;
54
- if (tag in Html)
55
- result[tag] = Html[tag];
56
- }
57
- }
58
- return result;
59
- };
40
+ export const allowHtmlOnly = filterUtils.allowOnly;
60
41
  /**
61
42
  * Produces an HTML renderer map that excludes only the specified tags.
62
43
  * Excluded tags are mapped to `UnsupportedHTML`, while all other tags use the
63
- * librarys default components. Optionally, you can override specific non‑excluded
44
+ * library's default components. Optionally, you can override specific non‑excluded
64
45
  * tags with custom components using `[tag, component]` tuples.
65
46
  *
66
47
  * Exclusions take precedence over overrides. If a tag is listed in `excluded`, an
@@ -80,20 +61,4 @@ export const allowHtmlOnly = (allowed) => {
80
61
  * // Disable span; override 'a' to CustomA component
81
62
  * const html = excludeHtmlOnly(['span'], [['a', CustomA]])
82
63
  */
83
- export const excludeHtmlOnly = (excluded, overrides) => {
84
- const result = { ...Html };
85
- for (const tag of excluded) {
86
- if (tag in Html)
87
- result[tag] = UnsupportedHTML;
88
- }
89
- if (overrides) {
90
- for (const [tag, component] of overrides) {
91
- // Exclusions take precedence; do not override excluded tags
92
- if (excluded.includes(tag))
93
- continue;
94
- if (tag in Html)
95
- result[tag] = component;
96
- }
97
- }
98
- return result;
99
- };
64
+ export const excludeHtmlOnly = filterUtils.excludeOnly;
@@ -5,7 +5,7 @@ import { type RendererKey } from './rendererKeys.js';
5
5
  * is set to the `Unsupported` component.
6
6
  *
7
7
  * @function buildUnsupportedRenderers
8
- * @returns {Partial<Renderers>} A map with all non‑HTML renderers set to `Unsupported`.
8
+ * @returns {Omit<Renderers, 'html'>} A map with all non‑HTML renderers set to `Unsupported`.
9
9
  * @example
10
10
  * import { buildUnsupportedRenderers } from '@humanspeak/svelte-markdown'
11
11
  * const renderers = {
@@ -13,17 +13,17 @@ import { type RendererKey } from './rendererKeys.js';
13
13
  * html: {} // customize HTML separately
14
14
  * }
15
15
  */
16
- export declare const buildUnsupportedRenderers: () => Partial<Renderers>;
16
+ export declare const buildUnsupportedRenderers: () => Omit<Renderers, "html">;
17
17
  /**
18
18
  * Produces a renderer map that allows only the specified markdown renderers (excluding `html`).
19
19
  * All non‑listed renderer keys are set to `Unsupported`.
20
- * Each entry can be either a renderer key (to use the librarys default component),
20
+ * Each entry can be either a renderer key (to use the library's default component),
21
21
  * or a tuple `[key, component]` to specify a custom component for that key.
22
22
  *
23
23
  * @function allowRenderersOnly
24
24
  * @param {Array<RendererKey | [RendererKey, RendererComponent]>} allowed
25
25
  * Renderer keys to allow, or tuples for custom component overrides.
26
- * @returns {Partial<Renderers>} A renderer map with only the provided keys enabled.
26
+ * @returns {Omit<Renderers, 'html'>} A renderer map with only the provided keys enabled.
27
27
  * @example
28
28
  * // Allow only paragraph and link with defaults
29
29
  * const renderers = allowRenderersOnly(['paragraph', 'link'])
@@ -32,10 +32,10 @@ export declare const buildUnsupportedRenderers: () => Partial<Renderers>;
32
32
  * // Allow paragraph with a custom component
33
33
  * const renderers = allowRenderersOnly([['paragraph', MyParagraph]])
34
34
  */
35
- export declare const allowRenderersOnly: (allowed: Array<RendererKey | [RendererKey, RendererComponent]>) => Partial<Renderers>;
35
+ export declare const allowRenderersOnly: (_allowed: Array<RendererKey | [RendererKey, RendererComponent]>) => Omit<Renderers, "html">;
36
36
  /**
37
37
  * Produces a renderer map that excludes only the specified markdown renderer keys (excluding `html`).
38
- * Excluded keys are mapped to `Unsupported`, while all other keys use the librarys default components.
38
+ * Excluded keys are mapped to `Unsupported`, while all other keys use the library's default components.
39
39
  * Optionally override specific non‑excluded keys with custom components via `[key, component]` tuples.
40
40
  *
41
41
  * Exclusions take precedence over overrides.
@@ -45,7 +45,7 @@ export declare const allowRenderersOnly: (allowed: Array<RendererKey | [Renderer
45
45
  * Renderer keys to exclude (set to `Unsupported`).
46
46
  * @param {Array<[RendererKey, RendererComponent]>} [overrides]
47
47
  * Optional tuples mapping non‑excluded keys to custom components.
48
- * @returns {Partial<Renderers>} A renderer map with only the provided keys excluded.
48
+ * @returns {Omit<Renderers, 'html'>} A renderer map with only the provided keys excluded.
49
49
  * @example
50
50
  * // Disable just paragraph and link, keep others as defaults
51
51
  * const renderers = excludeRenderersOnly(['paragraph', 'link'])
@@ -54,4 +54,4 @@ export declare const allowRenderersOnly: (allowed: Array<RendererKey | [Renderer
54
54
  * // Disable link; override paragraph to a custom component
55
55
  * const renderers = excludeRenderersOnly(['link'], [['paragraph', MyParagraph]])
56
56
  */
57
- export declare const excludeRenderersOnly: (excluded: RendererKey[], overrides?: Array<[RendererKey, RendererComponent]>) => Partial<Renderers>;
57
+ export declare const excludeRenderersOnly: (_excluded: RendererKey[], _overrides?: Array<[RendererKey, RendererComponent]>) => Omit<Renderers, "html">;
@@ -1,13 +1,15 @@
1
1
  import { Unsupported } from '../renderers/index.js';
2
2
  import { defaultRenderers } from './markdown-parser.js';
3
3
  import { rendererKeysInternal } from './rendererKeys.js';
4
- const allRendererKeys = rendererKeysInternal;
4
+ import { createFilterUtilities } from './createFilterUtilities.js';
5
+ // Create filter utilities using the generic factory
6
+ const filterUtils = createFilterUtilities(rendererKeysInternal, Unsupported, defaultRenderers);
5
7
  /**
6
8
  * Builds a map where every markdown renderer (excluding the special `html` map)
7
9
  * is set to the `Unsupported` component.
8
10
  *
9
11
  * @function buildUnsupportedRenderers
10
- * @returns {Partial<Renderers>} A map with all non‑HTML renderers set to `Unsupported`.
12
+ * @returns {Omit<Renderers, 'html'>} A map with all non‑HTML renderers set to `Unsupported`.
11
13
  * @example
12
14
  * import { buildUnsupportedRenderers } from '@humanspeak/svelte-markdown'
13
15
  * const renderers = {
@@ -15,23 +17,17 @@ const allRendererKeys = rendererKeysInternal;
15
17
  * html: {} // customize HTML separately
16
18
  * }
17
19
  */
18
- export const buildUnsupportedRenderers = () => {
19
- const result = {};
20
- for (const key of allRendererKeys) {
21
- result[key] = Unsupported;
22
- }
23
- return result;
24
- };
20
+ export const buildUnsupportedRenderers = filterUtils.buildUnsupported;
25
21
  /**
26
22
  * Produces a renderer map that allows only the specified markdown renderers (excluding `html`).
27
23
  * All non‑listed renderer keys are set to `Unsupported`.
28
- * Each entry can be either a renderer key (to use the librarys default component),
24
+ * Each entry can be either a renderer key (to use the library's default component),
29
25
  * or a tuple `[key, component]` to specify a custom component for that key.
30
26
  *
31
27
  * @function allowRenderersOnly
32
28
  * @param {Array<RendererKey | [RendererKey, RendererComponent]>} allowed
33
29
  * Renderer keys to allow, or tuples for custom component overrides.
34
- * @returns {Partial<Renderers>} A renderer map with only the provided keys enabled.
30
+ * @returns {Omit<Renderers, 'html'>} A renderer map with only the provided keys enabled.
35
31
  * @example
36
32
  * // Allow only paragraph and link with defaults
37
33
  * const renderers = allowRenderersOnly(['paragraph', 'link'])
@@ -40,25 +36,10 @@ export const buildUnsupportedRenderers = () => {
40
36
  * // Allow paragraph with a custom component
41
37
  * const renderers = allowRenderersOnly([['paragraph', MyParagraph]])
42
38
  */
43
- export const allowRenderersOnly = (allowed) => {
44
- const result = buildUnsupportedRenderers();
45
- for (const entry of allowed) {
46
- if (Array.isArray(entry)) {
47
- const [key, component] = entry;
48
- if (allRendererKeys.includes(key))
49
- result[key] = component;
50
- }
51
- else {
52
- const key = entry;
53
- if (allRendererKeys.includes(key))
54
- result[key] = defaultRenderers[key];
55
- }
56
- }
57
- return result;
58
- };
39
+ export const allowRenderersOnly = filterUtils.allowOnly;
59
40
  /**
60
41
  * Produces a renderer map that excludes only the specified markdown renderer keys (excluding `html`).
61
- * Excluded keys are mapped to `Unsupported`, while all other keys use the librarys default components.
42
+ * Excluded keys are mapped to `Unsupported`, while all other keys use the library's default components.
62
43
  * Optionally override specific non‑excluded keys with custom components via `[key, component]` tuples.
63
44
  *
64
45
  * Exclusions take precedence over overrides.
@@ -68,7 +49,7 @@ export const allowRenderersOnly = (allowed) => {
68
49
  * Renderer keys to exclude (set to `Unsupported`).
69
50
  * @param {Array<[RendererKey, RendererComponent]>} [overrides]
70
51
  * Optional tuples mapping non‑excluded keys to custom components.
71
- * @returns {Partial<Renderers>} A renderer map with only the provided keys excluded.
52
+ * @returns {Omit<Renderers, 'html'>} A renderer map with only the provided keys excluded.
72
53
  * @example
73
54
  * // Disable just paragraph and link, keep others as defaults
74
55
  * const renderers = excludeRenderersOnly(['paragraph', 'link'])
@@ -77,22 +58,4 @@ export const allowRenderersOnly = (allowed) => {
77
58
  * // Disable link; override paragraph to a custom component
78
59
  * const renderers = excludeRenderersOnly(['link'], [['paragraph', MyParagraph]])
79
60
  */
80
- export const excludeRenderersOnly = (excluded, overrides) => {
81
- const result = {};
82
- for (const key of allRendererKeys) {
83
- result[key] = defaultRenderers[key];
84
- }
85
- for (const key of excluded) {
86
- if (allRendererKeys.includes(key))
87
- result[key] = Unsupported;
88
- }
89
- if (overrides) {
90
- for (const [key, component] of overrides) {
91
- if (excluded.includes(key))
92
- continue;
93
- if (allRendererKeys.includes(key))
94
- result[key] = component;
95
- }
96
- }
97
- return result;
98
- };
61
+ export const excludeRenderersOnly = filterUtils.excludeOnly;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanspeak/svelte-markdown",
3
- "version": "0.8.13",
3
+ "version": "0.8.14",
4
4
  "description": "Fast, customizable markdown renderer for Svelte with built-in caching, TypeScript support, and Svelte 5 runes",
5
5
  "keywords": [
6
6
  "svelte",
@@ -63,51 +63,52 @@
63
63
  }
64
64
  },
65
65
  "dependencies": {
66
+ "@humanspeak/memory-cache": "^1.0.2",
66
67
  "github-slugger": "^2.0.0",
67
68
  "htmlparser2": "^10.0.0",
68
- "marked": "^16.4.0"
69
+ "marked": "^17.0.1"
69
70
  },
70
71
  "devDependencies": {
71
- "@eslint/compat": "^1.4.0",
72
- "@eslint/js": "^9.37.0",
73
- "@playwright/test": "^1.56.0",
74
- "@sveltejs/adapter-auto": "^6.1.1",
75
- "@sveltejs/kit": "^2.46.3",
76
- "@sveltejs/package": "^2.5.4",
77
- "@sveltejs/vite-plugin-svelte": "^6.2.1",
72
+ "@eslint/compat": "^2.0.0",
73
+ "@eslint/js": "^9.39.2",
74
+ "@playwright/test": "^1.57.0",
75
+ "@sveltejs/adapter-auto": "^7.0.0",
76
+ "@sveltejs/kit": "^2.49.3",
77
+ "@sveltejs/package": "^2.5.7",
78
+ "@sveltejs/vite-plugin-svelte": "^6.2.2",
78
79
  "@testing-library/jest-dom": "^6.9.1",
79
- "@testing-library/svelte": "^5.2.8",
80
+ "@testing-library/svelte": "^5.3.1",
80
81
  "@testing-library/user-event": "^14.6.1",
81
- "@types/node": "^24.7.0",
82
- "@typescript-eslint/eslint-plugin": "^8.46.0",
83
- "@typescript-eslint/parser": "^8.46.0",
84
- "@vitest/coverage-v8": "^3.2.4",
82
+ "@types/node": "^25.0.3",
83
+ "@typescript-eslint/eslint-plugin": "^8.52.0",
84
+ "@typescript-eslint/parser": "^8.52.0",
85
+ "@vitest/coverage-v8": "^4.0.16",
85
86
  "concurrently": "^9.2.1",
86
- "eslint": "^9.37.0",
87
+ "eslint": "^9.39.2",
87
88
  "eslint-config-prettier": "^10.1.8",
88
89
  "eslint-plugin-import": "^2.32.0",
89
- "eslint-plugin-svelte": "^3.12.4",
90
- "eslint-plugin-unused-imports": "^4.2.0",
91
- "globals": "^16.4.0",
90
+ "eslint-plugin-svelte": "^3.13.1",
91
+ "eslint-plugin-unused-imports": "^4.3.0",
92
+ "globals": "^17.0.0",
92
93
  "husky": "^9.1.7",
93
- "jsdom": "^27.0.0",
94
- "prettier": "^3.6.2",
94
+ "jsdom": "^27.4.0",
95
+ "prettier": "^3.7.4",
95
96
  "prettier-plugin-organize-imports": "^4.3.0",
96
- "prettier-plugin-svelte": "^3.4.0",
97
- "prettier-plugin-tailwindcss": "^0.6.14",
98
- "publint": "^0.3.14",
99
- "svelte": "^5.39.11",
100
- "svelte-check": "^4.3.2",
97
+ "prettier-plugin-svelte": "^3.4.1",
98
+ "prettier-plugin-tailwindcss": "^0.7.2",
99
+ "publint": "^0.3.16",
100
+ "svelte": "^5.46.1",
101
+ "svelte-check": "^4.3.5",
101
102
  "typescript": "^5.9.3",
102
- "typescript-eslint": "^8.46.0",
103
- "vite": "^7.1.9",
104
- "vitest": "^3.2.4"
103
+ "typescript-eslint": "^8.52.0",
104
+ "vite": "^7.3.1",
105
+ "vitest": "^4.0.16"
105
106
  },
106
107
  "peerDependencies": {
107
108
  "svelte": "^5.0.0"
108
109
  },
109
110
  "volta": {
110
- "node": "22.17.1"
111
+ "node": "24.12.0"
111
112
  },
112
113
  "publishConfig": {
113
114
  "access": "public"