@p-buddy/parkdown 0.0.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.
Files changed (43) hide show
  1. package/.assets/api-note.md +3 -0
  2. package/.assets/api.md +34 -0
  3. package/.assets/authoring.md +69 -0
  4. package/.assets/code/depopulate.ts +6 -0
  5. package/.assets/code/inclusions.ts +6 -0
  6. package/.assets/depopulated.md +25 -0
  7. package/.assets/invocation.md +16 -0
  8. package/.assets/populated/block.md +9 -0
  9. package/.assets/populated/inline.multi.md +5 -0
  10. package/.assets/populated/inline.single.md +3 -0
  11. package/.assets/query.md +73 -0
  12. package/.assets/remap-imports.md +0 -0
  13. package/.assets/unpopulated/block.md +5 -0
  14. package/.assets/unpopulated/inline.multi.md +3 -0
  15. package/.assets/unpopulated/inline.single.md +1 -0
  16. package/.devcontainer/Dockerfile +16 -0
  17. package/.devcontainer/devcontainer.json +35 -0
  18. package/LICENSE +21 -0
  19. package/README.md +418 -0
  20. package/dist/cli.js +14 -0
  21. package/dist/index.d.ts +7 -0
  22. package/dist/index.js +396 -0
  23. package/dist/index.umd.cjs +15 -0
  24. package/package.json +42 -0
  25. package/src/api/index.test.ts +32 -0
  26. package/src/api/index.ts +8 -0
  27. package/src/api/types.ts +78 -0
  28. package/src/api/utils.test.ts +132 -0
  29. package/src/api/utils.ts +161 -0
  30. package/src/cli.ts +31 -0
  31. package/src/include.test.ts +369 -0
  32. package/src/include.ts +252 -0
  33. package/src/index.ts +35 -0
  34. package/src/region.test.ts +145 -0
  35. package/src/region.ts +138 -0
  36. package/src/remap.test.ts +37 -0
  37. package/src/remap.ts +72 -0
  38. package/src/utils.test.ts +238 -0
  39. package/src/utils.ts +184 -0
  40. package/src/wrap.ts +61 -0
  41. package/tsconfig.json +5 -0
  42. package/vite.cli.config.ts +23 -0
  43. package/vite.config.ts +20 -0
