@himamshus06/git-auto 1.3.3 → 1.3.4

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 +119 -84
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@himamshus06/git-auto",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/tree.js CHANGED
@@ -12,73 +12,66 @@ class TreeParseError extends Error {
12
12
  }
13
13
 
14
14
  /**
15
- * Symbols used to define tree structures
16
- */
17
- const SYMBOLS = {
18
- unicode: {
19
- vertical: '│',
20
- branch: '├',
21
- leaf: '└',
22
- space: ' '
23
- },
24
- ascii: {
25
- vertical: '|',
26
- branch: '+',
27
- leaf: '`',
28
- space: ' '
29
- }
30
- };
31
-
32
- /**
33
- * Parses a visual tree string into a structured array of items.
34
- *
35
- * @param {string} text - The visual directory tree string.
36
- * @returns {Array<{name: string, depth: number, isDirectory: boolean, fullPath: string}>}
37
- * @throws {TreeParseError}
15
+ * Parse a visual directory tree into structured items.
38
16
  */
39
17
  function parseTree(text) {
40
18
  if (!text || text.trim() === '') {
41
19
  throw new TreeParseError('No tree provided');
42
20
  }
43
21
 
44
- const lines = text.split('\n').filter(line => line.trim() !== '');
45
- const stack = [];
22
+ const lines = text
23
+ .split(/\r?\n/)
24
+ .filter(line => line.trim() !== '');
25
+
46
26
  const result = [];
47
- let currentDepth = -1;
27
+ const stack = [];
48
28
 
49
29
  for (let i = 0; i < lines.length; i++) {
50
30
  const line = lines[i];
51
31
  const lineNum = i + 1;
52
32
 
53
- // Regex to split prefix symbols/spaces from the actual name
54
- const match = line.match(/^([│\s├└| \+`-]*) (.*)$/) || line.match(/^([│\s├└| \+`-]*)(.*)$/);
55
- if (!match) continue;
33
+ const parsed = parseLine(line);
56
34
 
57
- const prefix = match[1];
58
- let name = match[2].trim();
35
+ if (!parsed) {
36
+ throw new TreeParseError(
37
+ `Unable to parse tree line: "${line}"`,
38
+ lineNum
39
+ );
40
+ }
59
41
 
60
- if (!name) continue;
42
+ const { name, depth } = parsed;
61
43
 
62
- // Calculate depth based on "indentation units"
63
- // A unit is typically 4 characters (symbol + 3 spaces) or similar.
64
- // We count how many vertical bars or space blocks exist before the branch symbol.
65
- const depth = calculateDepth(prefix);
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
+ );
50
+ }
66
51
 
67
- // Logical Validation: Continuity Check
68
- if (depth > currentDepth + 1) {
69
- throw new TreeParseError(`Nesting depth jump detected (from ${currentDepth + 1} to ${depth})`, lineNum);
52
+ // Prevent impossible jumps
53
+ if (depth > stack.length) {
54
+ throw new TreeParseError(
55
+ `Nesting depth jump detected`,
56
+ lineNum
57
+ );
70
58
  }
71
59
 
72
- // Adjust stack to current depth
60
+ // Remove anything deeper than the current item
73
61
  while (stack.length > depth) {
74
62
  stack.pop();
75
63
  }
76
64
 
77
- // Reliable Type Detection
78
- const isDirectory = name.endsWith('/') || (!name.includes('.') && !name.startsWith('.'));
79
- const cleanName = isDirectory ? name.replace(/\/$/, '') : name;
65
+ // Detect directory/file
66
+ const isDirectory = detectDirectory(name);
80
67
 
68
+ const cleanName = isDirectory
69
+ ? name.replace(/\/$/, '')
70
+ : name;
71
+
72
+ // Add current item to stack
81
73
  stack.push(cleanName);
74
+
82
75
  const fullPath = path.join(...stack);
83
76
 
84
77
  result.push({
@@ -87,57 +80,86 @@ function parseTree(text) {
87
80
  isDirectory,
88
81
  fullPath
89
82
  });
90
-
91
- currentDepth = depth;
92
83
  }
93
84
 
94
85
  return result;
95
86
  }
