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 +93 -24
- package/dist/writr.d.ts +27 -3
- package/dist/writr.js +100 -22
- package/package.json +17 -15
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
|
-
*
|
|
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)
|
|
37
|
-
- [`.renderSync(options?: RenderOptions)
|
|
38
|
-
- [`.renderToFile(filePath: string, options
|
|
39
|
-
- [`.renderToFileSync(filePath: string, options
|
|
40
|
-
- [`.renderReact(options?: RenderOptions, reactOptions?: HTMLReactParserOptions)
|
|
41
|
-
- [`.renderReactSync( options?: RenderOptions, reactOptions?: HTMLReactParserOptions)
|
|
42
|
-
- [`.
|
|
43
|
-
- [`.
|
|
44
|
-
- [`.
|
|
45
|
-
- [`.
|
|
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>`
|
|
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)
|
|
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)
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
## `.
|
|
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)
|
|
359
|
+
## `.loadFromFileSync(filePath: string)`
|
|
303
360
|
|
|
304
361
|
Load your markdown content from a file path synchronously.
|
|
305
362
|
|
|
306
|
-
|
|
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)
|
|
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.
|
|
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 {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
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
|
|
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
|
|
17
|
-
import
|
|
18
|
-
import
|
|
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 {
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
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
|
-
|
|
158
|
+
const frontMatter = this.frontMatterRaw;
|
|
159
|
+
if (frontMatter === "") {
|
|
158
160
|
return this._content;
|
|
159
161
|
}
|
|
160
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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 ./
|
|
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": "
|
|
51
|
-
"
|
|
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": "^
|
|
57
|
-
"hookified": "^1.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
77
|
-
"@types/react": "^19.1.
|
|
79
|
+
"@types/node": "^24.5.2",
|
|
80
|
+
"@types/react": "^19.1.13",
|
|
78
81
|
"@vitest/coverage-v8": "^3.2.4",
|
|
79
|
-
"docula": "^0.
|
|
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.
|
|
86
|
+
"typescript": "^5.9.2",
|
|
84
87
|
"vitest": "^3.2.4",
|
|
85
|
-
"webpack": "^5.
|
|
86
|
-
"xo": "^1.2.1"
|
|
88
|
+
"webpack": "^5.101.3"
|
|
87
89
|
},
|
|
88
90
|
"xo": {
|
|
89
91
|
"ignores": [
|