@@ -0,0 +1,238 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { parse, nodeSort, getAllPositionNodes, extractContent, replaceWithContent, getContentInBetween, } from "./utils";
3
+
4
+ describe(nodeSort.name, () => {
5
+ test("should sort nodes by line number", () => {
6
+ const ast = parse.md(lorem.md[0]);
7
+ let previousLine = 0;
8
+ let previousColumn = 0;
9
+ for (const node of getAllPositionNodes(ast).sort(nodeSort)) {
10
+ const lineIncreased = node.position.start.line > previousLine;
11
+ const lineStayedTheSame = node.position.start.line === previousLine;
12
+ const columnIncreased = node.position.start.column > previousColumn;
13
+ const columnStayedTheSame = node.position.start.column === previousColumn;
14
+ expect(lineIncreased || (lineStayedTheSame && (columnIncreased || columnStayedTheSame))).toBe(true);
15
+ previousLine = node.position.start.line;
16
+ previousColumn = node.position.start.column;
17
+ }
18
+ previousLine += 1;
19
+ previousColumn = 0;
20
+ for (const node of getAllPositionNodes(ast).sort(nodeSort.reverse)) {
21
+ const lineDecreased = node.position.start.line < previousLine;
22
+ const lineStayedTheSame = node.position.start.line === previousLine;
23
+ const columnDecreased = node.position.start.column < previousColumn;
24
+ const columnStayedTheSame = node.position.start.column === previousColumn;
25
+ expect(lineDecreased || (lineStayedTheSame && (columnDecreased || columnStayedTheSame))).toBe(true);
26
+ previousLine = node.position.start.line;
27
+ previousColumn = node.position.start.column;
28
+ }
29
+ });
30
+ })
31
+
32
+ const getLinkAndCommentAst = (markdown: string) => {
33
+ const ast = parse.md(markdown);
34
+ const links = getAllPositionNodes(ast, "link");
35
+ expect(links.length).toBe(1);
36
+ const comments = getAllPositionNodes(ast, "html");
37
+ expect(comments.length).toBe(1);
38
+ return { link: links[0], comment: comments[0], ast, markdown };
39
+ }
40
+
41
+ describe(extractContent.name, () => {
42
+ test("should extract content from nodes", () => {
43
+ const { link, comment, ast, markdown } = getLinkAndCommentAst(`
44
+ # Title
45
+
46
+ [link](http://example.com)
47
+ hello
48
+ <!-- comment -->`
49
+ );
50
+
51
+ let content = extractContent(markdown, link);
52
+ expect(content).toBe("[link](http://example.com)");
53
+
54
+ content = extractContent(markdown, comment);
55
+ expect(content).toBe("<!-- comment -->");
56
+
57
+ content = extractContent(markdown, link, comment);
58
+ expect(content).toBe("[link](http://example.com)\nhello\n<!-- comment -->");
59
+ expect(content).toBe(extractContent(markdown, comment, link));
60
+ })
61
+ })
62
+
63
+ describe(replaceWithContent.name, () => {
64
+ test("should replace content with new content", () => {
65
+ const { link, comment, ast, markdown } = getLinkAndCommentAst(`
66
+ # Title
67
+
68
+ [link](http://example.com)
69
+ hello
70
+ <!-- comment -->
71
+
72
+ ahoy!`
73
+ );
74
+ const content = replaceWithContent(markdown, "new content", link, comment);
75
+ expect(content).toBe("\n# Title\n\nnew content\n\nahoy!");
76
+ })
77
+ });
78
+
79
+ describe(getContentInBetween.name, () => {
80
+ test("should get content in between two multiline nodes", () => {
81
+ const { link, comment, ast, markdown } = getLinkAndCommentAst(`
82
+ # Title
83
+
84
+ [link](http://example.com)
85
+ hello
86
+ <!-- comment -->`
87
+ );
88
+ const content = getContentInBetween(markdown, link, comment);
89
+ expect(content).toBe("\nhello\n");
90
+ });
91
+
92
+ test("should get content in between two singleline nodes", () => {
93
+ const { link, comment, ast, markdown } = getLinkAndCommentAst(`
94
+ # Title
95
+
96
+ [link](http://example.com) hello <!-- comment -->`
97
+ );
98
+ const content = getContentInBetween(markdown, link, comment);
99
+ expect(content).toBe(" hello ");
100
+ })
101
+ });
102
+
103
+ interface PsuedoDir {
104
+ [key: string]: PsuedoDir | string;
105
+ }
106
+
107
+ export class PsuedoFilesystem {
108
+ constructor(readonly root: PsuedoDir, options?: { setContentToPath?: boolean }) {
109
+ const { setContentToPath = false } = options ?? {};
110
+ if (setContentToPath) PsuedoFilesystem.SetAllFileContentToPath(this.root);
111
+ }
112
+
113
+ getFileFromAbsolutePath(path: string) {
114
+ return path.split("/").reduce((acc, part) => (acc as Record<string, PsuedoDir>)[part], this.root) as any as string;
115
+ }
116
+
117
+ static SetAllFileContentToPath(root: PsuedoDir, prefix?: string) {
118
+ for (const key in root) {
119
+ const value = root[key];
120
+ const path = prefix ? `${prefix}/${key}` : key;
121
+ if (typeof value === "string") root[key] = path;
122
+ else this.SetAllFileContentToPath(value, path);
123
+ }
124
+ }
125
+ }
126
+
127
+ export const lorem = {
128
+ md: [
129
+ `# Vi nactusque pelle at floribus irata quamvis
130
+
131
+ ## Moenibus voluptas ludit
132
+
133
+ Lorem markdownum cornua, iter quiete, officiumque arbor vocisque, [alti
134
+ lumina](http://fundae.io/illa.aspx) Agenore. Vendit ob meos, mihi monitis saxum
135
+ noster, est eandem ante, tuos sopitus scopulis volentem. Rege semper iaculo
136
+ protinus poenae curribus increpat Stygias scire: prohibent, et regis in.
137
+ Profanos mecum muneris, dum iudicis eurus non sua accepit auras utque staret.
138
+
139
+ ## Filius virgo culpa reliquit
140
+
141
+ Illa moenia vepre clauso o **praemia** fluidoque primo est, modo tamen tumultu
142
+ adorat: rogumque ursa **in**. Solum consensistis illis, Ithacis cuncti ver vidit
143
+ carbasa fluctibus ratione eundem mihi. Vineta *unda*, nec victricia, nullaque,
144
+ inploravere *poteram quae erat* et videt summas regia ferunt se, se?
145
+
146
+ ## Illa nuncupat ante proxima habenti prodit
147
+
148
+ Sua qui passis barbam *mira*: adfer pericula; aut. Tua purpuraque passim
149
+ attulerat lanas monitae Turnus patrium cuius fuerat stupet exercent sine.
150
+ Incaluitque premebat ad elisa ut meruisse dum *solutis*, damnare. Texit Libycas,
151
+ est nunc Phoebus. Dominaeque meriti caligine vestigia *extentam* citra tecto
152
+ undas impetus alma, quam radix.
153
+
154
+ 1. Umbras laudare per telo lacrimis
155
+ 2. Saturno Andraemon Iovem
156
+ 3. Cum eadem
157
+ 4. Vacent Britannos neque quae rupit socialia pulcherrime
158
+ 5. Vidit morsu
159
+
160
+ ## Aut visam
161
+
162
+ Micantes *flecti*. Capitolia et aut haec *Latoius manet submersum* et non tumens
163
+ paternis. Ope cornua calidumque artes. Quoque forma, quae gemitus sanguine per
164
+ cunctos hanc est haec abstulit acumine morte hoc fui.
165
+
166
+ > Trepidare cum expellere pectus Ismenus tempora fulminis pater; coniunctaque
167
+ > vocabis placandam et ebori separat. Regna inpensius pater accipienda epytus
168
+ > *Phryges cum angustis* vehit; nec summo excutit Aulidaque partibus texitur
169
+ > perque indomitas frater. Sua ferens discedet et quae, sonantia, comminus
170
+ > *ego*, auras. Dives **ille dubitate eum** poterit adest marito bracchia nec
171
+ > tune, discordemque tanti credas caede hactenus, dumque. At et agros Laiades,
172
+ > illa virtute adorat, est mox palmiferos robore flere ubera.
173
+
174
+ Color genuumque natis Pactolides plangore concipiunt proxima est, aliquid,
175
+ iraque ad natus quoque? **Quoque et** et classe fidemque incepta qui cumque
176
+ latitans ac [vestrum](http://fallitquevolucris.org/fuerant-eris).`,
177
+ `# Ipse oculis praecipitata nostro
178
+
179
+ ## Victis ferroque umbram mors plenis
180
+
181
+ Lorem markdownum, angues nec pecudis ponat dabitur qua resedit. Genitor tellus
182
+ et loqui hac: et nullae regnaque, est. Durescit videri. Nunc navita cruento,
183
+ cum, puerilibus aequor. Pro saepe [iamque](http://saturnoquod.com/nullum),
184
+ statione noverat, simul teneri hoc idem opem: Peleus.
185
+
186
+ ## Pro nisi vaticinos posse
187
+
188
+ Clymenen nec caesa reddi. Vocat cum, spectare in tamen te fugacibus, haut. Solus
189
+ extulit insistere pugnas praestatque modo purpureis venenata [tumet
190
+ sed](http://www.mihi-duc.com/ferro) curru sanguine levatus magnanimo. Dulichium
191
+ indulsit.
192
+
193
+ ## Humano Gorgoneum portus nil pavens laboriferi rapui
194
+
195
+ Faciles non, Iris vero [medeatur reclusis](http://www.tendere.io/somnus) digna
196
+ et sumptis est feres viae hic huc barbae salus laetos. Et ante! Quid lumen Isi:
197
+ nec Rhenum, profecturas priorum aegide medias in coniugis cinctaque ad ignis
198
+ posito. Nubila in alternaque Procnes terrae adferre sentit postquam cui rerum
199
+ nubilus fulvas iam illis cum virgultis, unda [ipse
200
+ tamen](http://www.terra.org/nam.html).
201
+
202
+ var port = uncForumRt(ccd * dpi_udp(1, 1, 52557),
203
+ pppoe_scraping_switch.passive(adf_domain, floating +
204
+ standaloneDnsPppoe, trashFileLed / safe_error_recursive), 2);
205
+ if (-2) {
206
+ bpsPOn = hardwareIso(1);
207
+ vaporware_biometrics.wddm_spool_compile += multimediaStation(
208
+ drop_property_boot, sms, crop_excel(2, snmp_truncate_inkjet));
209
+ }
210
+ softMetadata.installer = 4;
211
+ var hdtv = printer_json_southbridge;
212
+
213
+ ## Multorum pellant famularia praeterea humo
214
+
215
+ Carmine demisi super nantis **telo**! Dicimus requievit, lurida extenuant
216
+ **diverso paventis venter** ore medio deposuitque ex fons: Iuno latratibus.
217
+ Mediaque tum *Eurus*, unam nympha casu ille licet sinu est modo, celasse tamen.
218
+
219
+ ## Quem puto suis semper expers hominis Placatus
220
+
221
+ **Sidera arma**; Iuventae loca victis: necis acies ducunt ipse non **precesque
222
+ petit demptos** effetus: oculis mittunt. In nam vox regia sustinet nervosque
223
+ obsceno Delphi haec genetrix Nereus [versasque
224
+ quaeratur](http://te-urbem.net/aqua). Terga deae natalis *Aetnen* ingemuit, cum
225
+ arserunt vertice **egimus** fama visa illic ipsamque.
226
+
227
+ 1. Post petit unum accepisse obsequio populator praesagia
228
+ 2. Terrena iam mora libera
229
+ 3. Suas astra inflata litore crimine
230
+
231
+ Quibus viribus referam **est posse** Iphis extulerat oscula, clivum, tantum
232
+ patres formam villis [hic pruinas
233
+ remisit](http://quaeebur.com/condi-summas.aspx). In qui cum, **lac fugavi
234
+ Perseus**. Me iusta habitabat stabat tam locum similes pulsant; manu palantesque
235
+ deae cohaesit, nec ut opiferque fugientem eurus. Eodem vivit Aiaci minas non,
236
+ radices in petent audacia volabat pro dedit ducibusque et vertice abstinuit.`
237
+ ] as const
238
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,184 @@
1
+ import { unified, type Plugin } from 'unified';
2
+ import remarkParse from 'remark-parse';
3
+ import { visit } from 'unist-util-visit';
4
+ import hash from 'stable-hash';
5
+
6
+ export type AstRoot = typeof remarkParse extends Plugin<any, any, infer Root>
7
+ /**/ ? Root
8
+ /**/ : never;
9
+
10
+ export type MarkdownNode = AstRoot["children"][number];
11
+ export type NodeType = MarkdownNode["type"];
12
+ export type SpecificNode<T extends NodeType> = MarkdownNode & { type: T };
13
+
14
+ type RequiredDeep<T> = {
15
+ [P in keyof T]-?: T[P] extends object | undefined ? RequiredDeep<T[P]> : T[P];
16
+ };
17
+
18
+ export type HasPosition = RequiredDeep<Pick<MarkdownNode, "position">>;
19
+ export type Position = HasPosition['position'];
20
+ export type Point = Position['start' | 'end'];
21
+
22
+ export const nodeSort = (a: HasPosition, b: HasPosition) => a.position.start.offset - b.position.start.offset;
23
+
24
+ nodeSort.reverse = (a: HasPosition, b: HasPosition) => nodeSort(b, a);
25
+
26
+ export const hasPosition = <T extends MarkdownNode>(node: T): node is T & HasPosition =>
27
+ node.position !== undefined && node.position.start.offset !== undefined && node.position.end.offset !== undefined;
28
+
29
+ const processor = unified().use(remarkParse);
30
+
31
+ export const parse = {
32
+ md: (markdown: string) => processor.parse(markdown) satisfies AstRoot
33
+ } as const;
34
+
35
+ export const getAllPositionNodes = <T extends NodeType>(ast: AstRoot, type?: T) => {
36
+ type Node = SpecificNode<T> & HasPosition & { parentID: string; siblingIndex: number; siblingCount: number };
37
+ const nodes: Node[] = [];
38
+ visit(ast, (node, siblingIndex, parent) => {
39
+ if (node.type === "root") return;
40
+ else if (type && node.type !== type) return;
41
+ else if (hasPosition(node)) {
42
+ const parentID = hash(parent);
43
+ const siblingCount = (parent?.children.length ?? 0) - 1;
44
+ nodes.push({ ...node, parentID, siblingIndex, siblingCount } as Node);
45
+ }
46
+ });
47
+ return nodes;
48
+ }
49
+
50
+ export type PositionNode<T extends NodeType> = ReturnType<typeof getAllPositionNodes<T>>[number];
51
+
52
+ export type Link = PositionNode<"link">;
53
+ export type Html = PositionNode<"html">;
54
+
55
+ export const linkHasNoText = (node: Link) => node.children.length === 0;
56
+
57
+ export const extractContent = (markdown: string, ...nodes: HasPosition[]) => {
58
+ if (nodes.length === 0) throw new Error("No nodes to extract content from");
59
+ nodes.sort(nodeSort);
60
+ const head = nodes.at(0)!;
61
+ const tail = nodes.at(-1)!;
62
+ return markdown.slice(head.position.start.offset, tail.position.end.offset);
63
+ }
64
+
65
+ export const replaceWithContent = (markdown: string, content: string, ...nodes: HasPosition[]) => {
66
+ if (nodes.length === 0) throw new Error("No nodes to replace content from");
67
+ nodes.sort(nodeSort);
68
+ const head = nodes.at(0)!;
69
+ const tail = nodes.at(-1)!;
70
+ return markdown.slice(0, head.position.start.offset) + content + markdown.slice(tail.position.end.offset);
71
+ }
72
+
73
+ export const getContentInBetween = (markdown: string, a: HasPosition, b: HasPosition) => {
74
+ const head = Math.min(a.position.end.offset, b.position.end.offset);
75
+ const tail = Math.max(a.position.start.offset, b.position.start.offset);
76
+ return markdown.slice(head, tail);
77
+ }
78
+
79
+ type Join<T extends string[], D extends string> =
80
+ T extends []
81
+ /**/ ? ''
82
+ /**/ : T extends [infer F extends string]
83
+ /**/ ? F
84
+ /**/ : T extends [infer F extends string, ...infer R extends string[]]
85
+ /**/ ? `${F}${D}${Join<R, D>}`
86
+ /**/ : string;
87
+
88
+ export const spaced = <T extends string[]>(...args: T) => args.join(" ") as Join<T, " ">;
89
+ export const lined = <T extends string[]>(...args: T) => args.join("\n") as Join<T, "\n">;
90
+
91
+ export const start = ({ position: { start } }: HasPosition) => start;
92
+
93
+ const offsetIndex = ({ start, end }: Position, offset: number) =>
94
+ ({ start: { line: start.line + offset, column: start.column + offset }, end: { line: end.line + offset, column: end.column + offset } });
95
+
96
+ export const zeroIndexed = (position: Position) => offsetIndex(position, -1);
97
+ export const oneIndexed = (position: Position) => offsetIndex(position, 1);
98
+
99
+ export const seperateQueryParams = (path: string): [lhs: string, query: string] => {
100
+ const parts = path.split("?");
101
+ return parts.length > 1 ? [parts.slice(0, -1).join("?"), parts.at(-1)!] : [path, ""];
102
+ }
103
+
104
+ export const getQueryParams = (path: string) => seperateQueryParams(path)[1];
105
+
106
+ export const removeQueryParams = (path: string) => seperateQueryParams(path)[0];
107
+
108
+
109
+ export class Intervals {
110
+ private intervals: Array<[number, number]> = [];
111
+
112
+ push(start: number, end: number) {
113
+ this.intervals.push([Math.min(start, end), Math.max(start, end)]);
114
+ }
115
+
116
+ combine(rhs: Intervals) {
117
+ this.intervals.push(...rhs.intervals);
118
+ }
119
+
120
+ collapse() {
121
+ const { intervals } = this;
122
+ if (!intervals.length) return (this.intervals = []);
123
+
124
+ intervals.sort((a, b) => a[0] - b[0]);
125
+
126
+ const result: typeof this.intervals = [];
127
+ let [currStart, currEnd] = intervals[0];
128
+
129
+ for (let i = 1; i < intervals.length; i++) {
130
+ const [start, end] = intervals[i];
131
+ if (start <= currEnd) currEnd = Math.max(currEnd, end);
132
+ else {
133
+ result.push([currStart, currEnd]);
134
+ currStart = start;
135
+ currEnd = end;
136
+ }
137
+ }
138
+ result.push([currStart, currEnd]);
139
+
140
+ return (this.intervals = result);
141
+ }
142
+
143
+ subtract(rhs: Intervals) {
144
+ const { intervals } = this;
145
+ const { intervals: remove } = rhs;
146
+
147
+ if (!intervals.length || !remove.length) return intervals;
148
+
149
+ let result = [...intervals];
150
+ for (const [removeStart, removeEnd] of remove) {
151
+ const updated: typeof this.intervals = [];
152
+
153
+ for (const [start, end] of result) {
154
+ if (removeEnd <= start || removeStart >= end) {
155
+ updated.push([start, end]);
156
+ continue;
157
+ }
158
+
159
+ if (removeStart > start) updated.push([start, removeStart]);
160
+ if (removeEnd < end) updated.push([removeEnd, end]);
161
+ }
162
+
163
+ result = updated;
164
+ }
165
+
166
+ return (this.intervals = result);
167
+ }
168
+ }
169
+
170
+ export const COMMA_NOT_IN_PARENTHESIS = /,\s*(?![^()]*\))/;
171
+
172
+ /** p▼: sanitize */
173
+ const sanitizations: [from: RegExp | string, to: string][] = [
174
+ [/'''/g, `"`],
175
+ [/''/g, `'`],
176
+ [/parkdown:\s+/g, ``],
177
+ [/p▼:\s+/g, ``],
178
+ ]
179
+
180
+ export const sanitize = (replacement: string, space: string = "-") => {
181
+ const sanitized = sanitizations.reduce((acc, [from, to]) => acc.replaceAll(from, to), replacement)
182
+ return space ? sanitized.replaceAll(space, " ") : sanitized;
183
+ }
184
+ /** p▼: sanitize */
package/src/wrap.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { createParser, type MethodDefinition } from "./api/";
2
+
3
+ /** p▼: definition */
4
+ const definitions = [
5
+ /**
6
+ * Wraps the content in a markdown-formatted code block.
7
+ * @param lang The language of the code block (defaults to the file extension).
8
+ * @param meta Additional metadata to include in the top line of the code block (i.e. to the right of the `lang`).
9
+ * @example [](<url>?wrap=code)
10
+ * @example [](<url>?wrap=code())
11
+ * @example [](<url>?wrap=code(ts))
12
+ * @example [](<url>?wrap=code(,some-meta))
13
+ */
14
+ "code(lang?: string, meta?: string)",
15
+
16
+ /**
17
+ * Wraps the content in a markdown-formatted blockquote
18
+ * (using the `>` character if the content is a single line,
19
+ * or the `<blockquote>` tag if the content is a multi-line block).
20
+ * @example [](<url>?wrap=quote)
21
+ * @example [](<url>?wrap=quote())
22
+ * @example [](<url>?wrap=quote(,))
23
+ */
24
+ "quote()",
25
+
26
+ /**
27
+ * Wraps the content in a markdown-formatted dropdown (using the `<details>` and `<summary>` tags).
28
+ * @param summary The summary text of the dropdown.
29
+ * @param open Whether the dropdown should be open by default.
30
+ * @param space The space character to use between words in the summary (defaults to `-`).
31
+ * @example [](<url>?wrap=dropdown(hello-world))
32
+ * @example [](<url>?wrap=dropdown('hello,-world',true))
33
+ * @example [](<url>?wrap=dropdown(hello_world,,_))
34
+ */
35
+ "dropdown(summary: string, open?: boolean, space?: string)",
36
+
37
+ ] /** p▼: definition */ satisfies (MethodDefinition)[];
38
+
39
+ /** p▼: Default-Space */
40
+ const DEFAULT_SPACE = "-";
41
+ /** p▼: Default-Space */
42
+
43
+ const parse = createParser(definitions);
44
+
45
+ export const wrap = (content: string, query: string, details?: { extension: string, inline: boolean }): string => {
46
+ const parsed = parse(query);
47
+ const isSingleLine = details?.inline && !content.includes("\n\n");
48
+
49
+ switch (parsed.name) {
50
+ case "code":
51
+ const lang = parsed.lang ?? details?.extension ?? "";
52
+ const meta = parsed.meta ? ` ${parsed.meta}` : "";
53
+ return `\`\`\`${lang}${meta}\n${content}\n\`\`\``;
54
+ case "quote":
55
+ return isSingleLine ? `> ${content}` : `<blockquote>\n\n${content}\n\n</blockquote>\n`;
56
+ case "dropdown":
57
+ const head = `<details${parsed.open ? " open" : ""}>`;
58
+ const summary = `<summary>${parsed.summary.split(parsed.space ?? DEFAULT_SPACE).join(" ")}</summary>`;
59
+ return ["", head, summary, "", content, "</details>", ""].join("\n");
60
+ }
61
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "compilerOptions": {
3
+ "moduleResolution": "bundler",
4
+ }
5
+ }
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from 'vite';
2
+ import { resolve } from 'node:path';
3
+ import { externalizeDeps } from 'vite-plugin-externalize-deps'
4
+ import { name as packageName } from './package.json';
5
+
6
+ export default defineConfig({
7
+ resolve: {
8
+ alias: {
9
+ '.': packageName,
10
+ './': packageName,
11
+ './index': packageName,
12
+ },
13
+ },
14
+ plugins: [externalizeDeps({ nodeBuiltins: true, include: [packageName] })],
15
+ build: {
16
+ lib: {
17
+ fileName: 'cli',
18
+ entry: resolve(__dirname, 'src/cli.ts'),
19
+ formats: ['es']
20
+ },
21
+ emptyOutDir: false,
22
+ }
23
+ });
package/vite.config.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { resolve } from 'node:path';
2
+ import { defineConfig } from 'vitest/config';
3
+ import dts from 'vite-plugin-dts';
4
+ import { externalizeDeps } from 'vite-plugin-externalize-deps';
5
+
6
+ const testPattern = "src/**/*.{test,spec}.{js,ts}"
7
+
8
+ export default defineConfig({
9
+ plugins: [dts({ exclude: testPattern, rollupTypes: true }), externalizeDeps({ nodeBuiltins: true })],
10
+ build: {
11
+ lib: {
12
+ name: 'index',
13
+ fileName: 'index',
14
+ entry: resolve(__dirname, 'src/index.ts'),
15
+ },
16
+ },
17
+ test: {
18
+ include: [testPattern],
19
+ },
20
+ })