96
87
 
97
88
  /**
98
- * Determines the depth of a line based on its prefix.
99
- * Supports both Unicode and ASCII styles.
100
- *
101
- * @param {string} prefix
102
- * @returns {number}
89
+ * Parse one line of a tree.
103
90
  */
104
- function calculateDepth(prefix) {
105
- if (!prefix) return 0;
106
-
107
- // Standard visual trees use blocks of 4 characters per level (e.g., "│ " or " ")
108
- // We count these blocks.
109
- let depth = 0;
110
- let i = 0;
111
- while (i < prefix.length) {
112
- // Check if we've reached the terminal branch symbol (├ or └ or + or `)
113
- if (prefix[i] === SYMBOLS.unicode.branch || prefix[i] === SYMBOLS.unicode.leaf ||
114
- prefix[i] === SYMBOLS.ascii.branch || prefix[i] === SYMBOLS.ascii.leaf) {
115
- break;
116
- }
91
+ function parseLine(line) {
92
+ // Root:
93
+ // project/
94
+ if (!/^[│|├└+`]/.test(line)) {
95
+ return {
96
+ name: line.trim(),
97
+ depth: 0
98
+ };
99
+ }
117
100
 
118
- // Increment depth for every "unit" of indentation
119
- // We assume a unit is 4 characters wide in most common formats
120
- depth++;
121
- i += 4;
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;
122
119
  }
123
120
 
124
- // Because we increment depth for every 4-char block,
125
- // we need to handle cases where the prefix is shorter than a full block.
126
- // A more robust way is to count actual vertical markers.
127
- const markers = (prefix.match(/[│|]/g) || []).length;
121
+ const indentation = match[1];
122
+ const name = match[2].trim();
128
123
 
129
- // Heuristic: If we have markers, that's our depth.
130
- // Otherwise, we use the space-based block count.
131
- if (markers > 0) return markers;
124
+ // Every 4-character indentation block = one level.
125
+ const depth = indentation.length / 4 + 1;
132
126
 
133
- return Math.floor(prefix.length / 4);
127
+ return {
128
+ name,
129
+ depth
130
+ };
134
131
  }
135
132
 
136
133
  /**
137
- * Parses a visual directory tree and creates the corresponding directories/files.
134
+ * Determine whether an item is a directory.
138
135
  *
139
- * @param {string} text - The visual directory tree string.
140
- * @returns {Promise<{created: string[], errors: string[]}>}
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.
141
163
  */
142
164
  async function parseAndCreateTree(text) {
143
165
  const created = [];
@@ -149,29 +171,42 @@ async function parseAndCreateTree(text) {
149
171
  for (const item of treeStructure) {
150
172
  try {
151
173
  if (item.isDirectory) {
152
- fs.mkdirSync(item.fullPath, { recursive: true });
174
+ fs.mkdirSync(item.fullPath, {
175
+ recursive: true
176
+ });
153
177
  } else {
154
178
  const parentDir = path.dirname(item.fullPath);
155
- fs.mkdirSync(parentDir, { recursive: true });
179
+
180
+ fs.mkdirSync(parentDir, {
181
+ recursive: true
182
+ });
183
+
156
184
  fs.writeFileSync(item.fullPath, '');
157
185
  }
186
+
158
187
  created.push(item.fullPath);
159
188
  } catch (err) {
160
- errors.push(`Failed to create ${item.fullPath}: ${err.message}`);
189
+ errors.push(
190
+ `Failed to create ${item.fullPath}: ${err.message}`
191
+ );
161
192
  }
162
193
  }
163
194
  } catch (err) {
164
195
  if (err instanceof TreeParseError) {
165
- throw err; // Rethrow parsing errors for the CLI to handle
196
+ throw err;
166
197
  }
198
+
167
199
  throw new Error(`Unexpected error: ${err.message}`);
168
200
  }
169
201
 
170
- return { created, errors };
202
+ return {
203
+ created,
204
+ errors
205
+ };
171
206
  }
172
207
 
173
208
  module.exports = {
174
209
  parseAndCreateTree,
175
210
  parseTree,
176
211
  TreeParseError
177
- };
212
+ };