@p-buddy/parkdown 0.0.1 → 0.0.3

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.
@@ -1,369 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { dedent } from "ts-dedent";
3
- import { extractContent, getAllPositionNodes, nodeSort, parse } from "./utils";
4
- import { PsuedoFilesystem, lorem } from "./utils.test";
5
- import { join } from "node:path";
6
- import {
7
- getReplacementTargets,
8
- isSpecialLink,
9
- applyHeadingDepth,
10
- extendGetRelativePathContent,
11
- recursivelyPopulateInclusions,
12
- nodeDepthFinder,
13
- specialComment,
14
- isSpecialComment,
15
- getTopLevelCommentBlocks,
16
- } from "./include";
17
-
18
- describe(isSpecialLink.name, () => {
19
- const check = (md: string, expectation: boolean) =>
20
- getAllPositionNodes(parse.md(md), "link")
21
- .forEach(node => expect(isSpecialLink(node)).toBe(expectation));
22
-
23
- const cases = {
24
- "non-link": ["test", false],
25
- "link has text": ["[test](http://example.com)", false],
26
- "link has no text, but unsupported target": ["[](file.md)", false],
27
- "web link": ["[](http://example.com)", true],
28
- "relative file, same directory": ["[](./file.md)", true],
29
- "relative file, different directory": ["[](../file.md)", true],
30
- } as const;
31
-
32
- for (const [description, [md, expectation]] of Object.entries(cases))
33
- test(description, () => check(md, expectation));
34
- });
35
-
36
- describe(nodeDepthFinder.name, () => {
37
- test("find depth in lorem", () => {
38
- for (const markdown of lorem.md) {
39
- const headings = markdown.split("\n")
40
- .map((content, index) => ({ content, line: index + 1 }))
41
- .filter(({ content }) => content.startsWith("#"))
42
- .map(heading => ({ ...heading, depth: heading.content.match(/^#+/)?.[0].length }));
43
- const ast = parse.md(markdown);
44
- const findDepth = nodeDepthFinder(ast);
45
- let index = headings.length - 1;
46
- for (const node of getAllPositionNodes(ast).sort(nodeSort.reverse)) {
47
- if (node.position.start.line < headings[index].line) index--;
48
- const depth = findDepth(node);
49
- expect(depth).toBe(headings[index].depth,);
50
- }
51
- }
52
- });
53
- });
54
-
55
- describe('applyHeadingDepth', () => {
56
- test('should increase heading levels by the specified depth', () => {
57
- const markdown = "# Heading 1\n\n## Heading 2\n\n### Heading 3";
58
- const result = applyHeadingDepth(markdown, 1);
59
- expect(result).toBe("## Heading 1\n\n### Heading 2\n\n#### Heading 3");
60
- });
61
-
62
- test('should decrease heading levels by the specified depth', () => {
63
- const markdown = "### Heading 3\n\n## Heading 2\n\n# Heading 1";
64
- const result = applyHeadingDepth(markdown, -1);
65
- expect(result).toBe("## Heading 3\n\n# Heading 2\n\n# Heading 1");
66
- });
67
-
68
- test('should cap heading levels at 6', () => {
69
- const markdown = "#### Heading 4\n\n##### Heading 5\n\n###### Heading 6";
70
- const result = applyHeadingDepth(markdown, 2);
71
- expect(result).toBe("###### Heading 4\n\n###### Heading 5\n\n###### Heading 6");
72
- });
73
-
74
- test('should not modify non-heading content', () => {
75
- const markdown = "# Heading 1\n\nSome regular text\n\n## Heading 2\n\n- List item 1\n- List item 2";
76
- const result = applyHeadingDepth(markdown, 1);
77
- expect(result).toBe("## Heading 1\n\nSome regular text\n\n### Heading 2\n\n- List item 1\n- List item 2");
78
- });
79
-
80
- test('should handle headings with different formatting', () => {
81
- const markdown = "# *Italic Heading*\n\n## **Bold Heading**\n\n### `Code Heading`";
82
- const result = applyHeadingDepth(markdown, 1);
83
- expect(result).toBe("## *Italic Heading*\n\n### **Bold Heading**\n\n#### `Code Heading`");
84
- });
85
-
86
- test('should handle headings with special characters', () => {
87
- const markdown = "# Heading with & special < characters >";
88
- const result = applyHeadingDepth(markdown, 2);
89
- expect(result).toBe("### Heading with & special < characters >");
90
- });
91
-
92
- test('should accept an existing AST as input', () => {
93
- const markdown = "# Heading 1\n\n## Heading 2";
94
- const ast = parse.md(markdown);
95
- const result = applyHeadingDepth(markdown, 2, ast);
96
- expect(result).toBe("### Heading 1\n\n#### Heading 2");
97
- });
98
- });
99
-
100
- describe(getTopLevelCommentBlocks.name, () => {
101
- test("problematic case", () => {
102
- const content = dedent`
103
- # Main heading
104
-
105
- [](./child/README.md)
106
- <!-- p▼ Begin -->
107
- ## Child heading
108
-
109
- [](./grandchild/README.md)
110
- <!-- p▼ Begin -->
111
- ### Grandchild heading
112
-
113
- Hello!
114
- <!-- p▼ End -->
115
- <!-- p▼ End -->
116
-
117
- End
118
- `;
119
- const ast = parse.md(content);
120
- const openingComments = getAllPositionNodes(ast, "html").filter(isSpecialComment("begin"));
121
- const closingComments = getAllPositionNodes(ast, "html").filter(isSpecialComment("end"));
122
- expect(openingComments.length).toBe(2);
123
- expect(closingComments.length).toBe(2);
124
- const blocks = getTopLevelCommentBlocks(openingComments, closingComments);
125
- expect(blocks.length).toBe(1);
126
- expect(blocks[0].open).toBe(openingComments[0]);
127
- expect(blocks[0].close).toBe(closingComments[1]);
128
- })
129
- });
130
-
131
- describe(extendGetRelativePathContent.name, () => {
132
- test('should call original function with resolved path', () => {
133
- const filesystem = new PsuedoFilesystem({
134
- root: { base: "", child: { a: "", b: "", nested: { a: "" } } }
135
- }, { setContentToPath: true });
136
-
137
- const fromRoot = (path: string) => filesystem.getFileFromAbsolutePath(join("root", path));
138
-
139
- expect(fromRoot("./base")).toBe("root/base");
140
- expect(fromRoot("./child/a")).toBe("root/child/a");
141
- expect(fromRoot("./child/b")).toBe("root/child/b");
142
- expect(fromRoot("./child/nested/a")).toBe("root/child/nested/a");
143
-
144
- const extended = extendGetRelativePathContent(fromRoot, { url: "./child/a" });
145
- expect(extended("../base")).toBe("root/base");
146
- expect(extended("./a")).toBe("root/child/a");
147
- expect(extended("./b")).toBe("root/child/b");
148
- expect(extended("./nested/a")).toBe("root/child/nested/a");
149
- });
150
- });
151
-
152
-
153
-
154
- describe(recursivelyPopulateInclusions.name, () => {
155
- test("basic unpopulated", () => {
156
- const filesystem = new PsuedoFilesystem({
157
- "README.md": dedent`
158
- # Main heading
159
-
160
- [](./child/README.md)
161
-
162
- End parent
163
- `,
164
- child: {
165
- "README.md": dedent`
166
- # Child heading
167
-
168
- End child
169
- `,
170
- }
171
- });
172
-
173
- const fromRoot = (path: string) => filesystem.getFileFromAbsolutePath(join("", path));
174
-
175
- const result = recursivelyPopulateInclusions(filesystem.getFileFromAbsolutePath("README.md"), 0, fromRoot);
176
- expect(result).toBe(dedent`
177
- # Main heading
178
-
179
- [](./child/README.md)
180
- ${specialComment.begin}
181
- ## Child heading
182
-
183
- End child
184
- ${specialComment.end}
185
-
186
- End parent
187
- `)
188
- expect(result).toBe(recursivelyPopulateInclusions(result, 0, fromRoot));
189
- });
190
-
191
- test('should apply modifications to all top-level links in a markdown file', () => {
192
-
193
- const filesystem = new PsuedoFilesystem({
194
- "README.md": dedent`
195
- # Main heading
196
-
197
- [](./child/README.md)
198
-
199
- End
200
- `,
201
- child: {
202
- "README.md": dedent`
203
- # Child heading
204
-
205
- [](./grandchild/README.md)
206
- ${specialComment.begin}
207
- THIS SHOULD BE DELETED
208
- ${specialComment.end}
209
- `,
210
- grandchild: {
211
- "README.md": dedent`
212
- # Grandchild heading
213
-
214
- Hello!
215
- `,
216
- }
217
- }
218
- });
219
-
220
- const fromRoot = (path: string) => filesystem.getFileFromAbsolutePath(join("", path));
221
-
222
- const result = recursivelyPopulateInclusions(filesystem.getFileFromAbsolutePath("README.md"), 0, fromRoot);
223
- expect(result).toBe(recursivelyPopulateInclusions(result, 0, fromRoot));
224
- });
225
-
226
- test('with code boundary', () => {
227
- const filesystem = new PsuedoFilesystem({
228
- "README.md": dedent`
229
- # Main heading
230
-
231
- [](./child/file.ts?boundary=boundary)
232
-
233
- End
234
- `,
235
- child: {
236
- "file.ts": dedent`
237
- if (true) {
238
- /* boundary */
239
- const x = 5;
240
- /* boundary */
241
- }
242
- `,
243
- }
244
- });
245
-
246
- const fromRoot = (path: string) => filesystem.getFileFromAbsolutePath(join("", path));
247
-
248
- const result = recursivelyPopulateInclusions(filesystem.getFileFromAbsolutePath("README.md"), 0, fromRoot);
249
- expect(result).toBe(dedent`
250
- # Main heading
251
-
252
- [](./child/file.ts?boundary=boundary)
253
- ${specialComment.begin}
254
- \`\`\`ts
255
- const x = 5;
256
- \`\`\`
257
- ${specialComment.end}
258
-
259
- End`
260
- )
261
- })
262
-
263
- test('with wrapped in dropdown', () => {
264
- const filesystem = new PsuedoFilesystem({
265
- "README.md": dedent`
266
- # Main heading
267
-
268
- [](./child/README.md?tag=dropdown('Open-me,-please!'))
269
- `,
270
- child: {
271
- "README.md": dedent`
272
- Hello!
273
- `,
274
- }
275
- });
276
-
277
- const fromRoot = (path: string) => filesystem.getFileFromAbsolutePath(join("", path));
278
-
279
- const result = recursivelyPopulateInclusions(filesystem.getFileFromAbsolutePath("README.md"), 0, fromRoot);
280
- expect(result).toBe(dedent`
281
- # Main heading
282
-
283
- [](./child/README.md?tag=dropdown('Open-me,-please!'))
284
- ${specialComment.begin}
285
-
286
- <details>
287
- <summary>Open me, please!</summary>
288
-
289
- Hello!
290
- </details>
291
-
292
- ${specialComment.end}`
293
- );
294
- })
295
- });
296
-
297
- describe(getReplacementTargets.name, () => {
298
- test('should return empty array for no special links or comments', () => {
299
- const emptyMarkdown = "# Just a heading\n\nNo special links or comments here.";
300
- const emptyAst = parse.md(emptyMarkdown);
301
- expect(getReplacementTargets(emptyMarkdown, emptyAst)).toEqual([]);
302
- });
303
-
304
- test('should handle single unpopulated special link (no closing comment)', () => {
305
- const singleLinkMarkdown = "# Heading\n\n[](http://example.com)";
306
- const singleLinkAst = parse.md(singleLinkMarkdown);
307
- const singleLinkTargets = getReplacementTargets(singleLinkMarkdown, singleLinkAst);
308
- expect(singleLinkTargets.length).toBe(1);
309
- expect(singleLinkTargets[0].url).toBe("http://example.com");
310
- expect(singleLinkTargets[0].headingDepth).toBe(1);
311
- expect(extractContent(singleLinkMarkdown, singleLinkTargets[0])).toBe("[](http://example.com)");
312
- });
313
-
314
- test('should handle special link with closing comment', () => {
315
- const linkWithCommentMarkdown =
316
- "# Main heading\n\n" +
317
- "## Section\n\n" +
318
- "[](./file.md)\n" +
319
- specialComment.begin + "\n" +
320
- "Some content\n" +
321
- specialComment.end;
322
- const linkWithCommentAst = parse.md(linkWithCommentMarkdown);
323
- const linkWithCommentTargets = getReplacementTargets(linkWithCommentMarkdown, linkWithCommentAst);
324
- expect(linkWithCommentTargets.length).toBe(1);
325
- expect(linkWithCommentTargets[0].url).toBe("./file.md");
326
- expect(linkWithCommentTargets[0].headingDepth).toBe(2);
327
- expect(
328
- extractContent(linkWithCommentMarkdown, linkWithCommentTargets[0])
329
- ).toBe(`[](./file.md)\n${specialComment.begin}\nSome content\n${specialComment.end}`);
330
- });
331
-
332
- test('should handle multiple links and comments', () => {
333
- const complexMarkdown =
334
- "# Main heading\n\n" +
335
- "## First section\n\n" +
336
- "[](./first.md)\n" +
337
- specialComment.begin + "\n" +
338
- "Some content\n" +
339
- specialComment.end + "\n\n" +
340
- "## Second section\n\n" +
341
- "[](./second.md)\n" +
342
- specialComment.begin + "\n" +
343
- "More content\n" +
344
- specialComment.end + "\n\n" +
345
- "## Third section\n\n" +
346
- "[](http://example.com)";
347
- const complexAst = parse.md(complexMarkdown);
348
- const complexTargets = getReplacementTargets(complexMarkdown, complexAst);
349
- expect(complexTargets.length).toBe(3);
350
-
351
- expect(complexTargets[0].url).toBe("./first.md");
352
- expect(complexTargets[0].headingDepth).toBe(2);
353
- expect(
354
- extractContent(complexMarkdown, complexTargets[0])
355
- ).toBe(`[](./first.md)\n${specialComment.begin}\nSome content\n${specialComment.end}`);
356
-
357
- expect(complexTargets[1].url).toBe("./second.md");
358
- expect(complexTargets[1].headingDepth).toBe(2);
359
- expect(
360
- extractContent(complexMarkdown, complexTargets[1])
361
- ).toBe(`[](./second.md)\n${specialComment.begin}\nMore content\n${specialComment.end}`);
362
-
363
- expect(complexTargets[2].url).toBe("http://example.com");
364
- expect(complexTargets[2].headingDepth).toBe(2);
365
- expect(
366
- extractContent(complexMarkdown, complexTargets[2])
367
- ).toBe("[](http://example.com)");
368
- });
369
- });
package/src/include.ts DELETED
@@ -1,252 +0,0 @@
1
- import { URLSearchParams } from "node:url";
2
- import { getAllPositionNodes, parse, hasPosition, linkHasNoText, lined, spaced, Html, nodeSort, replaceWithContent, getContentInBetween } from "./utils";
3
- import { type AstRoot, type Link, type PositionNode, type HasPosition, COMMA_NOT_IN_PARENTHESIS } from "./utils"
4
- import { dirname, join, basename } from "node:path";
5
- import { wrap } from "./wrap";
6
- import { applyRegion, extractContentWithinRegionSpecifiers } from "./region";
7
-
8
- const specialLinkTargets = ["http", "./", "../"] as const;
9
- const isSpecialLinkTarget = ({ url }: Link) => specialLinkTargets.some(target => url.startsWith(target));
10
-
11
- export type SpecialLink = PositionNode<"link">;
12
- export const isSpecialLink = (node: Link): node is SpecialLink =>
13
- hasPosition(node) && linkHasNoText(node) && isSpecialLinkTarget(node);
14
- export const specialLinkText = ({ url }: Pick<SpecialLink, "url">, relative?: string) =>
15
- `[](${relative ? join(relative, url) : url})` as const;
16
-
17
- type CommentType = "begin" | "end";
18
-
19
- export const specialComment = {
20
- _open: "<!--" as const,
21
- _close: "-->" as const,
22
- _flag: "p▼" as const,
23
- get begin() { return spaced(specialComment._open, specialComment._flag, "BEGIN", specialComment._close) },
24
- get end() { return spaced(specialComment._open, specialComment._flag, "END", specialComment._close) },
25
- };
26
-
27
- export type SpecialComment<T extends CommentType = CommentType> = PositionNode<"html"> & { value: typeof specialComment[T] };
28
-
29
- export const isSpecialComment = <T extends CommentType>(type: T) =>
30
- (node: Html): node is SpecialComment<T> => hasPosition(node) && node.value === specialComment[type];
31
-
32
- export type ReplacementTarget = {
33
- url: string;
34
- headingDepth: number;
35
- inline: boolean;
36
- } & HasPosition;
37
-
38
- const replaceUnpopulated = (
39
- { position, url, siblingCount }: SpecialLink, headingDepth: number
40
- ): ReplacementTarget => ({ position, url, headingDepth, inline: siblingCount >= 1 })
41
-
42
- const replacePopulated = (
43
- { position: { start }, url, siblingCount }: SpecialLink, { position: { end } }: SpecialComment<"end">, headingDepth: number
44
- ): ReplacementTarget => ({ position: { start, end }, url, headingDepth, inline: siblingCount >= 1 });
45
-
46
- export const getReplacementContent = (target: Pick<ReplacementTarget, "url" | "inline">, content: string, relative?: string) =>
47
- target.inline
48
- ? `${specialLinkText(target, relative)} ${specialComment.begin} ${content} ${specialComment.end}` as const
49
- : lined(specialLinkText(target, relative), specialComment.begin, content, specialComment.end);
50
-
51
- export const nodeDepthFinder = (ast: AstRoot) => {
52
- const headingDepth = getAllPositionNodes(ast, "heading")
53
- .reduce((acc, { position, depth }) => acc.set(position.start.line, depth), new Map<number, number>())
54
- return (node: HasPosition) => {
55
- for (let i = node.position.start.line; i >= 1; i--) {
56
- const depth = headingDepth.get(i);
57
- if (depth) return depth;
58
- }
59
- return 0;
60
- }
61
- }
62
-
63
- const error = {
64
- openingCommentDoesNotFollowLink: ({ position: { start } }: SpecialComment<"begin">) =>
65
- new Error(`Opening comment (@${start.line}:${start.column}) does not follow link`),
66
- closingCommentNotMatchedToOpening: ({ position: { start } }: SpecialComment<"end">) =>
67
- new Error(`Closing comment (@${start.line}:${start.column}) does not match to opening comment`),
68
- openingCommentNotClosed: ({ position: { start } }: SpecialComment<"begin">) =>
69
- new Error(`Opening comment (@${start.line}:${start.column}) is not followed by a closing comment`),
70
- }
71
-
72
- export const getTopLevelCommentBlocks = (
73
- openingComments: SpecialComment<"begin">[], closingComments: SpecialComment<"end">[]
74
- ) => {
75
- const blocks: { open: SpecialComment<"begin">, close: SpecialComment<"end"> }[] = [];
76
-
77
- const combined = [
78
- ...openingComments.map(node => ({ node, type: "open" as const })),
79
- ...closingComments.map(node => ({ node, type: "close" as const }))
80
- ].sort((a, b) => nodeSort(a.node, b.node));
81
-
82
- const stack: (typeof combined[number] & { type: "open" })[] = [];
83
-
84
- for (const item of combined)
85
- if (item.type === "open") stack.push(item)
86
- else {
87
- const close = item.node as SpecialComment<"end">;
88
- if (stack.length === 0)
89
- throw error.closingCommentNotMatchedToOpening(close);
90
- const open = stack.pop()!.node;
91
- if (stack.length > 0) continue;
92
- blocks.push({ open, close });
93
- }
94
-
95
- if (stack.length > 0)
96
- throw error.openingCommentNotClosed(stack[0].node);
97
-
98
- return blocks;
99
- }
100
-
101
- type CommentBlocks = ReturnType<typeof getTopLevelCommentBlocks>;
102
-
103
- export const matchCommentBlocksToLinks = (
104
- markdown: string, links: SpecialLink[], blocks: CommentBlocks
105
- ) => {
106
- const linkCandidates = [...links].sort(nodeSort);
107
- const results: (SpecialLink | [SpecialLink, CommentBlocks[number]])[] = [];
108
-
109
- [...blocks]
110
- .sort((a, b) => nodeSort.reverse(a.open, b.open))
111
- .forEach(block => {
112
- while (linkCandidates.length > 0) {
113
- const link = linkCandidates.pop()!;
114
- if (link.position.start.offset < block.open.position.start.offset) {
115
- if (getContentInBetween(markdown, link, block.open).trim() !== "")
116
- throw error.openingCommentDoesNotFollowLink(block.open);
117
- return results.push([link, block]);
118
- }
119
- results.push(link);
120
- }
121
- throw error.openingCommentDoesNotFollowLink(block.open);
122
- });
123
-
124
- results.push(...linkCandidates.reverse());
125
- return results.reverse();
126
- }
127
-
128
- export const getReplacementTargets = (markdwn: string, ast?: AstRoot): ReplacementTarget[] => {
129
- ast ??= parse.md(markdwn);
130
- const findDepth = nodeDepthFinder(ast);
131
- const specialLinks = getAllPositionNodes(ast, "link").filter(isSpecialLink);
132
- const htmlNodes = getAllPositionNodes(ast, "html").sort(nodeSort);
133
- const openingComments = htmlNodes.filter(isSpecialComment("begin"));
134
- const closingComments = htmlNodes.filter(isSpecialComment("end"));
135
- const blocks = getTopLevelCommentBlocks(openingComments, closingComments);
136
- const resolved = matchCommentBlocksToLinks(markdwn, specialLinks, blocks)
137
- return resolved.map(block => Array.isArray(block)
138
- ? replacePopulated(block[0], block[1].close, findDepth(block[0]))
139
- : replaceUnpopulated(block, findDepth(block)))
140
- }
141
-
142
- export type GetRelativePathContent = (path: string) => string;
143
-
144
- export const extendGetRelativePathContent = (
145
- getRelativePathContent: GetRelativePathContent, { url }: Pick<ReplacementTarget, "url">
146
- ) => ((path) => getRelativePathContent(join(dirname(url), path))) satisfies GetRelativePathContent;
147
-
148
- const clampHeadingSum = (...toSum: number[]) => {
149
- const sum = toSum.reduce((a, b) => a + b, 0);
150
- return Math.min(Math.max(sum, 1), 6) as 1 | 2 | 3 | 4 | 5 | 6;
151
- }
152
-
153
- export const applyHeadingDepth = (markdown: string, headingDepth: number, ast?: AstRoot) => {
154
- if (headingDepth === 0) return markdown;
155
- ast ??= parse.md(markdown);
156
- const nodes = getAllPositionNodes(ast, "heading");
157
- const lines = markdown.split("\n");
158
- for (const node of nodes) {
159
- const { depth, position: { start, end } } = node;
160
- const adjusted = clampHeadingSum(depth, headingDepth);
161
- const text = lines[start.line - 1].slice(depth, end.column);
162
- const replacement = `#`.repeat(adjusted) + text;
163
- lines[start.line - 1] = replacement;
164
- node.depth = adjusted;
165
- }
166
- return lines.join("\n");
167
- }
168
-
169
- export const removePopulatedInclusions = (markdown: string) =>
170
- getReplacementTargets(markdown)
171
- .reverse()
172
- .sort(nodeSort.reverse)
173
- .reduce((md, target) => replaceWithContent(md, specialLinkText(target), target), markdown);
174
-
175
- export const recursivelyPopulateInclusions = (
176
- markdown: string,
177
- headingDepth: number,
178
- getRelativePathContent: GetRelativePathContent,
179
- basePath?: string
180
- ) => {
181
- markdown = removePopulatedInclusions(markdown);
182
- markdown = applyHeadingDepth(markdown, headingDepth);
183
- const ast = parse.md(markdown);
184
-
185
- return getReplacementTargets(markdown, ast)
186
- .sort(nodeSort)
187
- .reverse()
188
- .map(target => {
189
- const { url, headingDepth } = target;
190
- const [base, ...splitOnMark] = basename(url).split("?");
191
- const extension = base.split(".").pop() ?? "";
192
- const query = splitOnMark.join("?");
193
- const dir = dirname(url);
194
- const path = join(dir, base);
195
-
196
- if (url.startsWith("./") || url.startsWith("../")) {
197
- let content = getRelativePathContent(path);
198
-
199
- /** p▼: query */
200
- const params = new URLSearchParams(query);
201
- /** p▼: query */
202
-
203
- /** p▼: query */
204
- const regions = params.get("region")?.split(COMMA_NOT_IN_PARENTHESIS);
205
- /** p▼: query */
206
- content = regions?.reduce((content, region) => applyRegion(content, region), content) ?? content;
207
-
208
- /** p▼: query */
209
- const skip = params.has("skip");
210
- /** p▼: query */
211
-
212
- /** p▼: query */
213
- const headingModfiier = params.get("heading") ?? 0;
214
- /** p▼: query */
215
-
216
- /** p▼: query */
217
- const inlineOverride = params.has("inline");
218
- /** p▼: query */
219
-
220
- let { inline } = target;
221
- if (inlineOverride) inline = true;
222
-
223
- if (!skip)
224
- /** p▼: Default-Behavior */
225
- if (extension === "md") {
226
- /** p▼: ... */
227
- const getContent = extendGetRelativePathContent(getRelativePathContent, target);
228
- const relative = basePath ? join(basePath, dir) : dir;
229
- const depth = clampHeadingSum(headingDepth, Number(headingModfiier));
230
- /** p▼: ... */
231
- content = recursivelyPopulateInclusions(content, /** p▼: ... */ depth, getContent, relative /** p▼: ... */);
232
- }
233
- else if (/^(js|ts)x?|svelte$/i.test(extension))
234
- content = wrap(content, "code", /** p▼: ... */ { extension, inline } /** p▼: ... */);
235
- /** p▼: Default-Behavior */
236
-
237
- /** p▼: query */
238
- const wraps = params.get("wrap")?.split(COMMA_NOT_IN_PARENTHESIS);
239
- /** p▼: query */
240
- content = wraps
241
- ?.reduce((content, query) => wrap(content, query, { extension, inline }), content)
242
- ?? content;
243
-
244
- return { target, content: getReplacementContent(target, content, basePath) };
245
- }
246
- else if (url.startsWith("http"))
247
- throw new Error("External web links are not implemented yet");
248
- else
249
- throw new Error(`Unsupported link type: ${url}`);
250
- })
251
- .reduce((acc, { target, content }) => replaceWithContent(acc, content, target), markdown);
252
- }
package/src/index.ts DELETED
@@ -1,35 +0,0 @@
1
- import { readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { recursivelyPopulateInclusions, removePopulatedInclusions } from "./include";
4
- import { removeQueryParams } from "./utils";
5
-
6
- const read = (path: string) => readFileSync(path, "utf-8");
7
-
8
- const tryResolveFile = (file: string) => {
9
- const path = resolve(file);
10
- const dir = dirname(path);
11
- const markdown = read(path);
12
- return { markdown, dir, path };
13
- }
14
-
15
- export const populateMarkdownInclusions = (file: string, writeFile = true) => {
16
- const { dir, path, markdown } = tryResolveFile(file);
17
- const getContent = (relative: string) => read(resolve(dir, removeQueryParams(relative)));
18
- const result = recursivelyPopulateInclusions(markdown, 0, getContent);
19
- if (writeFile) writeFileSync(path, result);
20
- return result;
21
- };
22
-
23
- export const depopulateMarkdownInclusions = (file: string, writeFile = true) => {
24
- const { path, markdown } = tryResolveFile(file);
25
- const result = removePopulatedInclusions(markdown);
26
- if (writeFile) writeFileSync(path, result);
27
- return result;
28
- };
29
-
30
- export const remapImportSpecifiers = (file: string, writeFile = true) => {
31
- const { path, markdown } = tryResolveFile(file);
32
- // const result = remapImports(markdown);
33
- // if (writeFile) writeFileSync(path, result);
34
- // return result;
35
- };