@himamshus06/git-auto 1.3.4 → 1.3.6
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/package.json +1 -1
- package/src/tree.js +47 -166
package/package.json
CHANGED
package/src/tree.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
const fs = require('node:fs');
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Custom error for tree parsing failures
|
|
6
|
-
*/
|
|
7
4
|
class TreeParseError extends Error {
|
|
8
5
|
constructor(message, line) {
|
|
9
6
|
super(line ? `Line ${line}: ${message}` : message);
|
|
@@ -11,202 +8,86 @@ class TreeParseError extends Error {
|
|
|
11
8
|
}
|
|
12
9
|
}
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
// Every character that can appear in a branch-drawing prefix, unicode or ascii style.
|
|
12
|
+
// Note the U+2500 box-drawing horizontal (─) — NOT an ascii hyphen (-). Real tree
|
|
13
|
+
// output (and most pasted trees) uses U+2500, which is why the old regex silently
|
|
14
|
+
// failed to strip prefixes correctly.
|
|
15
|
+
const PREFIX_CHARS = /[│├└|+`\-─\s]/;
|
|
16
|
+
const CONNECTOR_CHARS = new Set(['├', '└', '+', '`']);
|
|
17
|
+
|
|
17
18
|
function parseTree(text) {
|
|
18
19
|
if (!text || text.trim() === '') {
|
|
19
20
|
throw new TreeParseError('No tree provided');
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
const lines = text
|
|
23
|
-
.split(/\r?\n/)
|
|
24
|
-
.filter(line => line.trim() !== '');
|
|
25
|
-
|
|
26
|
-
const result = [];
|
|
22
|
+
const lines = text.split('\n').filter(line => line.trim() !== '');
|
|
27
23
|
const stack = [];
|
|
24
|
+
const result = [];
|
|
28
25
|
|
|
29
26
|
for (let i = 0; i < lines.length; i++) {
|
|
30
27
|
const line = lines[i];
|
|
31
28
|
const lineNum = i + 1;
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`Unable to parse tree line: "${line}"`,
|
|
38
|
-
lineNum
|
|
39
|
-
);
|
|
30
|
+
// Find the connector character (├ / └ / ascii + / `), if any.
|
|
31
|
+
let connectorIdx = -1;
|
|
32
|
+
for (let c = 0; c < line.length; c++) {
|
|
33
|
+
if (CONNECTOR_CHARS.has(line[c])) { connectorIdx = c; break; }
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
36
|
+
let depth, rest;
|
|
37
|
+
if (connectorIdx === -1) {
|
|
38
|
+
// No connector at all -> this is the root line itself.
|
|
39
|
+
depth = 0;
|
|
40
|
+
rest = line.trim();
|
|
41
|
+
} else {
|
|
42
|
+
const prefix = line.slice(0, connectorIdx);
|
|
43
|
+
// Each ancestor level draws a fixed 4-char block ("│ " or " ").
|
|
44
|
+
// +1 because having a connector at all means "at least one level below root".
|
|
45
|
+
depth = Math.round(prefix.length / 4) + 1;
|
|
46
|
+
rest = line.slice(connectorIdx);
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
//
|
|
49
|
+
// Strip any remaining connector/line-drawing/space characters to get the name.
|
|
50
|
+
const name = rest.replace(new RegExp(`^${PREFIX_CHARS.source}+`), '').trim();
|
|
51
|
+
if (!name) continue;
|
|
52
|
+
|
|
53
53
|
if (depth > stack.length) {
|
|
54
54
|
throw new TreeParseError(
|
|
55
|
-
`Nesting depth jump detected`,
|
|
56
|
-
lineNum
|
|
55
|
+
`Nesting depth jump detected (from ${stack.length} to ${depth})`, lineNum
|
|
57
56
|
);
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
while (stack.length > depth) {
|
|
62
|
-
stack.pop();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Detect directory/file
|
|
66
|
-
const isDirectory = detectDirectory(name);
|
|
59
|
+
while (stack.length > depth) stack.pop();
|
|
67
60
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
: name;
|
|
61
|
+
const isDirectory = name.endsWith('/') || (!name.includes('.') && !name.startsWith('.'));
|
|
62
|
+
const cleanName = isDirectory ? name.replace(/\/$/, '') : name;
|
|
71
63
|
|
|
72
|
-
// Add current item to stack
|
|
73
64
|
stack.push(cleanName);
|
|
74
|
-
|
|
75
65
|
const fullPath = path.join(...stack);
|
|
76
66
|
|
|
77
|
-
result.push({
|
|
78
|
-
name: cleanName,
|
|
79
|
-
depth,
|
|
80
|
-
isDirectory,
|
|
81
|
-
fullPath
|
|
82
|
-
});
|
|
67
|
+
result.push({ name: cleanName, depth, isDirectory, fullPath });
|
|
83
68
|
}
|
|
84
|
-
|
|
85
69
|
return result;
|
|
86
70
|
}
|
|
87
71
|
|
|
88
|
-
/**
|
|
89
|
-
* Parse one line of a tree.
|
|
90
|
-
*/
|
|
91
|
-
function parseLine(line) {
|
|
92
|
-
// Root:
|
|
93
|
-
// project/
|
|
94
|
-
if (!/^[│|├└+`]/.test(line)) {
|
|
95
|
-
return {
|
|
96
|
-
name: line.trim(),
|
|
97
|
-
depth: 0
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/*
|
|
102
|
-
* Match tree indentation.
|
|
103
|
-
*
|
|
104
|
-
* Examples:
|
|
105
|
-
*
|
|
106
|
-
* ├── src/
|
|
107
|
-
* └── package.json
|
|
108
|
-
*
|
|
109
|
-
* │ ├── components/
|
|
110
|
-
* │ └── App.jsx
|
|
111
|
-
*
|
|
112
|
-
* │ │ └── Button.jsx
|
|
113
|
-
*/
|
|
114
|
-
|
|
115
|
-
const match = line.match(/^((?:│ | )*)(?:├── |└── |\+-- |`-- )(.*)$/);
|
|
116
|
-
|
|
117
|
-
if (!match) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const indentation = match[1];
|
|
122
|
-
const name = match[2].trim();
|
|
123
|
-
|
|
124
|
-
// Every 4-character indentation block = one level.
|
|
125
|
-
const depth = indentation.length / 4 + 1;
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
name,
|
|
129
|
-
depth
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Determine whether an item is a directory.
|
|
135
|
-
*
|
|
136
|
-
* Best case: directories end with /.
|
|
137
|
-
*
|
|
138
|
-
* For trees that don't include /, this falls back to
|
|
139
|
-
* extension-based detection.
|
|
140
|
-
*/
|
|
141
|
-
function detectDirectory(name) {
|
|
142
|
-
// Explicit directory marker
|
|
143
|
-
if (name.endsWith('/')) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Hidden files like .gitignore are files
|
|
148
|
-
if (name.startsWith('.')) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Files with extensions are files
|
|
153
|
-
if (path.extname(name) !== '') {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Otherwise assume directory
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Parse tree and create files/directories.
|
|
163
|
-
*/
|
|
164
72
|
async function parseAndCreateTree(text) {
|
|
165
73
|
const created = [];
|
|
166
74
|
const errors = [];
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
} else {
|
|
178
|
-
const parentDir = path.dirname(item.fullPath);
|
|
179
|
-
|
|
180
|
-
fs.mkdirSync(parentDir, {
|
|
181
|
-
recursive: true
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
fs.writeFileSync(item.fullPath, '');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
created.push(item.fullPath);
|
|
188
|
-
} catch (err) {
|
|
189
|
-
errors.push(
|
|
190
|
-
`Failed to create ${item.fullPath}: ${err.message}`
|
|
191
|
-
);
|
|
75
|
+
const treeStructure = parseTree(text); // let TreeParseError propagate to caller
|
|
76
|
+
|
|
77
|
+
for (const item of treeStructure) {
|
|
78
|
+
try {
|
|
79
|
+
if (item.isDirectory) {
|
|
80
|
+
fs.mkdirSync(item.fullPath, { recursive: true });
|
|
81
|
+
} else {
|
|
82
|
+
fs.mkdirSync(path.dirname(item.fullPath), { recursive: true });
|
|
83
|
+
fs.writeFileSync(item.fullPath, '');
|
|
192
84
|
}
|
|
85
|
+
created.push(item.fullPath);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
errors.push(`Failed to create ${item.fullPath}: ${err.message}`);
|
|
193
88
|
}
|
|
194
|
-
} catch (err) {
|
|
195
|
-
if (err instanceof TreeParseError) {
|
|
196
|
-
throw err;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
throw new Error(`Unexpected error: ${err.message}`);
|
|
200
89
|
}
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
created,
|
|
204
|
-
errors
|
|
205
|
-
};
|
|
90
|
+
return { created, errors };
|
|
206
91
|
}
|
|
207
92
|
|
|
208
|
-
module.exports = {
|
|
209
|
-
parseAndCreateTree,
|
|
210
|
-
parseTree,
|
|
211
|
-
TreeParseError
|
|
212
|
-
};
|
|
93
|
+
module.exports = { parseAndCreateTree, parseTree, TreeParseError };
|