vitepress-plugin-folder-tree 1.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.
- package/LICENSE +21 -0
- package/README.md +445 -0
- package/dist/VpFolderTree.vue +1486 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/config.d.ts +115 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/folder-tree-markdown.d.ts +35 -0
- package/dist/folder-tree-markdown.d.ts.map +1 -0
- package/dist/folder-tree-plugin.d.ts +4 -0
- package/dist/folder-tree-plugin.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/vitepress-plugin-folder-tree.es.mjs +326 -0
- package/dist/vitepress-plugin-folder-tree.umd.js +7 -0
- package/package.json +61 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single node in the rendered folder tree (internal representation).
|
|
3
|
+
*/
|
|
4
|
+
export interface TreeNode {
|
|
5
|
+
/** Display name */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Whether this node is a directory */
|
|
8
|
+
isFolder: boolean;
|
|
9
|
+
/** Children nodes (only for folders) */
|
|
10
|
+
children?: TreeNode[];
|
|
11
|
+
/** Short description shown as a badge next to the name */
|
|
12
|
+
description?: string;
|
|
13
|
+
/** Right-aligned note/label at the end of the row */
|
|
14
|
+
note?: string;
|
|
15
|
+
/** Highlight this row visually */
|
|
16
|
+
highlight?: boolean;
|
|
17
|
+
/** Custom icon text (emoji or Iconify class) — overrides the default SVG */
|
|
18
|
+
icon?: string;
|
|
19
|
+
/** Whether the folder is initially expanded */
|
|
20
|
+
open?: boolean;
|
|
21
|
+
/** Lock this folder — prevent toggling by the user */
|
|
22
|
+
locked?: boolean;
|
|
23
|
+
/** URL link — clicking the name navigates to this URL */
|
|
24
|
+
href?: string;
|
|
25
|
+
/** Preview text shown in tooltip on hover */
|
|
26
|
+
preview?: string;
|
|
27
|
+
/** Diff status indicator ('deleted' is an alias for 'removed') */
|
|
28
|
+
status?: 'added' | 'removed' | 'deleted' | 'modified';
|
|
29
|
+
/** Tab name — root-level nodes with this field are grouped into switchable tabs */
|
|
30
|
+
tab?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A single item in the YAML input.
|
|
34
|
+
* - A plain **string** → file with that name
|
|
35
|
+
* - An **object** → file or folder with metadata
|
|
36
|
+
*/
|
|
37
|
+
export type YamlInputItem = string | YamlInputObject;
|
|
38
|
+
export interface YamlInputObject {
|
|
39
|
+
/** File or folder name (required unless `tab` is set) */
|
|
40
|
+
name?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Explicit type. If omitted the plugin auto-detects:
|
|
43
|
+
* - has `children` → folder
|
|
44
|
+
* - otherwise → file
|
|
45
|
+
*/
|
|
46
|
+
type?: 'file' | 'folder';
|
|
47
|
+
/** Nested children (makes this node a folder) */
|
|
48
|
+
children?: YamlInputItem[];
|
|
49
|
+
/** Short description badge (shown next to name) */
|
|
50
|
+
description?: string;
|
|
51
|
+
/** Right-aligned note/label at the end of the row */
|
|
52
|
+
note?: string;
|
|
53
|
+
/** Highlight the row */
|
|
54
|
+
highlight?: boolean;
|
|
55
|
+
/** Custom icon (emoji or Iconify class, e.g. "🔥") */
|
|
56
|
+
icon?: string;
|
|
57
|
+
/** Initial expanded state for folders */
|
|
58
|
+
open?: boolean;
|
|
59
|
+
/** Lock this folder — user cannot toggle it */
|
|
60
|
+
locked?: boolean;
|
|
61
|
+
/** URL link — clicking the name opens this URL */
|
|
62
|
+
href?: string;
|
|
63
|
+
/** Preview text shown in tooltip on hover */
|
|
64
|
+
preview?: string;
|
|
65
|
+
/** Diff status: 'added' | 'removed' | 'deleted' | 'modified' ('deleted' is alias for 'removed') */
|
|
66
|
+
status?: 'added' | 'removed' | 'deleted' | 'modified';
|
|
67
|
+
/** Tab name — root-level items with this field are grouped into switchable tabs */
|
|
68
|
+
tab?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Plugin configuration for VitePress.
|
|
72
|
+
*/
|
|
73
|
+
export interface PluginConfig {
|
|
74
|
+
/**
|
|
75
|
+
* Code block language identifiers that trigger the tree rendering.
|
|
76
|
+
* @default ['folder-tree', 'file-tree', 'tree']
|
|
77
|
+
*/
|
|
78
|
+
languages?: string[];
|
|
79
|
+
/**
|
|
80
|
+
* Default expanded state for all folders.
|
|
81
|
+
* Individual nodes can override with `open`.
|
|
82
|
+
* @default true
|
|
83
|
+
*/
|
|
84
|
+
defaultOpen?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Show the Expand all / Collapse all toolbar.
|
|
87
|
+
* @default true
|
|
88
|
+
*/
|
|
89
|
+
showToolbar?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Show auto-generated file/folder count badges on folders.
|
|
92
|
+
* Manual `description` fields are always shown regardless.
|
|
93
|
+
* @default true
|
|
94
|
+
*/
|
|
95
|
+
showBadges?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Allow interactive expand/collapse.
|
|
98
|
+
* When false the tree is rendered as a static snapshot (no clicking).
|
|
99
|
+
* Individual nodes can still use `locked` to lock selectively.
|
|
100
|
+
* @default true
|
|
101
|
+
*/
|
|
102
|
+
interactive?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Root directory for resolving `from:` paths.
|
|
105
|
+
* @default process.cwd()
|
|
106
|
+
*/
|
|
107
|
+
root?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Custom icon mapping: keys are full filenames or extensions.
|
|
110
|
+
* Values are emoji or Iconify class names.
|
|
111
|
+
* Example: { 'vite.config.ts': '⚡', 'ts': 'logos:typescript-icon' }
|
|
112
|
+
*/
|
|
113
|
+
iconMap?: Record<string, string>;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IACtD,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAID;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,eAAe,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;IACtD,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PluginConfig, TreeNode } from './config';
|
|
2
|
+
export interface ValidationResult {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
errors: string[];
|
|
5
|
+
warnings: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Validate a raw YAML string before full parsing.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateTreeInput(raw: string): ValidationResult;
|
|
11
|
+
export interface ParsedTreeData {
|
|
12
|
+
tree: TreeNode[];
|
|
13
|
+
options: Record<string, boolean>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse YAML content.
|
|
17
|
+
* Supports two formats:
|
|
18
|
+
* 1. Root is an array → plain tree, no per-block options
|
|
19
|
+
* 2. Root is an object with `tree` (array) and optional `options` → per-block options
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseTree(raw: string): ParsedTreeData;
|
|
22
|
+
/**
|
|
23
|
+
* Detect if text looks like an ASCII tree (├── └── │ characters).
|
|
24
|
+
*/
|
|
25
|
+
export declare function isAsciiTree(text: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Parse an ASCII tree (├── └── format) into TreeNode[].
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseAsciiTree(text: string): TreeNode[];
|
|
30
|
+
/**
|
|
31
|
+
* Recursively scan a directory and build a TreeNode[] array.
|
|
32
|
+
*/
|
|
33
|
+
export declare function scanDirectory(dirPath: string, maxDepth: number, excludes?: string[], includes?: string[]): TreeNode[];
|
|
34
|
+
export declare const FolderTreeMarkdown: (md: any, pluginOptions?: PluginConfig) => void;
|
|
35
|
+
//# sourceMappingURL=folder-tree-markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-tree-markdown.d.ts","sourceRoot":"","sources":["../src/folder-tree-markdown.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAkC,MAAM,UAAU,CAAC;AAIvF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAYD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAuI/D;AAID,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAiBrD;AA+CD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAejD;AAgBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA8EvD;AAmBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAM,EAAO,EACvB,QAAQ,GAAE,MAAM,EAAO,GACtB,QAAQ,EAAE,CAqCZ;AAMD,eAAO,MAAM,kBAAkB,GAAI,IAAI,GAAG,EAAE,gBAAe,YAAiB,SAyJ3E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-tree-plugin.d.ts","sourceRoot":"","sources":["../src/folder-tree-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAO7C,wBAAgB,gBAAgB,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,CA2B/E"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { UserConfig } from 'vitepress';
|
|
2
|
+
import { PluginConfig } from './config';
|
|
3
|
+
export { FolderTreeMarkdown } from './folder-tree-markdown';
|
|
4
|
+
export { FolderTreePlugin } from './folder-tree-plugin';
|
|
5
|
+
export { parseTree, validateTreeInput, parseAsciiTree, isAsciiTree, scanDirectory } from './folder-tree-markdown';
|
|
6
|
+
export type { ValidationResult, ParsedTreeData } from './folder-tree-markdown';
|
|
7
|
+
export * from './config';
|
|
8
|
+
export { UserConfig };
|
|
9
|
+
declare module 'vitepress' {
|
|
10
|
+
interface UserConfig {
|
|
11
|
+
folderTree?: PluginConfig;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export declare const withFolderTree: (config: UserConfig) => UserConfig<any>;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClH,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC/E,cAAc,UAAU,CAAC;AAEzB,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,OAAO,QAAQ,WAAW,CAAC;IACzB,UAAU,UAAU;QAClB,UAAU,CAAC,EAAE,YAAY,CAAC;KAC3B;CACF;AAED,eAAO,MAAM,cAAc,GAAI,QAAQ,UAAU,oBA2BhD,CAAC"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import S from "yaml";
|
|
2
|
+
import * as j from "fs";
|
|
3
|
+
import * as F from "path";
|
|
4
|
+
import { dirname as z, resolve as B } from "path";
|
|
5
|
+
import { fileURLToPath as O } from "url";
|
|
6
|
+
const V = /* @__PURE__ */ new Set([
|
|
7
|
+
"name",
|
|
8
|
+
"type",
|
|
9
|
+
"children",
|
|
10
|
+
"description",
|
|
11
|
+
"note",
|
|
12
|
+
"highlight",
|
|
13
|
+
"icon",
|
|
14
|
+
"open",
|
|
15
|
+
"locked",
|
|
16
|
+
"href",
|
|
17
|
+
"status",
|
|
18
|
+
"preview",
|
|
19
|
+
"tab"
|
|
20
|
+
]), N = /* @__PURE__ */ new Set(["defaultOpen", "showToolbar", "showBadges", "interactive"]), W = /* @__PURE__ */ new Set(["added", "removed", "deleted", "modified"]);
|
|
21
|
+
function Y(t) {
|
|
22
|
+
const e = [], r = [], n = t.trim();
|
|
23
|
+
if (!n)
|
|
24
|
+
return e.push("Tree is empty — add at least one item."), { valid: !1, errors: e, warnings: r };
|
|
25
|
+
let l;
|
|
26
|
+
try {
|
|
27
|
+
l = S.parse(n);
|
|
28
|
+
} catch (o) {
|
|
29
|
+
return e.push(`YAML syntax error: ${o.message ?? "unknown"}`), { valid: !1, errors: e, warnings: r };
|
|
30
|
+
}
|
|
31
|
+
let f;
|
|
32
|
+
if (l && typeof l == "object" && !Array.isArray(l)) {
|
|
33
|
+
const o = l;
|
|
34
|
+
if (Array.isArray(o.tree)) {
|
|
35
|
+
if (o.options !== void 0)
|
|
36
|
+
if (typeof o.options != "object" || o.options === null || Array.isArray(o.options))
|
|
37
|
+
r.push('"options" should be an object with boolean values.');
|
|
38
|
+
else
|
|
39
|
+
for (const [c, p] of Object.entries(o.options))
|
|
40
|
+
N.has(c) || r.push(`options: unknown key "${c}" — known options: ${[...N].join(", ")}.`), typeof p != "boolean" && r.push(`options.${c}: should be true/false.`);
|
|
41
|
+
f = o.tree;
|
|
42
|
+
} else
|
|
43
|
+
return e.push('Root must be a YAML array (start lines with "- ") or an object with "tree" array.'), { valid: !1, errors: e, warnings: r };
|
|
44
|
+
} else
|
|
45
|
+
f = l;
|
|
46
|
+
if (!Array.isArray(f))
|
|
47
|
+
return e.push('Root must be a YAML array (start lines with "- ") or an object with "tree" array.'), { valid: !1, errors: e, warnings: r };
|
|
48
|
+
if (f.length === 0)
|
|
49
|
+
return e.push("Tree is empty — add at least one item."), { valid: !1, errors: e, warnings: r };
|
|
50
|
+
const a = (o, c) => {
|
|
51
|
+
for (let p = 0; p < o.length; p++) {
|
|
52
|
+
const h = o[p], u = `${c}[${p}]`;
|
|
53
|
+
if (typeof h == "string") {
|
|
54
|
+
h.trim() || e.push(`${u}: empty string — every item needs a name.`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (h == null) {
|
|
58
|
+
e.push(`${u}: null/empty item.`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (typeof h != "object" || Array.isArray(h)) {
|
|
62
|
+
e.push(`${u}: must be a string or an object with "name".`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const s = h, g = s.name && typeof s.name == "string" && s.name.trim(), i = s.tab && typeof s.tab == "string" && s.tab.trim();
|
|
66
|
+
if (!g && !i) {
|
|
67
|
+
e.push(`${u}: missing or empty "name" field.`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
s.type !== void 0 && s.type !== "file" && s.type !== "folder" && e.push(`${u} ("${s.name || s.tab}"): "type" must be "file" or "folder", got "${s.type}".`), s.children !== void 0 && (Array.isArray(s.children) ? (s.type === "file" && r.push(`${u} ("${s.name || s.tab}"): type is "file" but has "children" — will be treated as folder.`), a(s.children, `${u}.children`)) : e.push(`${u} ("${s.name || s.tab}"): "children" must be an array.`));
|
|
71
|
+
for (const d of ["open", "highlight", "locked"])
|
|
72
|
+
s[d] !== void 0 && typeof s[d] != "boolean" && r.push(`${u} ("${s.name || s.tab}"): "${d}" should be true/false.`);
|
|
73
|
+
for (const d of ["description", "note", "icon", "href", "preview", "tab"])
|
|
74
|
+
s[d] !== void 0 && typeof s[d] != "string" && r.push(`${u} ("${s.name || s.tab}"): "${d}" should be a string.`);
|
|
75
|
+
s.status !== void 0 && (typeof s.status != "string" || !W.has(s.status)) && r.push(`${u} ("${s.name || s.tab}"): "status" should be "added", "removed", "deleted", or "modified".`);
|
|
76
|
+
for (const d of Object.keys(s))
|
|
77
|
+
V.has(d) || r.push(`${u} ("${s.name || s.tab}"): unknown key "${d}" — will be ignored.`);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
return a(f, "root"), { valid: e.length === 0, errors: e, warnings: r };
|
|
81
|
+
}
|
|
82
|
+
function P(t) {
|
|
83
|
+
const e = S.parse(t.trim());
|
|
84
|
+
if (e && typeof e == "object" && !Array.isArray(e) && Array.isArray(e.tree)) {
|
|
85
|
+
const r = {};
|
|
86
|
+
if (e.options && typeof e.options == "object")
|
|
87
|
+
for (const [n, l] of Object.entries(e.options))
|
|
88
|
+
typeof l == "boolean" && (r[n] = l);
|
|
89
|
+
return { tree: E(e.tree), options: r };
|
|
90
|
+
}
|
|
91
|
+
return Array.isArray(e) ? { tree: E(e), options: {} } : { tree: [], options: {} };
|
|
92
|
+
}
|
|
93
|
+
function E(t) {
|
|
94
|
+
const e = [];
|
|
95
|
+
for (const r of t) {
|
|
96
|
+
if (r == null) continue;
|
|
97
|
+
if (typeof r == "string") {
|
|
98
|
+
const c = r.trim();
|
|
99
|
+
if (!c) continue;
|
|
100
|
+
e.push({ name: c, isFolder: !1 });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const n = r, l = n.name || n.tab || "";
|
|
104
|
+
if (!l) continue;
|
|
105
|
+
const f = Array.isArray(n.children) && n.children.length > 0, a = n.type === "folder" || f, o = { name: l, isFolder: a };
|
|
106
|
+
a && (o.children = f ? E(n.children) : []), n.description && (o.description = n.description), n.note && (o.note = n.note), n.highlight && (o.highlight = !0), n.icon && (o.icon = n.icon), n.open !== void 0 && (o.open = n.open), n.locked && (o.locked = !0), n.href && (o.href = n.href), n.status && (o.status = n.status === "deleted" ? "removed" : n.status), n.preview && (o.preview = n.preview), n.tab && (o.tab = n.tab), e.push(o);
|
|
107
|
+
}
|
|
108
|
+
return e;
|
|
109
|
+
}
|
|
110
|
+
function J(t) {
|
|
111
|
+
const e = t.trim(), r = e.split(`
|
|
112
|
+
`)[0]?.trim() || "";
|
|
113
|
+
return r.startsWith("- ") || r.startsWith("options:") || r.startsWith("tree:") || r.startsWith("url:") || r.startsWith("name:") ? !1 : /[├└]──/.test(e);
|
|
114
|
+
}
|
|
115
|
+
function G(t) {
|
|
116
|
+
for (let e = 0; e < t.length; e++) {
|
|
117
|
+
const r = t[e];
|
|
118
|
+
if (r === "├" || r === "└") return e;
|
|
119
|
+
}
|
|
120
|
+
return -1;
|
|
121
|
+
}
|
|
122
|
+
function K(t) {
|
|
123
|
+
const e = t.split(`
|
|
124
|
+
`).filter((a) => a.trim().length > 0);
|
|
125
|
+
if (e.length === 0) return [];
|
|
126
|
+
const r = [];
|
|
127
|
+
for (const a of e) {
|
|
128
|
+
const o = a.trimEnd(), c = G(o);
|
|
129
|
+
if (c >= 0) {
|
|
130
|
+
const p = Math.round(c / 4), h = o.indexOf("── ", c);
|
|
131
|
+
if (h < 0) continue;
|
|
132
|
+
let u = o.slice(h + 3).trim();
|
|
133
|
+
if (!u) continue;
|
|
134
|
+
const s = u.endsWith("/");
|
|
135
|
+
if (s && (u = u.slice(0, -1)), !u || u === ".") continue;
|
|
136
|
+
r.push({ name: u, depth: p, hintFolder: s });
|
|
137
|
+
} else {
|
|
138
|
+
let p = o.trim();
|
|
139
|
+
if (p === "." || p === "./") continue;
|
|
140
|
+
const h = p.endsWith("/");
|
|
141
|
+
if (h && (p = p.slice(0, -1)), !p) continue;
|
|
142
|
+
r.push({ name: p, depth: -1, hintFolder: h });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (r.length === 0) return [];
|
|
146
|
+
const n = { name: "", isFolder: !0, children: [], _depth: -2 }, l = [n];
|
|
147
|
+
for (const a of r) {
|
|
148
|
+
const o = {
|
|
149
|
+
name: a.name,
|
|
150
|
+
isFolder: a.hintFolder,
|
|
151
|
+
children: [],
|
|
152
|
+
_depth: a.depth
|
|
153
|
+
};
|
|
154
|
+
for (; l.length > 1 && l[l.length - 1]._depth >= a.depth; )
|
|
155
|
+
l.pop();
|
|
156
|
+
l[l.length - 1].children.push(o), l.push(o);
|
|
157
|
+
}
|
|
158
|
+
function f(a) {
|
|
159
|
+
return a.map((o) => {
|
|
160
|
+
const c = o.isFolder || o.children.length > 0, p = { name: o.name, isFolder: c };
|
|
161
|
+
return c && o.children.length > 0 ? p.children = f(o.children) : c && (p.children = []), p;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return f(n.children);
|
|
165
|
+
}
|
|
166
|
+
const X = [
|
|
167
|
+
"node_modules",
|
|
168
|
+
".git",
|
|
169
|
+
".DS_Store",
|
|
170
|
+
"dist",
|
|
171
|
+
".cache",
|
|
172
|
+
".vitepress",
|
|
173
|
+
"__pycache__",
|
|
174
|
+
".next",
|
|
175
|
+
".nuxt",
|
|
176
|
+
"Thumbs.db",
|
|
177
|
+
".idea",
|
|
178
|
+
".vscode",
|
|
179
|
+
".hg",
|
|
180
|
+
".svn",
|
|
181
|
+
".turbo",
|
|
182
|
+
".output",
|
|
183
|
+
"coverage"
|
|
184
|
+
];
|
|
185
|
+
function D(t, e) {
|
|
186
|
+
const r = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
187
|
+
return new RegExp(`^${r}$`).test(t);
|
|
188
|
+
}
|
|
189
|
+
function L(t, e, r = [], n = []) {
|
|
190
|
+
if (e <= 0) return [];
|
|
191
|
+
let l;
|
|
192
|
+
try {
|
|
193
|
+
l = j.readdirSync(t, { withFileTypes: !0 });
|
|
194
|
+
} catch {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
l.sort((a, o) => a.isDirectory() !== o.isDirectory() ? a.isDirectory() ? -1 : 1 : a.name.localeCompare(o.name));
|
|
198
|
+
const f = [];
|
|
199
|
+
for (const a of l) {
|
|
200
|
+
const o = a.name;
|
|
201
|
+
if (!r.some((c) => D(o, c))) {
|
|
202
|
+
if (a.isDirectory()) {
|
|
203
|
+
const c = L(
|
|
204
|
+
F.join(t, o),
|
|
205
|
+
e - 1,
|
|
206
|
+
r,
|
|
207
|
+
n
|
|
208
|
+
);
|
|
209
|
+
if (n.length > 0 && c.length === 0) continue;
|
|
210
|
+
f.push({ name: o, isFolder: !0, children: c });
|
|
211
|
+
} else if (a.isFile()) {
|
|
212
|
+
if (n.length > 0 && !n.some((c) => D(o, c))) continue;
|
|
213
|
+
f.push({ name: o, isFolder: !1 });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return f;
|
|
218
|
+
}
|
|
219
|
+
const q = ["folder-tree", "file-tree", "tree"], Q = (t, e = {}) => {
|
|
220
|
+
const r = e.languages || q, n = e.iconMap && Object.keys(e.iconMap).length ? ` icon-map="${encodeURIComponent(JSON.stringify(e.iconMap))}"` : "", l = t.renderer.rules.fence?.bind(t.renderer.rules) || ((f, a, o, c, p) => p.renderToken(f, a, o));
|
|
221
|
+
t.renderer.rules.fence = (f, a, o, c, p) => {
|
|
222
|
+
const h = f[a], u = h.info.trim().toLowerCase();
|
|
223
|
+
if (!r.includes(u))
|
|
224
|
+
return l(f, a, o, c, p);
|
|
225
|
+
const s = h.content;
|
|
226
|
+
try {
|
|
227
|
+
const i = S.parse(s.trim());
|
|
228
|
+
if (i && typeof i == "object" && !Array.isArray(i)) {
|
|
229
|
+
const d = {};
|
|
230
|
+
if (i.options && typeof i.options == "object")
|
|
231
|
+
for (const [m, x] of Object.entries(i.options))
|
|
232
|
+
typeof x == "boolean" && (d[m] = x);
|
|
233
|
+
const y = d.defaultOpen ?? e.defaultOpen !== !1, b = d.showToolbar ?? e.showToolbar !== !1, v = d.showBadges ?? e.showBadges !== !1, $ = d.interactive ?? e.interactive !== !1, w = "padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5";
|
|
234
|
+
if (i.url) {
|
|
235
|
+
const m = String(i.url);
|
|
236
|
+
return `<VpFolderTree configUrl="${t.utils.escapeHtml(m)}" :default-open="${y}" :show-toolbar="${b}" :show-badges="${v}" :interactive="${$}"${n} />`;
|
|
237
|
+
}
|
|
238
|
+
if (i.from) {
|
|
239
|
+
const m = String(i.from), x = typeof i.depth == "number" && i.depth > 0 ? i.depth : 10, k = Array.isArray(i.exclude) ? i.exclude.map(String) : [], M = Array.isArray(i.include) ? i.include.map(String) : [], C = [...X, ...k], H = i.showRoot !== !1, R = typeof i.name == "string" ? i.name : void 0, U = e.root || process.cwd(), T = F.isAbsolute(m) ? m : F.resolve(U, m);
|
|
240
|
+
if (!j.existsSync(T))
|
|
241
|
+
return `<div style="${w}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Directory not found: ${t.utils.escapeHtml(m)}</p></div>`;
|
|
242
|
+
let _;
|
|
243
|
+
try {
|
|
244
|
+
_ = j.statSync(T);
|
|
245
|
+
} catch {
|
|
246
|
+
return `<div style="${w}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Cannot access: ${t.utils.escapeHtml(m)}</p></div>`;
|
|
247
|
+
}
|
|
248
|
+
if (!_.isDirectory())
|
|
249
|
+
return `<div style="${w}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Not a directory: ${t.utils.escapeHtml(m)}</p></div>`;
|
|
250
|
+
const I = L(T, x, C, M);
|
|
251
|
+
let A;
|
|
252
|
+
return H ? A = [{ name: R || F.basename(T), isFolder: !0, children: I }] : A = I, A.length === 0 ? `<div style="${w}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">No files found in: ${t.utils.escapeHtml(m)}</p></div>` : `<VpFolderTree data="${encodeURIComponent(JSON.stringify(A))}" :default-open="${y}" :show-toolbar="${b}" :show-badges="${v}" :interactive="${$}"${n} />`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
if (J(s))
|
|
258
|
+
try {
|
|
259
|
+
const i = K(s);
|
|
260
|
+
if (i.length === 0)
|
|
261
|
+
return '<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Could not parse any nodes from the ASCII tree.</p></div>';
|
|
262
|
+
const d = encodeURIComponent(JSON.stringify(i)), y = e.defaultOpen !== !1, b = e.showToolbar !== !1, v = e.showBadges !== !1, $ = e.interactive !== !1;
|
|
263
|
+
return `<VpFolderTree data="${d}" :default-open="${y}" :show-toolbar="${b}" :show-badges="${v}" :interactive="${$}"${n} />`;
|
|
264
|
+
} catch (i) {
|
|
265
|
+
const d = i.message || "Unknown error";
|
|
266
|
+
return `<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">${t.utils.escapeHtml(d)}</p></div>`;
|
|
267
|
+
}
|
|
268
|
+
const g = Y(s);
|
|
269
|
+
if (!g.valid) {
|
|
270
|
+
const i = g.errors.map((y) => `<li>${t.utils.escapeHtml(y)}</li>`).join(""), d = g.warnings.length ? `<ul style="margin:4px 0 0;padding-left:1.2em;color:#b45309">${g.warnings.map((y) => `<li>${t.utils.escapeHtml(y)}</li>`).join("")}</ul>` : "";
|
|
271
|
+
return `<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><ul style="margin:4px 0 0;padding-left:1.2em;color:#dc2626">${i}</ul>${d}</div>`;
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const { tree: i, options: d } = P(s);
|
|
275
|
+
if (i.length === 0)
|
|
276
|
+
return '<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Could not parse any nodes from the provided YAML.</p></div>';
|
|
277
|
+
let y = "";
|
|
278
|
+
g.warnings.length && (y = `<div style="padding:8px 12px;margin:0 0 4px;border-radius:6px;border:1px solid #fde68a;background:#fffbeb;font-size:12px;line-height:1.4;color:#92400e"><strong>Warning:</strong><ul style="margin:2px 0 0;padding-left:1.2em">${g.warnings.map((k) => `<li>${t.utils.escapeHtml(k)}</li>`).join("")}</ul></div>`);
|
|
279
|
+
const b = encodeURIComponent(JSON.stringify(i)), v = d.defaultOpen ?? e.defaultOpen !== !1, $ = d.showToolbar ?? e.showToolbar !== !1, w = d.showBadges ?? e.showBadges !== !1, m = d.interactive ?? e.interactive !== !1;
|
|
280
|
+
return `${y}<VpFolderTree data="${b}" :default-open="${v}" :show-toolbar="${$}" :show-badges="${w}" :interactive="${m}"${n} />`;
|
|
281
|
+
} catch (i) {
|
|
282
|
+
const d = i.message || "Unknown error";
|
|
283
|
+
return `<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">${t.utils.escapeHtml(d)}</p></div>`;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}, Z = z(O(import.meta.url)), ee = B(Z, "VpFolderTree.vue");
|
|
287
|
+
function te(t) {
|
|
288
|
+
const e = "vitepress-plugin-folder-tree/VpFolderTree.vue";
|
|
289
|
+
return {
|
|
290
|
+
name: "vitepress-plugin-folder-tree",
|
|
291
|
+
enforce: "pre",
|
|
292
|
+
transform(r, n) {
|
|
293
|
+
if (n.includes("vitepress/dist/client/app/index.js")) {
|
|
294
|
+
r = `
|
|
295
|
+
import VpFolderTree from '${e}';
|
|
296
|
+
` + r;
|
|
297
|
+
const l = r.split(`
|
|
298
|
+
`), f = l.findIndex((a) => a.includes("app.component"));
|
|
299
|
+
return l.splice(f, 0, ' app.component("VpFolderTree", VpFolderTree);'), r = l.join(`
|
|
300
|
+
`), { code: r, map: null };
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
resolveId(r) {
|
|
304
|
+
if (r === e)
|
|
305
|
+
return ee;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
const ae = (t) => {
|
|
310
|
+
t.markdown || (t.markdown = {});
|
|
311
|
+
const e = t.markdown.config || (() => {
|
|
312
|
+
});
|
|
313
|
+
return t.markdown.config = (...r) => {
|
|
314
|
+
Q(...r, t.folderTree), e(...r);
|
|
315
|
+
}, t.vite || (t.vite = {}), t.vite.plugins || (t.vite.plugins = []), t.vite.plugins.push(te(t.folderTree)), t.vite.optimizeDeps || (t.vite.optimizeDeps = {}), t.vite.optimizeDeps.include || (t.vite.optimizeDeps.include = []), t.vite.optimizeDeps.include.push("yaml"), t.vite.ssr || (t.vite.ssr = {}), t.vite.ssr.noExternal || (t.vite.ssr.noExternal = []), Array.isArray(t.vite.ssr.noExternal) && t.vite.ssr.noExternal.push("vitepress-plugin-folder-tree"), t;
|
|
316
|
+
};
|
|
317
|
+
export {
|
|
318
|
+
Q as FolderTreeMarkdown,
|
|
319
|
+
te as FolderTreePlugin,
|
|
320
|
+
J as isAsciiTree,
|
|
321
|
+
K as parseAsciiTree,
|
|
322
|
+
P as parseTree,
|
|
323
|
+
L as scanDirectory,
|
|
324
|
+
Y as validateTreeInput,
|
|
325
|
+
ae as withFolderTree
|
|
326
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
(function(h,v){typeof exports=="object"&&typeof module<"u"?v(exports,require("yaml"),require("fs"),require("path"),require("url")):typeof define=="function"&&define.amd?define(["exports","yaml","fs","path","url"],v):(h=typeof globalThis<"u"?globalThis:h||self,v(h.FolderTreePlugin={},h.YAML,h.fs,h.path,h.url))})(this,(function(h,v,W,S,q){"use strict";var _=typeof document<"u"?document.currentScript:null;function R(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const r in t)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,n.get?n:{enumerable:!0,get:()=>t[r]})}}return e.default=t,Object.freeze(e)}const E=R(W),A=R(S),Y=new Set(["name","type","children","description","note","highlight","icon","open","locked","href","status","preview","tab"]),U=new Set(["defaultOpen","showToolbar","showBadges","interactive"]),J=new Set(["added","removed","deleted","modified"]);function C(t){const e=[],r=[],n=t.trim();if(!n)return e.push("Tree is empty — add at least one item."),{valid:!1,errors:e,warnings:r};let l;try{l=v.parse(n)}catch(o){return e.push(`YAML syntax error: ${o.message??"unknown"}`),{valid:!1,errors:e,warnings:r}}let f;if(l&&typeof l=="object"&&!Array.isArray(l)){const o=l;if(Array.isArray(o.tree)){if(o.options!==void 0)if(typeof o.options!="object"||o.options===null||Array.isArray(o.options))r.push('"options" should be an object with boolean values.');else for(const[c,p]of Object.entries(o.options))U.has(c)||r.push(`options: unknown key "${c}" — known options: ${[...U].join(", ")}.`),typeof p!="boolean"&&r.push(`options.${c}: should be true/false.`);f=o.tree}else return e.push('Root must be a YAML array (start lines with "- ") or an object with "tree" array.'),{valid:!1,errors:e,warnings:r}}else f=l;if(!Array.isArray(f))return e.push('Root must be a YAML array (start lines with "- ") or an object with "tree" array.'),{valid:!1,errors:e,warnings:r};if(f.length===0)return e.push("Tree is empty — add at least one item."),{valid:!1,errors:e,warnings:r};const a=(o,c)=>{for(let p=0;p<o.length;p++){const m=o[p],u=`${c}[${p}]`;if(typeof m=="string"){m.trim()||e.push(`${u}: empty string — every item needs a name.`);continue}if(m==null){e.push(`${u}: null/empty item.`);continue}if(typeof m!="object"||Array.isArray(m)){e.push(`${u}: must be a string or an object with "name".`);continue}const i=m,b=i.name&&typeof i.name=="string"&&i.name.trim(),s=i.tab&&typeof i.tab=="string"&&i.tab.trim();if(!b&&!s){e.push(`${u}: missing or empty "name" field.`);continue}i.type!==void 0&&i.type!=="file"&&i.type!=="folder"&&e.push(`${u} ("${i.name||i.tab}"): "type" must be "file" or "folder", got "${i.type}".`),i.children!==void 0&&(Array.isArray(i.children)?(i.type==="file"&&r.push(`${u} ("${i.name||i.tab}"): type is "file" but has "children" — will be treated as folder.`),a(i.children,`${u}.children`)):e.push(`${u} ("${i.name||i.tab}"): "children" must be an array.`));for(const d of["open","highlight","locked"])i[d]!==void 0&&typeof i[d]!="boolean"&&r.push(`${u} ("${i.name||i.tab}"): "${d}" should be true/false.`);for(const d of["description","note","icon","href","preview","tab"])i[d]!==void 0&&typeof i[d]!="string"&&r.push(`${u} ("${i.name||i.tab}"): "${d}" should be a string.`);i.status!==void 0&&(typeof i.status!="string"||!J.has(i.status))&&r.push(`${u} ("${i.name||i.tab}"): "status" should be "added", "removed", "deleted", or "modified".`);for(const d of Object.keys(i))Y.has(d)||r.push(`${u} ("${i.name||i.tab}"): unknown key "${d}" — will be ignored.`)}};return a(f,"root"),{valid:e.length===0,errors:e,warnings:r}}function O(t){const e=v.parse(t.trim());if(e&&typeof e=="object"&&!Array.isArray(e)&&Array.isArray(e.tree)){const r={};if(e.options&&typeof e.options=="object")for(const[n,l]of Object.entries(e.options))typeof l=="boolean"&&(r[n]=l);return{tree:I(e.tree),options:r}}return Array.isArray(e)?{tree:I(e),options:{}}:{tree:[],options:{}}}function I(t){const e=[];for(const r of t){if(r==null)continue;if(typeof r=="string"){const c=r.trim();if(!c)continue;e.push({name:c,isFolder:!1});continue}const n=r,l=n.name||n.tab||"";if(!l)continue;const f=Array.isArray(n.children)&&n.children.length>0,a=n.type==="folder"||f,o={name:l,isFolder:a};a&&(o.children=f?I(n.children):[]),n.description&&(o.description=n.description),n.note&&(o.note=n.note),n.highlight&&(o.highlight=!0),n.icon&&(o.icon=n.icon),n.open!==void 0&&(o.open=n.open),n.locked&&(o.locked=!0),n.href&&(o.href=n.href),n.status&&(o.status=n.status==="deleted"?"removed":n.status),n.preview&&(o.preview=n.preview),n.tab&&(o.tab=n.tab),e.push(o)}return e}function M(t){const e=t.trim(),r=e.split(`
|
|
2
|
+
`)[0]?.trim()||"";return r.startsWith("- ")||r.startsWith("options:")||r.startsWith("tree:")||r.startsWith("url:")||r.startsWith("name:")?!1:/[├└]──/.test(e)}function G(t){for(let e=0;e<t.length;e++){const r=t[e];if(r==="├"||r==="└")return e}return-1}function L(t){const e=t.split(`
|
|
3
|
+
`).filter(a=>a.trim().length>0);if(e.length===0)return[];const r=[];for(const a of e){const o=a.trimEnd(),c=G(o);if(c>=0){const p=Math.round(c/4),m=o.indexOf("── ",c);if(m<0)continue;let u=o.slice(m+3).trim();if(!u)continue;const i=u.endsWith("/");if(i&&(u=u.slice(0,-1)),!u||u===".")continue;r.push({name:u,depth:p,hintFolder:i})}else{let p=o.trim();if(p==="."||p==="./")continue;const m=p.endsWith("/");if(m&&(p=p.slice(0,-1)),!p)continue;r.push({name:p,depth:-1,hintFolder:m})}}if(r.length===0)return[];const n={name:"",isFolder:!0,children:[],_depth:-2},l=[n];for(const a of r){const o={name:a.name,isFolder:a.hintFolder,children:[],_depth:a.depth};for(;l.length>1&&l[l.length-1]._depth>=a.depth;)l.pop();l[l.length-1].children.push(o),l.push(o)}function f(a){return a.map(o=>{const c=o.isFolder||o.children.length>0,p={name:o.name,isFolder:c};return c&&o.children.length>0?p.children=f(o.children):c&&(p.children=[]),p})}return f(n.children)}const K=["node_modules",".git",".DS_Store","dist",".cache",".vitepress","__pycache__",".next",".nuxt","Thumbs.db",".idea",".vscode",".hg",".svn",".turbo",".output","coverage"];function H(t,e){const r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp(`^${r}$`).test(t)}function D(t,e,r=[],n=[]){if(e<=0)return[];let l;try{l=E.readdirSync(t,{withFileTypes:!0})}catch{return[]}l.sort((a,o)=>a.isDirectory()!==o.isDirectory()?a.isDirectory()?-1:1:a.name.localeCompare(o.name));const f=[];for(const a of l){const o=a.name;if(!r.some(c=>H(o,c))){if(a.isDirectory()){const c=D(A.join(t,o),e-1,r,n);if(n.length>0&&c.length===0)continue;f.push({name:o,isFolder:!0,children:c})}else if(a.isFile()){if(n.length>0&&!n.some(c=>H(o,c)))continue;f.push({name:o,isFolder:!1})}}}return f}const X=["folder-tree","file-tree","tree"],z=(t,e={})=>{const r=e.languages||X,n=e.iconMap&&Object.keys(e.iconMap).length?` icon-map="${encodeURIComponent(JSON.stringify(e.iconMap))}"`:"",l=t.renderer.rules.fence?.bind(t.renderer.rules)||((f,a,o,c,p)=>p.renderToken(f,a,o));t.renderer.rules.fence=(f,a,o,c,p)=>{const m=f[a],u=m.info.trim().toLowerCase();if(!r.includes(u))return l(f,a,o,c,p);const i=m.content;try{const s=v.parse(i.trim());if(s&&typeof s=="object"&&!Array.isArray(s)){const d={};if(s.options&&typeof s.options=="object")for(const[y,F]of Object.entries(s.options))typeof F=="boolean"&&(d[y]=F);const g=d.defaultOpen??e.defaultOpen!==!1,$=d.showToolbar??e.showToolbar!==!1,w=d.showBadges??e.showBadges!==!1,T=d.interactive??e.interactive!==!1,x="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5";if(s.url){const y=String(s.url);return`<VpFolderTree configUrl="${t.utils.escapeHtml(y)}" :default-open="${g}" :show-toolbar="${$}" :show-badges="${w}" :interactive="${T}"${n} />`}if(s.from){const y=String(s.from),F=typeof s.depth=="number"&&s.depth>0?s.depth:10,N=Array.isArray(s.exclude)?s.exclude.map(String):[],te=Array.isArray(s.include)?s.include.map(String):[],re=[...K,...N],oe=s.showRoot!==!1,ne=typeof s.name=="string"?s.name:void 0,ie=e.root||process.cwd(),k=A.isAbsolute(y)?y:A.resolve(ie,y);if(!E.existsSync(k))return`<div style="${x}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Directory not found: ${t.utils.escapeHtml(y)}</p></div>`;let P;try{P=E.statSync(k)}catch{return`<div style="${x}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Cannot access: ${t.utils.escapeHtml(y)}</p></div>`}if(!P.isDirectory())return`<div style="${x}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Not a directory: ${t.utils.escapeHtml(y)}</p></div>`;const V=D(k,F,re,te);let j;return oe?j=[{name:ne||A.basename(k),isFolder:!0,children:V}]:j=V,j.length===0?`<div style="${x}"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">No files found in: ${t.utils.escapeHtml(y)}</p></div>`:`<VpFolderTree data="${encodeURIComponent(JSON.stringify(j))}" :default-open="${g}" :show-toolbar="${$}" :show-badges="${w}" :interactive="${T}"${n} />`}}}catch{}if(M(i))try{const s=L(i);if(s.length===0)return'<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Could not parse any nodes from the ASCII tree.</p></div>';const d=encodeURIComponent(JSON.stringify(s)),g=e.defaultOpen!==!1,$=e.showToolbar!==!1,w=e.showBadges!==!1,T=e.interactive!==!1;return`<VpFolderTree data="${d}" :default-open="${g}" :show-toolbar="${$}" :show-badges="${w}" :interactive="${T}"${n} />`}catch(s){const d=s.message||"Unknown error";return`<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">${t.utils.escapeHtml(d)}</p></div>`}const b=C(i);if(!b.valid){const s=b.errors.map(g=>`<li>${t.utils.escapeHtml(g)}</li>`).join(""),d=b.warnings.length?`<ul style="margin:4px 0 0;padding-left:1.2em;color:#b45309">${b.warnings.map(g=>`<li>${t.utils.escapeHtml(g)}</li>`).join("")}</ul>`:"";return`<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><ul style="margin:4px 0 0;padding-left:1.2em;color:#dc2626">${s}</ul>${d}</div>`}try{const{tree:s,options:d}=O(i);if(s.length===0)return'<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">Could not parse any nodes from the provided YAML.</p></div>';let g="";b.warnings.length&&(g=`<div style="padding:8px 12px;margin:0 0 4px;border-radius:6px;border:1px solid #fde68a;background:#fffbeb;font-size:12px;line-height:1.4;color:#92400e"><strong>Warning:</strong><ul style="margin:2px 0 0;padding-left:1.2em">${b.warnings.map(N=>`<li>${t.utils.escapeHtml(N)}</li>`).join("")}</ul></div>`);const $=encodeURIComponent(JSON.stringify(s)),w=d.defaultOpen??e.defaultOpen!==!1,T=d.showToolbar??e.showToolbar!==!1,x=d.showBadges??e.showBadges!==!1,y=d.interactive??e.interactive!==!1;return`${g}<VpFolderTree data="${$}" :default-open="${w}" :show-toolbar="${T}" :show-badges="${x}" :interactive="${y}"${n} />`}catch(s){const d=s.message||"Unknown error";return`<div style="padding:12px 16px;margin:16px 0;border-radius:8px;border:1px solid #fca5a5;background:#fef2f2;font-size:13px;line-height:1.5"><strong style="color:#dc2626">Folder Tree Error</strong><p style="margin:4px 0 0;color:#dc2626">${t.utils.escapeHtml(d)}</p></div>`}}},Q=S.dirname(q.fileURLToPath(typeof document>"u"&&typeof location>"u"?require("url").pathToFileURL(__filename).href:typeof document>"u"?location.href:_&&_.tagName.toUpperCase()==="SCRIPT"&&_.src||new URL("vitepress-plugin-folder-tree.umd.js",document.baseURI).href)),Z=S.resolve(Q,"VpFolderTree.vue");function B(t){const e="vitepress-plugin-folder-tree/VpFolderTree.vue";return{name:"vitepress-plugin-folder-tree",enforce:"pre",transform(r,n){if(n.includes("vitepress/dist/client/app/index.js")){r=`
|
|
4
|
+
import VpFolderTree from '${e}';
|
|
5
|
+
`+r;const l=r.split(`
|
|
6
|
+
`),f=l.findIndex(a=>a.includes("app.component"));return l.splice(f,0,' app.component("VpFolderTree", VpFolderTree);'),r=l.join(`
|
|
7
|
+
`),{code:r,map:null}}},resolveId(r){if(r===e)return Z}}}const ee=t=>{t.markdown||(t.markdown={});const e=t.markdown.config||(()=>{});return t.markdown.config=(...r)=>{z(...r,t.folderTree),e(...r)},t.vite||(t.vite={}),t.vite.plugins||(t.vite.plugins=[]),t.vite.plugins.push(B(t.folderTree)),t.vite.optimizeDeps||(t.vite.optimizeDeps={}),t.vite.optimizeDeps.include||(t.vite.optimizeDeps.include=[]),t.vite.optimizeDeps.include.push("yaml"),t.vite.ssr||(t.vite.ssr={}),t.vite.ssr.noExternal||(t.vite.ssr.noExternal=[]),Array.isArray(t.vite.ssr.noExternal)&&t.vite.ssr.noExternal.push("vitepress-plugin-folder-tree"),t};h.FolderTreeMarkdown=z,h.FolderTreePlugin=B,h.isAsciiTree=M,h.parseAsciiTree=L,h.parseTree=O,h.scanDirectory=D,h.validateTreeInput=C,h.withFolderTree=ee,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitepress-plugin-folder-tree",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Folder tree plugin for VitePress - render beautiful file tree diagrams from markdown code blocks",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"main": "./dist/vitepress-plugin-folder-tree.umd.js",
|
|
8
|
+
"module": "./dist/vitepress-plugin-folder-tree.es.mjs",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/vitepress-plugin-folder-tree.es.mjs",
|
|
13
|
+
"require": "./dist/vitepress-plugin-folder-tree.umd.js"
|
|
14
|
+
},
|
|
15
|
+
"./VpFolderTree.vue": "./dist/VpFolderTree.vue"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"yaml": "^2.8.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.0.0",
|
|
25
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
26
|
+
"typescript": "^5.9.0",
|
|
27
|
+
"vite": "^7.3.1",
|
|
28
|
+
"vite-plugin-dts": "^4.5.0",
|
|
29
|
+
"vite-plugin-static-copy": "^3.0.0",
|
|
30
|
+
"vitepress": "^1.0.0",
|
|
31
|
+
"vue": "^3.5.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"vitepress": "^1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"vitepress",
|
|
38
|
+
"folder-tree",
|
|
39
|
+
"file-tree",
|
|
40
|
+
"tree",
|
|
41
|
+
"markdown",
|
|
42
|
+
"directory-structure",
|
|
43
|
+
"vue"
|
|
44
|
+
],
|
|
45
|
+
"author": "s00d",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/s00d/vitepress-plugin-folder-tree.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/s00d/vitepress-plugin-folder-tree",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/s00d/vitepress-plugin-folder-tree/issues"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "vite build",
|
|
57
|
+
"dev": "pnpm build && pnpm -C example docs:dev",
|
|
58
|
+
"docs:build": "pnpm build && pnpm -C example docs:build",
|
|
59
|
+
"docs:preview": "pnpm -C example docs:preview"
|
|
60
|
+
}
|
|
61
|
+
}
|