Skip to content

Commit 6e113a8

Browse files
committed
feat(new tool): Folder Tree Diagram
Fix CorentinTh#938
1 parent 9eac9cb commit 6e113a8

13 files changed

+724
-6
lines changed

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ declare module '@vue/runtime-core' {
7878
Encryption: typeof import('./src/tools/encryption/encryption.vue')['default']
7979
EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default']
8080
FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default']
81+
FolderStructureDiagram: typeof import('./src/tools/folder-structure-diagram/folder-structure-diagram.vue')['default']
8182
FormatTransformer: typeof import('./src/components/FormatTransformer.vue')['default']
8283
GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default']
8384
'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default']
@@ -159,6 +160,7 @@ declare module '@vue/runtime-core' {
159160
RouterLink: typeof import('vue-router')['RouterLink']
160161
RouterView: typeof import('vue-router')['RouterView']
161162
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
163+
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
162164
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
163165
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
164166
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
"@tiptap/starter-kit": "2.1.6",
4343
"@tiptap/vue-3": "2.0.3",
4444
"@types/figlet": "^1.5.8",
45+
"@types/lodash.defaultsdeep": "^4.6.9",
46+
"@types/lodash.flattendeep": "^4.4.9",
47+
"@types/lodash.last": "^3.0.9",
4548
"@vicons/material": "^0.12.0",
4649
"@vicons/tabler": "^0.12.0",
4750
"@vueuse/core": "^10.3.0",
@@ -68,6 +71,9 @@
6871
"jwt-decode": "^3.1.2",
6972
"libphonenumber-js": "^1.10.28",
7073
"lodash": "^4.17.21",
74+
"lodash.defaultsdeep": "^4.6.1",
75+
"lodash.flattendeep": "^4.4.0",
76+
"lodash.last": "^3.0.0",
7177
"marked": "^10.0.0",
7278
"mathjs": "^11.9.1",
7379
"mime-types": "^2.1.35",

pnpm-lock.yaml

Lines changed: 55 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<script setup lang="ts">
2+
import { generateTree } from './lib/generate-tree';
3+
import { parseInput } from './lib/parse-input';
4+
import { withDefaultOnError } from '@/utils/defaults';
5+
6+
const inputStructure = ref([
7+
'my-app',
8+
' src',
9+
' index.html',
10+
' main.ts',
11+
' main.scss',
12+
' - build',
13+
' - index.html',
14+
' main.js',
15+
' main.css',
16+
'',
17+
' ',
18+
' .prettierrc.json',
19+
' .gitlab-ci.yml',
20+
' README.md',
21+
'empty dir',
22+
].join('\n'));
23+
const outputTree = computed(() => withDefaultOnError(() => generateTree(parseInput(inputStructure.value)), ''));
24+
</script>
25+
26+
<template>
27+
<div>
28+
<c-input-text
29+
v-model:value="inputStructure"
30+
label="Your indented structure"
31+
placeholder="Paste your indented structure here..."
32+
rows="20"
33+
multiline
34+
raw-text
35+
monospace
36+
/>
37+
38+
<n-divider />
39+
40+
<n-form-item label="Your tree-like structure:">
41+
<TextareaCopyable :value="outputTree" />
42+
</n-form-item>
43+
</div>
44+
</template>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Folder } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Folder Structure Diagram',
6+
path: '/folder-structure-diagram',
7+
description: 'tree-like utility for generating ASCII folder structure diagrams',
8+
keywords: ['folder', 'structure', 'diagram', 'tree', 'ascii'],
9+
component: () => import('./folder-structure-diagram.vue'),
10+
icon: Folder,
11+
createdAt: new Date('2024-04-20'),
12+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Represents a single item in a file system
3+
* (i.e. a file or a folder)
4+
*/
5+
export interface FileStructure {
6+
/** The name of the file or folder */
7+
name: string
8+
9+
/** If a folder, the contents of the folder */
10+
children: FileStructure[]
11+
12+
/**
13+
* The number of spaces in front of the name
14+
* in the original source string
15+
*/
16+
indentCount: number
17+
18+
/** The parent directory of this file or folder */
19+
parent: FileStructure | null
20+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { generateTree } from './generate-tree';
3+
import { mockInput } from './mock-input';
4+
import { parseInput } from './parse-input';
5+
6+
describe('generateTree', () => {
7+
it('returns an UTF-8 representation of the provided FileStructure object', () => {
8+
const actual = generateTree(parseInput(mockInput));
9+
10+
const expected = `
11+
.
12+
├── my-app
13+
│ ├── src
14+
│ │ ├── index.html
15+
│ │ ├── main.ts
16+
│ │ └── main.scss
17+
│ ├── build
18+
│ │ ├── index.html
19+
│ │ ├── main.js
20+
│ │ └── main.css
21+
│ ├── .prettierrc.json
22+
│ ├── .gitlab-ci.yml
23+
│ └── README.md
24+
└── empty dir
25+
26+
`.trim();
27+
28+
expect(actual).toEqual(expected);
29+
});
30+
31+
it('returns an ASCII representation of the provided FileStructure object', () => {
32+
const actual = generateTree(parseInput(mockInput), { charset: 'ascii' });
33+
34+
const expected = `
35+
.
36+
|-- my-app
37+
| |-- src
38+
| | |-- index.html
39+
| | |-- main.ts
40+
| | \`-- main.scss
41+
| |-- build
42+
| | |-- index.html
43+
| | |-- main.js
44+
| | \`-- main.css
45+
| |-- .prettierrc.json
46+
| |-- .gitlab-ci.yml
47+
| \`-- README.md
48+
\`-- empty dir
49+
50+
`.trim();
51+
52+
expect(actual).toEqual(expected);
53+
});
54+
55+
it('does not render lines for parent directories that have already printed all of their children', () => {
56+
const input = `
57+
58+
grandparent
59+
parent
60+
child
61+
parent
62+
child
63+
grandchild
64+
65+
`;
66+
67+
const actual = generateTree(parseInput(input));
68+
69+
const expected = `
70+
.
71+
└── grandparent
72+
├── parent
73+
│ └── child
74+
└── parent
75+
└── child
76+
└── grandchild
77+
78+
`.trim();
79+
80+
expect(actual).toEqual(expected);
81+
});
82+
83+
it('appends a trailing slash to directories if trailingDirSlash === true', () => {
84+
const input = `
85+
86+
grandparent
87+
parent/
88+
child
89+
parent//
90+
child
91+
grandchild
92+
93+
`;
94+
95+
const actual = generateTree(parseInput(input), { trailingDirSlash: true });
96+
97+
const expected = `
98+
.
99+
└── grandparent/
100+
├── parent/
101+
│ └── child
102+
└── parent//
103+
└── child/
104+
└── grandchild
105+
106+
`.trim();
107+
108+
expect(actual).toEqual(expected);
109+
});
110+
111+
it('prints each items\' full path if fullPath === true', () => {
112+
const input = `
113+
114+
grandparent
115+
parent/
116+
child
117+
parent//
118+
child
119+
grandchild
120+
121+
`;
122+
123+
const actual = generateTree(parseInput(input), { fullPath: true });
124+
125+
const expected = `
126+
.
127+
└── ./grandparent
128+
├── ./grandparent/parent/
129+
│ └── ./grandparent/parent/child
130+
└── ./grandparent/parent//
131+
└── ./grandparent/parent//child
132+
└── ./grandparent/parent//child/grandchild
133+
134+
`.trim();
135+
136+
expect(actual).toEqual(expected);
137+
});
138+
139+
it('does not render the root dot if rootDot === false', () => {
140+
const input = `
141+
142+
grandparent
143+
parent
144+
child
145+
parent
146+
child
147+
grandchild
148+
149+
`;
150+
151+
const actual = generateTree(parseInput(input), { rootDot: false });
152+
153+
const expected = `
154+
grandparent
155+
├── parent
156+
│ └── child
157+
└── parent
158+
└── child
159+
└── grandchild
160+
161+
`.trim();
162+
163+
expect(actual).toEqual(expected);
164+
});
165+
});

0 commit comments

Comments
 (0)