@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tree.js +47 -166
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@himamshus06/git-auto",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "bin": {
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
- * Parse a visual directory tree into structured items.
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
- const parsed = parseLine(line);
34
-
35
- if (!parsed) {
36
- throw new TreeParseError(
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
- const { name, depth } = parsed;
43
-
44
- // Root must be depth 0
45
- if (i === 0 && depth !== 0) {
46
- throw new TreeParseError(
47
- 'Root item must have depth 0',
48
- lineNum
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
- // Prevent impossible jumps
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
- // Remove anything deeper than the current item
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 cleanName = isDirectory
69
- ? name.replace(/\/$/, '')
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
- try {
169
- const treeStructure = parseTree(text);
170
-
171
- for (const item of treeStructure) {
172
- try {
173
- if (item.isDirectory) {
174
- fs.mkdirSync(item.fullPath, {
175
- recursive: true
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 };