@hardlydifficult/text 1.0.19 → 1.0.21
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 +259 -180
- package/dist/buildFileTree.d.ts.map +1 -1
- package/dist/buildFileTree.js +5 -2
- package/dist/buildFileTree.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,300 +19,382 @@ import {
|
|
|
19
19
|
buildFileTree,
|
|
20
20
|
convertFormat,
|
|
21
21
|
createLinker,
|
|
22
|
+
healYaml,
|
|
23
|
+
escapeFence,
|
|
22
24
|
} from "@hardlydifficult/text";
|
|
23
25
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
// "Hello
|
|
26
|
+
// Replace template placeholders
|
|
27
|
+
replaceTemplate("Hello {{name}}!", { name: "World" });
|
|
28
|
+
// "Hello World!"
|
|
27
29
|
|
|
28
30
|
// Split long text into chunks
|
|
29
|
-
|
|
30
|
-
// ["
|
|
31
|
+
chunkText("This is a long text", 10);
|
|
32
|
+
// ["This is a", "long text"]
|
|
31
33
|
|
|
32
|
-
// Convert to URL-safe
|
|
33
|
-
|
|
34
|
-
// "my-feature"
|
|
34
|
+
// Convert to URL-safe slugs
|
|
35
|
+
slugify("My Feature Name!");
|
|
36
|
+
// "my-feature-name"
|
|
35
37
|
|
|
36
|
-
// Format
|
|
37
|
-
|
|
38
|
+
// Format durations
|
|
39
|
+
formatDuration(125_000);
|
|
38
40
|
// "2m 5s"
|
|
39
41
|
|
|
40
|
-
// Build
|
|
41
|
-
|
|
42
|
-
// src
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
//
|
|
42
|
+
// Build file trees
|
|
43
|
+
buildFileTree(["src/index.ts", "README.md"]);
|
|
44
|
+
// "src/\n index.ts\n\nREADME.md"
|
|
45
|
+
|
|
46
|
+
// Convert between JSON and YAML
|
|
47
|
+
convertFormat('{"name":"Alice"}', "yaml");
|
|
48
|
+
// "name: Alice\n"
|
|
49
|
+
convertFormat("name: Alice", "json");
|
|
50
|
+
// "{\n \"name\": \"Alice\"\n}"
|
|
51
|
+
|
|
52
|
+
// Apply link rules to text
|
|
53
|
+
const linker = createLinker().linear("my-org");
|
|
54
|
+
linker.apply("Fix ENG-533", { platform: "markdown" });
|
|
55
|
+
// "Fix [ENG-533](https://linear.app/my-org/issue/ENG-533)"
|
|
56
|
+
|
|
57
|
+
// Heal malformed YAML
|
|
58
|
+
healYaml("```yaml\nkey: value\n```");
|
|
59
|
+
// "key: value"
|
|
51
60
|
```
|
|
52
61
|
|
|
53
|
-
## Error
|
|
62
|
+
## Error Formatting
|
|
63
|
+
|
|
64
|
+
Consistent error handling utilities for message extraction and formatting.
|
|
65
|
+
|
|
66
|
+
### `getErrorMessage`
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
Extract a message string from an unknown error.
|
|
56
69
|
|
|
57
70
|
```typescript
|
|
58
|
-
import { getErrorMessage
|
|
71
|
+
import { getErrorMessage } from "@hardlydifficult/text";
|
|
59
72
|
|
|
60
|
-
|
|
73
|
+
getErrorMessage(new Error("something went wrong"));
|
|
74
|
+
// "something went wrong"
|
|
61
75
|
|
|
62
|
-
getErrorMessage(
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
getErrorMessage("plain string error");
|
|
77
|
+
// "plain string error"
|
|
78
|
+
|
|
79
|
+
getErrorMessage(42);
|
|
80
|
+
// "42"
|
|
65
81
|
```
|
|
66
82
|
|
|
67
|
-
###
|
|
83
|
+
### `formatError`
|
|
84
|
+
|
|
85
|
+
Format an error for user-facing output with optional context.
|
|
68
86
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
```typescript
|
|
88
|
+
import { formatError } from "@hardlydifficult/text";
|
|
89
|
+
|
|
90
|
+
formatError(new Error("disk full"));
|
|
91
|
+
// "disk full"
|
|
92
|
+
|
|
93
|
+
formatError(new Error("disk full"), "Failed to save");
|
|
94
|
+
// "Failed to save: disk full"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `formatErrorForLog`
|
|
98
|
+
|
|
99
|
+
Format an error for logging (includes more detail for non-Error types).
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { formatErrorForLog } from "@hardlydifficult/text";
|
|
103
|
+
|
|
104
|
+
formatErrorForLog(new Error("timeout"));
|
|
105
|
+
// "timeout"
|
|
106
|
+
|
|
107
|
+
formatErrorForLog({ code: 500 });
|
|
108
|
+
// "[object Object]"
|
|
109
|
+
```
|
|
74
110
|
|
|
75
111
|
## Template Replacement
|
|
76
112
|
|
|
77
113
|
Simple string interpolation using `{{variable}}` syntax.
|
|
78
114
|
|
|
115
|
+
### `replaceTemplate`
|
|
116
|
+
|
|
117
|
+
Replace template placeholders with values.
|
|
118
|
+
|
|
79
119
|
```typescript
|
|
80
|
-
import { replaceTemplate
|
|
120
|
+
import { replaceTemplate } from "@hardlydifficult/text";
|
|
121
|
+
|
|
122
|
+
replaceTemplate("Hello {{name}}!", { name: "World" });
|
|
123
|
+
// "Hello World!"
|
|
81
124
|
|
|
82
|
-
|
|
125
|
+
replaceTemplate("{{greeting}}, {{name}}!", {
|
|
126
|
+
greeting: "Hi",
|
|
83
127
|
name: "Alice",
|
|
84
|
-
age: "30",
|
|
85
128
|
});
|
|
86
|
-
// "
|
|
129
|
+
// "Hi, Alice!"
|
|
87
130
|
|
|
88
|
-
|
|
89
|
-
//
|
|
131
|
+
replaceTemplate("Hello {{name}}!", {});
|
|
132
|
+
// "Hello {{name}}!"
|
|
90
133
|
```
|
|
91
134
|
|
|
92
|
-
###
|
|
135
|
+
### `extractPlaceholders`
|
|
136
|
+
|
|
137
|
+
Extract all placeholder names from a template.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { extractPlaceholders } from "@hardlydifficult/text";
|
|
141
|
+
|
|
142
|
+
extractPlaceholders("{{a}} and {{b}} and {{a}} again");
|
|
143
|
+
// ["a", "b"]
|
|
93
144
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
| `extractPlaceholders(template)` | Return unique placeholder names |
|
|
145
|
+
extractPlaceholders("no placeholders here");
|
|
146
|
+
// []
|
|
147
|
+
```
|
|
98
148
|
|
|
99
149
|
## Text Chunking
|
|
100
150
|
|
|
101
|
-
Split long text into manageable chunks
|
|
151
|
+
Split long text into manageable chunks, preferring natural break points.
|
|
102
152
|
|
|
103
153
|
```typescript
|
|
104
154
|
import { chunkText } from "@hardlydifficult/text";
|
|
105
155
|
|
|
106
|
-
|
|
107
|
-
chunkText(text, 18);
|
|
156
|
+
chunkText("line one\nline two\nline three", 18);
|
|
108
157
|
// ["line one\nline two", "line three"]
|
|
109
|
-
```
|
|
110
158
|
|
|
111
|
-
|
|
159
|
+
chunkText("word1 word2 word3 word4 word5", 17);
|
|
160
|
+
// ["word1 word2 word3", "word4 word5"]
|
|
112
161
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
162
|
+
chunkText("abcdefghijklmnopqrstuvwxyz", 10);
|
|
163
|
+
// ["abcdefghij", "klmnopqrst", "uvwxyz"]
|
|
164
|
+
```
|
|
116
165
|
|
|
117
166
|
## Slugification
|
|
118
167
|
|
|
119
|
-
Convert strings
|
|
168
|
+
Convert strings into URL/filename-safe slugs.
|
|
120
169
|
|
|
121
170
|
```typescript
|
|
122
171
|
import { slugify } from "@hardlydifficult/text";
|
|
123
172
|
|
|
124
|
-
slugify("My Feature Name!");
|
|
125
|
-
|
|
126
|
-
```
|
|
173
|
+
slugify("My Feature Name!");
|
|
174
|
+
// "my-feature-name"
|
|
127
175
|
|
|
128
|
-
|
|
176
|
+
slugify("My Feature Name!", 10);
|
|
177
|
+
// "my-feature"
|
|
129
178
|
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
|
|
179
|
+
slugify(" Hello World ");
|
|
180
|
+
// "hello-world"
|
|
181
|
+
```
|
|
133
182
|
|
|
134
183
|
## Duration Formatting
|
|
135
184
|
|
|
136
|
-
Format milliseconds as human-readable
|
|
185
|
+
Format duration in milliseconds as a human-readable string.
|
|
137
186
|
|
|
138
187
|
```typescript
|
|
139
188
|
import { formatDuration } from "@hardlydifficult/text";
|
|
140
189
|
|
|
141
|
-
formatDuration(125_000);
|
|
142
|
-
|
|
143
|
-
formatDuration(500); // "<1s"
|
|
144
|
-
```
|
|
190
|
+
formatDuration(125_000);
|
|
191
|
+
// "2m 5s"
|
|
145
192
|
|
|
146
|
-
|
|
193
|
+
formatDuration(3_600_000);
|
|
194
|
+
// "1h"
|
|
147
195
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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` |
|
|
196
|
+
formatDuration(500);
|
|
197
|
+
// "<1s"
|
|
198
|
+
```
|
|
155
199
|
|
|
156
200
|
## File Tree Rendering
|
|
157
201
|
|
|
158
|
-
Build and render hierarchical file trees with depth-based truncation and
|
|
202
|
+
Build and render hierarchical file trees with depth-based truncation, annotations, and collapsed directory summaries.
|
|
159
203
|
|
|
160
204
|
```typescript
|
|
161
205
|
import { buildFileTree, FILE_TREE_DEFAULTS } from "@hardlydifficult/text";
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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)
|
|
206
|
+
|
|
207
|
+
buildFileTree(["src/index.ts", "src/utils.ts", "README.md"]);
|
|
208
|
+
// "src/\n index.ts\n utils.ts\n\nREADME.md"
|
|
182
209
|
```
|
|
183
210
|
|
|
184
211
|
### Options
|
|
185
212
|
|
|
186
|
-
|
|
|
187
|
-
|
|
188
|
-
| `maxLevel2`
|
|
189
|
-
| `maxLevel3`
|
|
190
|
-
| `annotations`
|
|
191
|
-
| `details`
|
|
192
|
-
| `collapseDirs` | `string[]`
|
|
213
|
+
| Parameter | Type | Description |
|
|
214
|
+
|----------------|-------------------------------------------|-----------------------------------------------------------------------------|
|
|
215
|
+
| `maxLevel2` | `number` | Maximum number of entries to show at level 2 (files in a directory) |
|
|
216
|
+
| `maxLevel3` | `number` | Maximum number of entries to show at level 3 (files in subdirectories) |
|
|
217
|
+
| `annotations` | `ReadonlyMap<string, string>` | Map of file/directory paths to annotation strings |
|
|
218
|
+
| `details` | `ReadonlyMap<string, readonly string[]>` | Map of file paths to extra detail lines to show under entries |
|
|
219
|
+
| `collapseDirs` | `readonly string[]` | Directory names to collapse with summary count |
|
|
220
|
+
|
|
221
|
+
### Examples
|
|
222
|
+
|
|
223
|
+
**Annotations**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
const annotations = new Map([
|
|
227
|
+
["src/index.ts", "Main entry point"],
|
|
228
|
+
["src", "Source code directory"],
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
buildFileTree(["src/index.ts"], { annotations });
|
|
232
|
+
// "src/ — Source code directory\n index.ts — Main entry point"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Details**
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const details = new Map([
|
|
239
|
+
["src/index.ts", ["> main (5-20): App entry point."]],
|
|
240
|
+
]);
|
|
193
241
|
|
|
194
|
-
|
|
242
|
+
buildFileTree(["src/index.ts"], { details });
|
|
243
|
+
// "src/\n index.ts\n > main (5-20): App entry point."
|
|
244
|
+
```
|
|
195
245
|
|
|
196
|
-
|
|
246
|
+
**Collapsed directories**
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
buildFileTree(
|
|
250
|
+
["src/index.ts", "test/unit/a.test.ts", "test/unit/b.test.ts"],
|
|
251
|
+
{ collapseDirs: ["test"] }
|
|
252
|
+
);
|
|
253
|
+
// "src/\n index.ts\n\ntest/\n (2 files)"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## JSON/YAML Format Conversion
|
|
257
|
+
|
|
258
|
+
Convert between JSON and YAML with automatic input detection and clean output formatting.
|
|
197
259
|
|
|
198
260
|
```typescript
|
|
199
261
|
import { convertFormat } from "@hardlydifficult/text";
|
|
200
|
-
import type { TextFormat } from "@hardlydifficult/text";
|
|
201
262
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// name: Alice
|
|
263
|
+
convertFormat('{"name":"Alice","age":30}', "yaml");
|
|
264
|
+
// "name: Alice\nage: 30\n"
|
|
205
265
|
|
|
206
|
-
// YAML to JSON (pretty-printed with 2-space indent)
|
|
207
266
|
convertFormat("name: Alice\nage: 30", "json");
|
|
208
|
-
// {
|
|
209
|
-
// "name": "Alice",
|
|
210
|
-
// "age": 30
|
|
211
|
-
// }
|
|
267
|
+
// "{\n \"name\": \"Alice\",\n \"age\": 30\n}"
|
|
212
268
|
```
|
|
213
269
|
|
|
214
|
-
###
|
|
270
|
+
### `TextFormat`
|
|
215
271
|
|
|
216
|
-
|
|
217
|
-
|----------|-------------|
|
|
218
|
-
| `convertFormat(content, to)` | Parse input and re-serialize to `json` or `yaml` |
|
|
272
|
+
Type alias for output format: `"json"` or `"yaml"`.
|
|
219
273
|
|
|
220
274
|
## YAML Formatting
|
|
221
275
|
|
|
222
|
-
Serialize data to clean YAML
|
|
276
|
+
Serialize data to clean YAML with intelligent block literal selection for long strings.
|
|
223
277
|
|
|
224
278
|
```typescript
|
|
225
279
|
import { formatYaml } from "@hardlydifficult/text";
|
|
226
280
|
|
|
227
281
|
formatYaml({
|
|
228
282
|
purpose:
|
|
229
|
-
"Core AI SDK: LLM integrations (Anthropic, Ollama)
|
|
283
|
+
"Core AI SDK implementation: LLM integrations (Anthropic Claude, Ollama), agent orchestration with streaming.",
|
|
230
284
|
});
|
|
285
|
+
|
|
286
|
+
// Uses block literal (|) for long strings containing ": "
|
|
231
287
|
// purpose: |
|
|
232
|
-
// Core AI SDK: LLM integrations (Anthropic, Ollama)
|
|
288
|
+
// Core AI SDK implementation: LLM integrations (Anthropic Claude, Ollama), agent orchestration with streaming.
|
|
233
289
|
```
|
|
234
290
|
|
|
235
|
-
### Behavior
|
|
236
|
-
|
|
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
|
|
240
|
-
|
|
241
291
|
## YAML Healing
|
|
242
292
|
|
|
243
|
-
|
|
293
|
+
Clean and repair YAML output from LLMs by stripping code fences and quoting problematic scalar values.
|
|
244
294
|
|
|
245
295
|
```typescript
|
|
246
296
|
import { healYaml } from "@hardlydifficult/text";
|
|
247
|
-
import { parse } from "yaml";
|
|
248
|
-
|
|
249
|
-
const badYaml = `\`\`\`yaml
|
|
250
|
-
purpose: |
|
|
251
|
-
Core AI: LLM integrations (Anthropic, Ollama)
|
|
252
|
-
description: Main deps: Node, TypeScript, Vitest.
|
|
253
|
-
\`\`\``;
|
|
254
297
|
|
|
255
|
-
|
|
256
|
-
//
|
|
257
|
-
// Core AI: LLM integrations (Anthropic, Ollama)
|
|
258
|
-
// description: "Main deps: Node, TypeScript, Vitest."
|
|
298
|
+
healYaml("```yaml\nkey: value\n```");
|
|
299
|
+
// "key: value"
|
|
259
300
|
|
|
260
|
-
|
|
301
|
+
healYaml('description: Development dependencies: Node types.');
|
|
302
|
+
// 'description: "Development dependencies: Node types."'
|
|
261
303
|
```
|
|
262
304
|
|
|
263
|
-
|
|
305
|
+
## Link Generation
|
|
264
306
|
|
|
265
|
-
|
|
266
|
-
- Quotes plain scalar values containing `: ` to avoid parse errors
|
|
307
|
+
Transform text with issue/PR references into formatted links across platforms like Slack, Discord, and Markdown.
|
|
267
308
|
|
|
268
|
-
|
|
309
|
+
### `createLinker`
|
|
269
310
|
|
|
270
|
-
|
|
311
|
+
Create a linker instance with optional initial rules.
|
|
271
312
|
|
|
272
313
|
```typescript
|
|
273
314
|
import { createLinker } from "@hardlydifficult/text";
|
|
274
315
|
|
|
275
316
|
const linker = createLinker()
|
|
276
|
-
.linear("
|
|
277
|
-
.githubPr("
|
|
317
|
+
.linear("my-org")
|
|
318
|
+
.githubPr("my-org/my-repo");
|
|
278
319
|
|
|
279
|
-
|
|
280
|
-
// Fix <https://linear.app/
|
|
320
|
+
linker.apply("Fix ENG-533 and PR#42", { platform: "slack" });
|
|
321
|
+
// "Fix <https://linear.app/my-org/issue/ENG-533|ENG-533> <https://github.com/my-org/my-repo/pull/42|PR#42>"
|
|
281
322
|
```
|
|
282
323
|
|
|
283
|
-
###
|
|
324
|
+
### `Linker` Class
|
|
284
325
|
|
|
285
|
-
|
|
286
|
-
|----------|---------------|
|
|
287
|
-
| `slack` | `<href\|text>` |
|
|
288
|
-
| `discord` | `[text](href)` |
|
|
289
|
-
| `markdown` | `[text](href)` |
|
|
290
|
-
| `plaintext` | `href` (raw URL) |
|
|
326
|
+
Stateful linker that applies configured rules to text.
|
|
291
327
|
|
|
292
|
-
|
|
328
|
+
**Methods:**
|
|
293
329
|
|
|
294
|
-
| Method
|
|
295
|
-
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
298
|
-
| `
|
|
299
|
-
| `
|
|
330
|
+
| Method | Description |
|
|
331
|
+
|----------------|------------------------------------------------------------------------|
|
|
332
|
+
| `addRule(rule)`| Add a custom link rule |
|
|
333
|
+
| `rule(...)` | Add a rule (supports fluent and named forms) |
|
|
334
|
+
| `custom(...)` | Add a custom rule with regex pattern and href builder |
|
|
335
|
+
| `linear(...)` | Add Linear issue reference rule (e.g., `ENG-533`) |
|
|
336
|
+
| `githubPr(...)`| Add GitHub PR reference rule (e.g., `PR#42`) |
|
|
337
|
+
| `apply(...)` | Apply linkification to text with options |
|
|
338
|
+
| `linkText(...)`| Alias for `apply` (same behavior) |
|
|
339
|
+
|
|
340
|
+
### Rules
|
|
341
|
+
|
|
342
|
+
| Parameter | Type | Description |
|
|
343
|
+
|-----------|---------------------------|-----------------------------------------------------------------------------|
|
|
344
|
+
| `pattern` | `RegExp` | Match pattern (global flag is enforced automatically) |
|
|
345
|
+
| `href` | `string` | URL template (supports `$0`/`$&`, `$1..$N`) |
|
|
346
|
+
| `toHref` | `string \| LinkHrefBuilder`| Either href template or callback; takes precedence over `href` |
|
|
347
|
+
| `priority`| `number` | Higher priority wins for overlapping matches (default: `0`) |
|
|
300
348
|
|
|
301
349
|
### Options
|
|
302
350
|
|
|
303
|
-
|
|
|
304
|
-
|
|
305
|
-
| `format` / `platform` | `LinkerPlatform`
|
|
306
|
-
| `skipCode`
|
|
307
|
-
| `skipExistingLinks`
|
|
351
|
+
| Parameter | Type | Description |
|
|
352
|
+
|-----------------------|-----------------------------|-------------------------------------------------------------------------|
|
|
353
|
+
| `format` / `platform` | `LinkerPlatform` | Output format: `"slack"`, `"discord"`, `"markdown"`, `"plaintext"` |
|
|
354
|
+
| `skipCode` | `boolean` | Skip linkification inside code spans (default: `true`) |
|
|
355
|
+
| `skipExistingLinks` | `boolean` | Skip linkification inside existing links (default: `true`) |
|
|
356
|
+
|
|
357
|
+
### Platforms
|
|
358
|
+
|
|
359
|
+
| Platform | Format |
|
|
360
|
+
|--------------|---------------------------------|
|
|
361
|
+
| `slack` | `<href|text>` |
|
|
362
|
+
| `discord` | `[text](href)` |
|
|
363
|
+
| `markdown` | `[text](href)` |
|
|
364
|
+
| `plaintext` | `href` (raw URL) |
|
|
308
365
|
|
|
309
|
-
###
|
|
366
|
+
### Examples
|
|
310
367
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
368
|
+
**Custom rules**
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const linker = createLinker().custom(
|
|
372
|
+
/\bINC-\d+\b/g,
|
|
373
|
+
({ match }) => `https://incident.io/${match}`
|
|
374
|
+
);
|
|
375
|
+
linker.apply("Handle INC-99", { format: "slack" });
|
|
376
|
+
// "Handle <https://incident.io/INC-99|INC-99>"
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Priority-based resolution**
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
const linker = createLinker()
|
|
383
|
+
.custom(/\bENG-\d+\b/g, "https://low.example/$0", { priority: 0 })
|
|
384
|
+
.custom(/\bENG-533\b/g, "https://high.example/$0", { priority: 10 });
|
|
385
|
+
|
|
386
|
+
linker.apply("ENG-533 and ENG-534", { format: "markdown" });
|
|
387
|
+
// "[ENG-533](https://high.example/ENG-533) and [ENG-534](https://low.example/ENG-534)"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Idempotent linkification**
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
const linker = createLinker().linear("my-org");
|
|
394
|
+
const first = linker.apply("Ship ENG-533", { format: "slack" });
|
|
395
|
+
const second = linker.apply(first, { format: "slack" });
|
|
396
|
+
// first === second (no double-linkification)
|
|
397
|
+
```
|
|
316
398
|
|
|
317
399
|
## Text with Line Numbers
|
|
318
400
|
|
|
@@ -331,16 +413,13 @@ formatWithLineNumbers("hello\nworld", 10);
|
|
|
331
413
|
// 11: world
|
|
332
414
|
```
|
|
333
415
|
|
|
334
|
-
## Markdown
|
|
416
|
+
## Escaping Markdown Fences
|
|
335
417
|
|
|
336
|
-
Escape
|
|
418
|
+
Escape markdown code fences by dynamically selecting a fence delimiter longer than any backtick sequence in the content.
|
|
337
419
|
|
|
338
420
|
```typescript
|
|
339
421
|
import { escapeFence } from "@hardlydifficult/text";
|
|
340
422
|
|
|
341
|
-
|
|
342
|
-
// { fence: "````", content: "
|
|
343
|
-
|
|
344
|
-
// Use as:
|
|
345
|
-
// ${result.fence}${result.content}${result.fence}
|
|
423
|
+
escapeFence("Content with `` backticks");
|
|
424
|
+
// { fence: "````", content: "Content with `` backticks" }
|
|
346
425
|
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildFileTree.d.ts","sourceRoot":"","sources":["../src/buildFileTree.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,mHAAmH;IACnH,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACjD,sGAAsG;IACtG,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,yDAAyD;AACzD,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CACvC,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,WAAW,CAAC,CAIlD,CAAC;AAiCF;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,OAAO,GAAE,gBAAqB,GAC7B,MAAM,
|
|
1
|
+
{"version":3,"file":"buildFileTree.d.ts","sourceRoot":"","sources":["../src/buildFileTree.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,mHAAmH;IACnH,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACjD,sGAAsG;IACtG,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,yDAAyD;AACzD,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CACvC,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,WAAW,CAAC,CAIlD,CAAC;AAiCF;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,OAAO,GAAE,gBAAqB,GAC7B,MAAM,CAuGR"}
|
package/dist/buildFileTree.js
CHANGED
|
@@ -103,8 +103,11 @@ function buildFileTree(filePaths, options = {}) {
|
|
|
103
103
|
lines.push(`${prefix}${child.name}${marker}${suffix}`);
|
|
104
104
|
// Render detail lines under files (e.g. key sections)
|
|
105
105
|
if (!child.isDir && details?.has(child.fullPath) === true) {
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const childDetails = details.get(child.fullPath);
|
|
107
|
+
if (childDetails !== undefined) {
|
|
108
|
+
for (const detail of childDetails) {
|
|
109
|
+
lines.push(`${prefix} ${detail}`);
|
|
110
|
+
}
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
if (child.isDir && collapseSet?.has(child.name) === true) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildFileTree.js","sourceRoot":"","sources":["../src/buildFileTree.ts"],"names":[],"mappings":";;;AA4DA,
|
|
1
|
+
{"version":3,"file":"buildFileTree.js","sourceRoot":"","sources":["../src/buildFileTree.ts"],"names":[],"mappings":"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"}
|