writr 4.4.6 → 4.5.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 CHANGED
@@ -9,7 +9,8 @@
9
9
 
10
10
  # Features
11
11
  * Removes the remark / unified complexity and easy to use.
12
- * Built in caching 💥 making it render very fast when there isnt a change
12
+ * Powered by the [unified processor](https://github.com/unifiedjs/unified) for an extensible plugin pipeline.
13
+ * Built in caching 💥 making it render very fast when there isn't a change
13
14
  * Frontmatter support built in by default. :tada:
14
15
  * Easily Render to `React` or `HTML`.
15
16
  * Generates a Table of Contents for your markdown files (remark-toc).
@@ -22,7 +23,17 @@
22
23
  * MDX Support (remark-mdx).
23
24
  * Built in Hooks for adding code to render pipeline.
24
25
 
26
+ # Unified Processor Engine
27
+
28
+ Writr builds on top of the open source [unified](https://github.com/unifiedjs/unified) processor – the core project that powers
29
+ [remark](https://github.com/remarkjs/remark), [rehype](https://github.com/rehypejs/rehype), and many other content tools. Unified
30
+ provides a pluggable pipeline where each plugin transforms a syntax tree. Writr configures a default set of plugins to turn
31
+ Markdown into HTML, but you can access the processor through the `.engine` property to add your own behavior with
32
+ `writr.engine.use(myPlugin)`. The [unified documentation](https://unifiedjs.com/) has more details and guides for building
33
+ plugins and working with the processor directly.
34
+
25
35
  # Table of Contents
36
+ - [Unified Processor Engine](#unified-processor-engine)
26
37
  - [Getting Started](#getting-started)
27
38
  - [API](#api)
28
39
  - [`new Writr(arg?: string | WritrOptions, options?: WritrOptions)`](#new-writrarg-string--writroptions-options-writroptions)
@@ -33,16 +44,18 @@
33
44
  - [`.frontMatterRaw`](#frontmatterraw)
34
45
  - [`.cache`](#cache)
35
46
  - [`.engine`](#engine)
36
- - [`.render(options?: RenderOptions): Promise<string>`](#renderoptions-renderoptions-promisestring)
37
- - [`.renderSync(options?: RenderOptions): string`](#rendersyncoptions-renderoptions-string)
38
- - [`.renderToFile(filePath: string, options?: RenderOptions)`](#rendertofilefilepath-string-options-renderoptions)
39
- - [`.renderToFileSync(filePath: string, options?: RenderOptions): void`](#rendertofilesyncfilepath-string-options-renderoptions-void)
40
- - [`.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />`](#renderreactoptions-renderoptions-reactoptions-htmlreactparseroptions-promise-reactjsxelement-)
41
- - [`.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element`](#renderreactsync-options-renderoptions-reactoptions-htmlreactparseroptions-reactjsxelement)
42
- - [`.loadFromFile(filePath: string): Promise<void>`](#loadfromfilefilepath-string-promisevoid)
43
- - [`.loadFromFileSync(filePath: string): void`](#loadfromfilesyncfilepath-string-void)
44
- - [`.saveToFile(filePath: string): Promise<void>`](#savetofilefilepath-string-promisevoid)
45
- - [`.saveToFileSync(filePath: string): void`](#savetofilesyncfilepath-string-void)
47
+ - [`.render(options?: RenderOptions)`](#renderoptions-renderoptions)
48
+ - [`.renderSync(options?: RenderOptions)`](#rendersyncoptions-renderoptions)
49
+ - [`.renderToFile(filePath: string, options?)`](#rendertofilefilepath-string-options-renderoptions)
50
+ - [`.renderToFileSync(filePath: string, options?)`](#rendertofilesyncfilepath-string-options-renderoptions)
51
+ - [`.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions)`](#renderreactoptions-renderoptions-reactoptions-htmlreactparseroptions)
52
+ - [`.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions)`](#renderreactsync-options-renderoptions-reactoptions-htmlreactparseroptions)
53
+ - [`.validate(content?: string, options?: RenderOptions)`](#validatecontent-string-options-renderoptions)
54
+ - [`.validateSync(content?: string, options?: RenderOptions)`](#validatesynccontent-string-options-renderoptions)
55
+ - [`.loadFromFile(filePath: string)`](#loadfromfilefilepath-string)
56
+ - [`.loadFromFileSync(filePath: string)`](#loadfromfilesyncfilepath-string)
57
+ - [`.saveToFile(filePath: string)`](#savetofilefilepath-string)
58
+ - [`.saveToFileSync(filePath: string)`](#savetofilesyncfilepath-string)
46
59
  - [Hooks](#hooks)
47
60
  - [ESM and Node Version Support](#esm-and-node-version-support)
48
61
  - [Code of Conduct and Contributing](#code-of-conduct-and-contributing)
@@ -219,10 +232,10 @@ const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This i
219
232
 
220
233
  ## `.engine`
221
234
 
222
- 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. You can learn more about the unified engine [here](https://unifiedjs.com/) and a getting started guide [here](https://unifiedjs.com/learn/guide/using-unified/).
235
+ Accessing the underlying engine for this instance of Writr. This is a `Processor<Root, Root, Root, undefined, undefined>` from the core [`unified`](https://github.com/unifiedjs/unified) project and uses the familiar `.use()` plugin pattern. You can chain additional unified plugins on this processor to customize the render pipeline. Learn more about the unified engine at [unifiedjs.com](https://unifiedjs.com/) and check out the [getting started guide](https://unifiedjs.com/learn/guide/using-unified/) for examples.
223
236
 
224
237
 
225
- ## `.render(options?: RenderOptions): Promise<string>`
238
+ ## `.render(options?: RenderOptions)`
226
239
 
227
240
  Rendering markdown to HTML. the options are based on RenderOptions. Which you can access from the Writr instance.
228
241
 
@@ -239,7 +252,7 @@ const options = {
239
252
  const html = await writr.render(options); // <h1>Hello World ::-):</h1><p>This is a test.</p>
240
253
  ```
241
254
 
242
- ## `.renderSync(options?: RenderOptions): string`
255
+ ## `.renderSync(options?: RenderOptions)`
243
256
 
244
257
  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.
245
258
 
@@ -249,7 +262,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
249
262
  const html = writr.renderSync(); // <h1>Hello World 🙂</h1><p>This is a test.</p>
250
263
  ```
251
264
 
252
- ## '.renderToFile(filePath: string, options?: RenderOptions)'
265
+ ## `.renderToFile(filePath: string, options?: RenderOptions)`
253
266
 
254
267
  Rendering markdown to a file. The options are based on RenderOptions.
255
268
 
@@ -259,7 +272,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
259
272
  await writr.renderToFile('path/to/file.html');
260
273
  ```
261
274
 
262
- ## '.renderToFileSync(filePath: string, options?: RenderOptions): void'
275
+ ## `.renderToFileSync(filePath: string, options?: RenderOptions)`
263
276
 
264
277
  Rendering markdown to a file synchronously. The options are based on RenderOptions.
265
278
 
@@ -269,7 +282,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
269
282
  writr.renderToFileSync('path/to/file.html');
270
283
  ```
271
284
 
272
- ## '.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions): Promise<React.JSX.Element />'
285
+ ## `.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions)`
273
286
 
274
287
  Rendering markdown to React. The options are based on RenderOptions and now HTMLReactParserOptions from `html-react-parser`.
275
288
 
@@ -279,7 +292,7 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
279
292
  const reactElement = await writr.renderReact(); // Will return a React.JSX.Element
280
293
  ```
281
294
 
282
- ## '.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions): React.JSX.Element'
295
+ ## `.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions)`
283
296
 
284
297
  Rendering markdown to React. The options are based on RenderOptions and now HTMLReactParserOptions from `html-react-parser`.
285
298
 
@@ -289,7 +302,51 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
289
302
  const reactElement = writr.renderReactSync(); // Will return a React.JSX.Element
290
303
  ```
291
304
 
292
- ## `.loadFromFile(filePath: string): Promise<void>`
305
+ ## `.validate(content?: string, options?: RenderOptions)`
306
+
307
+ Validate markdown content by attempting to render it. Returns a `WritrValidateResult` object with a `valid` boolean and optional `error` property. Note that this will disable caching on render to ensure accurate validation.
308
+
309
+ ```javascript
310
+ import { Writr } from 'writr';
311
+ const writr = new Writr(`# Hello World\n\nThis is a test.`);
312
+
313
+ // Validate current content
314
+ const result = await writr.validate();
315
+ console.log(result.valid); // true
316
+
317
+ // Validate external content without changing the instance
318
+ const externalResult = await writr.validate('## Different Content');
319
+ console.log(externalResult.valid); // true
320
+ console.log(writr.content); // Still "# Hello World\n\nThis is a test."
321
+
322
+ // Handle validation errors
323
+ const invalidWritr = new Writr('Put invalid markdown here');
324
+ const errorResult = await invalidWritr.validate();
325
+ console.log(errorResult.valid); // false
326
+ console.log(errorResult.error?.message); // "Failed to render markdown: Invalid plugin"
327
+ ```
328
+
329
+ ## `.validateSync(content?: string, options?: RenderOptions)`
330
+
331
+ Synchronously validate markdown content by attempting to render it. Returns a `WritrValidateResult` object with a `valid` boolean and optional `error` property.
332
+
333
+ This is the synchronous version of `.validate()` with the same parameters and behavior.
334
+
335
+ ```javascript
336
+ import { Writr } from 'writr';
337
+ const writr = new Writr(`# Hello World\n\nThis is a test.`);
338
+
339
+ // Validate current content synchronously
340
+ const result = writr.validateSync();
341
+ console.log(result.valid); // true
342
+
343
+ // Validate external content without changing the instance
344
+ const externalResult = writr.validateSync('## Different Content');
345
+ console.log(externalResult.valid); // true
346
+ console.log(writr.content); // Still "# Hello World\n\nThis is a test."
347
+ ```
348
+
349
+ ## `.loadFromFile(filePath: string)`
293
350
 
294
351
  Load your markdown content from a file path.
295
352
 
@@ -299,11 +356,17 @@ const writr = new Writr();
299
356
  await writr.loadFromFile('path/to/file.md');
300
357
  ```
301
358
 
302
- ## `.loadFromFileSync(filePath: string): void`
359
+ ## `.loadFromFileSync(filePath: string)`
303
360
 
304
361
  Load your markdown content from a file path synchronously.
305
362
 
306
- ## `.saveToFile(filePath: string): Promise<void>`
363
+ ```javascript
364
+ import { Writr } from 'writr';
365
+ const writr = new Writr();
366
+ writr.loadFromFileSync('path/to/file.md');
367
+ ```
368
+
369
+ ## `.saveToFile(filePath: string)`
307
370
 
308
371
  Save your markdown and frontmatter (if included) content to a file path.
309
372
 
@@ -313,10 +376,16 @@ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
313
376
  await writr.saveToFile('path/to/file.md');
314
377
  ```
315
378
 
316
- ## `.saveToFileSync(filePath: string): void`
379
+ ## `.saveToFileSync(filePath: string)`
317
380
 
318
381
  Save your markdown and frontmatter (if included) content to a file path synchronously.
319
382
 
383
+ ```javascript
384
+ import { Writr } from 'writr';
385
+ const writr = new Writr(`# Hello World ::-):\n\n This is a test.`);
386
+ writr.saveToFileSync('path/to/file.md');
387
+ ```
388
+
320
389
  # Caching On Render
321
390
 
322
391
  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:
@@ -409,10 +478,10 @@ This is called when you call `loadFromFile`, `loadFromFileSync`.
409
478
 
410
479
  # ESM and Node Version Support
411
480
 
412
- 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
481
+ 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.
413
482
 
414
483
  # Code of Conduct and Contributing
415
- [Code of Conduct](CODE_OF_CONDUCT.md) and [Contributing](CONTRIBUTING.md) guidelines.
484
+ Please use our [Code of Conduct](CODE_OF_CONDUCT.md) and [Contributing](CONTRIBUTING.md) guidelines for development and testing. We appreciate your contributions!
416
485
 
417
486
  # License
418
487
 
package/dist/writr.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as unified from 'unified';
2
2
  import * as hast from 'hast';
3
3
  import * as mdast from 'mdast';
4
- import React from 'react';
5
- import { HTMLReactParserOptions } from 'html-react-parser';
6
4
  import { Hookified } from 'hookified';
5
+ import { HTMLReactParserOptions } from 'html-react-parser';
6
+ import React from 'react';
7
7
  import { CacheableMemory } from 'cacheable';
8
8
 
9
9
  declare class WritrCache {
@@ -50,6 +50,16 @@ type RenderOptions = {
50
50
  mdx?: boolean;
51
51
  caching?: boolean;
52
52
  };
53
+ /**
54
+ * Validation result.
55
+ * @typedef {Object} WritrValidateResult
56
+ * @property {boolean} valid - Whether the markdown is valid
57
+ * @property {Error} [error] - Error if validation failed
58
+ */
59
+ type WritrValidateResult = {
60
+ valid: boolean;
61
+ error?: Error;
62
+ };
53
63
  declare enum WritrHooks {
54
64
  beforeRender = "beforeRender",
55
65
  afterRender = "afterRender",
@@ -134,6 +144,20 @@ declare class Writr extends Hookified {
134
144
  * @returns {string} The rendered HTML content.
135
145
  */
136
146
  renderSync(options?: RenderOptions): string;
147
+ /**
148
+ * Validate the markdown content by attempting to render it.
149
+ * @param {string} [content] The markdown content to validate. If not provided, uses the current content.
150
+ * @param {RenderOptions} [options] The render options.
151
+ * @returns {Promise<WritrValidateResult>} An object with a valid boolean and optional error.
152
+ */
153
+ validate(content?: string, options?: RenderOptions): Promise<WritrValidateResult>;
154
+ /**
155
+ * Validate the markdown content by attempting to render it synchronously.
156
+ * @param {string} [content] The markdown content to validate. If not provided, uses the current content.
157
+ * @param {RenderOptions} [options] The render options.
158
+ * @returns {WritrValidateResult} An object with a valid boolean and optional error.
159
+ */
160
+ validateSync(content?: string, options?: RenderOptions): WritrValidateResult;
137
161
  /**
138
162
  * Render the markdown content and save it to a file. If the directory doesn't exist it will be created.
139
163
  * @param {string} filePath The file path to save the rendered markdown content to.
@@ -190,4 +214,4 @@ declare class Writr extends Hookified {
190
214
  private mergeRenderOptions;
191
215
  }
192
216
 
193
- export { type RenderOptions, Writr, WritrHooks, type WritrOptions };
217
+ export { type RenderOptions, Writr, WritrHooks, type WritrOptions, type WritrValidateResult };
package/dist/writr.js CHANGED
@@ -1,24 +1,24 @@
1
1
  // src/writr.ts
2
2
  import fs from "fs";
3
3
  import { dirname } from "path";
4
- import { unified } from "unified";
5
- import remarkParse from "remark-parse";
6
- import remarkRehype from "remark-rehype";
7
- import rehypeSlug from "rehype-slug";
4
+ import { Hookified } from "hookified";
5
+ import parse from "html-react-parser";
6
+ import * as yaml from "js-yaml";
8
7
  import rehypeHighlight from "rehype-highlight";
9
- import rehypeStringify from "rehype-stringify";
10
- import remarkToc from "remark-toc";
11
- import remarkMath from "remark-math";
12
8
  import rehypeKatex from "rehype-katex";
13
- import remarkGfm from "remark-gfm";
9
+ import rehypeSlug from "rehype-slug";
10
+ import rehypeStringify from "rehype-stringify";
14
11
  import remarkEmoji from "remark-emoji";
12
+ import remarkGfm from "remark-gfm";
13
+ import remarkMath from "remark-math";
15
14
  import remarkMDX from "remark-mdx";
16
- import parse from "html-react-parser";
17
- import * as yaml from "js-yaml";
18
- import { Hookified } from "hookified";
15
+ import remarkParse from "remark-parse";
16
+ import remarkRehype from "remark-rehype";
17
+ import remarkToc from "remark-toc";
18
+ import { unified } from "unified";
19
19
 
20
20
  // src/writr-cache.ts
21
- import { CacheableMemory, Cacheable } from "cacheable";
21
+ import { Cacheable, CacheableMemory } from "cacheable";
22
22
  var WritrCache = class {
23
23
  _store = new CacheableMemory();
24
24
  _hashStore = new CacheableMemory();
@@ -142,23 +142,24 @@ var Writr = class extends Hookified {
142
142
  if (!this._content.trimStart().startsWith("---")) {
143
143
  return "";
144
144
  }
145
- const start = this._content.indexOf("---\n");
146
- const end = this._content.indexOf("\n---\n", start + 4);
147
- if (end === -1) {
148
- return "";
145
+ const match = /^\s*(---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$))/.exec(
146
+ this._content
147
+ );
148
+ if (match) {
149
+ return match[1];
149
150
  }
150
- return this._content.slice(start, end + 5);
151
+ return "";
151
152
  }
152
153
  /**
153
154
  * Get the body content without the front matter.
154
155
  * @type {string} The markdown content without the front matter.
155
156
  */
156
157
  get body() {
157
- if (this.frontMatterRaw === "") {
158
+ const frontMatter = this.frontMatterRaw;
159
+ if (frontMatter === "") {
158
160
  return this._content;
159
161
  }
160
- const end = this._content.indexOf("\n---\n");
161
- return this._content.slice(Math.max(0, end + 5)).trim();
162
+ return this._content.slice(this._content.indexOf(frontMatter) + frontMatter.length).trim();
162
163
  }
163
164
  /**
164
165
  * Get the markdown content. This is an alias for the body property.
@@ -171,6 +172,7 @@ var Writr = class extends Hookified {
171
172
  * Get the front matter content as an object.
172
173
  * @type {Record<string, any>} The front matter content as an object.
173
174
  */
175
+ // biome-ignore lint/suspicious/noExplicitAny: expected
174
176
  get frontMatter() {
175
177
  const frontMatter = this.frontMatterRaw;
176
178
  const match = /^---\s*([\s\S]*?)\s*---\s*/.exec(frontMatter);
@@ -187,6 +189,7 @@ var Writr = class extends Hookified {
187
189
  * Set the front matter content as an object.
188
190
  * @type {Record<string, any>} The front matter content as an object.
189
191
  */
192
+ // biome-ignore lint/suspicious/noExplicitAny: expected
190
193
  set frontMatter(data) {
191
194
  const frontMatter = this.frontMatterRaw;
192
195
  const yamlString = yaml.dump(data);
@@ -233,7 +236,11 @@ ${yamlString}---
233
236
  const file = await engine.process(renderData.body);
234
237
  resultData.result = String(file);
235
238
  if (this.isCacheEnabled(renderData.options)) {
236
- this._cache.set(renderData.content, resultData.result, renderData.options);
239
+ this._cache.set(
240
+ renderData.content,
241
+ resultData.result,
242
+ renderData.options
243
+ );
237
244
  }
238
245
  await this.hook("afterRender" /* afterRender */, resultData);
239
246
  return resultData.result;
@@ -271,7 +278,11 @@ ${yamlString}---
271
278
  const file = engine.processSync(renderData.body);
272
279
  resultData.result = String(file);
273
280
  if (this.isCacheEnabled(renderData.options)) {
274
- this._cache.set(renderData.content, resultData.result, renderData.options);
281
+ this._cache.set(
282
+ renderData.content,
283
+ resultData.result,
284
+ renderData.options
285
+ );
275
286
  }
276
287
  this.hook("afterRender" /* afterRender */, resultData);
277
288
  return resultData.result;
@@ -279,6 +290,72 @@ ${yamlString}---
279
290
  throw new Error(`Failed to render markdown: ${error.message}`);
280
291
  }
281
292
  }
293
+ /**
294
+ * Validate the markdown content by attempting to render it.
295
+ * @param {string} [content] The markdown content to validate. If not provided, uses the current content.
296
+ * @param {RenderOptions} [options] The render options.
297
+ * @returns {Promise<WritrValidateResult>} An object with a valid boolean and optional error.
298
+ */
299
+ async validate(content, options) {
300
+ const originalContent = this._content;
301
+ try {
302
+ if (content !== void 0) {
303
+ this._content = content;
304
+ }
305
+ let { engine } = this;
306
+ if (options) {
307
+ options = {
308
+ ...this._options.renderOptions,
309
+ ...options,
310
+ caching: false
311
+ };
312
+ engine = this.createProcessor(options);
313
+ }
314
+ await engine.run(engine.parse(this.body));
315
+ if (content !== void 0) {
316
+ this._content = originalContent;
317
+ }
318
+ return { valid: true };
319
+ } catch (error) {
320
+ if (content !== void 0) {
321
+ this._content = originalContent;
322
+ }
323
+ return { valid: false, error };
324
+ }
325
+ }
326
+ /**
327
+ * Validate the markdown content by attempting to render it synchronously.
328
+ * @param {string} [content] The markdown content to validate. If not provided, uses the current content.
329
+ * @param {RenderOptions} [options] The render options.
330
+ * @returns {WritrValidateResult} An object with a valid boolean and optional error.
331
+ */
332
+ validateSync(content, options) {
333
+ const originalContent = this._content;
334
+ try {
335
+ if (content !== void 0) {
336
+ this._content = content;
337
+ }
338
+ let { engine } = this;
339
+ if (options) {
340
+ options = {
341
+ ...this._options.renderOptions,
342
+ ...options,
343
+ caching: false
344
+ };
345
+ engine = this.createProcessor(options);
346
+ }
347
+ engine.runSync(engine.parse(this.body));
348
+ if (content !== void 0) {
349
+ this._content = originalContent;
350
+ }
351
+ return { valid: true };
352
+ } catch (error) {
353
+ if (content !== void 0) {
354
+ this._content = originalContent;
355
+ }
356
+ return { valid: false, error };
357
+ }
358
+ }
282
359
  /**
283
360
  * Render the markdown content and save it to a file. If the directory doesn't exist it will be created.
284
361
  * @param {string} filePath The file path to save the rendered markdown content to.
@@ -438,6 +515,7 @@ ${yamlString}---
438
515
  }
439
516
  return this._options?.renderOptions?.caching ?? false;
440
517
  }
518
+ // biome-ignore lint/suspicious/noExplicitAny: expected unified processor
441
519
  createProcessor(options) {
442
520
  const processor = unified().use(remarkParse);
443
521
  if (options.gfm) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "writr",
3
- "version": "4.4.6",
3
+ "version": "4.5.1",
4
4
  "description": "Markdown Rendering Simplified",
5
5
  "type": "module",
6
6
  "main": "./dist/writr.js",
@@ -45,45 +45,47 @@
45
45
  "markdown-to-react"
46
46
  ],
47
47
  "scripts": {
48
- "clean": "rimraf ./dist ./coverage ./node_modules ./package-lock.json ./yarn.lock ./site/README.md ./site/dist",
48
+ "clean": "rimraf ./dist ./coverage ./node_modules ./pnpm-lock.yaml ./site/README.md ./site/dist",
49
49
  "build": "rimraf ./dist && tsup src/writr.ts --format esm --dts --clean",
50
- "prepare": "npm run build",
51
- "test": "xo --fix && vitest run --coverage",
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",
52
54
  "website:build": "rimraf ./site/README.md ./site/dist && npx docula build -s ./site -o ./site/dist",
53
55
  "website:serve": "rimraf ./site/README.md ./site/dist && npx docula serve -s ./site -o ./site/dist"
54
56
  },
55
57
  "dependencies": {
56
- "cacheable": "^1.10.3",
57
- "hookified": "^1.11.0",
58
+ "cacheable": "^2.0.2",
59
+ "hookified": "^1.12.1",
58
60
  "html-react-parser": "^5.2.6",
59
61
  "js-yaml": "^4.1.0",
60
- "react": "^19.1.0",
62
+ "react": "^19.1.1",
61
63
  "rehype-highlight": "^7.0.2",
62
64
  "rehype-katex": "^7.0.1",
63
65
  "rehype-slug": "^6.0.0",
64
66
  "rehype-stringify": "^10.0.1",
65
- "remark-emoji": "^5.0.1",
67
+ "remark-emoji": "^5.0.2",
66
68
  "remark-gfm": "^4.0.1",
67
69
  "remark-math": "^6.0.0",
68
- "remark-mdx": "^3.1.0",
70
+ "remark-mdx": "^3.1.1",
69
71
  "remark-parse": "^11.0.0",
70
72
  "remark-rehype": "^11.1.2",
71
73
  "remark-toc": "^9.0.0",
72
74
  "unified": "^11.0.5"
73
75
  },
74
76
  "devDependencies": {
77
+ "@biomejs/biome": "^2.2.4",
75
78
  "@types/js-yaml": "^4.0.9",
76
- "@types/node": "^24.1.0",
77
- "@types/react": "^19.1.8",
79
+ "@types/node": "^24.5.2",
80
+ "@types/react": "^19.1.13",
78
81
  "@vitest/coverage-v8": "^3.2.4",
79
- "docula": "^0.13.1",
82
+ "docula": "^0.30.0",
80
83
  "rimraf": "^6.0.1",
81
84
  "ts-node": "^10.9.2",
82
85
  "tsup": "^8.5.0",
83
- "typescript": "^5.8.3",
86
+ "typescript": "^5.9.2",
84
87
  "vitest": "^3.2.4",
85
- "webpack": "^5.100.2",
86
- "xo": "^1.2.1"
88
+ "webpack": "^5.101.3"
87
89
  },
88
90
  "xo": {
89
91
  "ignores": [