@hardlydifficult/text 1.0.16 → 1.0.18
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 +212 -243
- package/dist/escapeFence.d.ts +9 -0
- package/dist/escapeFence.d.ts.map +1 -0
- package/dist/escapeFence.js +22 -0
- package/dist/escapeFence.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,154 +11,199 @@ npm install @hardlydifficult/text
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
replaceTemplate,
|
|
16
|
+
chunkText,
|
|
17
|
+
slugify,
|
|
18
|
+
formatDuration,
|
|
19
|
+
buildFileTree,
|
|
20
|
+
convertFormat,
|
|
21
|
+
createLinker,
|
|
22
|
+
} from "@hardlydifficult/text";
|
|
15
23
|
|
|
16
24
|
// Template replacement
|
|
17
|
-
const greeting =
|
|
18
|
-
|
|
25
|
+
const greeting = replaceTemplate("Hello {{name}}!", { name: "Alice" });
|
|
26
|
+
// "Hello Alice!"
|
|
19
27
|
|
|
20
|
-
//
|
|
21
|
-
const chunks =
|
|
22
|
-
|
|
28
|
+
// Split long text into chunks
|
|
29
|
+
const chunks = chunkText("Line 1\nLine 2\nLine 3", 10);
|
|
30
|
+
// ["Line 1\nLine 2", "Line 3"]
|
|
23
31
|
|
|
24
|
-
//
|
|
25
|
-
|
|
32
|
+
// Convert to URL-safe slug
|
|
33
|
+
const slug = slugify("My Feature Name!", 10);
|
|
34
|
+
// "my-feature"
|
|
26
35
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
## Error Handling
|
|
32
|
-
|
|
33
|
-
### `getErrorMessage(err: unknown): string`
|
|
36
|
+
// Format duration in ms
|
|
37
|
+
const formatted = formatDuration(125_000);
|
|
38
|
+
// "2m 5s"
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
// Build a file tree
|
|
41
|
+
const tree = buildFileTree(["src/index.ts", "src/utils.ts", "README.md"]);
|
|
42
|
+
// src/
|
|
43
|
+
// index.ts
|
|
44
|
+
// utils.ts
|
|
45
|
+
//
|
|
46
|
+
// README.md
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.error(getErrorMessage(err)); // "Something went wrong"
|
|
44
|
-
}
|
|
48
|
+
// Convert between JSON/YAML
|
|
49
|
+
const yaml = convertFormat('{"name":"Alice"}', "yaml");
|
|
50
|
+
// name: Alice
|
|
45
51
|
```
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
## Error Handling
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
Consistent error extraction and formatting utilities.
|
|
50
56
|
|
|
51
57
|
```typescript
|
|
52
|
-
import { formatError } from "@hardlydifficult/text";
|
|
53
|
-
|
|
54
|
-
formatError(new Error("not found"), "User lookup"); // "User lookup: not found"
|
|
55
|
-
formatError(new Error("not found")); // "not found"
|
|
56
|
-
```
|
|
58
|
+
import { getErrorMessage, formatError, formatErrorForLog } from "@hardlydifficult/text";
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
const err = new Error("disk full");
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
getErrorMessage(err); // "disk full"
|
|
63
|
+
formatError(err, "Failed to save"); // "Failed to save: disk full"
|
|
64
|
+
formatErrorForLog(err); // "disk full"
|
|
65
|
+
```
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
import { formatErrorForLog } from "@hardlydifficult/text";
|
|
67
|
+
### Functions
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
| Function | Description |
|
|
70
|
+
|----------|-------------|
|
|
71
|
+
| `getErrorMessage(err)` | Extract message string from unknown error |
|
|
72
|
+
| `formatError(err, context?)` | Format error with optional context prefix |
|
|
73
|
+
| `formatErrorForLog(err)` | Format error for logging (non-Error → string) |
|
|
67
74
|
|
|
68
75
|
## Template Replacement
|
|
69
76
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Replace `{{variable}}` placeholders with provided values.
|
|
77
|
+
Simple string interpolation using `{{variable}}` syntax.
|
|
73
78
|
|
|
74
79
|
```typescript
|
|
75
|
-
import { replaceTemplate } from "@hardlydifficult/text";
|
|
80
|
+
import { replaceTemplate, extractPlaceholders } from "@hardlydifficult/text";
|
|
76
81
|
|
|
77
|
-
replaceTemplate("Hello {{name}}
|
|
82
|
+
const text = replaceTemplate("Hello {{name}}! You are {{age}}.", {
|
|
78
83
|
name: "Alice",
|
|
79
|
-
|
|
84
|
+
age: "30",
|
|
80
85
|
});
|
|
81
|
-
// "Hello Alice
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### `extractPlaceholders(template: string): string[]`
|
|
85
|
-
|
|
86
|
-
Extract all unique placeholder names from a template string.
|
|
86
|
+
// "Hello Alice! You are 30."
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
extractPlaceholders("{{name}} is in {{place}}"); // ["name", "place"]
|
|
88
|
+
const vars = extractPlaceholders("{{greeting}}, {{name}}!");
|
|
89
|
+
// ["greeting", "name"]
|
|
92
90
|
```
|
|
93
91
|
|
|
94
|
-
|
|
92
|
+
### Functions
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
| Function | Description |
|
|
95
|
+
|----------|-------------|
|
|
96
|
+
| `replaceTemplate(template, values)` | Replace all `{{variable}}` placeholders |
|
|
97
|
+
| `extractPlaceholders(template)` | Return unique placeholder names |
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
## Text Chunking
|
|
100
|
+
|
|
101
|
+
Split long text into manageable chunks respecting natural breaks.
|
|
99
102
|
|
|
100
103
|
```typescript
|
|
101
104
|
import { chunkText } from "@hardlydifficult/text";
|
|
102
105
|
|
|
103
|
-
const text = "
|
|
104
|
-
|
|
105
|
-
// ["
|
|
106
|
+
const text = "line one\nline two\nline three";
|
|
107
|
+
chunkText(text, 18);
|
|
108
|
+
// ["line one\nline two", "line three"]
|
|
106
109
|
```
|
|
107
110
|
|
|
108
|
-
###
|
|
111
|
+
### Behavior
|
|
112
|
+
|
|
113
|
+
- Breaks on newlines first, then spaces
|
|
114
|
+
- Falls back to hard breaks when no natural break point exists
|
|
115
|
+
- Trims leading whitespace from subsequent chunks
|
|
109
116
|
|
|
110
|
-
|
|
117
|
+
## Slugification
|
|
118
|
+
|
|
119
|
+
Convert strings to URL/filename-safe slugs.
|
|
111
120
|
|
|
112
121
|
```typescript
|
|
113
122
|
import { slugify } from "@hardlydifficult/text";
|
|
114
123
|
|
|
115
124
|
slugify("My Feature Name!"); // "my-feature-name"
|
|
116
125
|
slugify("My Feature Name!", 10); // "my-feature"
|
|
117
|
-
slugify(" spaces & symbols! "); // "spaces-symbols"
|
|
118
126
|
```
|
|
119
127
|
|
|
120
|
-
###
|
|
128
|
+
### Features
|
|
121
129
|
|
|
122
|
-
|
|
130
|
+
- Lowercases and replaces non-alphanumeric runs with single hyphens
|
|
131
|
+
- Trims leading/trailing hyphens
|
|
132
|
+
- Optional `maxLength` truncates at hyphen boundary when possible
|
|
123
133
|
|
|
124
|
-
|
|
125
|
-
import { formatWithLineNumbers } from "@hardlydifficult/text";
|
|
134
|
+
## Duration Formatting
|
|
126
135
|
|
|
127
|
-
|
|
128
|
-
// " 1: foo\n 2: bar\n 3: baz"
|
|
136
|
+
Format milliseconds as human-readable duration strings.
|
|
129
137
|
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
```typescript
|
|
139
|
+
import { formatDuration } from "@hardlydifficult/text";
|
|
140
|
+
|
|
141
|
+
formatDuration(125_000); // "2m 5s"
|
|
142
|
+
formatDuration(3_600_000); // "1h"
|
|
143
|
+
formatDuration(500); // "<1s"
|
|
132
144
|
```
|
|
133
145
|
|
|
134
|
-
###
|
|
146
|
+
### Format Rules
|
|
135
147
|
|
|
136
|
-
|
|
148
|
+
| Duration | Output |
|
|
149
|
+
|----------|--------|
|
|
150
|
+
| < 1000ms | `<1s` |
|
|
151
|
+
| 1–59 seconds | `<seconds>s` |
|
|
152
|
+
| 1–59 minutes | `<minutes>m` or `<minutes>m <seconds>s` |
|
|
153
|
+
| 1–23 hours | `<hours>h` or `<hours>h <minutes>m` |
|
|
154
|
+
| ≥ 1 day | `<days>d` or `<days>d <hours>h` |
|
|
155
|
+
|
|
156
|
+
## File Tree Rendering
|
|
157
|
+
|
|
158
|
+
Build and render hierarchical file trees with depth-based truncation and annotations.
|
|
137
159
|
|
|
138
160
|
```typescript
|
|
139
|
-
import {
|
|
161
|
+
import { buildFileTree, FILE_TREE_DEFAULTS } from "@hardlydifficult/text";
|
|
162
|
+
import type { BuildTreeOptions } from "@hardlydifficult/text";
|
|
140
163
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
164
|
+
const paths = [
|
|
165
|
+
"src/index.ts",
|
|
166
|
+
"test/unit/a.test.ts",
|
|
167
|
+
"test/unit/b.test.ts",
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
const options: BuildTreeOptions = {
|
|
171
|
+
maxLevel2: 2,
|
|
172
|
+
annotations: new Map([["src/index.ts", "Entry point"]]),
|
|
173
|
+
collapseDirs: ["test"],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
buildFileTree(paths, options);
|
|
177
|
+
// src/
|
|
178
|
+
// index.ts — Entry point
|
|
179
|
+
//
|
|
180
|
+
// test/
|
|
181
|
+
// (2 files across 1 dir)
|
|
145
182
|
```
|
|
146
183
|
|
|
147
|
-
|
|
184
|
+
### Options
|
|
185
|
+
|
|
186
|
+
| Option | Type | Default | Description |
|
|
187
|
+
|--------|------|---------|-------------|
|
|
188
|
+
| `maxLevel2` | `number` | 10 | Max children to show at depth 2 (top-level dirs) |
|
|
189
|
+
| `maxLevel3` | `number` | 3 | Max children to show at depth 3 (files/dirs inside top dirs) |
|
|
190
|
+
| `annotations` | `Map<string, string>` | `undefined` | File/dir descriptions |
|
|
191
|
+
| `details` | `Map<string, string[]>` | `undefined` | Extra lines to show under file entries |
|
|
192
|
+
| `collapseDirs` | `string[]` | `undefined` | Directory names to collapse and summarize |
|
|
148
193
|
|
|
149
|
-
|
|
194
|
+
## Format Conversion
|
|
150
195
|
|
|
151
|
-
Convert between JSON and YAML
|
|
196
|
+
Convert text between JSON and YAML with auto-detection.
|
|
152
197
|
|
|
153
198
|
```typescript
|
|
154
199
|
import { convertFormat } from "@hardlydifficult/text";
|
|
200
|
+
import type { TextFormat } from "@hardlydifficult/text";
|
|
155
201
|
|
|
156
202
|
// JSON to YAML
|
|
157
|
-
convertFormat('{"name":
|
|
203
|
+
convertFormat('{"name":"Alice"}', "yaml");
|
|
158
204
|
// name: Alice
|
|
159
|
-
// age: 30
|
|
160
205
|
|
|
161
|
-
// YAML to JSON
|
|
206
|
+
// YAML to JSON (pretty-printed with 2-space indent)
|
|
162
207
|
convertFormat("name: Alice\nage: 30", "json");
|
|
163
208
|
// {
|
|
164
209
|
// "name": "Alice",
|
|
@@ -166,212 +211,136 @@ convertFormat("name: Alice\nage: 30", "json");
|
|
|
166
211
|
// }
|
|
167
212
|
```
|
|
168
213
|
|
|
169
|
-
###
|
|
214
|
+
### Functions
|
|
170
215
|
|
|
171
|
-
|
|
216
|
+
| Function | Description |
|
|
217
|
+
|----------|-------------|
|
|
218
|
+
| `convertFormat(content, to)` | Parse input and re-serialize to `json` or `yaml` |
|
|
172
219
|
|
|
173
|
-
|
|
174
|
-
import { formatYaml } from "@hardlydifficult/text";
|
|
220
|
+
## YAML Formatting
|
|
175
221
|
|
|
176
|
-
|
|
177
|
-
// "message: >\n Hello: World"
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### `healYaml(dirtyYaml: string): string`
|
|
181
|
-
|
|
182
|
-
Clean malformed YAML by stripping markdown code fences and quoting problematic scalar values containing colons.
|
|
222
|
+
Serialize data to clean YAML, using block literals for long strings containing `: `.
|
|
183
223
|
|
|
184
224
|
```typescript
|
|
185
|
-
import {
|
|
225
|
+
import { formatYaml } from "@hardlydifficult/text";
|
|
186
226
|
|
|
187
|
-
|
|
188
|
-
|
|
227
|
+
formatYaml({
|
|
228
|
+
purpose:
|
|
229
|
+
"Core AI SDK: LLM integrations (Anthropic, Ollama) and streaming support.",
|
|
230
|
+
});
|
|
231
|
+
// purpose: |
|
|
232
|
+
// Core AI SDK: LLM integrations (Anthropic, Ollama) and streaming support.
|
|
189
233
|
```
|
|
190
234
|
|
|
191
|
-
|
|
235
|
+
### Behavior
|
|
192
236
|
|
|
193
|
-
|
|
237
|
+
- Long strings (`>60 chars`) containing `: ` render as block literals (`|`)
|
|
238
|
+
- Short strings and safe scalars remain plain or quoted as needed
|
|
239
|
+
- Preserves round-trip parseability
|
|
194
240
|
|
|
195
|
-
|
|
241
|
+
## YAML Healing
|
|
242
|
+
|
|
243
|
+
Sanitize malformed YAML from LLMs by stripping code fences and quoting scalar values containing colons.
|
|
196
244
|
|
|
197
245
|
```typescript
|
|
198
|
-
import {
|
|
246
|
+
import { healYaml } from "@hardlydifficult/text";
|
|
247
|
+
import { parse } from "yaml";
|
|
199
248
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
249
|
+
const badYaml = `\`\`\`yaml
|
|
250
|
+
purpose: |
|
|
251
|
+
Core AI: LLM integrations (Anthropic, Ollama)
|
|
252
|
+
description: Main deps: Node, TypeScript, Vitest.
|
|
253
|
+
\`\`\``;
|
|
203
254
|
|
|
204
|
-
|
|
205
|
-
//
|
|
255
|
+
const cleaned = healYaml(badYaml);
|
|
256
|
+
// purpose: |
|
|
257
|
+
// Core AI: LLM integrations (Anthropic, Ollama)
|
|
258
|
+
// description: "Main deps: Node, TypeScript, Vitest."
|
|
206
259
|
|
|
207
|
-
|
|
208
|
-
// "[ENG-533](https://linear.app/fairmint/issue/ENG-533) and [PR#42](https://github.com/Fairmint/api/pull/42)"
|
|
260
|
+
parse(cleaned); // Parses successfully
|
|
209
261
|
```
|
|
210
262
|
|
|
211
|
-
|
|
263
|
+
### Fixes Applied
|
|
212
264
|
|
|
213
|
-
|
|
265
|
+
- Strips markdown code fences (` ```yaml ` or ` ``` `)
|
|
266
|
+
- Quotes plain scalar values containing `: ` to avoid parse errors
|
|
214
267
|
|
|
215
|
-
|
|
268
|
+
## Linkification
|
|
216
269
|
|
|
217
|
-
|
|
218
|
-
const linker = createLinker().linear("fairmint");
|
|
219
|
-
linker.linkText("Fix ENG-533", { format: "markdown" });
|
|
220
|
-
// "[ENG-533](https://linear.app/fairmint/issue/ENG-533)"
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**`.githubPr(repository: string, options?: { name?: string; priority?: number }): Linker`**
|
|
224
|
-
|
|
225
|
-
Add a rule for GitHub pull request references (e.g., `PR#42`).
|
|
270
|
+
Transform text with issue/PR references into formatted links.
|
|
226
271
|
|
|
227
272
|
```typescript
|
|
228
|
-
|
|
229
|
-
linker.linkText("Merge PR#42", { format: "markdown" });
|
|
230
|
-
// "[PR#42](https://github.com/Fairmint/api/pull/42)"
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
**`.custom(pattern: RegExp, toHref: string | LinkHrefBuilder, options?: { name?: string; priority?: number }): Linker`**
|
|
273
|
+
import { createLinker } from "@hardlydifficult/text";
|
|
234
274
|
|
|
235
|
-
|
|
275
|
+
const linker = createLinker()
|
|
276
|
+
.linear("fairmint")
|
|
277
|
+
.githubPr("Fairmint/api");
|
|
236
278
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
/\bINC-\d+\b/g,
|
|
240
|
-
({ match }) => `https://incident.io/${match}`
|
|
241
|
-
);
|
|
242
|
-
linker.linkText("Resolve INC-99", { format: "markdown" });
|
|
243
|
-
// "[INC-99](https://incident.io/INC-99)"
|
|
279
|
+
const output = linker.apply("Fix ENG-533 and PR#42", { platform: "slack" });
|
|
280
|
+
// Fix <https://linear.app/fairmint/issue/ENG-533|ENG-533> <https://github.com/Fairmint/api/pull/42|PR#42>
|
|
244
281
|
```
|
|
245
282
|
|
|
246
|
-
|
|
283
|
+
### Platforms
|
|
247
284
|
|
|
248
|
-
|
|
285
|
+
| Platform | Output Format |
|
|
286
|
+
|----------|---------------|
|
|
287
|
+
| `slack` | `<href\|text>` |
|
|
288
|
+
| `discord` | `[text](href)` |
|
|
289
|
+
| `markdown` | `[text](href)` |
|
|
290
|
+
| `plaintext` | `href` (raw URL) |
|
|
249
291
|
|
|
250
|
-
|
|
292
|
+
### Linker Methods
|
|
251
293
|
|
|
252
|
-
|
|
294
|
+
| Method | Description |
|
|
295
|
+
|--------|-------------|
|
|
296
|
+
| `custom(pattern, toHref, options)` | Register a new rule with regex pattern and href builder |
|
|
297
|
+
| `linear(workspace, options)` | Match `PROJECT-123` and link to Linear |
|
|
298
|
+
| `githubPr(repository, options)` | Match `PR#123` and link to GitHub Pull Request |
|
|
299
|
+
| `apply(text, options)` | Transform text with configured rules |
|
|
253
300
|
|
|
254
|
-
|
|
301
|
+
### Options
|
|
255
302
|
|
|
256
303
|
| Option | Type | Default | Description |
|
|
257
304
|
|--------|------|---------|-------------|
|
|
258
|
-
| `format`
|
|
259
|
-
| `
|
|
260
|
-
| `
|
|
261
|
-
| `skipExistingLinks` | `boolean` | `true` | Skip linkification inside existing links (Slack, Markdown, and plain URLs) |
|
|
262
|
-
|
|
263
|
-
#### URL Template Syntax
|
|
264
|
-
|
|
265
|
-
When using a string template for `href` or `toHref`, the following substitutions are available:
|
|
266
|
-
|
|
267
|
-
- `$0` or `$&` — Full regex match
|
|
268
|
-
- `$1`, `$2`, etc. — Capture groups
|
|
269
|
-
- `$$` — Literal `$`
|
|
305
|
+
| `format` / `platform` | `LinkerPlatform` | `"markdown"` | Output format |
|
|
306
|
+
| `skipCode` | `boolean` | `true` | Skip linkification inside code blocks/inline code |
|
|
307
|
+
| `skipExistingLinks` | `boolean` | `true` | Skip linkification inside existing links |
|
|
270
308
|
|
|
271
|
-
|
|
272
|
-
const linker = createLinker().custom(
|
|
273
|
-
/\b([A-Z]{2,6})-(\d+)\b/g,
|
|
274
|
-
"https://example.com/issues/$1/$2"
|
|
275
|
-
);
|
|
276
|
-
linker.linkText("ENG-533", { format: "markdown" });
|
|
277
|
-
// "[ENG-533](https://example.com/issues/ENG/533)"
|
|
278
|
-
```
|
|
309
|
+
### LinkRule Options
|
|
279
310
|
|
|
280
|
-
|
|
311
|
+
| Property | Type | Description |
|
|
312
|
+
|----------|------|-------------|
|
|
313
|
+
| `pattern` | `RegExp` | Match pattern (global matching enforced) |
|
|
314
|
+
| `href` / `toHref` | `string` or `LinkHrefBuilder` | URL template (supports `$0`/`$&`, `$1`..`$N`) or callback |
|
|
315
|
+
| `priority` | `number` | `0` | Higher priority wins overlapping matches |
|
|
281
316
|
|
|
282
|
-
|
|
317
|
+
## Text with Line Numbers
|
|
283
318
|
|
|
284
|
-
|
|
319
|
+
Format text content with right-aligned line numbers.
|
|
285
320
|
|
|
286
321
|
```typescript
|
|
287
|
-
import {
|
|
288
|
-
|
|
289
|
-
const files = [
|
|
290
|
-
"src/index.ts",
|
|
291
|
-
"src/utils.ts",
|
|
292
|
-
"src/components/Button.tsx",
|
|
293
|
-
"README.md",
|
|
294
|
-
];
|
|
295
|
-
|
|
296
|
-
buildFileTree(files);
|
|
297
|
-
// src/
|
|
298
|
-
// index.ts
|
|
299
|
-
// utils.ts
|
|
300
|
-
// components/
|
|
301
|
-
// Button.tsx
|
|
302
|
-
//
|
|
303
|
-
// README.md
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
#### Tree Options
|
|
307
|
-
|
|
308
|
-
| Option | Type | Default | Description |
|
|
309
|
-
|--------|------|---------|-------------|
|
|
310
|
-
| `maxLevel2` | `number` | `10` | Maximum children to show at depth 2 |
|
|
311
|
-
| `maxLevel3` | `number` | `3` | Maximum children to show at depth 3+ |
|
|
312
|
-
| `annotations` | `Map<string, string>` | — | Descriptions to append to entries (e.g., `"src/index.ts" → "Main entry point"`) |
|
|
313
|
-
| `details` | `Map<string, string[]>` | — | Extra indented lines to show under file entries (e.g., key sections) |
|
|
314
|
-
| `collapseDirs` | `string[]` | — | Directory names to collapse with a summary instead of expanding |
|
|
322
|
+
import { formatWithLineNumbers } from "@hardlydifficult/text";
|
|
315
323
|
|
|
316
|
-
|
|
324
|
+
formatWithLineNumbers("foo\nbar\nbaz");
|
|
325
|
+
// 1: foo
|
|
326
|
+
// 2: bar
|
|
327
|
+
// 3: baz
|
|
317
328
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
["src/index.ts", "Main entry point"],
|
|
322
|
-
]);
|
|
323
|
-
|
|
324
|
-
buildFileTree(["src/index.ts", "src/utils.ts"], { annotations });
|
|
325
|
-
// src/ — Source code
|
|
326
|
-
// index.ts — Main entry point
|
|
327
|
-
// utils.ts
|
|
329
|
+
formatWithLineNumbers("hello\nworld", 10);
|
|
330
|
+
// 10: hello
|
|
331
|
+
// 11: world
|
|
328
332
|
```
|
|
329
333
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
```typescript
|
|
333
|
-
const details = new Map([
|
|
334
|
-
[
|
|
335
|
-
"src/index.ts",
|
|
336
|
-
[
|
|
337
|
-
"> main (5-20): App entry point.",
|
|
338
|
-
"> shutdown (22-35): Cleanup handler.",
|
|
339
|
-
],
|
|
340
|
-
],
|
|
341
|
-
]);
|
|
342
|
-
|
|
343
|
-
buildFileTree(["src/index.ts"], { details });
|
|
344
|
-
// src/
|
|
345
|
-
// index.ts
|
|
346
|
-
// > main (5-20): App entry point.
|
|
347
|
-
// > shutdown (22-35): Cleanup handler.
|
|
348
|
-
```
|
|
334
|
+
## Markdown Fence Escaping
|
|
349
335
|
|
|
350
|
-
|
|
336
|
+
Escape content by wrapping with more backticks than contained in the content.
|
|
351
337
|
|
|
352
338
|
```typescript
|
|
353
|
-
|
|
354
|
-
[
|
|
355
|
-
"node_modules/package-a/index.js",
|
|
356
|
-
"node_modules/package-b/index.js",
|
|
357
|
-
"src/index.ts",
|
|
358
|
-
],
|
|
359
|
-
{ collapseDirs: ["node_modules"] }
|
|
360
|
-
);
|
|
361
|
-
// node_modules/
|
|
362
|
-
// (2 files across 2 dirs)
|
|
363
|
-
//
|
|
364
|
-
// src/
|
|
365
|
-
// index.ts
|
|
366
|
-
```
|
|
339
|
+
import { escapeFence } from "@hardlydifficult/text";
|
|
367
340
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
Default truncation limits for file tree rendering.
|
|
371
|
-
|
|
372
|
-
```typescript
|
|
373
|
-
import { FILE_TREE_DEFAULTS } from "@hardlydifficult/text";
|
|
341
|
+
const result = escapeFence("content with ``` triple backticks");
|
|
342
|
+
// { fence: "````", content: "content with ``` triple backticks" }
|
|
374
343
|
|
|
375
|
-
|
|
376
|
-
|
|
344
|
+
// Use as:
|
|
345
|
+
// ${result.fence}${result.content}${result.fence}
|
|
377
346
|
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes markdown code fences by wrapping content with more backticks than it contains.
|
|
3
|
+
* If content has ```, wraps with ````. If content has ````, wraps with `````, etc.
|
|
4
|
+
*/
|
|
5
|
+
export declare function escapeFence(content: string): {
|
|
6
|
+
fence: string;
|
|
7
|
+
content: string;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=escapeFence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escapeFence.d.ts","sourceRoot":"","sources":["../src/escapeFence.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAeA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeFence = escapeFence;
|
|
4
|
+
/**
|
|
5
|
+
* Escapes markdown code fences by wrapping content with more backticks than it contains.
|
|
6
|
+
* If content has ```, wraps with ````. If content has ````, wraps with `````, etc.
|
|
7
|
+
*/
|
|
8
|
+
function escapeFence(content) {
|
|
9
|
+
let maxBackticks = 3; // Start with triple backticks (standard markdown fence)
|
|
10
|
+
// Find the longest sequence of consecutive backticks in the content
|
|
11
|
+
const backtickMatches = content.match(/`+/g);
|
|
12
|
+
if (backtickMatches) {
|
|
13
|
+
for (const match of backtickMatches) {
|
|
14
|
+
if (match.length >= maxBackticks) {
|
|
15
|
+
maxBackticks = match.length + 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const fence = "`".repeat(maxBackticks);
|
|
20
|
+
return { fence, content };
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=escapeFence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escapeFence.js","sourceRoot":"","sources":["../src/escapeFence.ts"],"names":[],"mappings":";;AAIA,kCAkBC;AAtBD;;;GAGG;AACH,SAAgB,WAAW,CAAC,OAAe;IAIzC,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC,wDAAwD;IAE9E,oEAAoE;IACpE,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC;gBACjC,YAAY,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACvC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC5B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,4 +11,5 @@ export { formatWithLineNumbers } from "./formatWithLineNumbers.js";
|
|
|
11
11
|
export { formatYaml } from "./formatYaml.js";
|
|
12
12
|
export { healYaml } from "./healYaml.js";
|
|
13
13
|
export { Linker, createLinker, type LinkRule, type LinkHrefBuilder, type LinkMatchContext, type LinkerApplyOptions, type LinkerPlatform, } from "./linker.js";
|
|
14
|
+
export { escapeFence } from "./escapeFence.js";
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,MAAM,EACN,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,MAAM,EACN,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createLinker = exports.Linker = exports.healYaml = exports.formatYaml = exports.formatWithLineNumbers = exports.convertFormat = exports.FILE_TREE_DEFAULTS = exports.buildFileTree = exports.formatDuration = exports.slugify = exports.chunkText = exports.extractPlaceholders = exports.replaceTemplate = exports.formatErrorForLog = exports.formatError = exports.getErrorMessage = void 0;
|
|
3
|
+
exports.escapeFence = exports.createLinker = exports.Linker = exports.healYaml = exports.formatYaml = exports.formatWithLineNumbers = exports.convertFormat = exports.FILE_TREE_DEFAULTS = exports.buildFileTree = exports.formatDuration = exports.slugify = exports.chunkText = exports.extractPlaceholders = exports.replaceTemplate = exports.formatErrorForLog = exports.formatError = exports.getErrorMessage = void 0;
|
|
4
4
|
var errors_js_1 = require("./errors.js");
|
|
5
5
|
Object.defineProperty(exports, "getErrorMessage", { enumerable: true, get: function () { return errors_js_1.getErrorMessage; } });
|
|
6
6
|
Object.defineProperty(exports, "formatError", { enumerable: true, get: function () { return errors_js_1.formatError; } });
|
|
@@ -28,4 +28,6 @@ Object.defineProperty(exports, "healYaml", { enumerable: true, get: function ()
|
|
|
28
28
|
var linker_js_1 = require("./linker.js");
|
|
29
29
|
Object.defineProperty(exports, "Linker", { enumerable: true, get: function () { return linker_js_1.Linker; } });
|
|
30
30
|
Object.defineProperty(exports, "createLinker", { enumerable: true, get: function () { return linker_js_1.createLinker; } });
|
|
31
|
+
var escapeFence_js_1 = require("./escapeFence.js");
|
|
32
|
+
Object.defineProperty(exports, "escapeFence", { enumerable: true, get: function () { return escapeFence_js_1.escapeFence; } });
|
|
31
33
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAA8E;AAArE,4GAAA,eAAe,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AACxD,6CAAqE;AAA5D,8GAAA,eAAe,OAAA;AAAE,kHAAA,mBAAmB,OAAA;AAC7C,+CAA2C;AAAlC,yGAAA,SAAS,OAAA;AAClB,2CAAuC;AAA9B,qGAAA,OAAO,OAAA;AAChB,yDAAqD;AAA5C,mHAAA,cAAc,OAAA;AACvB,uDAAuE;AAA9D,iHAAA,aAAa,OAAA;AAAE,sHAAA,kBAAkB,OAAA;AAE1C,uDAAmD;AAA1C,iHAAA,aAAa,OAAA;AAEtB,uEAAmE;AAA1D,iIAAA,qBAAqB,OAAA;AAC9B,iDAA6C;AAApC,2GAAA,UAAU,OAAA;AACnB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,yCAQqB;AAPnB,mGAAA,MAAM,OAAA;AACN,yGAAA,YAAY,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAA8E;AAArE,4GAAA,eAAe,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AACxD,6CAAqE;AAA5D,8GAAA,eAAe,OAAA;AAAE,kHAAA,mBAAmB,OAAA;AAC7C,+CAA2C;AAAlC,yGAAA,SAAS,OAAA;AAClB,2CAAuC;AAA9B,qGAAA,OAAO,OAAA;AAChB,yDAAqD;AAA5C,mHAAA,cAAc,OAAA;AACvB,uDAAuE;AAA9D,iHAAA,aAAa,OAAA;AAAE,sHAAA,kBAAkB,OAAA;AAE1C,uDAAmD;AAA1C,iHAAA,aAAa,OAAA;AAEtB,uEAAmE;AAA1D,iIAAA,qBAAqB,OAAA;AAC9B,iDAA6C;AAApC,2GAAA,UAAU,OAAA;AACnB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,yCAQqB;AAPnB,mGAAA,MAAM,OAAA;AACN,yGAAA,YAAY,OAAA;AAOd,mDAA+C;AAAtC,6GAAA,WAAW,OAAA"}
|