writr 4.5.1 → 5.0.1
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.
- package/README.md +251 -0
- package/dist/writr.d.ts +10 -3
- package/dist/writr.js +88 -28
- package/package.json +33 -35
package/README.md
CHANGED
|
@@ -56,7 +56,18 @@ plugins and working with the processor directly.
|
|
|
56
56
|
- [`.loadFromFileSync(filePath: string)`](#loadfromfilesyncfilepath-string)
|
|
57
57
|
- [`.saveToFile(filePath: string)`](#savetofilefilepath-string)
|
|
58
58
|
- [`.saveToFileSync(filePath: string)`](#savetofilesyncfilepath-string)
|
|
59
|
+
- [Caching On Render](#caching-on-render)
|
|
60
|
+
- [GitHub Flavored Markdown (GFM)](#github-flavored-markdown-gfm)
|
|
61
|
+
- [GFM Features](#gfm-features)
|
|
62
|
+
- [Using GFM](#using-gfm)
|
|
63
|
+
- [Disabling GFM](#disabling-gfm)
|
|
59
64
|
- [Hooks](#hooks)
|
|
65
|
+
- [Emitters](#emitters)
|
|
66
|
+
- [Error Events](#error-events)
|
|
67
|
+
- [Listening to Error Events](#listening-to-error-events)
|
|
68
|
+
- [Methods that Emit Errors](#methods-that-emit-errors)
|
|
69
|
+
- [Error Event Examples](#error-event-examples)
|
|
70
|
+
- [Event Emitter Methods](#event-emitter-methods)
|
|
60
71
|
- [ESM and Node Version Support](#esm-and-node-version-support)
|
|
61
72
|
- [Code of Conduct and Contributing](#code-of-conduct-and-contributing)
|
|
62
73
|
- [License](#license)
|
|
@@ -413,6 +424,117 @@ writr.cache.store.lruSize = 100;
|
|
|
413
424
|
writr.cache.store.ttl = '5m'; // setting it to 5 minutes
|
|
414
425
|
```
|
|
415
426
|
|
|
427
|
+
# GitHub Flavored Markdown (GFM)
|
|
428
|
+
|
|
429
|
+
Writr includes full support for [GitHub Flavored Markdown](https://github.github.com/gfm/) (GFM) through the `remark-gfm` and `remark-github-blockquote-alert` plugins. GFM is enabled by default and adds several powerful features to standard Markdown.
|
|
430
|
+
|
|
431
|
+
## GFM Features
|
|
432
|
+
|
|
433
|
+
When GFM is enabled (which it is by default), you get access to the following features:
|
|
434
|
+
|
|
435
|
+
### Tables
|
|
436
|
+
|
|
437
|
+
Create tables using pipes and hyphens:
|
|
438
|
+
|
|
439
|
+
```markdown
|
|
440
|
+
| Feature | Supported |
|
|
441
|
+
|---------|-----------|
|
|
442
|
+
| Tables | Yes |
|
|
443
|
+
| Alerts | Yes |
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Strikethrough
|
|
447
|
+
|
|
448
|
+
Use `~~` to create strikethrough text:
|
|
449
|
+
|
|
450
|
+
```markdown
|
|
451
|
+
~~This text is crossed out~~
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Task Lists
|
|
455
|
+
|
|
456
|
+
Create interactive checkboxes:
|
|
457
|
+
|
|
458
|
+
```markdown
|
|
459
|
+
- [x] Completed task
|
|
460
|
+
- [ ] Incomplete task
|
|
461
|
+
- [ ] Another task
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Autolinks
|
|
465
|
+
|
|
466
|
+
URLs are automatically converted to clickable links:
|
|
467
|
+
|
|
468
|
+
```markdown
|
|
469
|
+
https://github.com
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### GitHub Blockquote Alerts
|
|
473
|
+
|
|
474
|
+
GitHub-style alerts are supported to emphasize critical information. These are blockquote-based admonitions that render with special styling:
|
|
475
|
+
|
|
476
|
+
```markdown
|
|
477
|
+
> [!NOTE]
|
|
478
|
+
> Useful information that users should know, even when skimming content.
|
|
479
|
+
|
|
480
|
+
> [!TIP]
|
|
481
|
+
> Helpful advice for doing things better or more easily.
|
|
482
|
+
|
|
483
|
+
> [!IMPORTANT]
|
|
484
|
+
> Key information users need to know to achieve their goal.
|
|
485
|
+
|
|
486
|
+
> [!WARNING]
|
|
487
|
+
> Urgent info that needs immediate user attention to avoid problems.
|
|
488
|
+
|
|
489
|
+
> [!CAUTION]
|
|
490
|
+
> Advises about risks or negative outcomes of certain actions.
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Using GFM
|
|
494
|
+
|
|
495
|
+
GFM is enabled by default. Here's an example:
|
|
496
|
+
|
|
497
|
+
```javascript
|
|
498
|
+
import { Writr } from 'writr';
|
|
499
|
+
|
|
500
|
+
const markdown = `
|
|
501
|
+
# Task List Example
|
|
502
|
+
|
|
503
|
+
- [x] Learn Writr basics
|
|
504
|
+
- [ ] Master GFM features
|
|
505
|
+
|
|
506
|
+
> [!NOTE]
|
|
507
|
+
> GitHub Flavored Markdown is enabled by default!
|
|
508
|
+
|
|
509
|
+
| Feature | Status |
|
|
510
|
+
|---------|--------|
|
|
511
|
+
| GFM | ✓ |
|
|
512
|
+
`;
|
|
513
|
+
|
|
514
|
+
const writr = new Writr(markdown);
|
|
515
|
+
const html = await writr.render(); // Renders with full GFM support
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Disabling GFM
|
|
519
|
+
|
|
520
|
+
If you need to disable GFM features, you can set `gfm: false` in the render options:
|
|
521
|
+
|
|
522
|
+
```javascript
|
|
523
|
+
import { Writr } from 'writr';
|
|
524
|
+
|
|
525
|
+
const writr = new Writr('~~strikethrough~~ text');
|
|
526
|
+
|
|
527
|
+
// Disable GFM
|
|
528
|
+
const html = await writr.render({ gfm: false });
|
|
529
|
+
// Output: <p>~~strikethrough~~ text</p>
|
|
530
|
+
|
|
531
|
+
// With GFM enabled (default)
|
|
532
|
+
const htmlWithGfm = await writr.render({ gfm: true });
|
|
533
|
+
// Output: <p><del>strikethrough</del> text</p>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Note: When GFM is disabled, GitHub blockquote alerts will not be processed and will render as regular blockquotes.
|
|
537
|
+
|
|
416
538
|
# Hooks
|
|
417
539
|
|
|
418
540
|
Hooks are a way to add additional parsing to the render pipeline. You can add hooks to the the Writr instance. Here is an example of adding a hook to the instance of Writr:
|
|
@@ -476,6 +598,135 @@ export type loadFromFileData = {
|
|
|
476
598
|
|
|
477
599
|
This is called when you call `loadFromFile`, `loadFromFileSync`.
|
|
478
600
|
|
|
601
|
+
# Emitters
|
|
602
|
+
|
|
603
|
+
Writr extends the [Hookified](https://github.com/jaredwray/hookified) class, which provides event emitter capabilities. This means you can listen to events emitted by Writr during its lifecycle, particularly error events.
|
|
604
|
+
|
|
605
|
+
## Error Events
|
|
606
|
+
|
|
607
|
+
Writr emits an `error` event whenever an error occurs in any of its methods. This provides a centralized way to handle errors without wrapping every method call in a try/catch block.
|
|
608
|
+
|
|
609
|
+
### Listening to Error Events
|
|
610
|
+
|
|
611
|
+
You can listen to error events using the `.on()` method:
|
|
612
|
+
|
|
613
|
+
```javascript
|
|
614
|
+
import { Writr } from 'writr';
|
|
615
|
+
|
|
616
|
+
const writr = new Writr('# Hello World');
|
|
617
|
+
|
|
618
|
+
// Listen for any errors
|
|
619
|
+
writr.on('error', (error) => {
|
|
620
|
+
console.error('An error occurred:', error.message);
|
|
621
|
+
// Handle the error appropriately
|
|
622
|
+
// Log to error tracking service, display to user, etc.
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// Now when any error occurs, your listener will be notified
|
|
626
|
+
try {
|
|
627
|
+
await writr.render();
|
|
628
|
+
} catch (error) {
|
|
629
|
+
// Error is also thrown, so you can handle it here too
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Methods that Emit Errors
|
|
634
|
+
|
|
635
|
+
The following methods emit error events when they fail:
|
|
636
|
+
|
|
637
|
+
**Rendering Methods:**
|
|
638
|
+
- `render()` - Emits error before throwing when markdown rendering fails
|
|
639
|
+
- `renderSync()` - Emits error before throwing when markdown rendering fails
|
|
640
|
+
- `renderReact()` - Emits error before throwing when React rendering fails
|
|
641
|
+
- `renderReactSync()` - Emits error before throwing when React rendering fails
|
|
642
|
+
|
|
643
|
+
**Validation Methods:**
|
|
644
|
+
- `validate()` - Emits error when validation fails (returns error in result object)
|
|
645
|
+
- `validateSync()` - Emits error when validation fails (returns error in result object)
|
|
646
|
+
|
|
647
|
+
**File Operations:**
|
|
648
|
+
- `renderToFile()` - Emits error when file writing fails (does not throw if `throwErrors: false`)
|
|
649
|
+
- `renderToFileSync()` - Emits error when file writing fails (does not throw if `throwErrors: false`)
|
|
650
|
+
- `loadFromFile()` - Emits error when file reading fails (does not throw if `throwErrors: false`)
|
|
651
|
+
- `loadFromFileSync()` - Emits error when file reading fails (does not throw if `throwErrors: false`)
|
|
652
|
+
- `saveToFile()` - Emits error when file writing fails (does not throw if `throwErrors: false`)
|
|
653
|
+
- `saveToFileSync()` - Emits error when file writing fails (does not throw if `throwErrors: false`)
|
|
654
|
+
|
|
655
|
+
**Front Matter Operations:**
|
|
656
|
+
- `frontMatter` getter - Emits error when YAML parsing fails
|
|
657
|
+
- `frontMatter` setter - Emits error when YAML serialization fails
|
|
658
|
+
|
|
659
|
+
### Error Event Examples
|
|
660
|
+
|
|
661
|
+
**Example 1: Global Error Handler**
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
import { Writr } from 'writr';
|
|
665
|
+
|
|
666
|
+
const writr = new Writr();
|
|
667
|
+
|
|
668
|
+
// Set up a global error handler
|
|
669
|
+
writr.on('error', (error) => {
|
|
670
|
+
// Log to your monitoring service
|
|
671
|
+
console.error('Writr error:', error);
|
|
672
|
+
|
|
673
|
+
// Send to error tracking (e.g., Sentry, Rollbar)
|
|
674
|
+
// errorTracker.captureException(error);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// All errors will be emitted to the listener above
|
|
678
|
+
await writr.loadFromFile('./content.md');
|
|
679
|
+
const html = await writr.render();
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Example 2: Validation with Error Listening**
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
import { Writr } from 'writr';
|
|
686
|
+
|
|
687
|
+
const writr = new Writr('# My Content');
|
|
688
|
+
let lastError = null;
|
|
689
|
+
|
|
690
|
+
writr.on('error', (error) => {
|
|
691
|
+
lastError = error;
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const result = await writr.validate();
|
|
695
|
+
|
|
696
|
+
if (!result.valid) {
|
|
697
|
+
console.log('Validation failed');
|
|
698
|
+
console.log('Error details:', lastError);
|
|
699
|
+
// result.error is also available
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**Example 3: File Operations Without Try/Catch**
|
|
704
|
+
|
|
705
|
+
```javascript
|
|
706
|
+
import { Writr } from 'writr';
|
|
707
|
+
|
|
708
|
+
const writr = new Writr('# Content', { throwErrors: false });
|
|
709
|
+
|
|
710
|
+
writr.on('error', (error) => {
|
|
711
|
+
console.error('File operation failed:', error.message);
|
|
712
|
+
// Handle gracefully - maybe use default content
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// Won't throw, but will emit error event if file doesn't exist
|
|
716
|
+
await writr.loadFromFile('./maybe-missing.md');
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Event Emitter Methods
|
|
720
|
+
|
|
721
|
+
Since Writr extends Hookified, you have access to standard event emitter methods:
|
|
722
|
+
|
|
723
|
+
- `writr.on(event, handler)` - Add an event listener
|
|
724
|
+
- `writr.once(event, handler)` - Add a one-time event listener
|
|
725
|
+
- `writr.off(event, handler)` - Remove an event listener
|
|
726
|
+
- `writr.emit(event, data)` - Emit an event (used internally)
|
|
727
|
+
|
|
728
|
+
For more information about event handling capabilities, see the [Hookified documentation](https://github.com/jaredwray/hookified).
|
|
729
|
+
|
|
479
730
|
# ESM and Node Version Support
|
|
480
731
|
|
|
481
732
|
This package is ESM only and tested on the current lts version and its previous. Please don't open issues for questions regarding CommonJS / ESM or previous Nodejs versions.
|
package/dist/writr.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ declare class WritrCache {
|
|
|
16
16
|
set(markdown: string, value: string, options?: RenderOptions): void;
|
|
17
17
|
clear(): void;
|
|
18
18
|
hash(markdown: string, options?: RenderOptions): string;
|
|
19
|
+
/**
|
|
20
|
+
* Sanitizes render options to only include serializable properties for caching.
|
|
21
|
+
* This prevents issues with structuredClone when options contain Promises, functions, or circular references.
|
|
22
|
+
* @param {RenderOptions} [options] The render options to sanitize
|
|
23
|
+
* @returns {RenderOptions | undefined} A new object with only the known RenderOptions properties
|
|
24
|
+
*/
|
|
25
|
+
private sanitizeOptions;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
/**
|
|
@@ -37,8 +44,8 @@ type WritrOptions = {
|
|
|
37
44
|
* @property {boolean} [highlight] - Code highlighting (default: true)
|
|
38
45
|
* @property {boolean} [gfm] - Github flavor markdown (default: true)
|
|
39
46
|
* @property {boolean} [math] - Math support (default: true)
|
|
40
|
-
* @property {boolean} [mdx] - MDX support (default:
|
|
41
|
-
* @property {boolean} [caching] - Caching (default:
|
|
47
|
+
* @property {boolean} [mdx] - MDX support (default: false)
|
|
48
|
+
* @property {boolean} [caching] - Caching (default: true)
|
|
42
49
|
*/
|
|
43
50
|
type RenderOptions = {
|
|
44
51
|
emoji?: boolean;
|
|
@@ -208,9 +215,9 @@ declare class Writr extends Hookified {
|
|
|
208
215
|
* @returns {void}
|
|
209
216
|
*/
|
|
210
217
|
saveToFileSync(filePath: string): void;
|
|
218
|
+
mergeOptions(current: WritrOptions, options: WritrOptions): WritrOptions;
|
|
211
219
|
private isCacheEnabled;
|
|
212
220
|
private createProcessor;
|
|
213
|
-
private mergeOptions;
|
|
214
221
|
private mergeRenderOptions;
|
|
215
222
|
}
|
|
216
223
|
|
package/dist/writr.js
CHANGED
|
@@ -10,6 +10,7 @@ import rehypeSlug from "rehype-slug";
|
|
|
10
10
|
import rehypeStringify from "rehype-stringify";
|
|
11
11
|
import remarkEmoji from "remark-emoji";
|
|
12
12
|
import remarkGfm from "remark-gfm";
|
|
13
|
+
import remarkGithubBlockquoteAlert from "remark-github-blockquote-alert";
|
|
13
14
|
import remarkMath from "remark-math";
|
|
14
15
|
import remarkMDX from "remark-mdx";
|
|
15
16
|
import remarkParse from "remark-parse";
|
|
@@ -18,11 +19,12 @@ import remarkToc from "remark-toc";
|
|
|
18
19
|
import { unified } from "unified";
|
|
19
20
|
|
|
20
21
|
// src/writr-cache.ts
|
|
21
|
-
import {
|
|
22
|
+
import { CacheableMemory } from "cacheable";
|
|
23
|
+
import { Hashery } from "hashery";
|
|
22
24
|
var WritrCache = class {
|
|
23
25
|
_store = new CacheableMemory();
|
|
24
26
|
_hashStore = new CacheableMemory();
|
|
25
|
-
_hash = new
|
|
27
|
+
_hash = new Hashery();
|
|
26
28
|
get store() {
|
|
27
29
|
return this._store;
|
|
28
30
|
}
|
|
@@ -42,15 +44,53 @@ var WritrCache = class {
|
|
|
42
44
|
this._hashStore.clear();
|
|
43
45
|
}
|
|
44
46
|
hash(markdown, options) {
|
|
45
|
-
const
|
|
47
|
+
const sanitizedOptions = this.sanitizeOptions(options);
|
|
48
|
+
const content = { markdown, options: sanitizedOptions };
|
|
46
49
|
const key = JSON.stringify(content);
|
|
47
|
-
|
|
50
|
+
const result = this._hashStore.get(key);
|
|
48
51
|
if (result) {
|
|
49
52
|
return result;
|
|
50
53
|
}
|
|
51
|
-
|
|
52
|
-
this._hashStore.set(key,
|
|
53
|
-
return
|
|
54
|
+
const hash = this._hash.toHashSync(content);
|
|
55
|
+
this._hashStore.set(key, hash);
|
|
56
|
+
return hash;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Sanitizes render options to only include serializable properties for caching.
|
|
60
|
+
* This prevents issues with structuredClone when options contain Promises, functions, or circular references.
|
|
61
|
+
* @param {RenderOptions} [options] The render options to sanitize
|
|
62
|
+
* @returns {RenderOptions | undefined} A new object with only the known RenderOptions properties
|
|
63
|
+
*/
|
|
64
|
+
sanitizeOptions(options) {
|
|
65
|
+
if (!options) {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
const sanitized = {};
|
|
69
|
+
if (options.emoji !== void 0) {
|
|
70
|
+
sanitized.emoji = options.emoji;
|
|
71
|
+
}
|
|
72
|
+
if (options.toc !== void 0) {
|
|
73
|
+
sanitized.toc = options.toc;
|
|
74
|
+
}
|
|
75
|
+
if (options.slug !== void 0) {
|
|
76
|
+
sanitized.slug = options.slug;
|
|
77
|
+
}
|
|
78
|
+
if (options.highlight !== void 0) {
|
|
79
|
+
sanitized.highlight = options.highlight;
|
|
80
|
+
}
|
|
81
|
+
if (options.gfm !== void 0) {
|
|
82
|
+
sanitized.gfm = options.gfm;
|
|
83
|
+
}
|
|
84
|
+
if (options.math !== void 0) {
|
|
85
|
+
sanitized.math = options.math;
|
|
86
|
+
}
|
|
87
|
+
if (options.mdx !== void 0) {
|
|
88
|
+
sanitized.mdx = options.mdx;
|
|
89
|
+
}
|
|
90
|
+
if (options.caching !== void 0) {
|
|
91
|
+
sanitized.caching = options.caching;
|
|
92
|
+
}
|
|
93
|
+
return sanitized;
|
|
54
94
|
}
|
|
55
95
|
};
|
|
56
96
|
|
|
@@ -64,7 +104,7 @@ var WritrHooks = /* @__PURE__ */ ((WritrHooks2) => {
|
|
|
64
104
|
return WritrHooks2;
|
|
65
105
|
})(WritrHooks || {});
|
|
66
106
|
var Writr = class extends Hookified {
|
|
67
|
-
engine = unified().use(remarkParse).use(remarkGfm).use(remarkToc).use(remarkEmoji).use(remarkRehype).use(rehypeSlug).use(remarkMath).use(rehypeKatex).use(rehypeHighlight).use(
|
|
107
|
+
engine = unified().use(remarkParse).use(remarkGfm).use(remarkToc).use(remarkEmoji).use(remarkRehype).use(rehypeSlug).use(remarkMath).use(rehypeKatex).use(rehypeHighlight).use(rehypeStringify);
|
|
68
108
|
// Stringify HTML
|
|
69
109
|
_options = {
|
|
70
110
|
throwErrors: false,
|
|
@@ -75,8 +115,8 @@ var Writr = class extends Hookified {
|
|
|
75
115
|
highlight: true,
|
|
76
116
|
gfm: true,
|
|
77
117
|
math: true,
|
|
78
|
-
mdx:
|
|
79
|
-
caching:
|
|
118
|
+
mdx: false,
|
|
119
|
+
caching: true
|
|
80
120
|
}
|
|
81
121
|
};
|
|
82
122
|
_content = "";
|
|
@@ -191,12 +231,16 @@ var Writr = class extends Hookified {
|
|
|
191
231
|
*/
|
|
192
232
|
// biome-ignore lint/suspicious/noExplicitAny: expected
|
|
193
233
|
set frontMatter(data) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
234
|
+
try {
|
|
235
|
+
const frontMatter = this.frontMatterRaw;
|
|
236
|
+
const yamlString = yaml.dump(data);
|
|
237
|
+
const newFrontMatter = `---
|
|
197
238
|
${yamlString}---
|
|
198
239
|
`;
|
|
199
|
-
|
|
240
|
+
this._content = this._content.replace(frontMatter, newFrontMatter);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
this.emit("error", error);
|
|
243
|
+
}
|
|
200
244
|
}
|
|
201
245
|
/**
|
|
202
246
|
* Get the front matter value for a key.
|
|
@@ -245,6 +289,7 @@ ${yamlString}---
|
|
|
245
289
|
await this.hook("afterRender" /* afterRender */, resultData);
|
|
246
290
|
return resultData.result;
|
|
247
291
|
} catch (error) {
|
|
292
|
+
this.emit("error", error);
|
|
248
293
|
throw new Error(`Failed to render markdown: ${error.message}`);
|
|
249
294
|
}
|
|
250
295
|
}
|
|
@@ -287,6 +332,7 @@ ${yamlString}---
|
|
|
287
332
|
this.hook("afterRender" /* afterRender */, resultData);
|
|
288
333
|
return resultData.result;
|
|
289
334
|
} catch (error) {
|
|
335
|
+
this.emit("error", error);
|
|
290
336
|
throw new Error(`Failed to render markdown: ${error.message}`);
|
|
291
337
|
}
|
|
292
338
|
}
|
|
@@ -317,6 +363,7 @@ ${yamlString}---
|
|
|
317
363
|
}
|
|
318
364
|
return { valid: true };
|
|
319
365
|
} catch (error) {
|
|
366
|
+
this.emit("error", error);
|
|
320
367
|
if (content !== void 0) {
|
|
321
368
|
this._content = originalContent;
|
|
322
369
|
}
|
|
@@ -350,6 +397,7 @@ ${yamlString}---
|
|
|
350
397
|
}
|
|
351
398
|
return { valid: true };
|
|
352
399
|
} catch (error) {
|
|
400
|
+
this.emit("error", error);
|
|
353
401
|
if (content !== void 0) {
|
|
354
402
|
this._content = originalContent;
|
|
355
403
|
}
|
|
@@ -410,8 +458,13 @@ ${yamlString}---
|
|
|
410
458
|
* @returns {Promise<string | React.JSX.Element | React.JSX.Element[]>} The rendered React content.
|
|
411
459
|
*/
|
|
412
460
|
async renderReact(options, reactParseOptions) {
|
|
413
|
-
|
|
414
|
-
|
|
461
|
+
try {
|
|
462
|
+
const html = await this.render(options);
|
|
463
|
+
return parse(html, reactParseOptions);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
this.emit("error", error);
|
|
466
|
+
throw new Error(`Failed to render React: ${error.message}`);
|
|
467
|
+
}
|
|
415
468
|
}
|
|
416
469
|
/**
|
|
417
470
|
* Render the markdown content to React synchronously.
|
|
@@ -420,8 +473,13 @@ ${yamlString}---
|
|
|
420
473
|
* @returns {string | React.JSX.Element | React.JSX.Element[]} The rendered React content.
|
|
421
474
|
*/
|
|
422
475
|
renderReactSync(options, reactParseOptions) {
|
|
423
|
-
|
|
424
|
-
|
|
476
|
+
try {
|
|
477
|
+
const html = this.renderSync(options);
|
|
478
|
+
return parse(html, reactParseOptions);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
this.emit("error", error);
|
|
481
|
+
throw new Error(`Failed to render React: ${error.message}`);
|
|
482
|
+
}
|
|
425
483
|
}
|
|
426
484
|
/**
|
|
427
485
|
* Load markdown content from a file.
|
|
@@ -509,6 +567,16 @@ ${yamlString}---
|
|
|
509
567
|
}
|
|
510
568
|
}
|
|
511
569
|
}
|
|
570
|
+
mergeOptions(current, options) {
|
|
571
|
+
if (options.throwErrors !== void 0) {
|
|
572
|
+
current.throwErrors = options.throwErrors;
|
|
573
|
+
}
|
|
574
|
+
if (options.renderOptions) {
|
|
575
|
+
current.renderOptions ??= {};
|
|
576
|
+
this.mergeRenderOptions(current.renderOptions, options.renderOptions);
|
|
577
|
+
}
|
|
578
|
+
return current;
|
|
579
|
+
}
|
|
512
580
|
isCacheEnabled(options) {
|
|
513
581
|
if (options?.caching !== void 0) {
|
|
514
582
|
return options.caching;
|
|
@@ -520,6 +588,7 @@ ${yamlString}---
|
|
|
520
588
|
const processor = unified().use(remarkParse);
|
|
521
589
|
if (options.gfm) {
|
|
522
590
|
processor.use(remarkGfm);
|
|
591
|
+
processor.use(remarkGithubBlockquoteAlert);
|
|
523
592
|
}
|
|
524
593
|
if (options.toc) {
|
|
525
594
|
processor.use(remarkToc, { heading: "toc|table of contents" });
|
|
@@ -543,16 +612,6 @@ ${yamlString}---
|
|
|
543
612
|
processor.use(rehypeStringify);
|
|
544
613
|
return processor;
|
|
545
614
|
}
|
|
546
|
-
mergeOptions(current, options) {
|
|
547
|
-
if (options.throwErrors !== void 0) {
|
|
548
|
-
current.throwErrors = options.throwErrors;
|
|
549
|
-
}
|
|
550
|
-
if (options.renderOptions) {
|
|
551
|
-
current.renderOptions ??= {};
|
|
552
|
-
this.mergeRenderOptions(current.renderOptions, options.renderOptions);
|
|
553
|
-
}
|
|
554
|
-
return current;
|
|
555
|
-
}
|
|
556
615
|
mergeRenderOptions(current, options) {
|
|
557
616
|
if (options.emoji !== void 0) {
|
|
558
617
|
current.emoji = options.emoji;
|
|
@@ -585,3 +644,4 @@ export {
|
|
|
585
644
|
Writr,
|
|
586
645
|
WritrHooks
|
|
587
646
|
};
|
|
647
|
+
/* v8 ignore next -- @preserve */
|
package/package.json
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "writr",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Markdown Rendering Simplified",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/writr.js",
|
|
7
7
|
"types": "./dist/writr.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
+
"types": "./dist/writr.d.ts",
|
|
10
11
|
"import": "./dist/writr.js"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
13
|
-
"repository":
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/jaredwray/writr.git"
|
|
17
|
+
},
|
|
14
18
|
"author": "Jared Wray <me@jaredwray.com>",
|
|
15
19
|
"engines": {
|
|
16
20
|
"node": ">=20"
|
|
@@ -44,28 +48,20 @@
|
|
|
44
48
|
"react-markdown",
|
|
45
49
|
"markdown-to-react"
|
|
46
50
|
],
|
|
47
|
-
"scripts": {
|
|
48
|
-
"clean": "rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist",
|
|
49
|
-
"build": "rimraf ./dist && tsup src/writr.ts --format esm --dts --clean",
|
|
50
|
-
"prepare": "pnpm build",
|
|
51
|
-
"lint": "biome check --write --error-on-warnings",
|
|
52
|
-
"test": "pnpm lint && vitest run --coverage",
|
|
53
|
-
"test:ci": "biome check --error-on-warnings && vitest run --coverage",
|
|
54
|
-
"website:build": "rimraf ./site/README.md ./site/dist && npx docula build -s ./site -o ./site/dist",
|
|
55
|
-
"website:serve": "rimraf ./site/README.md ./site/dist && npx docula serve -s ./site -o ./site/dist"
|
|
56
|
-
},
|
|
57
51
|
"dependencies": {
|
|
58
|
-
"cacheable": "^2.0
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
52
|
+
"cacheable": "^2.2.0",
|
|
53
|
+
"hashery": "^1.2.0",
|
|
54
|
+
"hookified": "^1.13.0",
|
|
55
|
+
"html-react-parser": "^5.2.10",
|
|
56
|
+
"js-yaml": "^4.1.1",
|
|
57
|
+
"react": "^19.2.0",
|
|
63
58
|
"rehype-highlight": "^7.0.2",
|
|
64
59
|
"rehype-katex": "^7.0.1",
|
|
65
60
|
"rehype-slug": "^6.0.0",
|
|
66
61
|
"rehype-stringify": "^10.0.1",
|
|
67
62
|
"remark-emoji": "^5.0.2",
|
|
68
63
|
"remark-gfm": "^4.0.1",
|
|
64
|
+
"remark-github-blockquote-alert": "^2.0.0",
|
|
69
65
|
"remark-math": "^6.0.0",
|
|
70
66
|
"remark-mdx": "^3.1.1",
|
|
71
67
|
"remark-parse": "^11.0.0",
|
|
@@ -74,27 +70,29 @@
|
|
|
74
70
|
"unified": "^11.0.5"
|
|
75
71
|
},
|
|
76
72
|
"devDependencies": {
|
|
77
|
-
"@biomejs/biome": "^2.
|
|
73
|
+
"@biomejs/biome": "^2.3.7",
|
|
78
74
|
"@types/js-yaml": "^4.0.9",
|
|
79
|
-
"@types/node": "^24.
|
|
80
|
-
"@types/react": "^19.
|
|
81
|
-
"@vitest/coverage-v8": "^
|
|
82
|
-
"docula": "^0.
|
|
83
|
-
"rimraf": "^6.
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"vitest": "^3.2.4",
|
|
88
|
-
"webpack": "^5.101.3"
|
|
89
|
-
},
|
|
90
|
-
"xo": {
|
|
91
|
-
"ignores": [
|
|
92
|
-
"docula.config.*"
|
|
93
|
-
]
|
|
75
|
+
"@types/node": "^24.10.1",
|
|
76
|
+
"@types/react": "^19.2.7",
|
|
77
|
+
"@vitest/coverage-v8": "^4.0.14",
|
|
78
|
+
"docula": "^0.31.1",
|
|
79
|
+
"rimraf": "^6.1.2",
|
|
80
|
+
"tsup": "^8.5.1",
|
|
81
|
+
"typescript": "^5.9.3",
|
|
82
|
+
"vitest": "^4.0.14"
|
|
94
83
|
},
|
|
95
84
|
"files": [
|
|
96
85
|
"dist",
|
|
97
86
|
"README.md",
|
|
98
87
|
"LICENSE"
|
|
99
|
-
]
|
|
100
|
-
|
|
88
|
+
],
|
|
89
|
+
"scripts": {
|
|
90
|
+
"clean": "rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist",
|
|
91
|
+
"build": "rimraf ./dist && tsup src/writr.ts --format esm --dts --clean",
|
|
92
|
+
"lint": "biome check --write --error-on-warnings",
|
|
93
|
+
"test": "pnpm lint && vitest run --coverage",
|
|
94
|
+
"test:ci": "biome check --error-on-warnings && vitest run --coverage",
|
|
95
|
+
"website:build": "rimraf ./site/README.md ./site/dist && npx docula build -s ./site -o ./site/dist",
|
|
96
|
+
"website:serve": "rimraf ./site/README.md ./site/dist && npx docula serve -s ./site -o ./site/dist"
|
|
97
|
+
}
|
|
98
|
+
}
|