writr 3.2.3 → 4.1.0

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  ---
4
4
 
5
5
  ## Markdown Rendering Simplified
6
- [![Build](https://github.com/jaredwray/writr/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/writr/actions/workflows/tests.yml)
6
+ [![tests](https://github.com/jaredwray/writr/actions/workflows/tests.yml/badge.svg)](https://github.com/jaredwray/writr/actions/workflows/tests.yml)
7
7
  [![GitHub license](https://img.shields.io/github/license/jaredwray/writr)](https://github.com/jaredwray/writr/blob/master/LICENSE)
8
8
  [![codecov](https://codecov.io/gh/jaredwray/writr/branch/master/graph/badge.svg?token=1YdMesM07X)](https://codecov.io/gh/jaredwray/writr)
9
9
  [![npm](https://img.shields.io/npm/dm/writr)](https://npmjs.com/package/writr)
@@ -16,8 +16,9 @@
16
16
  - [License - MIT](#license)
17
17
 
18
18
  ## Features
19
- * Takes the complexity of Remark and makes it easy to use.
20
- * Simpke to use and easy API
19
+ * Removes the remark / unified complexity and easy to use.
20
+ * Built in caching 💥 making it render very fast when there isnt a change
21
+ * Frontmatter support built in by default. :tada:
21
22
  * Easily Render to `React` or `HTML`.
22
23
  * Generates a Table of Contents for your markdown files (remark-toc).
23
24
  * Slug generation for your markdown files (rehype-slug).
@@ -27,67 +28,65 @@
27
28
  * Github Flavor Markdown (remark-gfm).
28
29
  * Emoji Support (remark-emoji).
29
30
  * MDX Support (remark-mdx).
31
+ * ESM and Node 20+
30
32
 
31
33
  ## Getting Started
32
34
 
33
- ## 1. Install Writr
35
+ ## Install Writr
34
36
 
35
37
  ```bash
36
38
  > npm install writr
37
39
  ```
38
40
 
39
- ## 2. Render from Markdown
41
+ Then you can use it like this:
40
42
 
41
43
  ```javascript
42
44
  import { Writr } from 'writr';
43
45
 
44
- const writr = new Writr();
45
- const markdown = `# Hello World ::-):\n\n This is a test.`;
46
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
46
47
 
47
- const html = await writr.render(markdown); // <h1>Hello World 🙂</h1><p>This is a test.</p>
48
+ const html = await writr.render(); // <h1>Hello World 🙂</h1><p>This is a test.</p>
48
49
  ```
49
50
  Its just that simple. Want to add some options? No problem.
50
51
 
51
52
  ```javascript
52
53
  import { Writr } from 'writr';
53
- const writr = new Writr();
54
- const markdown = `# Hello World ::-):\n\n This is a test.`;
54
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
55
55
  const options = {
56
56
  emoji: false
57
57
  }
58
- const html = await writr.render(markdown, options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
59
- ```
60
-
61
- Want to render to a translation? No problem.
62
-
63
- ```javascript
64
- import { Writr } from 'writr';
65
- const writr = new Writr({ openai: 'your-api-key'});
66
- const markdown = `# Hello World ::-):\n\n This is a test.`;
67
- const langCode = 'es';
68
- const html = await writr.renderTranslation(markdown, langCode, options); // <h1>Hola Mundo 🙂</h1><p>Esta es una prueba.</p>
58
+ const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
69
59
  ```
70
60
 
71
- How about generating keywords and descriptions for your front matter?
61
+ An example passing in the options also via the constructor:
72
62
 
73
63
  ```javascript
74
- import { Writr } from 'writr';
75
- const writr = new Writr({ openai: 'your-api-key'});
76
- const markdown = `# Hello World ::-):\n\n This is a test.`;
77
- const keywords = await writr.keywords(markdown); // ['Hello World', 'Test']
78
- const description = await writr.description(markdown); // 'Hello World Test'
64
+ import { Writr, WritrOptions } from 'writr';
65
+ const writrOptions = {
66
+ renderOptions: {
67
+ emoji: true,
68
+ toc: true,
69
+ slug: true,
70
+ highlight: true,
71
+ gfm: true,
72
+ math: true,
73
+ mdx: true,
74
+ caching: true,
75
+ }
76
+ };
77
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`, writrOptions);
78
+ const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
79
79
  ```
80
80
 
81
81
  ## API
82
82
 
83
- ### `new Writr(options?: WritrOptions)`
83
+ ### `new Writr(arg?: string | WritrOptions, options?: WritrOptions)`
84
84
 
85
- You can access the `WritrOptions` from the instance of Writr. Here is an example of WritrOptions.
85
+ 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.
86
86
 
87
87
  ```javascript
88
88
  import { Writr, WritrOptions } from 'writr';
89
89
  const writrOptions = {
90
- openai: 'your-api-key', // openai api key (default: undefined)
91
90
  renderOptions: {
92
91
  emoji: true,
93
92
  toc: true,
@@ -95,22 +94,59 @@ const writrOptions = {
95
94
  highlight: true,
96
95
  gfm: true,
97
96
  math: true,
98
- mdx: true
97
+ mdx: true,
98
+ caching: true,
99
99
  }
100
100
  };
101
101
  const writr = new Writr(writrOptions);
102
102
  ```
103
103
 
104
- ### `.engine`
104
+ ### `.content`
105
105
 
106
- Accessing the underlying engine for this instance of Writr. This is a `Processor<Root, Root, Root, undefined, undefined>` fromt the unified `.use()` function. You can use this to add additional plugins to the engine.
106
+ 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`.
107
107
 
108
+ ```javascript
109
+
110
+ import { Writr } from 'writr';
111
+ const writr = new Writr();
112
+ writr.content = `---
113
+ title: Hello World
114
+ ---
115
+ # Hello World ::-):\n\n This is a test.`;
116
+ ```
108
117
 
109
118
  ### `.options`
110
119
 
111
120
  Accessing the default options for this instance of Writr.
112
121
 
113
- ### `.render(markdown: string, options?: RenderOptions): Promise<string>`
122
+ ### `.cache`
123
+
124
+ 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:
125
+
126
+ ```javascript
127
+ import { Writr } from 'writr';
128
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
129
+ const options = {
130
+ caching: false
131
+ }
132
+ const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
133
+ ```
134
+
135
+ If you would like to use a specific storage adapter from https://keyv.org you can pass in the adapter like so:
136
+
137
+ ```javascript
138
+ import { Writr } from 'writr';
139
+ import Keyv from '@keyv/redis';
140
+ const keyvRedis = new Keyv('redis://user:pass@localhost:6379');
141
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
142
+ writr.cache.setStorageAdapter(keyvRedis);
143
+ ```
144
+
145
+ ### `.engine`
146
+
147
+ 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.
148
+
149
+ ### `.render(options?: RenderOptions): Promise<string>`
114
150
 
115
151
  Rendering markdown to HTML. the options are based on RenderOptions. Which you can access from the Writr instance.
116
152
 
@@ -120,48 +156,76 @@ import { Writr, RenderOptions } from 'writr';
120
156
  ## `RenderOptions`
121
157
 
122
158
  ```js
123
- interface RenderOptions {
124
- emoji?: boolean; // emoji support
125
- toc?: boolean; // table of contents generation
126
- slug?: boolean; // slug generation
127
- highlight?: boolean; // code highlighting
128
- gfm?: boolean; // github flavor markdown
129
- }
159
+ type RenderOptions = {
160
+ emoji?: boolean; // Emoji support (default: true)
161
+ toc?: boolean; // Table of contents generation (default: true)
162
+ slug?: boolean; // Slug generation (default: true)
163
+ highlight?: boolean; // Code highlighting (default: true)
164
+ gfm?: boolean; // Github flavor markdown (default: true)
165
+ math?: boolean; // Math support (default: true)
166
+ mdx?: boolean; // MDX support (default: true)
167
+ caching?: boolean; // Caching (default: true)
168
+ };
130
169
  ```
131
170
 
132
- ### `.renderSync(markdown: string, options?: RenderOptions): string`
171
+ ### `.renderSync(options?: RenderOptions): string`
133
172
 
134
173
  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.
135
174
 
136
175
  ```javascript
137
176
  import { Writr } from 'writr';
138
- const writr = new Writr();
139
- const markdown = `# Hello World ::-):\n\n This is a test.`;
140
- const html = writr.renderSync(markdown); // <h1>Hello World 🙂</h1><p>This is a test.</p>
177
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
178
+ const html = writr.renderSync(); // <h1>Hello World 🙂</h1><p>This is a test.</p>
141
179
  ```
142
180
 
143
- ### '.renderReact(markdown: string, options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />'
181
+ ### '.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />'
144
182
 
145
183
  Rendering markdown to React. The options are based on RenderOptions and now HTMLReactParserOptions from `html-react-parser`.
146
184
 
147
185
  ```javascript
148
186
  import { Writr } from 'writr';
149
- const writr = new Writr();
150
- const markdown = `# Hello World ::-):\n\n This is a test.`;
151
- const reactElement = await writr.renderReact(markdown); // Will return a React.JSX.Element
187
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
188
+ const reactElement = await writr.renderReact(); // Will return a React.JSX.Element
152
189
  ```
153
190
 
154
- ### '.renderReactSync(markdown: string, options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element'
191
+ ### '.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element'
155
192
 
156
193
  Rendering markdown to React. The options are based on RenderOptions and now HTMLReactParserOptions from `html-react-parser`.
157
194
 
195
+ ```javascript
196
+ import { Writr } from 'writr';
197
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
198
+ const reactElement = writr.renderReactSync(); // Will return a React.JSX.Element
199
+ ```
200
+
201
+ ### `.loadFromFile(filePath: string): Promise<void>`
202
+
203
+ Load your markdown content from a file path.
204
+
158
205
  ```javascript
159
206
  import { Writr } from 'writr';
160
207
  const writr = new Writr();
161
- const markdown = `# Hello World ::-):\n\n This is a test.`;
162
- const reactElement = writr.renderReactSync(markdown); // Will return a React.JSX.Element
208
+ await writr.loadFromFile('path/to/file.md');
163
209
  ```
164
210
 
211
+ ### `.loadFromFileSync(filePath: string): void`
212
+
213
+ Load your markdown content from a file path synchronously.
214
+
215
+ ### `.saveToFile(filePath: string): Promise<void>`
216
+
217
+ Save your markdown and frontmatter (if included) content to a file path.
218
+
219
+ ```javascript
220
+ import { Writr } from 'writr';
221
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
222
+ await writr.saveToFile('path/to/file.md');
223
+ ```
224
+
225
+ ### `.saveToFileSync(filePath: string): void`
226
+
227
+ Save your markdown and frontmatter (if included) content to a file path synchronously.
228
+
165
229
  ## Code of Conduct and Contributing
166
230
  [Code of Conduct](CODE_OF_CONDUCT.md) and [Contributing](CONTRIBUTING.md) guidelines.
167
231
 
package/dist/writr.cjs ADDED
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/writr.ts
31
+ var writr_exports = {};
32
+ __export(writr_exports, {
33
+ Writr: () => Writr
34
+ });
35
+ module.exports = __toCommonJS(writr_exports);
36
+ var import_node_fs = __toESM(require("fs"), 1);
37
+ var import_node_path = require("path");
38
+ var import_unified = require("unified");
39
+ var import_remark_parse = __toESM(require("remark-parse"), 1);
40
+ var import_remark_rehype = __toESM(require("remark-rehype"), 1);
41
+ var import_rehype_slug = __toESM(require("rehype-slug"), 1);
42
+ var import_rehype_highlight = __toESM(require("rehype-highlight"), 1);
43
+ var import_rehype_stringify = __toESM(require("rehype-stringify"), 1);
44
+ var import_remark_toc = __toESM(require("remark-toc"), 1);
45
+ var import_remark_math = __toESM(require("remark-math"), 1);
46
+ var import_rehype_katex = __toESM(require("rehype-katex"), 1);
47
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
48
+ var import_remark_emoji = __toESM(require("remark-emoji"), 1);
49
+ var import_remark_mdx = __toESM(require("remark-mdx"), 1);
50
+ var import_html_react_parser = __toESM(require("html-react-parser"), 1);
51
+ var yaml = __toESM(require("js-yaml"), 1);
52
+
53
+ // src/writr-cache.ts
54
+ var import_node_crypto = require("crypto");
55
+ var import_cacheable = require("cacheable");
56
+ var WritrCache = class {
57
+ _markdownStore = new import_cacheable.Cacheable();
58
+ _markdownStoreSync = new import_cacheable.CacheableMemory();
59
+ _hashStore = new import_cacheable.CacheableMemory();
60
+ get markdownStore() {
61
+ return this._markdownStore;
62
+ }
63
+ get markdownStoreSync() {
64
+ return this._markdownStoreSync;
65
+ }
66
+ get hashStore() {
67
+ return this._hashStore;
68
+ }
69
+ async getMarkdown(markdown, options) {
70
+ const key = this.hash(markdown, options);
71
+ return this.get(key);
72
+ }
73
+ getMarkdownSync(markdown, options) {
74
+ const key = this.hash(markdown, options);
75
+ return this.getSync(key);
76
+ }
77
+ async setMarkdown(markdown, value, options) {
78
+ const key = this.hash(markdown, options);
79
+ return this.set(key, value);
80
+ }
81
+ setMarkdownSync(markdown, value, options) {
82
+ const key = this.hash(markdown, options);
83
+ this.setSync(key, value);
84
+ return true;
85
+ }
86
+ async get(key) {
87
+ return this._markdownStore.get(key);
88
+ }
89
+ getSync(key) {
90
+ return this._markdownStoreSync.get(key);
91
+ }
92
+ async set(key, value) {
93
+ return this._markdownStore.set(key, value);
94
+ }
95
+ setSync(key, value) {
96
+ this._markdownStoreSync.set(key, value);
97
+ return true;
98
+ }
99
+ async clear() {
100
+ await this._markdownStore.clear();
101
+ this._markdownStoreSync.clear();
102
+ this._hashStore.clear();
103
+ }
104
+ setStorageAdapter(adapter) {
105
+ this._markdownStore = new import_cacheable.Cacheable({ primary: adapter });
106
+ }
107
+ hash(markdown, options) {
108
+ const key = JSON.stringify({ markdown, options });
109
+ let result = this._hashStore.get(key);
110
+ if (result) {
111
+ return result;
112
+ }
113
+ result = (0, import_node_crypto.createHash)("sha256").update(key).digest("hex");
114
+ this._hashStore.set(key, result);
115
+ return result;
116
+ }
117
+ };
118
+
119
+ // src/writr.ts
120
+ var Writr = class {
121
+ engine = (0, import_unified.unified)().use(import_remark_parse.default).use(import_remark_gfm.default).use(import_remark_toc.default).use(import_remark_emoji.default).use(import_remark_rehype.default).use(import_rehype_slug.default).use(import_remark_math.default).use(import_rehype_katex.default).use(import_rehype_highlight.default).use(import_remark_mdx.default).use(import_rehype_stringify.default);
122
+ // Stringify HTML
123
+ _options = {
124
+ openai: void 0,
125
+ renderOptions: {
126
+ emoji: true,
127
+ toc: true,
128
+ slug: true,
129
+ highlight: true,
130
+ gfm: true,
131
+ math: true,
132
+ mdx: true,
133
+ caching: true
134
+ }
135
+ };
136
+ _content = "";
137
+ _cache = new WritrCache();
138
+ constructor(arguments1, arguments2) {
139
+ if (typeof arguments1 === "string") {
140
+ this._content = arguments1;
141
+ } else if (arguments1) {
142
+ this._options = { ...this._options, ...arguments1 };
143
+ if (this._options.renderOptions) {
144
+ this.engine = this.createProcessor(this._options.renderOptions);
145
+ }
146
+ }
147
+ if (arguments2) {
148
+ this._options = { ...this._options, ...arguments2 };
149
+ if (this._options.renderOptions) {
150
+ this.engine = this.createProcessor(this._options.renderOptions);
151
+ }
152
+ }
153
+ }
154
+ get options() {
155
+ return this._options;
156
+ }
157
+ get content() {
158
+ return this._content;
159
+ }
160
+ set content(value) {
161
+ this._content = value;
162
+ }
163
+ get cache() {
164
+ return this._cache;
165
+ }
166
+ get frontMatterRaw() {
167
+ const start = this._content.indexOf("---\n");
168
+ if (start === -1) {
169
+ return "";
170
+ }
171
+ const end = this._content.indexOf("\n---\n", start + 4);
172
+ if (end === -1) {
173
+ return "";
174
+ }
175
+ return this._content.slice(start, end + 5);
176
+ }
177
+ get body() {
178
+ const start = this._content.indexOf("---\n");
179
+ if (start === -1) {
180
+ return this._content;
181
+ }
182
+ const end = this._content.indexOf("\n---\n", start + 4);
183
+ if (end === -1) {
184
+ return this._content;
185
+ }
186
+ return this._content.slice(Math.max(0, end + 5)).trim();
187
+ }
188
+ get markdown() {
189
+ return this.body;
190
+ }
191
+ get frontMatter() {
192
+ const frontMatter = this.frontMatterRaw;
193
+ const match = /^---\s*([\s\S]*?)\s*---\s*/.exec(frontMatter);
194
+ if (match) {
195
+ return yaml.load(match[1].trim());
196
+ }
197
+ return {};
198
+ }
199
+ set frontMatter(data) {
200
+ const frontMatter = this.frontMatterRaw;
201
+ const yamlString = yaml.dump(data);
202
+ const newFrontMatter = `---
203
+ ${yamlString}---
204
+ `;
205
+ this._content = this._content.replace(frontMatter, newFrontMatter);
206
+ }
207
+ getFrontMatterValue(key) {
208
+ return this.frontMatter[key];
209
+ }
210
+ async render(options) {
211
+ try {
212
+ let result = "";
213
+ if (this.isCacheEnabled(options)) {
214
+ const cached = await this._cache.getMarkdown(this._content, options);
215
+ if (cached) {
216
+ return cached;
217
+ }
218
+ }
219
+ let { engine } = this;
220
+ if (options) {
221
+ options = { ...this._options.renderOptions, ...options };
222
+ engine = this.createProcessor(options);
223
+ }
224
+ const file = await engine.process(this.body);
225
+ result = String(file);
226
+ if (this.isCacheEnabled(options)) {
227
+ await this._cache.setMarkdown(this._content, result, options);
228
+ }
229
+ return result;
230
+ } catch (error) {
231
+ throw new Error(`Failed to render markdown: ${error.message}`);
232
+ }
233
+ }
234
+ renderSync(options) {
235
+ try {
236
+ let result = "";
237
+ if (this.isCacheEnabled(options)) {
238
+ const cached = this._cache.getMarkdownSync(this._content, options);
239
+ if (cached) {
240
+ return cached;
241
+ }
242
+ }
243
+ let { engine } = this;
244
+ if (options) {
245
+ options = { ...this._options.renderOptions, ...options };
246
+ engine = this.createProcessor(options);
247
+ }
248
+ const file = engine.processSync(this.body);
249
+ result = String(file);
250
+ if (this.isCacheEnabled(options)) {
251
+ this._cache.setMarkdownSync(this._content, result, options);
252
+ }
253
+ return result;
254
+ } catch (error) {
255
+ throw new Error(`Failed to render markdown: ${error.message}`);
256
+ }
257
+ }
258
+ async renderReact(options, reactParseOptions) {
259
+ const html = await this.render(options);
260
+ return (0, import_html_react_parser.default)(html, reactParseOptions);
261
+ }
262
+ renderReactSync(options, reactParseOptions) {
263
+ const html = this.renderSync(options);
264
+ return (0, import_html_react_parser.default)(html, reactParseOptions);
265
+ }
266
+ async loadFromFile(filePath) {
267
+ const { readFile } = import_node_fs.default.promises;
268
+ this._content = await readFile(filePath, "utf8");
269
+ }
270
+ loadFromFileSync(filePath) {
271
+ this._content = import_node_fs.default.readFileSync(filePath, "utf8");
272
+ }
273
+ async saveToFile(filePath) {
274
+ const { writeFile, mkdir } = import_node_fs.default.promises;
275
+ const directoryPath = (0, import_node_path.dirname)(filePath);
276
+ await mkdir(directoryPath, { recursive: true });
277
+ await writeFile(filePath, this._content, "utf8");
278
+ }
279
+ saveToFileSync(filePath) {
280
+ const directoryPath = (0, import_node_path.dirname)(filePath);
281
+ import_node_fs.default.mkdirSync(directoryPath, { recursive: true });
282
+ import_node_fs.default.writeFileSync(filePath, this._content, "utf8");
283
+ }
284
+ isCacheEnabled(options) {
285
+ if (options?.caching !== void 0) {
286
+ return options.caching;
287
+ }
288
+ return this._options?.renderOptions?.caching ?? false;
289
+ }
290
+ createProcessor(options) {
291
+ const processor = (0, import_unified.unified)().use(import_remark_parse.default);
292
+ if (options.gfm) {
293
+ processor.use(import_remark_gfm.default);
294
+ }
295
+ if (options.toc) {
296
+ processor.use(import_remark_toc.default, { heading: "toc|table of contents" });
297
+ }
298
+ if (options.emoji) {
299
+ processor.use(import_remark_emoji.default);
300
+ }
301
+ processor.use(import_remark_rehype.default);
302
+ if (options.slug) {
303
+ processor.use(import_rehype_slug.default);
304
+ }
305
+ if (options.highlight) {
306
+ processor.use(import_rehype_highlight.default);
307
+ }
308
+ if (options.math) {
309
+ processor.use(import_remark_math.default).use(import_rehype_katex.default);
310
+ }
311
+ if (options.mdx) {
312
+ processor.use(import_remark_mdx.default);
313
+ }
314
+ processor.use(import_rehype_stringify.default);
315
+ return processor;
316
+ }
317
+ };
318
+ // Annotate the CommonJS export names for ESM import in node:
319
+ 0 && (module.exports = {
320
+ Writr
321
+ });
@@ -0,0 +1,71 @@
1
+ import * as unified from 'unified';
2
+ import * as hast from 'hast';
3
+ import * as mdast from 'mdast';
4
+ import React from 'react';
5
+ import { HTMLReactParserOptions } from 'html-react-parser';
6
+ import { Cacheable, CacheableMemory } from 'cacheable';
7
+ import { KeyvStoreAdapter } from 'keyv';
8
+
9
+ declare class WritrCache {
10
+ private _markdownStore;
11
+ private readonly _markdownStoreSync;
12
+ private readonly _hashStore;
13
+ get markdownStore(): Cacheable;
14
+ get markdownStoreSync(): CacheableMemory;
15
+ 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;
26
+ hash(markdown: string, options?: RenderOptions): string;
27
+ }
28
+
29
+ type WritrOptions = {
30
+ openai?: string;
31
+ renderOptions?: RenderOptions;
32
+ };
33
+ type RenderOptions = {
34
+ emoji?: boolean;
35
+ toc?: boolean;
36
+ slug?: boolean;
37
+ highlight?: boolean;
38
+ gfm?: boolean;
39
+ math?: boolean;
40
+ mdx?: boolean;
41
+ caching?: boolean;
42
+ };
43
+ declare class Writr {
44
+ engine: unified.Processor<mdast.Root, mdast.Root, hast.Root, hast.Root, string>;
45
+ private readonly _options;
46
+ private _content;
47
+ private readonly _cache;
48
+ constructor(arguments1?: string | WritrOptions, arguments2?: WritrOptions);
49
+ get options(): WritrOptions;
50
+ get content(): string;
51
+ set content(value: string);
52
+ get cache(): WritrCache;
53
+ get frontMatterRaw(): string;
54
+ get body(): string;
55
+ get markdown(): string;
56
+ get frontMatter(): Record<string, any>;
57
+ set frontMatter(data: Record<string, any>);
58
+ getFrontMatterValue<T>(key: string): T;
59
+ render(options?: RenderOptions): Promise<string>;
60
+ renderSync(options?: RenderOptions): string;
61
+ renderReact(options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): Promise<string | React.JSX.Element | React.JSX.Element[]>;
62
+ renderReactSync(options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): string | React.JSX.Element | React.JSX.Element[];
63
+ loadFromFile(filePath: string): Promise<void>;
64
+ loadFromFileSync(filePath: string): void;
65
+ saveToFile(filePath: string): Promise<void>;
66
+ saveToFileSync(filePath: string): void;
67
+ private isCacheEnabled;
68
+ private createProcessor;
69
+ }
70
+
71
+ export { type RenderOptions, Writr, type WritrOptions };
package/dist/writr.d.ts CHANGED
@@ -1,6 +1,31 @@
1
- import { Processor } from 'unified';
2
- import type React from 'react';
3
- import { type HTMLReactParserOptions } from 'html-react-parser';
1
+ import * as unified from 'unified';
2
+ import * as hast from 'hast';
3
+ import * as mdast from 'mdast';
4
+ import React from 'react';
5
+ import { HTMLReactParserOptions } from 'html-react-parser';
6
+ import { Cacheable, CacheableMemory } from 'cacheable';
7
+ import { KeyvStoreAdapter } from 'keyv';
8
+
9
+ declare class WritrCache {
10
+ private _markdownStore;
11
+ private readonly _markdownStoreSync;
12
+ private readonly _hashStore;
13
+ get markdownStore(): Cacheable;
14
+ get markdownStoreSync(): CacheableMemory;
15
+ 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;
26
+ hash(markdown: string, options?: RenderOptions): string;
27
+ }
28
+
4
29
  type WritrOptions = {
5
30
  openai?: string;
6
31
  renderOptions?: RenderOptions;
@@ -13,16 +38,34 @@ type RenderOptions = {
13
38
  gfm?: boolean;
14
39
  math?: boolean;
15
40
  mdx?: boolean;
41
+ caching?: boolean;
16
42
  };
17
43
  declare class Writr {
18
- engine: Processor<import("mdast").Root, import("mdast").Root, import("hast").Root, import("hast").Root, string>;
44
+ engine: unified.Processor<mdast.Root, mdast.Root, hast.Root, hast.Root, string>;
19
45
  private readonly _options;
20
- constructor(options?: WritrOptions);
46
+ private _content;
47
+ private readonly _cache;
48
+ constructor(arguments1?: string | WritrOptions, arguments2?: WritrOptions);
21
49
  get options(): WritrOptions;
22
- render(markdown: string, options?: RenderOptions): Promise<string>;
23
- renderSync(markdown: string, options?: RenderOptions): string;
24
- renderReact(markdown: string, options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): Promise<string | React.JSX.Element | React.JSX.Element[]>;
25
- renderReactSync(markdown: string, options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): string | React.JSX.Element | React.JSX.Element[];
50
+ get content(): string;
51
+ set content(value: string);
52
+ get cache(): WritrCache;
53
+ get frontMatterRaw(): string;
54
+ get body(): string;
55
+ get markdown(): string;
56
+ get frontMatter(): Record<string, any>;
57
+ set frontMatter(data: Record<string, any>);
58
+ getFrontMatterValue<T>(key: string): T;
59
+ render(options?: RenderOptions): Promise<string>;
60
+ renderSync(options?: RenderOptions): string;
61
+ renderReact(options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): Promise<string | React.JSX.Element | React.JSX.Element[]>;
62
+ renderReactSync(options?: RenderOptions, reactParseOptions?: HTMLReactParserOptions): string | React.JSX.Element | React.JSX.Element[];
63
+ loadFromFile(filePath: string): Promise<void>;
64
+ loadFromFileSync(filePath: string): void;
65
+ saveToFile(filePath: string): Promise<void>;
66
+ saveToFileSync(filePath: string): void;
67
+ private isCacheEnabled;
26
68
  private createProcessor;
27
69
  }
28
- export { Writr, type WritrOptions, type RenderOptions };
70
+
71
+ export { type RenderOptions, Writr, type WritrOptions };
package/dist/writr.js CHANGED
@@ -1,118 +1,286 @@
1
- import { unified } from 'unified';
2
- import remarkParse from 'remark-parse';
3
- import remarkRehype from 'remark-rehype';
4
- import rehypeSlug from 'rehype-slug';
5
- import rehypeHighlight from 'rehype-highlight';
6
- import rehypeStringify from 'rehype-stringify';
7
- import remarkToc from 'remark-toc';
8
- import remarkMath from 'remark-math';
9
- import rehypeKatex from 'rehype-katex';
10
- import remarkGfm from 'remark-gfm';
11
- import remarkEmoji from 'remark-emoji';
12
- import remarkMDX from 'remark-mdx';
13
- import parse from 'html-react-parser';
14
- class Writr {
15
- engine = unified()
16
- .use(remarkParse)
17
- .use(remarkGfm) // Use GitHub Flavored Markdown
18
- .use(remarkToc) // Add table of contents
19
- .use(remarkEmoji) // Add emoji support
20
- .use(remarkRehype) // Convert markdown to HTML
21
- .use(rehypeSlug) // Add slugs to headings in HTML
22
- .use(remarkMath) // Add math support
23
- .use(rehypeKatex) // Add math support
24
- .use(rehypeHighlight) // Apply syntax highlighting
25
- .use(remarkMDX) // Add MDX support
26
- .use(rehypeStringify); // Stringify HTML
27
- _options = {
28
- openai: undefined,
29
- renderOptions: {
30
- emoji: true,
31
- toc: true,
32
- slug: true,
33
- highlight: true,
34
- gfm: true,
35
- math: true,
36
- mdx: true,
37
- },
38
- };
39
- constructor(options) {
40
- if (options) {
41
- this._options = { ...this._options, ...options };
42
- if (this._options.renderOptions) {
43
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
44
- this.engine = this.createProcessor(this._options.renderOptions);
45
- }
46
- }
1
+ // src/writr.ts
2
+ import fs from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { unified } from "unified";
5
+ import remarkParse from "remark-parse";
6
+ import remarkRehype from "remark-rehype";
7
+ import rehypeSlug from "rehype-slug";
8
+ import rehypeHighlight from "rehype-highlight";
9
+ import rehypeStringify from "rehype-stringify";
10
+ import remarkToc from "remark-toc";
11
+ import remarkMath from "remark-math";
12
+ import rehypeKatex from "rehype-katex";
13
+ import remarkGfm from "remark-gfm";
14
+ import remarkEmoji from "remark-emoji";
15
+ import remarkMDX from "remark-mdx";
16
+ import parse from "html-react-parser";
17
+ import * as yaml from "js-yaml";
18
+
19
+ // src/writr-cache.ts
20
+ import { createHash } from "node:crypto";
21
+ import { Cacheable, CacheableMemory } from "cacheable";
22
+ var WritrCache = class {
23
+ _markdownStore = new Cacheable();
24
+ _markdownStoreSync = new CacheableMemory();
25
+ _hashStore = new CacheableMemory();
26
+ get markdownStore() {
27
+ return this._markdownStore;
28
+ }
29
+ get markdownStoreSync() {
30
+ return this._markdownStoreSync;
31
+ }
32
+ get hashStore() {
33
+ return this._hashStore;
34
+ }
35
+ async getMarkdown(markdown, options) {
36
+ const key = this.hash(markdown, options);
37
+ return this.get(key);
38
+ }
39
+ getMarkdownSync(markdown, options) {
40
+ const key = this.hash(markdown, options);
41
+ return this.getSync(key);
42
+ }
43
+ async setMarkdown(markdown, value, options) {
44
+ const key = this.hash(markdown, options);
45
+ return this.set(key, value);
46
+ }
47
+ setMarkdownSync(markdown, value, options) {
48
+ const key = this.hash(markdown, options);
49
+ this.setSync(key, value);
50
+ return true;
51
+ }
52
+ async get(key) {
53
+ return this._markdownStore.get(key);
54
+ }
55
+ getSync(key) {
56
+ return this._markdownStoreSync.get(key);
57
+ }
58
+ async set(key, value) {
59
+ return this._markdownStore.set(key, value);
60
+ }
61
+ setSync(key, value) {
62
+ this._markdownStoreSync.set(key, value);
63
+ return true;
64
+ }
65
+ async clear() {
66
+ await this._markdownStore.clear();
67
+ this._markdownStoreSync.clear();
68
+ this._hashStore.clear();
69
+ }
70
+ setStorageAdapter(adapter) {
71
+ this._markdownStore = new Cacheable({ primary: adapter });
72
+ }
73
+ hash(markdown, options) {
74
+ const key = JSON.stringify({ markdown, options });
75
+ let result = this._hashStore.get(key);
76
+ if (result) {
77
+ return result;
47
78
  }
48
- get options() {
49
- return this._options;
50
- }
51
- async render(markdown, options) {
52
- try {
53
- let { engine } = this;
54
- if (options) {
55
- options = { ...this._options.renderOptions, ...options };
56
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
57
- engine = this.createProcessor(options);
58
- }
59
- const file = await engine.process(markdown);
60
- return String(file);
61
- }
62
- catch (error) {
63
- throw new Error(`Failed to render markdown: ${error.message}`);
64
- }
79
+ result = createHash("sha256").update(key).digest("hex");
80
+ this._hashStore.set(key, result);
81
+ return result;
82
+ }
83
+ };
84
+
85
+ // src/writr.ts
86
+ var Writr = class {
87
+ 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
+ // Stringify HTML
89
+ _options = {
90
+ openai: void 0,
91
+ renderOptions: {
92
+ emoji: true,
93
+ toc: true,
94
+ slug: true,
95
+ highlight: true,
96
+ gfm: true,
97
+ math: true,
98
+ mdx: true,
99
+ caching: true
65
100
  }
66
- renderSync(markdown, options) {
67
- try {
68
- let { engine } = this;
69
- if (options) {
70
- options = { ...this._options.renderOptions, ...options };
71
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
72
- engine = this.createProcessor(options);
73
- }
74
- const file = engine.processSync(markdown);
75
- return String(file);
76
- }
77
- catch (error) {
78
- throw new Error(`Failed to render markdown: ${error.message}`);
79
- }
101
+ };
102
+ _content = "";
103
+ _cache = new WritrCache();
104
+ constructor(arguments1, arguments2) {
105
+ if (typeof arguments1 === "string") {
106
+ this._content = arguments1;
107
+ } else if (arguments1) {
108
+ this._options = { ...this._options, ...arguments1 };
109
+ if (this._options.renderOptions) {
110
+ this.engine = this.createProcessor(this._options.renderOptions);
111
+ }
80
112
  }
81
- async renderReact(markdown, options, reactParseOptions) {
82
- const html = await this.render(markdown, options);
83
- return parse(html, reactParseOptions);
113
+ if (arguments2) {
114
+ this._options = { ...this._options, ...arguments2 };
115
+ if (this._options.renderOptions) {
116
+ this.engine = this.createProcessor(this._options.renderOptions);
117
+ }
84
118
  }
85
- renderReactSync(markdown, options, reactParseOptions) {
86
- const html = this.renderSync(markdown, options);
87
- return parse(html, reactParseOptions);
119
+ }
120
+ get options() {
121
+ return this._options;
122
+ }
123
+ get content() {
124
+ return this._content;
125
+ }
126
+ set content(value) {
127
+ this._content = value;
128
+ }
129
+ get cache() {
130
+ return this._cache;
131
+ }
132
+ get frontMatterRaw() {
133
+ const start = this._content.indexOf("---\n");
134
+ if (start === -1) {
135
+ return "";
88
136
  }
89
- createProcessor(options) {
90
- const processor = unified().use(remarkParse);
91
- if (options.gfm) {
92
- processor.use(remarkGfm);
93
- }
94
- if (options.toc) {
95
- processor.use(remarkToc, { heading: 'toc|table of contents' });
96
- }
97
- if (options.emoji) {
98
- processor.use(remarkEmoji);
99
- }
100
- processor.use(remarkRehype);
101
- if (options.slug) {
102
- processor.use(rehypeSlug);
103
- }
104
- if (options.highlight) {
105
- processor.use(rehypeHighlight);
106
- }
107
- if (options.math) {
108
- processor.use(remarkMath).use(rehypeKatex);
137
+ const end = this._content.indexOf("\n---\n", start + 4);
138
+ if (end === -1) {
139
+ return "";
140
+ }
141
+ return this._content.slice(start, end + 5);
142
+ }
143
+ get body() {
144
+ const start = this._content.indexOf("---\n");
145
+ if (start === -1) {
146
+ return this._content;
147
+ }
148
+ const end = this._content.indexOf("\n---\n", start + 4);
149
+ if (end === -1) {
150
+ return this._content;
151
+ }
152
+ return this._content.slice(Math.max(0, end + 5)).trim();
153
+ }
154
+ get markdown() {
155
+ return this.body;
156
+ }
157
+ get frontMatter() {
158
+ const frontMatter = this.frontMatterRaw;
159
+ const match = /^---\s*([\s\S]*?)\s*---\s*/.exec(frontMatter);
160
+ if (match) {
161
+ return yaml.load(match[1].trim());
162
+ }
163
+ return {};
164
+ }
165
+ set frontMatter(data) {
166
+ const frontMatter = this.frontMatterRaw;
167
+ const yamlString = yaml.dump(data);
168
+ const newFrontMatter = `---
169
+ ${yamlString}---
170
+ `;
171
+ this._content = this._content.replace(frontMatter, newFrontMatter);
172
+ }
173
+ getFrontMatterValue(key) {
174
+ return this.frontMatter[key];
175
+ }
176
+ async render(options) {
177
+ try {
178
+ let result = "";
179
+ if (this.isCacheEnabled(options)) {
180
+ const cached = await this._cache.getMarkdown(this._content, options);
181
+ if (cached) {
182
+ return cached;
109
183
  }
110
- if (options.mdx) {
111
- processor.use(remarkMDX);
184
+ }
185
+ let { engine } = this;
186
+ if (options) {
187
+ options = { ...this._options.renderOptions, ...options };
188
+ engine = this.createProcessor(options);
189
+ }
190
+ const file = await engine.process(this.body);
191
+ result = String(file);
192
+ if (this.isCacheEnabled(options)) {
193
+ await this._cache.setMarkdown(this._content, result, options);
194
+ }
195
+ return result;
196
+ } catch (error) {
197
+ throw new Error(`Failed to render markdown: ${error.message}`);
198
+ }
199
+ }
200
+ renderSync(options) {
201
+ try {
202
+ let result = "";
203
+ if (this.isCacheEnabled(options)) {
204
+ const cached = this._cache.getMarkdownSync(this._content, options);
205
+ if (cached) {
206
+ return cached;
112
207
  }
113
- processor.use(rehypeStringify);
114
- return processor;
208
+ }
209
+ let { engine } = this;
210
+ if (options) {
211
+ options = { ...this._options.renderOptions, ...options };
212
+ engine = this.createProcessor(options);
213
+ }
214
+ const file = engine.processSync(this.body);
215
+ result = String(file);
216
+ if (this.isCacheEnabled(options)) {
217
+ this._cache.setMarkdownSync(this._content, result, options);
218
+ }
219
+ return result;
220
+ } catch (error) {
221
+ throw new Error(`Failed to render markdown: ${error.message}`);
222
+ }
223
+ }
224
+ async renderReact(options, reactParseOptions) {
225
+ const html = await this.render(options);
226
+ return parse(html, reactParseOptions);
227
+ }
228
+ renderReactSync(options, reactParseOptions) {
229
+ const html = this.renderSync(options);
230
+ return parse(html, reactParseOptions);
231
+ }
232
+ async loadFromFile(filePath) {
233
+ const { readFile } = fs.promises;
234
+ this._content = await readFile(filePath, "utf8");
235
+ }
236
+ loadFromFileSync(filePath) {
237
+ this._content = fs.readFileSync(filePath, "utf8");
238
+ }
239
+ async saveToFile(filePath) {
240
+ const { writeFile, mkdir } = fs.promises;
241
+ const directoryPath = dirname(filePath);
242
+ await mkdir(directoryPath, { recursive: true });
243
+ await writeFile(filePath, this._content, "utf8");
244
+ }
245
+ saveToFileSync(filePath) {
246
+ const directoryPath = dirname(filePath);
247
+ fs.mkdirSync(directoryPath, { recursive: true });
248
+ fs.writeFileSync(filePath, this._content, "utf8");
249
+ }
250
+ isCacheEnabled(options) {
251
+ if (options?.caching !== void 0) {
252
+ return options.caching;
253
+ }
254
+ return this._options?.renderOptions?.caching ?? false;
255
+ }
256
+ createProcessor(options) {
257
+ const processor = unified().use(remarkParse);
258
+ if (options.gfm) {
259
+ processor.use(remarkGfm);
260
+ }
261
+ if (options.toc) {
262
+ processor.use(remarkToc, { heading: "toc|table of contents" });
263
+ }
264
+ if (options.emoji) {
265
+ processor.use(remarkEmoji);
266
+ }
267
+ processor.use(remarkRehype);
268
+ if (options.slug) {
269
+ processor.use(rehypeSlug);
270
+ }
271
+ if (options.highlight) {
272
+ processor.use(rehypeHighlight);
273
+ }
274
+ if (options.math) {
275
+ processor.use(remarkMath).use(rehypeKatex);
276
+ }
277
+ if (options.mdx) {
278
+ processor.use(remarkMDX);
115
279
  }
116
- }
117
- export { Writr };
118
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdHIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvd3JpdHIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLE9BQU8sRUFBWSxNQUFNLFNBQVMsQ0FBQztBQUMzQyxPQUFPLFdBQVcsTUFBTSxjQUFjLENBQUM7QUFDdkMsT0FBTyxZQUFZLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sVUFBVSxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLGVBQWUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLGVBQWUsTUFBTSxrQkFBa0IsQ0FBQztBQUMvQyxPQUFPLFNBQVMsTUFBTSxZQUFZLENBQUM7QUFDbkMsT0FBTyxVQUFVLE1BQU0sYUFBYSxDQUFDO0FBQ3JDLE9BQU8sV0FBVyxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLFNBQVMsTUFBTSxZQUFZLENBQUM7QUFDbkMsT0FBTyxXQUFXLE1BQU0sY0FBYyxDQUFDO0FBQ3ZDLE9BQU8sU0FBUyxNQUFNLFlBQVksQ0FBQztBQUVuQyxPQUFPLEtBQW9DLE1BQU0sbUJBQW1CLENBQUM7QUFpQnJFLE1BQU0sS0FBSztJQUNILE1BQU0sR0FBRyxPQUFPLEVBQUU7U0FDdkIsR0FBRyxDQUFDLFdBQVcsQ0FBQztTQUNoQixHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsK0JBQStCO1NBQzlDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyx3QkFBd0I7U0FDdkMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLG9CQUFvQjtTQUNyQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUMsMkJBQTJCO1NBQzdDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyxnQ0FBZ0M7U0FDaEQsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDLG1CQUFtQjtTQUNuQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsbUJBQW1CO1NBQ3BDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQyw0QkFBNEI7U0FDakQsR0FB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LENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzFCLENBQUM7UUFFRCxTQUFTLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBRS9CLE9BQU8sU0FBUyxDQUFDO0lBQ2xCLENBQUM7Q0FDRDtBQUVELE9BQU8sRUFBQyxLQUFLLEVBQXdDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge3VuaWZpZWQsIFByb2Nlc3Nvcn0gZnJvbSAndW5pZmllZCc7XG5pbXBvcnQgcmVtYXJrUGFyc2UgZnJvbSAncmVtYXJrLXBhcnNlJztcbmltcG9ydCByZW1hcmtSZWh5cGUgZnJvbSAncmVtYXJrLXJlaHlwZSc7XG5pbXBvcnQgcmVoeXBlU2x1ZyBmcm9tICdyZWh5cGUtc2x1Zyc7XG5pbXBvcnQgcmVoeXBlSGlnaGxpZ2h0IGZyb20gJ3JlaHlwZS1oaWdobGlnaHQnO1xuaW1wb3J0IHJlaHlwZVN0cmluZ2lmeSBmcm9tICdyZWh5cGUtc3RyaW5naWZ5JztcbmltcG9ydCByZW1hcmtUb2MgZnJvbSAncmVtYXJrLXRvYyc7XG5pbXBvcnQgcmVtYXJrTWF0aCBmcm9tICdyZW1hcmstbWF0aCc7XG5pbXBvcnQgcmVoeXBlS2F0ZXggZnJvbSAncmVoeXBlLWthdGV4JztcbmltcG9ydCByZW1hcmtHZm0gZnJvbSAncmVtYXJrLWdmbSc7XG5pbXBvcnQgcmVtYXJrRW1vamkgZnJvbSAncmVtYXJrLWVtb2ppJztcbmltcG9ydCByZW1hcmtNRFggZnJvbSAncmVtYXJrLW1keCc7XG5pbXBvcnQgdHlwZSBSZWFjdCBmcm9tICdyZWFjdCc7XG5pbXBvcnQgcGFyc2UsIHt0eXBlIEhUTUxSZWFjdFBhcnNlck9wdGlvbnN9IGZyb20gJ2h0bWwtcmVhY3QtcGFyc2VyJztcblxudHlwZSBXcml0ck9wdGlvbnMgPSB7XG5cdG9wZW5haT86IHN0cmluZzsgLy8gT3BlbmFpIGFwaSBrZXkgKGRlZmF1bHQ6IHVuZGVmaW5lZClcblx0cmVuZGVyT3B0aW9ucz86IFJlbmRlck9wdGlvbnM7IC8vIERlZmF1bHQgcmVuZGVyIG9wdGlvbnMgKGRlZmF1bHQ6IHVuZGVmaW5lZClcbn07XG5cbnR5cGUgUmVuZGVyT3B0aW9ucyA9IHtcblx0ZW1vamk/OiBib29sZWFuOyAvLyBFbW9qaSBzdXBwb3J0IChkZWZhdWx0OiB0cnVlKVxuXHR0b2M/OiBib29sZWFuOyAvLyBUYWJsZSBvZiBjb250ZW50cyBnZW5lcmF0aW9uIChkZWZhdWx0OiB0cnVlKVxuXHRzbHVnPzogYm9vbGVhbjsgLy8gU2x1ZyBnZW5lcmF0aW9uIChkZWZhdWx0OiB0cnVlKVxuXHRoaWdobGlnaHQ/OiBib29sZWFuOyAvLyBDb2RlIGhpZ2hsaWdodGluZyAoZGVmYXVsdDogdHJ1ZSlcblx0Z2ZtPzogYm9vbGVhbjsgLy8gR2l0aHViIGZsYXZvciBtYXJrZG93biAoZGVmYXVsdDogdHJ1ZSlcblx0bWF0aD86IGJvb2xlYW47IC8vIE1hdGggc3VwcG9ydCAoZGVmYXVsdDogdHJ1ZSlcblx0bWR4PzogYm9vbGVhbjsgLy8gTURYIHN1cHBvcnQgKGRlZmF1bHQ6IHRydWUpXG59O1xuXG5jbGFzcyBXcml0ciB7XG5cdHB1YmxpYyBlbmdpbmUgPSB1bmlmaWVkKClcblx0XHQudXNlKHJlbWFya1BhcnNlKVxuXHRcdC51c2UocmVtYXJrR2ZtKSAvLyBVc2UgR2l0SHViIEZsYXZvcmVkIE1hcmtkb3duXG5cdFx0LnVzZShyZW1hcmtUb2MpIC8vIEFkZCB0YWJsZSBvZiBjb250ZW50c1xuXHRcdC51c2UocmVtYXJrRW1vamkpIC8vIEFkZCBlbW9qaSBzdXBwb3J0XG5cdFx0LnVzZShyZW1hcmtSZWh5cGUpIC8vIENvbnZlcnQgbWFya2Rvd24gdG8gSFRNTFxuXHRcdC51c2UocmVoeXBlU2x1ZykgLy8gQWRkIHNsdWdzIHRvIGhlYWRpbmdzIGluIEhUTUxcblx0XHQudXNlKHJlbWFya01hdGgpIC8vIEFkZCBtYXRoIHN1cHBvcnRcblx0XHQudXNlKHJlaHlwZUthdGV4KSAvLyBBZGQgbWF0aCBzdXBwb3J0XG5cdFx0LnVzZShyZWh5cGVIaWdobGlnaHQpIC8vIEFwcGx5IHN5bnRheCBoaWdobGlnaHRpbmdcblx0XHQudXNlKHJlbWFya01EWCkgLy8gQWRkIE1EWCBzdXBwb3J0XG5cdFx0LnVzZShyZWh5cGVTdHJpbmdpZnkpOyAvLyBTdHJpbmdpZnkgSFRNTFxuXG5cdHByaXZhdGUgcmVhZG9ubHkgX29wdGlvbnM6IFdyaXRyT3B0aW9ucyA9IHtcblx0XHRvcGVuYWk6IHVuZGVmaW5lZCxcblx0XHRyZW5kZXJPcHRpb25zOiB7XG5cdFx0XHRlbW9qaTogdHJ1ZSxcblx0XHRcdHRvYzogdHJ1ZSxcblx0XHRcdHNsdWc6IHRydWUsXG5cdFx0XHRoaWdobGlnaHQ6IHRydWUsXG5cdFx0XHRnZm06IHRydWUsXG5cdFx0XHRtYXRoOiB0cnVlLFxuXHRcdFx0bWR4OiB0cnVlLFxuXHRcdH0sXG5cdH07XG5cblx0Y29uc3RydWN0b3Iob3B0aW9ucz86IFdyaXRyT3B0aW9ucykge1xuXHRcdGlmIChvcHRpb25zKSB7XG5cdFx0XHR0aGlzLl9vcHRpb25zID0gey4uLnRoaXMuX29wdGlvbnMsIC4uLm9wdGlvbnN9O1xuXHRcdFx0aWYgKHRoaXMuX29wdGlvbnMucmVuZGVyT3B0aW9ucykge1xuXHRcdFx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVuc2FmZS1hc3NpZ25tZW50XG5cdFx0XHRcdHRoaXMuZW5naW5lID0gdGhpcy5jcmVhdGVQcm9jZXNzb3IodGhpcy5fb3B0aW9ucy5yZW5kZXJPcHRpb25zKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRwdWJsaWMgZ2V0IG9wdGlvbnMoKTogV3JpdHJPcHRpb25zIHtcblx0XHRyZXR1cm4gdGhpcy5fb3B0aW9ucztcblx0fVxuXG5cdGFzeW5jIHJlbmRlcihtYXJrZG93bjogc3RyaW5nLCBvcHRpb25zPzogUmVuZGVyT3B0aW9ucyk6IFByb21pc2U8c3RyaW5nPiB7XG5cdFx0dHJ5IHtcblx0XHRcdGxldCB7ZW5naW5lfSA9IHRoaXM7XG5cdFx0XHRpZiAob3B0aW9ucykge1xuXHRcdFx0XHRvcHRpb25zID0gey4uLnRoaXMuX29wdGlvbnMucmVuZGVyT3B0aW9ucywgLi4ub3B0aW9uc307XG5cdFx0XHRcdC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW5zYWZlLWFzc2lnbm1lbnRcblx0XHRcdFx0ZW5naW5lID0gdGhpcy5jcmVhdGVQcm9jZXNzb3Iob3B0aW9ucyk7XG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IGZpbGUgPSBhd2FpdCBlbmdpbmUucHJvY2VzcyhtYXJrZG93bik7XG5cdFx0XHRyZXR1cm4gU3RyaW5nKGZpbGUpO1xuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byByZW5kZXIgbWFya2Rvd246ICR7KGVycm9yIGFzIEVycm9yKS5tZXNzYWdlfWApO1xuXHRcdH1cblx0fVxuXG5cdHJlbmRlclN5bmMobWFya2Rvd246IHN0cmluZywgb3B0aW9ucz86IFJlbmRlck9wdGlvbnMpOiBzdHJpbmcge1xuXHRcdHRyeSB7XG5cdFx0XHRsZXQge2VuZ2luZX0gPSB0aGlzO1xuXHRcdFx0aWYgKG9wdGlvbnMpIHtcblx0XHRcdFx0b3B0aW9ucyA9IHsuLi50aGlzLl9vcHRpb25zLnJlbmRlck9wdGlvbnMsIC4uLm9wdGlvbnN9O1xuXHRcdFx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVuc2FmZS1hc3NpZ25tZW50XG5cdFx0XHRcdGVuZ2luZSA9IHRoaXMuY3JlYXRlUHJvY2Vzc29yKG9wdGlvbnMpO1xuXHRcdFx0fVxuXG5cdFx0XHRjb25zdCBmaWxlID0gZW5naW5lLnByb2Nlc3NTeW5jKG1hcmtkb3duKTtcblx0XHRcdHJldHVybiBTdHJpbmcoZmlsZSk7XG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIHJlbmRlciBtYXJrZG93bjogJHsoZXJyb3IgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG5cdFx0fVxuXHR9XG5cblx0YXN5bmMgcmVuZGVyUmVhY3QobWFya2Rvd246IHN0cmluZywgb3B0aW9ucz86IFJlbmRlck9wdGlvbnMsIHJlYWN0UGFyc2VPcHRpb25zPzogSFRNTFJlYWN0UGFyc2VyT3B0aW9ucyk6IFByb21pc2U8c3RyaW5nIHwgUmVhY3QuSlNYLkVsZW1lbnQgfCBSZWFjdC5KU1guRWxlbWVudFtdPiB7XG5cdFx0Y29uc3QgaHRtbCA9IGF3YWl0IHRoaXMucmVuZGVyKG1hcmtkb3duLCBvcHRpb25zKTtcblxuXHRcdHJldHVybiBwYXJzZShodG1sLCByZWFjdFBhcnNlT3B0aW9ucyk7XG5cdH1cblxuXHRyZW5kZXJSZWFjdFN5bmMobWFya2Rvd246IHN0cmluZywgb3B0aW9ucz86IFJlbmRlck9wdGlvbnMsIHJlYWN0UGFyc2VPcHRpb25zPzogSFRNTFJlYWN0UGFyc2VyT3B0aW9ucyk6IHN0cmluZyB8IFJlYWN0LkpTWC5FbGVtZW50IHwgUmVhY3QuSlNYLkVsZW1lbnRbXSB7XG5cdFx0Y29uc3QgaHRtbCA9IHRoaXMucmVuZGVyU3luYyhtYXJrZG93biwgb3B0aW9ucyk7XG5cdFx0cmV0dXJuIHBhcnNlKGh0bWwsIHJlYWN0UGFyc2VPcHRpb25zKTtcblx0fVxuXG5cdHByaXZhdGUgY3JlYXRlUHJvY2Vzc29yKG9wdGlvbnM6IFJlbmRlck9wdGlvbnMpOiBhbnkge1xuXHRcdGNvbnN0IHByb2Nlc3NvciA9IHVuaWZpZWQoKS51c2UocmVtYXJrUGFyc2UpO1xuXG5cdFx0aWYgKG9wdGlvbnMuZ2ZtKSB7XG5cdFx0XHRwcm9jZXNzb3IudXNlKHJlbWFya0dmbSk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMudG9jKSB7XG5cdFx0XHRwcm9jZXNzb3IudXNlKHJlbWFya1RvYywge2hlYWRpbmc6ICd0b2N8dGFibGUgb2YgY29udGVudHMnfSk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMuZW1vamkpIHtcblx0XHRcdHByb2Nlc3Nvci51c2UocmVtYXJrRW1vamkpO1xuXHRcdH1cblxuXHRcdHByb2Nlc3Nvci51c2UocmVtYXJrUmVoeXBlKTtcblxuXHRcdGlmIChvcHRpb25zLnNsdWcpIHtcblx0XHRcdHByb2Nlc3Nvci51c2UocmVoeXBlU2x1Zyk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMuaGlnaGxpZ2h0KSB7XG5cdFx0XHRwcm9jZXNzb3IudXNlKHJlaHlwZUhpZ2hsaWdodCk7XG5cdFx0fVxuXG5cdFx0aWYgKG9wdGlvbnMubWF0aCkge1xuXHRcdFx0cHJvY2Vzc29yLnVzZShyZW1hcmtNYXRoKS51c2UocmVoeXBlS2F0ZXgpO1xuXHRcdH1cblxuXHRcdGlmIChvcHRpb25zLm1keCkge1xuXHRcdFx0cHJvY2Vzc29yLnVzZShyZW1hcmtNRFgpO1xuXHRcdH1cblxuXHRcdHByb2Nlc3Nvci51c2UocmVoeXBlU3RyaW5naWZ5KTtcblxuXHRcdHJldHVybiBwcm9jZXNzb3I7XG5cdH1cbn1cblxuZXhwb3J0IHtXcml0ciwgdHlwZSBXcml0ck9wdGlvbnMsIHR5cGUgUmVuZGVyT3B0aW9uc307XG5cbiJdfQ==
280
+ processor.use(rehypeStringify);
281
+ return processor;
282
+ }
283
+ };
284
+ export {
285
+ Writr
286
+ };
package/package.json CHANGED
@@ -1,14 +1,21 @@
1
1
  {
2
2
  "name": "writr",
3
- "version": "3.2.3",
3
+ "version": "4.1.0",
4
4
  "description": "Markdown Rendering Simplified",
5
5
  "type": "module",
6
- "exports": "./dist/writr.js",
6
+ "main": "./dist/writr.cjs",
7
+ "module": "./dist/writr.js",
7
8
  "types": "./dist/writr.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "require": "./dist/writr.cjs",
12
+ "import": "./dist/writr.js"
13
+ }
14
+ },
8
15
  "repository": "https://github.com/jaredwray/writr.git",
9
16
  "author": "Jared Wray <me@jaredwray.com>",
10
17
  "engines": {
11
- "node": ">=18.0.0"
18
+ "node": ">=20"
12
19
  },
13
20
  "license": "MIT",
14
21
  "keywords": [
@@ -40,40 +47,48 @@
40
47
  "markdown-to-react"
41
48
  ],
42
49
  "scripts": {
43
- "clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./site/README.md ./site-output",
44
- "build": "rimraf ./dist && tsc",
45
- "test": "xo --fix && vitest run --coverage",
50
+ "clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./site/README.md ./site/dist",
51
+ "build": "rimraf ./dist && tsup src/writr.ts --format cjs,esm --dts --clean",
46
52
  "prepare": "npm run build",
47
- "website:build": "rimraf ./site/README.md ./site-output && npx docula build -s ./site -o ./site-output",
48
- "website:serve": "rimraf ./site/README.md ./site-output && npx docula serve -s ./site -o ./site-output"
53
+ "test": "xo --fix && 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"
49
56
  },
50
57
  "dependencies": {
51
- "html-react-parser": "^5.1.10",
58
+ "cacheable": "^1.7.1",
59
+ "crypto": "^1.0.1",
60
+ "fs": "^0.0.1-security",
61
+ "html-react-parser": "^5.1.16",
62
+ "js-yaml": "^4.1.0",
63
+ "keyv": "^5.0.3",
52
64
  "react": "^18.3.1",
53
65
  "rehype-highlight": "^7.0.0",
54
- "rehype-katex": "^7.0.0",
66
+ "rehype-katex": "^7.0.1",
55
67
  "rehype-slug": "^6.0.0",
56
- "rehype-stringify": "^10.0.0",
57
- "remark-emoji": "^4.0.1",
68
+ "rehype-stringify": "^10.0.1",
69
+ "remark-emoji": "^5.0.1",
58
70
  "remark-gfm": "^4.0.0",
59
71
  "remark-math": "^6.0.0",
60
72
  "remark-mdx": "^3.0.1",
61
73
  "remark-parse": "^11.0.0",
62
- "remark-rehype": "^11.1.0",
74
+ "remark-rehype": "^11.1.1",
63
75
  "remark-toc": "^9.0.0",
64
- "ts-node": "^10.9.2",
65
- "unified": "^11.0.4"
76
+ "unified": "^11.0.5"
66
77
  },
67
78
  "devDependencies": {
68
- "@types/node": "^20.12.11",
69
- "@types/react": "^18.3.2",
70
- "@vitest/coverage-v8": "^1.6.0",
71
- "docula": "^0.5.3",
72
- "rimraf": "^5.0.7",
73
- "typescript": "^5.4.5",
74
- "vitest": "^1.6.0",
75
- "webpack": "^5.91.0",
76
- "xo": "^0.58.0"
79
+ "@keyv/sqlite": "^4.0.1",
80
+ "@types/js-yaml": "^4.0.9",
81
+ "@types/node": "^22.7.4",
82
+ "@types/react": "^18.3.10",
83
+ "@vitest/coverage-v8": "^2.1.1",
84
+ "docula": "^0.9.1",
85
+ "rimraf": "^6.0.1",
86
+ "ts-node": "^10.9.2",
87
+ "tsup": "^8.3.0",
88
+ "typescript": "^5.6.2",
89
+ "vitest": "^2.1.1",
90
+ "webpack": "^5.95.0",
91
+ "xo": "^0.59.3"
77
92
  },
78
93
  "xo": {
79
94
  "ignores": [
@@ -81,6 +96,8 @@
81
96
  ]
82
97
  },
83
98
  "files": [
84
- "dist"
99
+ "dist",
100
+ "README.md",
101
+ "LICENSE"
85
102
  ]
86
103
  }