peasy-document 0.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/LICENSE +21 -0
- package/README.md +282 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.js +461 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peasy Tools
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# peasy-document
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/peasy-document)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.npmjs.com/package/peasy-document)
|
|
7
|
+
|
|
8
|
+
Pure TypeScript document conversion library for Markdown, HTML, CSV, JSON, and YAML transformations. Convert between 6 document formats with 10 conversion functions and frozen result objects -- all with zero runtime dependencies. Every conversion uses pure string processing, making it lightweight and fast for any JavaScript or TypeScript project.
|
|
9
|
+
|
|
10
|
+
Part of the [Peasy Tools](https://peasytools.com) developer tools ecosystem.
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
- [Install](#install)
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
16
|
+
- [What You Can Do](#what-you-can-do)
|
|
17
|
+
- [Markdown Conversion](#markdown-conversion)
|
|
18
|
+
- [HTML Processing](#html-processing)
|
|
19
|
+
- [CSV and JSON Conversion](#csv-and-json-conversion)
|
|
20
|
+
- [YAML Generation](#yaml-generation)
|
|
21
|
+
- [Table Formatting](#table-formatting)
|
|
22
|
+
- [TypeScript Types](#typescript-types)
|
|
23
|
+
- [API Reference](#api-reference)
|
|
24
|
+
- [Also Available for Python](#also-available-for-python)
|
|
25
|
+
- [Peasy Developer Tools](#peasy-developer-tools)
|
|
26
|
+
- [License](#license)
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install peasy-document
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Zero runtime dependencies. Works in Node.js, Deno, Bun, and modern bundlers.
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { markdownToHtml, csvToJson, htmlToText } from "peasy-document";
|
|
40
|
+
|
|
41
|
+
// Convert Markdown to HTML with headings, bold, italic, code blocks, and lists
|
|
42
|
+
const result = markdownToHtml("# Hello World\n\nThis is **bold** text.");
|
|
43
|
+
console.log(result.content);
|
|
44
|
+
// <h1>Hello World</h1>
|
|
45
|
+
// <p>This is <strong>bold</strong> text.</p>
|
|
46
|
+
|
|
47
|
+
// Convert CSV data to JSON array of objects
|
|
48
|
+
const json = csvToJson("name,age\nAlice,30\nBob,25");
|
|
49
|
+
console.log(json.content);
|
|
50
|
+
// [{"name": "Alice", "age": "30"}, {"name": "Bob", "age": "25"}]
|
|
51
|
+
|
|
52
|
+
// Strip HTML to plain text with entity decoding
|
|
53
|
+
const text = htmlToText("<h1>Title</h1><p>Hello & welcome.</p>");
|
|
54
|
+
console.log(text.content);
|
|
55
|
+
// Title
|
|
56
|
+
// Hello & welcome.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
All functions return a `ConversionResult` with metadata:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const result = markdownToHtml("# Hello");
|
|
63
|
+
console.log(result.sourceFormat); // "markdown"
|
|
64
|
+
console.log(result.targetFormat); // "html"
|
|
65
|
+
console.log(result.sourceSize); // 7 (bytes)
|
|
66
|
+
console.log(result.targetSize); // 14 (bytes)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## What You Can Do
|
|
70
|
+
|
|
71
|
+
### Markdown Conversion
|
|
72
|
+
|
|
73
|
+
Convert Markdown to HTML using a built-in pure TypeScript parser. Supports headings (h1-h6), bold, italic, inline code, fenced code blocks with language hints, links, images, unordered and ordered lists, blockquotes, horizontal rules, and paragraphs.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { markdownToHtml } from "peasy-document";
|
|
77
|
+
|
|
78
|
+
// Full Markdown document with multiple elements
|
|
79
|
+
const result = markdownToHtml(`
|
|
80
|
+
# API Documentation
|
|
81
|
+
|
|
82
|
+
| Feature | Status |
|
|
83
|
+
|---------|--------|
|
|
84
|
+
| Auth | Done |
|
|
85
|
+
|
|
86
|
+
\`\`\`typescript
|
|
87
|
+
const api = new Client({ key: "abc" });
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
- First item
|
|
91
|
+
- Second item
|
|
92
|
+
|
|
93
|
+
> Important note about the API
|
|
94
|
+
`);
|
|
95
|
+
|
|
96
|
+
console.log(result.content);
|
|
97
|
+
// <h1>API Documentation</h1>
|
|
98
|
+
// <pre><code class="language-typescript">...</code></pre>
|
|
99
|
+
// <ul><li>First item</li>...</ul>
|
|
100
|
+
// <blockquote><p>Important note about the API</p></blockquote>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### HTML Processing
|
|
104
|
+
|
|
105
|
+
Extract plain text from HTML documents, convert HTML to Markdown, or turn plain text into HTML paragraphs.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { htmlToText, htmlToMarkdown, textToHtml } from "peasy-document";
|
|
109
|
+
|
|
110
|
+
// Strip all HTML tags and decode entities
|
|
111
|
+
const text = htmlToText(`
|
|
112
|
+
<html><body>
|
|
113
|
+
<h1>Welcome</h1>
|
|
114
|
+
<p>This is a <strong>formatted</strong> document with & entities.</p>
|
|
115
|
+
<script>alert('ignored')</script>
|
|
116
|
+
</body></html>
|
|
117
|
+
`);
|
|
118
|
+
console.log(text.content);
|
|
119
|
+
// Welcome
|
|
120
|
+
// This is a formatted document with & entities.
|
|
121
|
+
|
|
122
|
+
// Convert HTML back to Markdown (handles h1-h6, a, strong, em, lists, code, pre, img)
|
|
123
|
+
const md = htmlToMarkdown(`
|
|
124
|
+
<h1>Document</h1>
|
|
125
|
+
<p>Visit <a href="https://example.com">our site</a> for <strong>more info</strong>.</p>
|
|
126
|
+
<ul><li>First</li><li>Second</li></ul>
|
|
127
|
+
`);
|
|
128
|
+
console.log(md.content);
|
|
129
|
+
// # Document
|
|
130
|
+
// Visit [our site](https://example.com) for **more info**.
|
|
131
|
+
// - First
|
|
132
|
+
// - Second
|
|
133
|
+
|
|
134
|
+
// Convert plain text to HTML paragraphs
|
|
135
|
+
const html = textToHtml("First paragraph.\n\nSecond paragraph.\nWith a line break.");
|
|
136
|
+
console.log(html.content);
|
|
137
|
+
// <p>First paragraph.</p>
|
|
138
|
+
// <p>Second paragraph.<br>With a line break.</p>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### CSV and JSON Conversion
|
|
142
|
+
|
|
143
|
+
Transform between CSV and JSON formats with proper handling of quoted fields, commas inside values, escaped quotes, and custom delimiters. Roundtrip-safe.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { csvToJson, jsonToCsv } from "peasy-document";
|
|
147
|
+
|
|
148
|
+
// CSV to JSON array of objects
|
|
149
|
+
const json = csvToJson("name,role,team\nAlice,Engineer,Backend\nBob,Designer,Frontend");
|
|
150
|
+
console.log(json.content);
|
|
151
|
+
// [
|
|
152
|
+
// {"name": "Alice", "role": "Engineer", "team": "Backend"},
|
|
153
|
+
// {"name": "Bob", "role": "Designer", "team": "Frontend"}
|
|
154
|
+
// ]
|
|
155
|
+
|
|
156
|
+
// JSON back to CSV
|
|
157
|
+
const csv = jsonToCsv(json.content);
|
|
158
|
+
console.log(csv.content);
|
|
159
|
+
// name,role,team
|
|
160
|
+
// Alice,Engineer,Backend
|
|
161
|
+
// Bob,Designer,Frontend
|
|
162
|
+
|
|
163
|
+
// Tab-separated values
|
|
164
|
+
const tsv = csvToJson("name\tage\nAlice\t30", "\t");
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### YAML Generation
|
|
168
|
+
|
|
169
|
+
Convert JSON to YAML-like format without any external YAML library. Handles nested objects, arrays, strings, numbers, booleans, and null.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { jsonToYaml } from "peasy-document";
|
|
173
|
+
|
|
174
|
+
const result = jsonToYaml(JSON.stringify({
|
|
175
|
+
server: { host: "localhost", port: 8080 },
|
|
176
|
+
features: ["auth", "logging"],
|
|
177
|
+
debug: true,
|
|
178
|
+
cache: null,
|
|
179
|
+
}));
|
|
180
|
+
console.log(result.content);
|
|
181
|
+
// server:
|
|
182
|
+
// host: localhost
|
|
183
|
+
// port: 8080
|
|
184
|
+
// features:
|
|
185
|
+
// - auth
|
|
186
|
+
// - logging
|
|
187
|
+
// debug: true
|
|
188
|
+
// cache: null
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Table Formatting
|
|
192
|
+
|
|
193
|
+
Parse CSV into structured table data, or render it directly as Markdown or HTML tables.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import { csvToTable, csvToMarkdown, csvToHtml } from "peasy-document";
|
|
197
|
+
|
|
198
|
+
// Parse into structured TableData
|
|
199
|
+
const table = csvToTable("Name,Age,City\nAlice,30,NYC\nBob,25,LA");
|
|
200
|
+
console.log(table.headers); // ["Name", "Age", "City"]
|
|
201
|
+
console.log(table.rowCount); // 2
|
|
202
|
+
console.log(table.columnCount); // 3
|
|
203
|
+
console.log(table.rows[0]); // ["Alice", "30", "NYC"]
|
|
204
|
+
|
|
205
|
+
// Render as Markdown table with aligned columns
|
|
206
|
+
const md = csvToMarkdown("Name,Age,City\nAlice,30,NYC\nBob,25,LA");
|
|
207
|
+
console.log(md.content);
|
|
208
|
+
// | Name | Age | City |
|
|
209
|
+
// | ----- | --- | ---- |
|
|
210
|
+
// | Alice | 30 | NYC |
|
|
211
|
+
// | Bob | 25 | LA |
|
|
212
|
+
|
|
213
|
+
// Render as HTML table with thead/tbody structure
|
|
214
|
+
const html = csvToHtml("Name,Age\nAlice,30");
|
|
215
|
+
console.log(html.content);
|
|
216
|
+
// <table>
|
|
217
|
+
// <thead>
|
|
218
|
+
// <tr><th>Name</th><th>Age</th></tr>
|
|
219
|
+
// </thead>
|
|
220
|
+
// <tbody>
|
|
221
|
+
// <tr><td>Alice</td><td>30</td></tr>
|
|
222
|
+
// </tbody>
|
|
223
|
+
// </table>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## TypeScript Types
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
interface ConversionResult {
|
|
230
|
+
content: string; // The converted content
|
|
231
|
+
sourceFormat: string; // e.g. "markdown", "html", "csv", "json", "text"
|
|
232
|
+
targetFormat: string; // e.g. "html", "text", "json", "csv", "yaml", "markdown"
|
|
233
|
+
sourceSize: number; // Byte size of source (UTF-8)
|
|
234
|
+
targetSize: number; // Byte size of output (UTF-8)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
interface TableData {
|
|
238
|
+
headers: string[]; // Column headers from first row
|
|
239
|
+
rows: string[][]; // Data rows
|
|
240
|
+
rowCount: number; // Number of data rows
|
|
241
|
+
columnCount: number; // Number of columns
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## API Reference
|
|
246
|
+
|
|
247
|
+
| Function | Input | Output | Description |
|
|
248
|
+
|----------|-------|--------|-------------|
|
|
249
|
+
| `markdownToHtml(source)` | Markdown | HTML | Headings, bold, italic, code, lists, links, images, blockquotes |
|
|
250
|
+
| `htmlToText(source)` | HTML | Plain text | Strip tags, decode entities, normalize whitespace |
|
|
251
|
+
| `htmlToMarkdown(source)` | HTML | Markdown | h1-h6, a, strong, em, lists, code, pre, img, blockquote |
|
|
252
|
+
| `textToHtml(source)` | Plain text | HTML | Paragraphs to `<p>` tags, newlines to `<br>` |
|
|
253
|
+
| `csvToJson(source, delimiter?)` | CSV | JSON | First row as headers, quoted field support |
|
|
254
|
+
| `jsonToCsv(source)` | JSON | CSV | All unique keys as headers, nested values stringified |
|
|
255
|
+
| `csvToTable(source, delimiter?)` | CSV | `TableData` | Structured headers, rows, and counts |
|
|
256
|
+
| `csvToMarkdown(source, delimiter?)` | CSV | Markdown | Pipe-separated table with alignment row |
|
|
257
|
+
| `csvToHtml(source, delimiter?)` | CSV | HTML | `<table>` with `<thead>` and `<tbody>` |
|
|
258
|
+
| `jsonToYaml(source)` | JSON | YAML | Objects, arrays, primitives, null handling |
|
|
259
|
+
|
|
260
|
+
## Also Available for Python
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
pip install peasy-document
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
The Python version provides the same 10 conversion functions with frozen dataclass results. See [peasy-document on PyPI](https://pypi.org/project/peasy-document/).
|
|
267
|
+
|
|
268
|
+
## Peasy Developer Tools
|
|
269
|
+
|
|
270
|
+
| Package | PyPI | npm | Description |
|
|
271
|
+
|---------|------|-----|-------------|
|
|
272
|
+
| **peasy-document** | [PyPI](https://pypi.org/project/peasy-document/) | [npm](https://www.npmjs.com/package/peasy-document) | Document conversion -- Markdown, HTML, CSV, JSON, YAML |
|
|
273
|
+
| peasy-pdf | [PyPI](https://pypi.org/project/peasy-pdf/) | -- | PDF manipulation and conversion |
|
|
274
|
+
| peasy-image | [PyPI](https://pypi.org/project/peasy-image/) | -- | Image format conversion and optimization |
|
|
275
|
+
| peasytext | [PyPI](https://pypi.org/project/peasytext/) | [npm](https://www.npmjs.com/package/peasytext) | Text analysis and transformation |
|
|
276
|
+
| peasy-css | [PyPI](https://pypi.org/project/peasy-css/) | [npm](https://www.npmjs.com/package/peasy-css) | CSS minification and processing |
|
|
277
|
+
| peasy-compress | [PyPI](https://pypi.org/project/peasy-compress/) | [npm](https://www.npmjs.com/package/peasy-compress) | File compression utilities |
|
|
278
|
+
| peasy-convert | [PyPI](https://pypi.org/project/peasy-convert/) | -- | Unified CLI for all Peasy tools |
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of a document format conversion.
|
|
3
|
+
*
|
|
4
|
+
* All conversion functions return this interface with the converted content
|
|
5
|
+
* and metadata about the source/target formats and sizes (in bytes).
|
|
6
|
+
*/
|
|
7
|
+
interface ConversionResult {
|
|
8
|
+
/** The converted content string. */
|
|
9
|
+
content: string;
|
|
10
|
+
/** Source format identifier (e.g. "markdown", "html", "csv", "json", "text"). */
|
|
11
|
+
sourceFormat: string;
|
|
12
|
+
/** Target format identifier (e.g. "html", "text", "json", "csv", "yaml", "markdown"). */
|
|
13
|
+
targetFormat: string;
|
|
14
|
+
/** Byte size of the source input (UTF-8 encoded). */
|
|
15
|
+
sourceSize: number;
|
|
16
|
+
/** Byte size of the converted output (UTF-8 encoded). */
|
|
17
|
+
targetSize: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Structured table data parsed from CSV or similar tabular sources.
|
|
21
|
+
*/
|
|
22
|
+
interface TableData {
|
|
23
|
+
/** Column header names from the first row. */
|
|
24
|
+
headers: string[];
|
|
25
|
+
/** Data rows (each row is an array of cell values). */
|
|
26
|
+
rows: string[][];
|
|
27
|
+
/** Number of data rows (excluding the header). */
|
|
28
|
+
rowCount: number;
|
|
29
|
+
/** Number of columns. */
|
|
30
|
+
columnCount: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Document conversion engine — pure TypeScript functions for Markdown, HTML,
|
|
35
|
+
* CSV, JSON, and YAML transformations.
|
|
36
|
+
*
|
|
37
|
+
* Zero runtime dependencies. All conversions use string processing only.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert Markdown to HTML.
|
|
42
|
+
*
|
|
43
|
+
* Supports headings (#-######), bold, italic, inline code, fenced code blocks,
|
|
44
|
+
* links, images, unordered lists (- or *), ordered lists (1.), blockquotes (>),
|
|
45
|
+
* horizontal rules (---/***), and paragraphs.
|
|
46
|
+
*/
|
|
47
|
+
declare function markdownToHtml(source: string): ConversionResult;
|
|
48
|
+
/**
|
|
49
|
+
* Strip all HTML tags and decode entities to produce plain text.
|
|
50
|
+
*
|
|
51
|
+
* Removes script/style contents. Normalizes whitespace.
|
|
52
|
+
*/
|
|
53
|
+
declare function htmlToText(source: string): ConversionResult;
|
|
54
|
+
/**
|
|
55
|
+
* Convert CSV to a JSON array of objects.
|
|
56
|
+
*
|
|
57
|
+
* The first row is used as headers (object keys). Handles quoted fields,
|
|
58
|
+
* escaped quotes, and custom delimiters.
|
|
59
|
+
*/
|
|
60
|
+
declare function csvToJson(source: string, delimiter?: string): ConversionResult;
|
|
61
|
+
/**
|
|
62
|
+
* Convert a JSON array of objects to CSV.
|
|
63
|
+
*
|
|
64
|
+
* Extracts all unique keys as headers. Nested values are JSON-stringified.
|
|
65
|
+
*/
|
|
66
|
+
declare function jsonToCsv(source: string): ConversionResult;
|
|
67
|
+
/**
|
|
68
|
+
* Parse CSV into structured TableData with headers, rows, and counts.
|
|
69
|
+
*/
|
|
70
|
+
declare function csvToTable(source: string, delimiter?: string): TableData;
|
|
71
|
+
/**
|
|
72
|
+
* Convert CSV to a Markdown table with pipe separators and alignment row.
|
|
73
|
+
*/
|
|
74
|
+
declare function csvToMarkdown(source: string, delimiter?: string): ConversionResult;
|
|
75
|
+
/**
|
|
76
|
+
* Convert CSV to an HTML table with proper thead/tbody structure.
|
|
77
|
+
*/
|
|
78
|
+
declare function csvToHtml(source: string, delimiter?: string): ConversionResult;
|
|
79
|
+
/**
|
|
80
|
+
* Convert a JSON string to YAML-like format.
|
|
81
|
+
*
|
|
82
|
+
* Handles objects, arrays, strings, numbers, booleans, and null.
|
|
83
|
+
* No external YAML library required.
|
|
84
|
+
*/
|
|
85
|
+
declare function jsonToYaml(source: string): ConversionResult;
|
|
86
|
+
/**
|
|
87
|
+
* Convert plain text to HTML paragraphs.
|
|
88
|
+
*
|
|
89
|
+
* Splits on double newlines, wraps each paragraph in <p> tags.
|
|
90
|
+
* Single newlines within a paragraph become <br> tags.
|
|
91
|
+
* HTML entities in the source text are escaped.
|
|
92
|
+
*/
|
|
93
|
+
declare function textToHtml(source: string): ConversionResult;
|
|
94
|
+
/**
|
|
95
|
+
* Convert basic HTML to Markdown.
|
|
96
|
+
*
|
|
97
|
+
* Handles: h1-h6, p, a, strong/b, em/i, ul/ol/li, code, pre, br, img, blockquote.
|
|
98
|
+
* Uses regex-based tag replacement.
|
|
99
|
+
*/
|
|
100
|
+
declare function htmlToMarkdown(source: string): ConversionResult;
|
|
101
|
+
|
|
102
|
+
export { type ConversionResult, type TableData, csvToHtml, csvToJson, csvToMarkdown, csvToTable, htmlToMarkdown, htmlToText, jsonToCsv, jsonToYaml, markdownToHtml, textToHtml };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
// src/engine.ts
|
|
2
|
+
function byteLength(s) {
|
|
3
|
+
return new TextEncoder().encode(s).length;
|
|
4
|
+
}
|
|
5
|
+
function makeResult(content, sourceFormat, targetFormat, source) {
|
|
6
|
+
return {
|
|
7
|
+
content,
|
|
8
|
+
sourceFormat,
|
|
9
|
+
targetFormat,
|
|
10
|
+
sourceSize: byteLength(source),
|
|
11
|
+
targetSize: byteLength(content)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function escapeHtml(s) {
|
|
15
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
16
|
+
}
|
|
17
|
+
function decodeEntities(s) {
|
|
18
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(/ /g, " ").replace(/&#(\d+);/g, (_match, code) => String.fromCharCode(Number(code))).replace(
|
|
19
|
+
/&#x([0-9a-fA-F]+);/g,
|
|
20
|
+
(_match, hex) => String.fromCharCode(parseInt(hex, 16))
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
function parseCsv(source, delimiter = ",") {
|
|
24
|
+
const rows = [];
|
|
25
|
+
let row = [];
|
|
26
|
+
let field = "";
|
|
27
|
+
let inQuotes = false;
|
|
28
|
+
for (let i = 0; i < source.length; i++) {
|
|
29
|
+
const ch = source[i];
|
|
30
|
+
if (inQuotes) {
|
|
31
|
+
if (ch === '"') {
|
|
32
|
+
if (i + 1 < source.length && source[i + 1] === '"') {
|
|
33
|
+
field += '"';
|
|
34
|
+
i++;
|
|
35
|
+
} else {
|
|
36
|
+
inQuotes = false;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
field += ch;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
if (ch === '"') {
|
|
43
|
+
inQuotes = true;
|
|
44
|
+
} else if (ch === delimiter) {
|
|
45
|
+
row.push(field);
|
|
46
|
+
field = "";
|
|
47
|
+
} else if (ch === "\n" || ch === "\r" && source[i + 1] === "\n") {
|
|
48
|
+
row.push(field);
|
|
49
|
+
field = "";
|
|
50
|
+
rows.push(row);
|
|
51
|
+
row = [];
|
|
52
|
+
if (ch === "\r") i++;
|
|
53
|
+
} else if (ch === "\r") {
|
|
54
|
+
row.push(field);
|
|
55
|
+
field = "";
|
|
56
|
+
rows.push(row);
|
|
57
|
+
row = [];
|
|
58
|
+
} else {
|
|
59
|
+
field += ch;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (field || row.length > 0) {
|
|
64
|
+
row.push(field);
|
|
65
|
+
rows.push(row);
|
|
66
|
+
}
|
|
67
|
+
return rows;
|
|
68
|
+
}
|
|
69
|
+
function processInline(text) {
|
|
70
|
+
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">');
|
|
71
|
+
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
|
72
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
|
|
73
|
+
text = text.replace(/__(.+?)__/g, "<strong>$1</strong>");
|
|
74
|
+
text = text.replace(/\*(.+?)\*/g, "<em>$1</em>");
|
|
75
|
+
text = text.replace(/(?<!\w)_(.+?)_(?!\w)/g, "<em>$1</em>");
|
|
76
|
+
text = text.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
77
|
+
return text;
|
|
78
|
+
}
|
|
79
|
+
function markdownToHtml(source) {
|
|
80
|
+
const lines = source.split("\n");
|
|
81
|
+
const htmlParts = [];
|
|
82
|
+
let inCodeBlock = false;
|
|
83
|
+
let codeBlockContent = "";
|
|
84
|
+
let codeBlockLang = "";
|
|
85
|
+
let inList = null;
|
|
86
|
+
let paragraphBuffer = [];
|
|
87
|
+
function flushParagraph() {
|
|
88
|
+
if (paragraphBuffer.length > 0) {
|
|
89
|
+
const content2 = paragraphBuffer.map(processInline).join("\n");
|
|
90
|
+
htmlParts.push(`<p>${content2}</p>`);
|
|
91
|
+
paragraphBuffer = [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function closeList() {
|
|
95
|
+
if (inList) {
|
|
96
|
+
htmlParts.push(inList === "ul" ? "</ul>" : "</ol>");
|
|
97
|
+
inList = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < lines.length; i++) {
|
|
101
|
+
const line = lines[i];
|
|
102
|
+
if (line.trimStart().startsWith("```")) {
|
|
103
|
+
if (!inCodeBlock) {
|
|
104
|
+
flushParagraph();
|
|
105
|
+
closeList();
|
|
106
|
+
inCodeBlock = true;
|
|
107
|
+
codeBlockLang = line.trimStart().slice(3).trim();
|
|
108
|
+
codeBlockContent = "";
|
|
109
|
+
} else {
|
|
110
|
+
const langAttr = codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : "";
|
|
111
|
+
htmlParts.push(
|
|
112
|
+
`<pre><code${langAttr}>${escapeHtml(codeBlockContent.replace(/\n$/, ""))}</code></pre>`
|
|
113
|
+
);
|
|
114
|
+
inCodeBlock = false;
|
|
115
|
+
codeBlockContent = "";
|
|
116
|
+
codeBlockLang = "";
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (inCodeBlock) {
|
|
121
|
+
codeBlockContent += (codeBlockContent ? "\n" : "") + line;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (line.trim() === "") {
|
|
125
|
+
flushParagraph();
|
|
126
|
+
closeList();
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (/^(\s*[-*_]\s*){3,}$/.test(line)) {
|
|
130
|
+
flushParagraph();
|
|
131
|
+
closeList();
|
|
132
|
+
htmlParts.push("<hr>");
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
136
|
+
if (headingMatch) {
|
|
137
|
+
flushParagraph();
|
|
138
|
+
closeList();
|
|
139
|
+
const level = headingMatch[1].length;
|
|
140
|
+
const text = processInline(headingMatch[2]);
|
|
141
|
+
htmlParts.push(`<h${level}>${text}</h${level}>`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const bqMatch = line.match(/^>\s?(.*)$/);
|
|
145
|
+
if (bqMatch) {
|
|
146
|
+
flushParagraph();
|
|
147
|
+
closeList();
|
|
148
|
+
const content2 = processInline(bqMatch[1]);
|
|
149
|
+
htmlParts.push(`<blockquote><p>${content2}</p></blockquote>`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const ulMatch = line.match(/^[\s]*[-*]\s+(.+)$/);
|
|
153
|
+
if (ulMatch) {
|
|
154
|
+
flushParagraph();
|
|
155
|
+
if (inList !== "ul") {
|
|
156
|
+
closeList();
|
|
157
|
+
htmlParts.push("<ul>");
|
|
158
|
+
inList = "ul";
|
|
159
|
+
}
|
|
160
|
+
htmlParts.push(`<li>${processInline(ulMatch[1])}</li>`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const olMatch = line.match(/^[\s]*\d+\.\s+(.+)$/);
|
|
164
|
+
if (olMatch) {
|
|
165
|
+
flushParagraph();
|
|
166
|
+
if (inList !== "ol") {
|
|
167
|
+
closeList();
|
|
168
|
+
htmlParts.push("<ol>");
|
|
169
|
+
inList = "ol";
|
|
170
|
+
}
|
|
171
|
+
htmlParts.push(`<li>${processInline(olMatch[1])}</li>`);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
paragraphBuffer.push(line);
|
|
175
|
+
}
|
|
176
|
+
if (inCodeBlock) {
|
|
177
|
+
const langAttr = codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : "";
|
|
178
|
+
htmlParts.push(
|
|
179
|
+
`<pre><code${langAttr}>${escapeHtml(codeBlockContent)}</code></pre>`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
flushParagraph();
|
|
183
|
+
closeList();
|
|
184
|
+
const content = htmlParts.join("\n");
|
|
185
|
+
return makeResult(content, "markdown", "html", source);
|
|
186
|
+
}
|
|
187
|
+
function htmlToText(source) {
|
|
188
|
+
let text = source;
|
|
189
|
+
text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
190
|
+
text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
191
|
+
text = text.replace(
|
|
192
|
+
/<\/?(?:p|div|br|h[1-6]|li|tr|blockquote|pre|hr)(?:\s[^>]*)?\/?>/gi,
|
|
193
|
+
"\n"
|
|
194
|
+
);
|
|
195
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
196
|
+
text = decodeEntities(text);
|
|
197
|
+
const lines = text.split("\n").map((line) => line.replace(/\s+/g, " ").trim());
|
|
198
|
+
text = lines.join("\n");
|
|
199
|
+
while (text.includes("\n\n\n")) {
|
|
200
|
+
text = text.replace(/\n\n\n/g, "\n\n");
|
|
201
|
+
}
|
|
202
|
+
const content = text.trim();
|
|
203
|
+
return makeResult(content, "html", "text", source);
|
|
204
|
+
}
|
|
205
|
+
function csvToJson(source, delimiter = ",") {
|
|
206
|
+
const rows = parseCsv(source, delimiter);
|
|
207
|
+
if (rows.length === 0) {
|
|
208
|
+
return makeResult("[]", "csv", "json", source);
|
|
209
|
+
}
|
|
210
|
+
const headers = rows[0];
|
|
211
|
+
const dataRows = rows.slice(1);
|
|
212
|
+
const objects = dataRows.map((row) => {
|
|
213
|
+
const obj = {};
|
|
214
|
+
headers.forEach((header, i) => {
|
|
215
|
+
obj[header] = i < row.length ? row[i] : "";
|
|
216
|
+
});
|
|
217
|
+
return obj;
|
|
218
|
+
});
|
|
219
|
+
const content = JSON.stringify(objects, null, 2);
|
|
220
|
+
return makeResult(content, "csv", "json", source);
|
|
221
|
+
}
|
|
222
|
+
function quoteCsvField(value) {
|
|
223
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
|
|
224
|
+
return '"' + value.replace(/"/g, '""') + '"';
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
function jsonToCsv(source) {
|
|
229
|
+
const data = JSON.parse(source);
|
|
230
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
231
|
+
return makeResult("", "json", "csv", source);
|
|
232
|
+
}
|
|
233
|
+
const allKeys = [];
|
|
234
|
+
const seen = /* @__PURE__ */ new Set();
|
|
235
|
+
for (const item of data) {
|
|
236
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
237
|
+
for (const key of Object.keys(item)) {
|
|
238
|
+
if (!seen.has(key)) {
|
|
239
|
+
allKeys.push(key);
|
|
240
|
+
seen.add(key);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const lines = [];
|
|
246
|
+
lines.push(allKeys.map(quoteCsvField).join(","));
|
|
247
|
+
for (const item of data) {
|
|
248
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
249
|
+
const record = item;
|
|
250
|
+
const row = allKeys.map((key) => {
|
|
251
|
+
const val = record[key];
|
|
252
|
+
if (val === null || val === void 0) return "";
|
|
253
|
+
if (typeof val === "object") return quoteCsvField(JSON.stringify(val));
|
|
254
|
+
return quoteCsvField(String(val));
|
|
255
|
+
});
|
|
256
|
+
lines.push(row.join(","));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const content = lines.join("\n");
|
|
260
|
+
return makeResult(content, "json", "csv", source);
|
|
261
|
+
}
|
|
262
|
+
function csvToTable(source, delimiter = ",") {
|
|
263
|
+
const rows = parseCsv(source, delimiter);
|
|
264
|
+
if (rows.length === 0) {
|
|
265
|
+
return { headers: [], rows: [], rowCount: 0, columnCount: 0 };
|
|
266
|
+
}
|
|
267
|
+
const headers = rows[0];
|
|
268
|
+
const dataRows = rows.slice(1);
|
|
269
|
+
return {
|
|
270
|
+
headers,
|
|
271
|
+
rows: dataRows,
|
|
272
|
+
rowCount: dataRows.length,
|
|
273
|
+
columnCount: headers.length
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function csvToMarkdown(source, delimiter = ",") {
|
|
277
|
+
const table = csvToTable(source, delimiter);
|
|
278
|
+
if (table.headers.length === 0) {
|
|
279
|
+
return makeResult("", "csv", "markdown", source);
|
|
280
|
+
}
|
|
281
|
+
const widths = table.headers.map((h) => h.length);
|
|
282
|
+
for (const row of table.rows) {
|
|
283
|
+
for (let i = 0; i < row.length && i < widths.length; i++) {
|
|
284
|
+
widths[i] = Math.max(widths[i], row[i].length);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
for (let i = 0; i < widths.length; i++) {
|
|
288
|
+
widths[i] = Math.max(widths[i], 3);
|
|
289
|
+
}
|
|
290
|
+
const headerCells = table.headers.map((h, i) => h.padEnd(widths[i]));
|
|
291
|
+
const headerLine = "| " + headerCells.join(" | ") + " |";
|
|
292
|
+
const sepCells = widths.map((w) => "-".repeat(w));
|
|
293
|
+
const sepLine = "| " + sepCells.join(" | ") + " |";
|
|
294
|
+
const dataLines = table.rows.map((row) => {
|
|
295
|
+
const cells = table.headers.map((_, i) => {
|
|
296
|
+
const value = i < row.length ? row[i] : "";
|
|
297
|
+
return value.padEnd(widths[i]);
|
|
298
|
+
});
|
|
299
|
+
return "| " + cells.join(" | ") + " |";
|
|
300
|
+
});
|
|
301
|
+
const content = [headerLine, sepLine, ...dataLines].join("\n");
|
|
302
|
+
return makeResult(content, "csv", "markdown", source);
|
|
303
|
+
}
|
|
304
|
+
function csvToHtml(source, delimiter = ",") {
|
|
305
|
+
const table = csvToTable(source, delimiter);
|
|
306
|
+
if (table.headers.length === 0) {
|
|
307
|
+
const content2 = "<table></table>";
|
|
308
|
+
return makeResult(content2, "csv", "html", source);
|
|
309
|
+
}
|
|
310
|
+
const lines = ["<table>", " <thead>", " <tr>"];
|
|
311
|
+
for (const h of table.headers) {
|
|
312
|
+
lines.push(` <th>${escapeHtml(h)}</th>`);
|
|
313
|
+
}
|
|
314
|
+
lines.push(" </tr>", " </thead>", " <tbody>");
|
|
315
|
+
for (const row of table.rows) {
|
|
316
|
+
lines.push(" <tr>");
|
|
317
|
+
for (let i = 0; i < table.headers.length; i++) {
|
|
318
|
+
const value = i < row.length ? row[i] : "";
|
|
319
|
+
lines.push(` <td>${escapeHtml(value)}</td>`);
|
|
320
|
+
}
|
|
321
|
+
lines.push(" </tr>");
|
|
322
|
+
}
|
|
323
|
+
lines.push(" </tbody>", "</table>");
|
|
324
|
+
const content = lines.join("\n");
|
|
325
|
+
return makeResult(content, "csv", "html", source);
|
|
326
|
+
}
|
|
327
|
+
function yamlScalar(value) {
|
|
328
|
+
if (value === null || value === void 0) return "null";
|
|
329
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
330
|
+
if (typeof value === "number") return String(value);
|
|
331
|
+
const s = String(value);
|
|
332
|
+
if (/[:#\[\]{},"&*!|>']/.test(s) || /^(true|false|null|yes|no|on|off)$/i.test(s) || s === "") {
|
|
333
|
+
const escaped = s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
334
|
+
return `"${escaped}"`;
|
|
335
|
+
}
|
|
336
|
+
return s;
|
|
337
|
+
}
|
|
338
|
+
function toYamlLines(obj, indent = 0) {
|
|
339
|
+
const prefix = " ".repeat(indent);
|
|
340
|
+
const lines = [];
|
|
341
|
+
if (obj && typeof obj === "object" && !Array.isArray(obj)) {
|
|
342
|
+
const record = obj;
|
|
343
|
+
for (const [key, value] of Object.entries(record)) {
|
|
344
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
345
|
+
lines.push(`${prefix}${key}:`);
|
|
346
|
+
lines.push(...toYamlLines(value, indent + 1));
|
|
347
|
+
} else if (Array.isArray(value)) {
|
|
348
|
+
lines.push(`${prefix}${key}:`);
|
|
349
|
+
for (const item of value) {
|
|
350
|
+
if (item && typeof item === "object") {
|
|
351
|
+
lines.push(`${prefix} -`);
|
|
352
|
+
lines.push(...toYamlLines(item, indent + 2));
|
|
353
|
+
} else {
|
|
354
|
+
lines.push(`${prefix} - ${yamlScalar(item)}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
lines.push(`${prefix}${key}: ${yamlScalar(value)}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
} else if (Array.isArray(obj)) {
|
|
362
|
+
for (const item of obj) {
|
|
363
|
+
if (item && typeof item === "object") {
|
|
364
|
+
lines.push(`${prefix}-`);
|
|
365
|
+
lines.push(...toYamlLines(item, indent + 1));
|
|
366
|
+
} else {
|
|
367
|
+
lines.push(`${prefix}- ${yamlScalar(item)}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
lines.push(`${prefix}${yamlScalar(obj)}`);
|
|
372
|
+
}
|
|
373
|
+
return lines;
|
|
374
|
+
}
|
|
375
|
+
function jsonToYaml(source) {
|
|
376
|
+
const data = JSON.parse(source);
|
|
377
|
+
const lines = toYamlLines(data);
|
|
378
|
+
const content = lines.join("\n");
|
|
379
|
+
return makeResult(content, "json", "yaml", source);
|
|
380
|
+
}
|
|
381
|
+
function textToHtml(source) {
|
|
382
|
+
if (!source.trim()) {
|
|
383
|
+
return makeResult("", "text", "html", source);
|
|
384
|
+
}
|
|
385
|
+
const paragraphs = source.split(/\n\n+/).map((p) => p.trim()).filter((p) => p);
|
|
386
|
+
const htmlParts = paragraphs.map((para) => {
|
|
387
|
+
const escaped = escapeHtml(para);
|
|
388
|
+
const withBreaks = escaped.replace(/\n/g, "<br>");
|
|
389
|
+
return `<p>${withBreaks}</p>`;
|
|
390
|
+
});
|
|
391
|
+
const content = htmlParts.join("\n");
|
|
392
|
+
return makeResult(content, "text", "html", source);
|
|
393
|
+
}
|
|
394
|
+
function htmlToMarkdown(source) {
|
|
395
|
+
let md = source;
|
|
396
|
+
md = md.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
397
|
+
md = md.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
398
|
+
md = md.replace(
|
|
399
|
+
/<pre[^>]*>\s*<code[^>]*>([\s\S]*?)<\/code>\s*<\/pre>/gi,
|
|
400
|
+
(_match, content2) => "\n\n```\n" + decodeEntities(content2).trim() + "\n```\n\n"
|
|
401
|
+
);
|
|
402
|
+
md = md.replace(
|
|
403
|
+
/<pre[^>]*>([\s\S]*?)<\/pre>/gi,
|
|
404
|
+
(_match, content2) => "\n\n```\n" + decodeEntities(content2).trim() + "\n```\n\n"
|
|
405
|
+
);
|
|
406
|
+
md = md.replace(
|
|
407
|
+
/<img[^>]*\ssrc=["']([^"']+)["'][^>]*\salt=["']([^"']*?)["'][^>]*\/?>/gi,
|
|
408
|
+
(_match, src, alt) => ``
|
|
409
|
+
);
|
|
410
|
+
md = md.replace(
|
|
411
|
+
/<img[^>]*\salt=["']([^"']*?)["'][^>]*\ssrc=["']([^"']+)["'][^>]*\/?>/gi,
|
|
412
|
+
(_match, alt, src) => ``
|
|
413
|
+
);
|
|
414
|
+
md = md.replace(
|
|
415
|
+
/<img[^>]*\ssrc=["']([^"']+)["'][^>]*\/?>/gi,
|
|
416
|
+
(_match, src) => ``
|
|
417
|
+
);
|
|
418
|
+
md = md.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "\n# $1\n");
|
|
419
|
+
md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "\n## $1\n");
|
|
420
|
+
md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "\n### $1\n");
|
|
421
|
+
md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "\n#### $1\n");
|
|
422
|
+
md = md.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, "\n##### $1\n");
|
|
423
|
+
md = md.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, "\n###### $1\n");
|
|
424
|
+
md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, (_match, content2) => {
|
|
425
|
+
const clean = content2.replace(/<\/?p[^>]*>/gi, "").trim();
|
|
426
|
+
return "\n> " + clean + "\n";
|
|
427
|
+
});
|
|
428
|
+
md = md.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
|
|
429
|
+
md = md.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
|
|
430
|
+
md = md.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, "`$1`");
|
|
431
|
+
md = md.replace(
|
|
432
|
+
/<a[^>]*\shref=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi,
|
|
433
|
+
(_match, href, text) => `[${text}](${href})`
|
|
434
|
+
);
|
|
435
|
+
md = md.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_match, content2) => {
|
|
436
|
+
return "- " + content2.replace(/<\/?[^>]+>/g, "").trim() + "\n";
|
|
437
|
+
});
|
|
438
|
+
md = md.replace(/<\/?(?:ul|ol)[^>]*>/gi, "\n");
|
|
439
|
+
md = md.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, "\n$1\n");
|
|
440
|
+
md = md.replace(/<br\s*\/?>/gi, " \n");
|
|
441
|
+
md = md.replace(/<hr\s*\/?>/gi, "\n---\n");
|
|
442
|
+
md = md.replace(/<[^>]+>/g, "");
|
|
443
|
+
md = decodeEntities(md);
|
|
444
|
+
while (md.includes("\n\n\n")) {
|
|
445
|
+
md = md.replace(/\n\n\n/g, "\n\n");
|
|
446
|
+
}
|
|
447
|
+
const content = md.trim();
|
|
448
|
+
return makeResult(content, "html", "markdown", source);
|
|
449
|
+
}
|
|
450
|
+
export {
|
|
451
|
+
csvToHtml,
|
|
452
|
+
csvToJson,
|
|
453
|
+
csvToMarkdown,
|
|
454
|
+
csvToTable,
|
|
455
|
+
htmlToMarkdown,
|
|
456
|
+
htmlToText,
|
|
457
|
+
jsonToCsv,
|
|
458
|
+
jsonToYaml,
|
|
459
|
+
markdownToHtml,
|
|
460
|
+
textToHtml
|
|
461
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "peasy-document",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Document conversion library — Markdown, HTML, CSV, JSON, YAML. Zero dependencies, TypeScript-first.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"document",
|
|
24
|
+
"markdown",
|
|
25
|
+
"html",
|
|
26
|
+
"csv",
|
|
27
|
+
"json",
|
|
28
|
+
"yaml",
|
|
29
|
+
"converter",
|
|
30
|
+
"peasy"
|
|
31
|
+
],
|
|
32
|
+
"author": "Peasy Tools",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"url": "https://github.com/peasytools/peasy-document-js.git"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://peasytools.com",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"tsup": "^8.0",
|
|
40
|
+
"typescript": "^5.7",
|
|
41
|
+
"vitest": "^3.0"
|
|
42
|
+
}
|
|
43
|
+
}
|