writr 4.1.3 → 4.1.4

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.
Files changed (4) hide show
  1. package/README.md +129 -49
  2. package/dist/writr.d.ts +119 -17
  3. package/dist/writr.js +153 -49
  4. package/package.json +12 -13
package/README.md CHANGED
@@ -1,21 +1,38 @@
1
1
  ![Writr](site/logo.svg)
2
2
 
3
- ---
4
-
5
- ## Markdown Rendering Simplified
3
+ # Markdown Rendering Simplified
6
4
  [![tests](https://github.com/jaredwray/writr/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/writr/actions/workflows/tests.yml)
7
5
  [![GitHub license](https://img.shields.io/github/license/jaredwray/writr)](https://github.com/jaredwray/writr/blob/master/LICENSE)
8
6
  [![codecov](https://codecov.io/gh/jaredwray/writr/branch/master/graph/badge.svg?token=1YdMesM07X)](https://codecov.io/gh/jaredwray/writr)
9
7
  [![npm](https://img.shields.io/npm/dm/writr)](https://npmjs.com/package/writr)
10
8
  [![npm](https://img.shields.io/npm/v/writr)](https://npmjs.com/package/writr)
11
9
 
12
- ---
13
- ## Table of Contents
10
+ # Table of Contents
14
11
  - [Features](#features)
12
+ - [ESM and Node Version Support](#esm-and-node-version-support)
15
13
  - [Getting Started](#getting-started)
16
- - [License - MIT](#license)
17
-
18
- ## Features
14
+ - [API](#api)
15
+ - [`new Writr(arg?: string | WritrOptions, options?: WritrOptions)`](#new-writrarg-string--writroptions-options-writroptions)
16
+ - [`.content`](#content)
17
+ - [`.body`](#body)
18
+ - [`.options`](#options)
19
+ - [`.frontmatter`](#frontmatter)
20
+ - [`.frontMatterRaw`](#frontmatterraw)
21
+ - [`.cache`](#cache)
22
+ - [`.engine`](#engine)
23
+ - [`.render(options?: RenderOptions): Promise<string>`](#renderoptions-renderoptions-promisestring)
24
+ - [`.renderSync(options?: RenderOptions): string`](#rendersyncoptions-renderoptions-string)
25
+ - [`.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />`](#renderreactoptions-renderoptions-reactoptions-htmlreactparseroptions-promise-reactjsxelement-)
26
+ - [`.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element`](#renderreactsync-options-renderoptions-reactoptions-htmlreactparseroptions-reactjsxelement)
27
+ - [`.loadFromFile(filePath: string): Promise<void>`](#loadfromfilefilepath-string-promisevoid)
28
+ - [`.loadFromFileSync(filePath: string): void`](#loadfromfilesyncfilepath-string-void)
29
+ - [`.saveToFile(filePath: string): Promise<void>`](#savetofilefilepath-string-promisevoid)
30
+ - [`.saveToFileSync(filePath: string): void`](#savetofilesyncfilepath-string-void)
31
+ - [Code of Conduct and Contributing](#code-of-conduct-and-contributing)
32
+ - [License](#license)
33
+
34
+
35
+ # Features
19
36
  * Removes the remark / unified complexity and easy to use.
20
37
  * Built in caching 💥 making it render very fast when there isnt a change
21
38
  * Frontmatter support built in by default. :tada:
@@ -29,11 +46,11 @@
29
46
  * Emoji Support (remark-emoji).
30
47
  * MDX Support (remark-mdx).
31
48
 
32
- ## ESM and Node Version Support
49
+ # ESM and Node Version Support
33
50
 
34
51
  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. To learn more about using ESM please read this from `sindresorhus`: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
35
52
 
36
- ## Getting Started
53
+ # Getting Started
37
54
 
38
55
  ```bash
39
56
  > npm install writr
@@ -79,9 +96,9 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`, writrOptions)
79
96
  const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
80
97
  ```
81
98
 
82
- ## API
99
+ # API
83
100
 
84
- ### `new Writr(arg?: string | WritrOptions, options?: WritrOptions)`
101
+ ## `new Writr(arg?: string | WritrOptions, options?: WritrOptions)`
85
102
 
86
103
  By default the constructor takes in a markdown `string` or `WritrOptions` in the first parameter. You can also send in nothing and set the markdown via `.content` property. If you want to pass in your markdown and options you can easily do this with `new Writr('## Your Markdown Here', { ...options here})`. You can access the `WritrOptions` from the instance of Writr. Here is an example of WritrOptions.
87
104
 
@@ -102,7 +119,7 @@ const writrOptions = {
102
119
  const writr = new Writr(writrOptions);
103
120
  ```
104
121
 
105
- ### `.content`
122
+ ## `.content`
106
123
 
107
124
  Setting the markdown content for the instance of Writr. This can be set via the constructor or directly on the instance and can even handle `frontmatter`.
108
125
 
@@ -116,60 +133,96 @@ title: Hello World
116
133
  # Hello World ::-):\n\n This is a test.`;
117
134
  ```
118
135
 
119
- ### `.options`
136
+ ## `.body`
137
+
138
+ gets the body of the markdown content. This is the content without the frontmatter.
139
+
140
+ ```javascript
141
+ import { Writr } from 'writr';
142
+ const writr = new Writr();
143
+ writr.content = `---
144
+ title: Hello World
145
+ ---
146
+ # Hello World ::-):\n\n This is a test.`;
147
+ console.log(writr.body); // '# Hello World ::-):\n\n This is a test.'
148
+ ```
149
+
150
+ ## `.options`
120
151
 
121
152
  Accessing the default options for this instance of Writr.
122
153
 
123
- ### `.cache`
154
+ ## `.frontmatter`
124
155
 
125
- Accessing the cache for this instance of Writr. By default this is an in memory cache and is enabled by default. You can disable this by setting `caching: false` in the `RenderOptions` of the `WritrOptions` or when calling render passing the `RenderOptions` like here:
156
+ Accessing the frontmatter for this instance of Writr. This is a `Record<string, any>` and can be set via the `.content` property.
126
157
 
127
158
  ```javascript
128
159
  import { Writr } from 'writr';
129
- const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
130
- const options = {
131
- caching: false
132
- }
133
- const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
160
+ const writr = new Writr();
161
+ writr.content = `---
162
+ title: Hello World
163
+ ---
164
+ # Hello World ::-):\n\n This is a test.`;
165
+ console.log(writr.frontmatter); // { title: 'Hello World' }
134
166
  ```
135
167
 
136
- If you would like to use a specific storage adapter from https://keyv.org you can pass in the adapter like so:
168
+ you can also set the front matter directly like this:
169
+
170
+ ```javascript
171
+ import { Writr } from 'writr';
172
+ const writr = new Writr();
173
+ writr.frontmatter = { title: 'Hello World' };
174
+ ```
175
+
176
+ ## `.frontMatterRaw`
177
+
178
+ Accessing the raw frontmatter for this instance of Writr. This is a `string` and can be set via the `.content` property.
179
+
180
+ ```javascript
181
+ import { Writr } from 'writr';
182
+ const writr = new Writr();
183
+ writr.content = `---
184
+ title: Hello World
185
+ ---
186
+ # Hello World ::-):\n\n This is a test.`;
187
+ console.log(writr.frontMatterRaw); // '---\ntitle: Hello World\n---'
188
+ ```
189
+
190
+ ## `.cache`
191
+
192
+ Accessing the cache for this instance of Writr. By default this is an in memory cache and is disabled (set to false) by default. You can enable this by setting `caching: true` in the `RenderOptions` of the `WritrOptions` or when calling render passing the `RenderOptions` like here:
137
193
 
138
194
  ```javascript
139
195
  import { Writr } from 'writr';
140
- import Keyv from '@keyv/redis';
141
- const keyvRedis = new Keyv('redis://user:pass@localhost:6379');
142
196
  const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
143
- writr.cache.setStorageAdapter(keyvRedis);
197
+ const options = {
198
+ caching: true
199
+ }
200
+ const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
144
201
  ```
145
202
 
146
- ### `.engine`
203
+
204
+ ## `.engine`
147
205
 
148
206
  Accessing the underlying engine for this instance of Writr. This is a `Processor<Root, Root, Root, undefined, undefined>` fro the unified `.use()` function. You can use this to add additional plugins to the engine.
149
207
 
150
- ### `.render(options?: RenderOptions): Promise<string>`
208
+ ## `.render(options?: RenderOptions): Promise<string>`
151
209
 
152
210
  Rendering markdown to HTML. the options are based on RenderOptions. Which you can access from the Writr instance.
153
211
 
154
212
  ```javascript
155
- import { Writr, RenderOptions } from 'writr';
213
+ import { Writr } from 'writr';
214
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
215
+ const html = await writr.render(); // <h1>Hello World 🙂</h1><p>This is a test.</p>
156
216
 
157
- ## `RenderOptions`
217
+ //passing in with render options
218
+ const options = {
219
+ emoji: false
220
+ }
158
221
 
159
- ```js
160
- type RenderOptions = {
161
- emoji?: boolean; // Emoji support (default: true)
162
- toc?: boolean; // Table of contents generation (default: true)
163
- slug?: boolean; // Slug generation (default: true)
164
- highlight?: boolean; // Code highlighting (default: true)
165
- gfm?: boolean; // Github flavor markdown (default: true)
166
- math?: boolean; // Math support (default: true)
167
- mdx?: boolean; // MDX support (default: true)
168
- caching?: boolean; // Caching (default: true)
169
- };
222
+ const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
170
223
  ```
171
224
 
172
- ### `.renderSync(options?: RenderOptions): string`
225
+ ## `.renderSync(options?: RenderOptions): string`
173
226
 
174
227
  Rendering markdown to HTML synchronously. the options are based on RenderOptions. Which you can access from the Writr instance. The parameters are the same as the `.render()` function.
175
228
 
@@ -179,7 +232,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
179
232
  const html = writr.renderSync(); // <h1>Hello World 🙂</h1><p>This is a test.</p>
180
233
  ```
181
234
 
182
- ### '.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />'
235
+ ## '.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />'
183
236
 
184
237
  Rendering markdown to React. The options are based on RenderOptions and now HTMLReactParserOptions from `html-react-parser`.
185
238
 
@@ -189,7 +242,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
189
242
  const reactElement = await writr.renderReact(); // Will return a React.JSX.Element
190
243
  ```
191
244
 
192
- ### '.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element'
245
+ ## '.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element'
193
246
 
194
247
  Rendering markdown to React. The options are based on RenderOptions and now HTMLReactParserOptions from `html-react-parser`.
195
248
 
@@ -199,7 +252,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
199
252
  const reactElement = writr.renderReactSync(); // Will return a React.JSX.Element
200
253
  ```
201
254
 
202
- ### `.loadFromFile(filePath: string): Promise<void>`
255
+ ## `.loadFromFile(filePath: string): Promise<void>`
203
256
 
204
257
  Load your markdown content from a file path.
205
258
 
@@ -209,11 +262,11 @@ const writr = new Writr();
209
262
  await writr.loadFromFile('path/to/file.md');
210
263
  ```
211
264
 
212
- ### `.loadFromFileSync(filePath: string): void`
265
+ ## `.loadFromFileSync(filePath: string): void`
213
266
 
214
267
  Load your markdown content from a file path synchronously.
215
268
 
216
- ### `.saveToFile(filePath: string): Promise<void>`
269
+ ## `.saveToFile(filePath: string): Promise<void>`
217
270
 
218
271
  Save your markdown and frontmatter (if included) content to a file path.
219
272
 
@@ -223,13 +276,40 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
223
276
  await writr.saveToFile('path/to/file.md');
224
277
  ```
225
278
 
226
- ### `.saveToFileSync(filePath: string): void`
279
+ ## `.saveToFileSync(filePath: string): void`
227
280
 
228
281
  Save your markdown and frontmatter (if included) content to a file path synchronously.
229
282
 
230
- ## Code of Conduct and Contributing
283
+ # Caching On Render
284
+
285
+ Caching is built into Writr and is an in-memory cache using `CacheableMemory` from [Cacheable](https://cacheable.org). It is turned off by default and can be enabled by setting `caching: true` in the `RenderOptions` of the `WritrOptions` or when calling render passing the `RenderOptions` like here:
286
+
287
+ ```javascript
288
+ import { Writr } from 'writr';
289
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`, { renderOptions: { caching: true } });
290
+ ```
291
+
292
+ or via `RenderOptions` such as:
293
+
294
+ ```javascript
295
+ import { Writr } from 'writr';
296
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
297
+ await writr.render({ caching: true});
298
+ ```
299
+
300
+ If you want to set the caching options for the instance of Writr you can do so like this:
301
+
302
+ ```javascript
303
+ // we will set the lruSize of the cache and the default ttl
304
+ import {Writr} from 'writr';
305
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`, { renderOptions: { caching: true } });
306
+ writr.cache.store.lruSize = 100;
307
+ writr.cache.store.ttl = '5m'; // setting it to 5 minutes
308
+ ```
309
+
310
+ # Code of Conduct and Contributing
231
311
  [Code of Conduct](CODE_OF_CONDUCT.md) and [Contributing](CONTRIBUTING.md) guidelines.
232
312
 
233
- ## License
313
+ # License
234
314
 
235
315
  MIT © [Jared Wray](https://jaredwray.com)
package/dist/writr.d.ts CHANGED
@@ -3,33 +3,42 @@ import * as hast from 'hast';
3
3
  import * as mdast from 'mdast';
4
4
  import React from 'react';
5
5
  import { HTMLReactParserOptions } from 'html-react-parser';
6
- import { Cacheable, CacheableMemory } from 'cacheable';
7
- import { KeyvStoreAdapter } from 'keyv';
6
+ import { Hookified } from 'hookified';
7
+ import { CacheableMemory } from 'cacheable';
8
8
 
9
9
  declare class WritrCache {
10
- private _markdownStore;
11
- private readonly _markdownStoreSync;
10
+ private readonly _store;
12
11
  private readonly _hashStore;
13
- get markdownStore(): Cacheable;
14
- get markdownStoreSync(): CacheableMemory;
12
+ get store(): CacheableMemory;
15
13
  get hashStore(): CacheableMemory;
16
- getMarkdown(markdown: string, options?: RenderOptions): Promise<string | undefined>;
17
- getMarkdownSync(markdown: string, options?: RenderOptions): string | undefined;
18
- setMarkdown(markdown: string, value: string, options?: RenderOptions): Promise<boolean>;
19
- setMarkdownSync(markdown: string, value: string, options?: RenderOptions): boolean;
20
- get(key: string): Promise<string | undefined>;
21
- getSync(key: string): string | undefined;
22
- set(key: string, value: string): Promise<boolean>;
23
- setSync(key: string, value: string): boolean;
24
- clear(): Promise<void>;
25
- setStorageAdapter(adapter: KeyvStoreAdapter): void;
14
+ get(markdown: string, options?: RenderOptions): string | undefined;
15
+ set(markdown: string, value: string, options?: RenderOptions): void;
16
+ clear(): void;
26
17
  hash(markdown: string, options?: RenderOptions): string;
27
18
  }
28
19
 
20
+ /**
21
+ * Writr options.
22
+ * @typedef {Object} WritrOptions
23
+ * @property {string} [openai] - Openai api key (default: undefined)
24
+ * @property {RenderOptions} [renderOptions] - Default render options (default: undefined)
25
+ */
29
26
  type WritrOptions = {
30
27
  openai?: string;
31
28
  renderOptions?: RenderOptions;
32
29
  };
30
+ /**
31
+ * Render options.
32
+ * @typedef {Object} RenderOptions
33
+ * @property {boolean} [emoji] - Emoji support (default: true)
34
+ * @property {boolean} [toc] - Table of contents generation (default: true)
35
+ * @property {boolean} [slug] - Slug generation (default: true)
36
+ * @property {boolean} [highlight] - Code highlighting (default: true)
37
+ * @property {boolean} [gfm] - Github flavor markdown (default: true)
38
+ * @property {boolean} [math] - Math support (default: true)
39
+ * @property {boolean} [mdx] - MDX support (default: true)
40
+ * @property {boolean} [caching] - Caching (default: true)
41
+ */
33
42
  type RenderOptions = {
34
43
  emoji?: boolean;
35
44
  toc?: boolean;
@@ -40,32 +49,125 @@ type RenderOptions = {
40
49
  mdx?: boolean;
41
50
  caching?: boolean;
42
51
  };
43
- declare class Writr {
52
+ declare class Writr extends Hookified {
44
53
  engine: unified.Processor<mdast.Root, mdast.Root, hast.Root, hast.Root, string>;
45
54
  private readonly _options;
46
55
  private _content;
47
56
  private readonly _cache;
57
+ /**
58
+ * Initialize Writr. Accepts a string or options object.
59
+ * @param {string | WritrOptions} [arguments1] If you send in a string, it will be used as the markdown content. If you send in an object, it will be used as the options.
60
+ * @param {WritrOptions} [arguments2] This is if you send in the content in the first argument and also want to send in options.
61
+ *
62
+ * @example
63
+ * const writr = new Writr('Hello, world!', {caching: false});
64
+ */
48
65
  constructor(arguments1?: string | WritrOptions, arguments2?: WritrOptions);
66
+ /**
67
+ * Get the options.
68
+ * @type {WritrOptions}
69
+ */
49
70
  get options(): WritrOptions;
71
+ /**
72
+ * Get the Content. This is the markdown content and front matter if it exists.
73
+ * @type {WritrOptions}
74
+ */
50
75
  get content(): string;
76
+ /**
77
+ * Set the Content. This is the markdown content and front matter if it exists.
78
+ * @type {WritrOptions}
79
+ */
51
80
  set content(value: string);
81
+ /**
82
+ * Get the cache.
83
+ * @type {WritrCache}
84
+ */
52
85
  get cache(): WritrCache;
86
+ /**
87
+ * Get the front matter raw content.
88
+ * @type {string} The front matter content including the delimiters.
89
+ */
53
90
  get frontMatterRaw(): string;
91
+ /**
92
+ * Get the body content without the front matter.
93
+ * @type {string} The markdown content without the front matter.
94
+ */
54
95
  get body(): string;
96
+ /**
97
+ * Get the markdown content. This is an alias for the body property.
98
+ * @type {string} The markdown content.
99
+ */
55
100
  get markdown(): string;
101
+ /**
102
+ * Get the front matter content as an object.
103
+ * @type {Record<string, any>} The front matter content as an object.
104
+ */
56
105
  get frontMatter(): Record<string, any>;
106
+ /**
107
+ * Set the front matter content as an object.
108
+ * @type {Record<string, any>} The front matter content as an object.
109
+ */
57
110
  set frontMatter(data: Record<string, any>);
111
+ /**
112
+ * Get the front matter value for a key.
113
+ * @param {string} key The key to get the value for.
114
+ * @returns {T} The value for the key.
115
+ */
58
116
  getFrontMatterValue<T>(key: string): T;
117
+ /**
118
+ * Render the markdown content to HTML.
119
+ * @param {RenderOptions} [options] The render options.
120
+ * @returns {Promise<string>} The rendered HTML content.
121
+ */
59
122
  render(options?: RenderOptions): Promise<string>;
123
+ /**
124
+ * Render the markdown content to HTML synchronously.
125
+ * @param {RenderOptions} [options] The render options.
126
+ * @returns {string} The rendered HTML content.
127
+ */
60
128
  renderSync(options?: RenderOptions): string;
129
+ /**
130
+ * Render the markdown content to React.
131
+ * @param {RenderOptions} [options] The render options.
132
+ * @param {HTMLReactParserOptions} [reactParseOptions] The HTML React parser options.
133
+ * @returns {Promise<string | React.JSX.Element | React.JSX.Element[]>} The rendered React content.
134
+ */
61
135
  renderReact(options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): Promise<string | React.JSX.Element | React.JSX.Element[]>;
136
+ /**
137
+ * Render the markdown content to React synchronously.
138
+ * @param {RenderOptions} [options] The render options.
139
+ * @param {HTMLReactParserOptions} [reactParseOptions] The HTML React parser options.
140
+ * @returns {string | React.JSX.Element | React.JSX.Element[]} The rendered React content.
141
+ */
62
142
  renderReactSync(options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): string | React.JSX.Element | React.JSX.Element[];
143
+ /**
144
+ * Load markdown content from a file.
145
+ * @param {string} filePath The file path to load the markdown content from.
146
+ * @returns {Promise<void>}
147
+ */
63
148
  loadFromFile(filePath: string): Promise<void>;
149
+ /**
150
+ * Load markdown content from a file synchronously.
151
+ * @param {string} filePath The file path to load the markdown content from.
152
+ * @returns {void}
153
+ */
64
154
  loadFromFileSync(filePath: string): void;
155
+ /**
156
+ * Save the markdown content to a file. If the directory doesn't exist it will be created.
157
+ * @param {string} filePath The file path to save the markdown content to.
158
+ * @returns {Promise<void>}
159
+ */
65
160
  saveToFile(filePath: string): Promise<void>;
161
+ /**
162
+ * Save the markdown content to a file synchronously. If the directory doesn't exist it will be created.
163
+ * @param {string} filePath The file path to save the markdown content to.
164
+ * @returns {void}
165
+ */
66
166
  saveToFileSync(filePath: string): void;
67
167
  private isCacheEnabled;
68
168
  private createProcessor;
169
+ private mergeOptions;
170
+ private mergeRenderOptions;
69
171
  }
70
172
 
71
173
  export { type RenderOptions, Writr, type WritrOptions };
package/dist/writr.js CHANGED
@@ -15,60 +15,31 @@ import remarkEmoji from "remark-emoji";
15
15
  import remarkMDX from "remark-mdx";
16
16
  import parse from "html-react-parser";
17
17
  import * as yaml from "js-yaml";
18
+ import { Hookified } from "hookified";
18
19
 
19
20
  // src/writr-cache.ts
20
- import { Cacheable, CacheableMemory } from "cacheable";
21
+ import { CacheableMemory } from "cacheable";
21
22
  var WritrCache = class {
22
- _markdownStore = new Cacheable();
23
- _markdownStoreSync = new CacheableMemory();
23
+ _store = new CacheableMemory();
24
24
  _hashStore = new CacheableMemory();
25
- get markdownStore() {
26
- return this._markdownStore;
27
- }
28
- get markdownStoreSync() {
29
- return this._markdownStoreSync;
25
+ get store() {
26
+ return this._store;
30
27
  }
31
28
  get hashStore() {
32
29
  return this._hashStore;
33
30
  }
34
- async getMarkdown(markdown, options) {
35
- const key = this.hash(markdown, options);
36
- return this.get(key);
37
- }
38
- getMarkdownSync(markdown, options) {
31
+ get(markdown, options) {
39
32
  const key = this.hash(markdown, options);
40
- return this.getSync(key);
33
+ return this._store.get(key);
41
34
  }
42
- async setMarkdown(markdown, value, options) {
35
+ set(markdown, value, options) {
43
36
  const key = this.hash(markdown, options);
44
- return this.set(key, value);
45
- }
46
- setMarkdownSync(markdown, value, options) {
47
- const key = this.hash(markdown, options);
48
- this.setSync(key, value);
49
- return true;
50
- }
51
- async get(key) {
52
- return this._markdownStore.get(key);
37
+ this._store.set(key, value);
53
38
  }
54
- getSync(key) {
55
- return this._markdownStoreSync.get(key);
56
- }
57
- async set(key, value) {
58
- return this._markdownStore.set(key, value);
59
- }
60
- setSync(key, value) {
61
- this._markdownStoreSync.set(key, value);
62
- return true;
63
- }
64
- async clear() {
65
- await this._markdownStore.clear();
66
- this._markdownStoreSync.clear();
39
+ clear() {
40
+ this._store.clear();
67
41
  this._hashStore.clear();
68
42
  }
69
- setStorageAdapter(adapter) {
70
- this._markdownStore = new Cacheable({ primary: adapter });
71
- }
72
43
  hash(markdown, options) {
73
44
  const content = { markdown, options };
74
45
  const key = JSON.stringify(content);
@@ -83,7 +54,7 @@ var WritrCache = class {
83
54
  };
84
55
 
85
56
  // src/writr.ts
86
- var Writr = class {
57
+ var Writr = class extends Hookified {
87
58
  engine = unified().use(remarkParse).use(remarkGfm).use(remarkToc).use(remarkEmoji).use(remarkRehype).use(rehypeSlug).use(remarkMath).use(rehypeKatex).use(rehypeHighlight).use(remarkMDX).use(rehypeStringify);
88
59
  // Stringify HTML
89
60
  _options = {
@@ -96,39 +67,68 @@ var Writr = class {
96
67
  gfm: true,
97
68
  math: true,
98
69
  mdx: true,
99
- caching: true
70
+ caching: false
100
71
  }
101
72
  };
102
73
  _content = "";
103
74
  _cache = new WritrCache();
75
+ /**
76
+ * Initialize Writr. Accepts a string or options object.
77
+ * @param {string | WritrOptions} [arguments1] If you send in a string, it will be used as the markdown content. If you send in an object, it will be used as the options.
78
+ * @param {WritrOptions} [arguments2] This is if you send in the content in the first argument and also want to send in options.
79
+ *
80
+ * @example
81
+ * const writr = new Writr('Hello, world!', {caching: false});
82
+ */
104
83
  constructor(arguments1, arguments2) {
84
+ super();
105
85
  if (typeof arguments1 === "string") {
106
86
  this._content = arguments1;
107
87
  } else if (arguments1) {
108
- this._options = { ...this._options, ...arguments1 };
88
+ this._options = this.mergeOptions(this._options, arguments1);
109
89
  if (this._options.renderOptions) {
110
90
  this.engine = this.createProcessor(this._options.renderOptions);
111
91
  }
112
92
  }
113
93
  if (arguments2) {
114
- this._options = { ...this._options, ...arguments2 };
94
+ this._options = this.mergeOptions(this._options, arguments2);
115
95
  if (this._options.renderOptions) {
116
96
  this.engine = this.createProcessor(this._options.renderOptions);
117
97
  }
118
98
  }
119
99
  }
100
+ /**
101
+ * Get the options.
102
+ * @type {WritrOptions}
103
+ */
120
104
  get options() {
121
105
  return this._options;
122
106
  }
107
+ /**
108
+ * Get the Content. This is the markdown content and front matter if it exists.
109
+ * @type {WritrOptions}
110
+ */
123
111
  get content() {
124
112
  return this._content;
125
113
  }
114
+ /**
115
+ * Set the Content. This is the markdown content and front matter if it exists.
116
+ * @type {WritrOptions}
117
+ */
126
118
  set content(value) {
127
119
  this._content = value;
128
120
  }
121
+ /**
122
+ * Get the cache.
123
+ * @type {WritrCache}
124
+ */
129
125
  get cache() {
130
126
  return this._cache;
131
127
  }
128
+ /**
129
+ * Get the front matter raw content.
130
+ * @type {string} The front matter content including the delimiters.
131
+ */
132
132
  get frontMatterRaw() {
133
133
  if (!this._content.trimStart().startsWith("---")) {
134
134
  return "";
@@ -140,6 +140,10 @@ var Writr = class {
140
140
  }
141
141
  return this._content.slice(start, end + 5);
142
142
  }
143
+ /**
144
+ * Get the body content without the front matter.
145
+ * @type {string} The markdown content without the front matter.
146
+ */
143
147
  get body() {
144
148
  if (this.frontMatterRaw === "") {
145
149
  return this._content;
@@ -147,17 +151,33 @@ var Writr = class {
147
151
  const end = this._content.indexOf("\n---\n");
148
152
  return this._content.slice(Math.max(0, end + 5)).trim();
149
153
  }
154
+ /**
155
+ * Get the markdown content. This is an alias for the body property.
156
+ * @type {string} The markdown content.
157
+ */
150
158
  get markdown() {
151
159
  return this.body;
152
160
  }
161
+ /**
162
+ * Get the front matter content as an object.
163
+ * @type {Record<string, any>} The front matter content as an object.
164
+ */
153
165
  get frontMatter() {
154
166
  const frontMatter = this.frontMatterRaw;
155
167
  const match = /^---\s*([\s\S]*?)\s*---\s*/.exec(frontMatter);
156
168
  if (match) {
157
- return yaml.load(match[1].trim());
169
+ try {
170
+ return yaml.load(match[1].trim());
171
+ } catch (error) {
172
+ this.emit("error", error);
173
+ }
158
174
  }
159
175
  return {};
160
176
  }
177
+ /**
178
+ * Set the front matter content as an object.
179
+ * @type {Record<string, any>} The front matter content as an object.
180
+ */
161
181
  set frontMatter(data) {
162
182
  const frontMatter = this.frontMatterRaw;
163
183
  const yamlString = yaml.dump(data);
@@ -166,14 +186,24 @@ ${yamlString}---
166
186
  `;
167
187
  this._content = this._content.replace(frontMatter, newFrontMatter);
168
188
  }
189
+ /**
190
+ * Get the front matter value for a key.
191
+ * @param {string} key The key to get the value for.
192
+ * @returns {T} The value for the key.
193
+ */
169
194
  getFrontMatterValue(key) {
170
195
  return this.frontMatter[key];
171
196
  }
197
+ /**
198
+ * Render the markdown content to HTML.
199
+ * @param {RenderOptions} [options] The render options.
200
+ * @returns {Promise<string>} The rendered HTML content.
201
+ */
172
202
  async render(options) {
173
203
  try {
174
204
  let result = "";
175
205
  if (this.isCacheEnabled(options)) {
176
- const cached = await this._cache.getMarkdown(this._content, options);
206
+ const cached = this._cache.get(this._content, options);
177
207
  if (cached) {
178
208
  return cached;
179
209
  }
@@ -186,18 +216,23 @@ ${yamlString}---
186
216
  const file = await engine.process(this.body);
187
217
  result = String(file);
188
218
  if (this.isCacheEnabled(options)) {
189
- await this._cache.setMarkdown(this._content, result, options);
219
+ this._cache.set(this._content, result, options);
190
220
  }
191
221
  return result;
192
222
  } catch (error) {
193
223
  throw new Error(`Failed to render markdown: ${error.message}`);
194
224
  }
195
225
  }
226
+ /**
227
+ * Render the markdown content to HTML synchronously.
228
+ * @param {RenderOptions} [options] The render options.
229
+ * @returns {string} The rendered HTML content.
230
+ */
196
231
  renderSync(options) {
197
232
  try {
198
233
  let result = "";
199
234
  if (this.isCacheEnabled(options)) {
200
- const cached = this._cache.getMarkdownSync(this._content, options);
235
+ const cached = this._cache.get(this._content, options);
201
236
  if (cached) {
202
237
  return cached;
203
238
  }
@@ -210,34 +245,66 @@ ${yamlString}---
210
245
  const file = engine.processSync(this.body);
211
246
  result = String(file);
212
247
  if (this.isCacheEnabled(options)) {
213
- this._cache.setMarkdownSync(this._content, result, options);
248
+ this._cache.set(this._content, result, options);
214
249
  }
215
250
  return result;
216
251
  } catch (error) {
217
252
  throw new Error(`Failed to render markdown: ${error.message}`);
218
253
  }
219
254
  }
255
+ /**
256
+ * Render the markdown content to React.
257
+ * @param {RenderOptions} [options] The render options.
258
+ * @param {HTMLReactParserOptions} [reactParseOptions] The HTML React parser options.
259
+ * @returns {Promise<string | React.JSX.Element | React.JSX.Element[]>} The rendered React content.
260
+ */
220
261
  async renderReact(options, reactParseOptions) {
221
262
  const html = await this.render(options);
222
263
  return parse(html, reactParseOptions);
223
264
  }
265
+ /**
266
+ * Render the markdown content to React synchronously.
267
+ * @param {RenderOptions} [options] The render options.
268
+ * @param {HTMLReactParserOptions} [reactParseOptions] The HTML React parser options.
269
+ * @returns {string | React.JSX.Element | React.JSX.Element[]} The rendered React content.
270
+ */
224
271
  renderReactSync(options, reactParseOptions) {
225
272
  const html = this.renderSync(options);
226
273
  return parse(html, reactParseOptions);
227
274
  }
275
+ /**
276
+ * Load markdown content from a file.
277
+ * @param {string} filePath The file path to load the markdown content from.
278
+ * @returns {Promise<void>}
279
+ */
228
280
  async loadFromFile(filePath) {
229
281
  const { readFile } = fs.promises;
230
282
  this._content = await readFile(filePath, "utf8");
231
283
  }
284
+ /**
285
+ * Load markdown content from a file synchronously.
286
+ * @param {string} filePath The file path to load the markdown content from.
287
+ * @returns {void}
288
+ */
232
289
  loadFromFileSync(filePath) {
233
290
  this._content = fs.readFileSync(filePath, "utf8");
234
291
  }
292
+ /**
293
+ * Save the markdown content to a file. If the directory doesn't exist it will be created.
294
+ * @param {string} filePath The file path to save the markdown content to.
295
+ * @returns {Promise<void>}
296
+ */
235
297
  async saveToFile(filePath) {
236
298
  const { writeFile, mkdir } = fs.promises;
237
299
  const directoryPath = dirname(filePath);
238
300
  await mkdir(directoryPath, { recursive: true });
239
301
  await writeFile(filePath, this._content, "utf8");
240
302
  }
303
+ /**
304
+ * Save the markdown content to a file synchronously. If the directory doesn't exist it will be created.
305
+ * @param {string} filePath The file path to save the markdown content to.
306
+ * @returns {void}
307
+ */
241
308
  saveToFileSync(filePath) {
242
309
  const directoryPath = dirname(filePath);
243
310
  fs.mkdirSync(directoryPath, { recursive: true });
@@ -276,6 +343,43 @@ ${yamlString}---
276
343
  processor.use(rehypeStringify);
277
344
  return processor;
278
345
  }
346
+ mergeOptions(current, options) {
347
+ if (options.openai) {
348
+ current.openai = options.openai;
349
+ }
350
+ if (options.renderOptions) {
351
+ current.renderOptions ??= {};
352
+ this.mergeRenderOptions(current.renderOptions, options.renderOptions);
353
+ }
354
+ return current;
355
+ }
356
+ mergeRenderOptions(current, options) {
357
+ if (options.emoji !== void 0) {
358
+ current.emoji = options.emoji;
359
+ }
360
+ if (options.toc !== void 0) {
361
+ current.toc = options.toc;
362
+ }
363
+ if (options.slug !== void 0) {
364
+ current.slug = options.slug;
365
+ }
366
+ if (options.highlight !== void 0) {
367
+ current.highlight = options.highlight;
368
+ }
369
+ if (options.gfm !== void 0) {
370
+ current.gfm = options.gfm;
371
+ }
372
+ if (options.math !== void 0) {
373
+ current.math = options.math;
374
+ }
375
+ if (options.mdx !== void 0) {
376
+ current.mdx = options.mdx;
377
+ }
378
+ if (options.caching !== void 0) {
379
+ current.caching = options.caching;
380
+ }
381
+ return current;
382
+ }
279
383
  };
280
384
  export {
281
385
  Writr
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "writr",
3
- "version": "4.1.3",
3
+ "version": "4.1.4",
4
4
  "description": "Markdown Rendering Simplified",
5
5
  "type": "module",
6
6
  "main": "./dist/writr.js",
@@ -53,36 +53,35 @@
53
53
  "website:serve": "rimraf ./site/README.md ./site/dist && npx docula serve -s ./site -o ./site/dist"
54
54
  },
55
55
  "dependencies": {
56
- "cacheable": "^1.8.0",
56
+ "cacheable": "^1.8.2",
57
+ "hookified": "^1.5.0",
57
58
  "html-react-parser": "^5.1.18",
58
59
  "js-yaml": "^4.1.0",
59
- "keyv": "^5.1.0",
60
60
  "react": "^18.3.1",
61
- "rehype-highlight": "^7.0.0",
61
+ "rehype-highlight": "^7.0.1",
62
62
  "rehype-katex": "^7.0.1",
63
63
  "rehype-slug": "^6.0.0",
64
64
  "rehype-stringify": "^10.0.1",
65
65
  "remark-emoji": "^5.0.1",
66
66
  "remark-gfm": "^4.0.0",
67
67
  "remark-math": "^6.0.0",
68
- "remark-mdx": "^3.0.1",
68
+ "remark-mdx": "^3.1.0",
69
69
  "remark-parse": "^11.0.0",
70
70
  "remark-rehype": "^11.1.1",
71
71
  "remark-toc": "^9.0.0",
72
72
  "unified": "^11.0.5"
73
73
  },
74
74
  "devDependencies": {
75
- "@keyv/sqlite": "^4.0.1",
76
75
  "@types/js-yaml": "^4.0.9",
77
- "@types/node": "^22.7.5",
78
- "@types/react": "^18.3.11",
79
- "@vitest/coverage-v8": "^2.1.2",
80
- "docula": "^0.9.1",
76
+ "@types/node": "^22.8.4",
77
+ "@types/react": "^18.3.12",
78
+ "@vitest/coverage-v8": "^2.1.4",
79
+ "docula": "^0.9.4",
81
80
  "rimraf": "^6.0.1",
82
81
  "ts-node": "^10.9.2",
83
- "tsup": "^8.3.0",
84
- "typescript": "^5.6.2",
85
- "vitest": "^2.1.2",
82
+ "tsup": "^8.3.5",
83
+ "typescript": "^5.6.3",
84
+ "vitest": "^2.1.4",
86
85
  "webpack": "^5.95.0",
87
86
  "xo": "^0.59.3"
88
87
  },