tree-fs 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -4,13 +4,17 @@
4
4
 
5
5
  It is designed to be the **standard "Paste & Go" receiver for AI-generated code**.
6
6
 
7
- [![npm version](https://img.shields.io/npm/v/tree-fs.svg)](https://www.npmjs.com/package/tree-fs)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
-
10
- ---
7
+ <a href="https://www.npmjs.com/package/tree-fs"><img src="https://img.shields.io/npm/v/tree-fs.svg?style=flat-square&color=007acc" alt="npm version"></a>
8
+ <a href="https://www.npmjs.com/package/tree-fs"><img src="https://img.shields.io/npm/dt/tree-fs.svg?style=flat-square&color=success" alt="npm downloads"></a>
9
+ <a href="https://github.com/mgks/tree-fs/blob/main/LICENSE"><img src="https://img.shields.io/github/license/mgks/tree-fs.svg?style=flat-square&color=blue" alt="license"></a>
10
+ <a href="https://github.com/mgks/tree-fs/stargazers"><img src="https://img.shields.io/github/stars/mgks/tree-fs?style=flat-square&logo=github" alt="stars"></a>
11
11
 
12
12
  ## ⚔ Why tree-fs?
13
13
 
14
+ <p align="">
15
+ <img src="https://github.com/mgks/tree-fs/blob/main/preview.gif?raw=true" width="720">
16
+ </p>
17
+
14
18
  LLMs (ChatGPT, Claude, DeepSeek) are great at planning architectures but bad at executing them.
15
19
  They often output this:
16
20
 
@@ -31,8 +35,6 @@ Copying that structure manually is tedious. **tree-fs** makes it instant.
31
35
  * **Smart:** Distinguishes `v1.0` (folder) from `v1.0.js` (file) automatically.
32
36
  * **Zero Dependencies:** Installs in seconds.
33
37
 
34
- ---
35
-
36
38
  ## šŸš€ Usage
37
39
 
38
40
  ### 1. Interactive Mode (The "Paste" Workflow)
@@ -85,8 +87,6 @@ generateFS(tree, path.resolve(__dirname, "./output"))
85
87
  console.log("Structure created!")
86
88
  ```
87
89
 
88
- ---
89
-
90
90
  ## šŸ’” Syntax Guide & Robustness
91
91
 
92
92
  tree-fs is built to handle the "messy reality" of text inputs.
@@ -131,8 +131,6 @@ project
131
131
  Known files without extensions are correctly identified as files.
132
132
  * `Dockerfile`, `Makefile`, `LICENSE`, `Procfile`, `.gitignore`, `Jenkinsfile`
133
133
 
134
- ---
135
-
136
134
  ## šŸ“¦ CI/CD Integration
137
135
 
138
136
  You can use `tree-fs` to scaffold environments in GitHub Actions or pipelines.
@@ -147,8 +145,6 @@ To test without writing files (Dry Run):
147
145
  tree-fs structure.txt --dry-run
148
146
  ```
149
147
 
150
- ---
151
-
152
148
  ## License
153
149
 
154
- MIT
150
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tree-fs",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Generate file system structures from text-based directory trees. The standard receiver for AI-generated project scaffolding.",
5
5
  "bin": {
6
6
  "tree-fs": "bin/tree-fs.js"
@@ -17,7 +17,7 @@
17
17
  "node": ">=14.0.0"
18
18
  },
19
19
  "scripts": {
20
- "test": "echo \"Error: no test specified\" && exit 1",
20
+ "test": "echo \"Error: no test specified\" && exit 0",
21
21
  "start": "node bin/tree-fs.js"
22
22
  },
23
23
  "repository": {
@@ -40,5 +40,5 @@
40
40
  "bugs": {
41
41
  "url": "https://github.com/mgks/tree-fs/issues"
42
42
  },
43
- "homepage": "https://github.com/mgks/tree-fs"
43
+ "homepage": "https://github.com/mgks/tree-fs#readme"
44
44
  }
package/src/generator.js CHANGED
@@ -14,12 +14,11 @@ function generateFS(tree, baseDir = process.cwd(), options = {}) {
14
14
  let rootPath = baseDir
15
15
 
16
16
  // Intelligent Root Detection:
17
- // If there is exactly one top-level folder, we treat it as the project root.
17
+ // If there is exactly one top-level folder, treat it as the project root.
18
18
  if (nodes.length === 1 && nodes[0].type === "folder") {
19
19
  rootPath = path.join(baseDir, nodes[0].name)
20
20
  nodes = nodes[0].children
21
21
 
22
- // Create the root directory physically
23
22
  if (!dryRun && !fs.existsSync(rootPath)) {
24
23
  fs.mkdirSync(rootPath, { recursive: true })
25
24
  }
@@ -27,9 +26,16 @@ function generateFS(tree, baseDir = process.cwd(), options = {}) {
27
26
 
28
27
  function walk(nodes, currentPath) {
29
28
  for (const node of nodes) {
30
- // Robustness: normalize path separators for cross-platform safety
31
29
  const target = path.join(currentPath, node.name)
32
30
 
31
+ // SECURITY CHECK: Prevent directory traversal
32
+ // If the relative path starts with '..', it's trying to escape the root
33
+ const relative = path.relative(rootPath, target)
34
+ if (relative.startsWith("..") && !path.isAbsolute(relative)) {
35
+ console.warn(`āš ļø Skipping unsafe path: ${node.name}`)
36
+ continue
37
+ }
38
+
33
39
  if (node.type === "folder") {
34
40
  if (!fs.existsSync(target)) {
35
41
  if (!dryRun) fs.mkdirSync(target, { recursive: true })
@@ -43,7 +49,7 @@ function generateFS(tree, baseDir = process.cwd(), options = {}) {
43
49
  continue
44
50
  }
45
51
 
46
- // Ensure parent directory exists (handles cases like "src/utils/helpers.js" as a single line)
52
+ // Ensure parent directory exists (Safety for weird edge cases)
47
53
  const parentDir = path.dirname(target)
48
54
  if (!dryRun && !fs.existsSync(parentDir)) {
49
55
  fs.mkdirSync(parentDir, { recursive: true })
package/src/normaliser.js CHANGED
@@ -1,36 +1,39 @@
1
1
  // src/normaliser.js
2
2
 
3
- // 1. Expanded Regex to catch +, >, and markdown bullets
3
+ // Catch standard tree characters, markdown bullets, and ASCII symbols (+, >, -)
4
4
  const STRIP_REGEX = /^[\sā”‚ā”œā””ā”€ā€¢*|\-+>]+/
5
5
 
6
6
  function normaliseLines(input) {
7
7
  return input
8
8
  .split("\n")
9
- .map(line => line.replace(/\r/g, ""))
9
+ .map(line => line.replace(/\r/g, "")) // Handle Windows CRLF
10
10
  .filter(Boolean)
11
11
  .map(raw => {
12
- // 2. Detect indentation based on prefix length
13
- const match = raw.match(STRIP_REGEX)
12
+ // 1. Windows Fix: Normalize backslashes to forward slashes immediately
13
+ const normalizedRaw = raw.replace(/\\/g, "/")
14
+
15
+ // 2. Calculate indent based on the full prefix (spaces + tree chars)
16
+ const match = normalizedRaw.match(STRIP_REGEX)
14
17
  const prefixLength = match ? match[0].length : 0
15
-
18
+
16
19
  // 3. Strip comments (anything after ' #')
17
- // We explicitly look for " #" so we don't break "page#1.js"
18
- const commentIndex = raw.indexOf(" #")
19
- let cleaned = commentIndex !== -1 ? raw.substring(0, commentIndex) : raw
20
+ const commentIndex = normalizedRaw.indexOf(" #")
21
+ let cleaned = commentIndex !== -1 ? normalizedRaw.substring(0, commentIndex) : normalizedRaw
20
22
 
21
- // 4. Check for explicit trailing slash (User saying "This is a folder/")
23
+ // 4. Check for explicit trailing slash
22
24
  const endsWithSlash = cleaned.trim().endsWith("/")
23
25
 
26
+ // 5. Clean up the name
24
27
  cleaned = cleaned
25
28
  .replace(STRIP_REGEX, "") // Remove tree characters
26
- .replace(/\/$/, "") // Remove that trailing slash for the name
29
+ .replace(/\/$/, "") // Remove trailing slash for name storage
27
30
  .trim()
28
31
 
29
32
  return {
30
- raw,
33
+ raw: normalizedRaw,
31
34
  indent: prefixLength,
32
35
  name: cleaned,
33
- explicitFolder: endsWithSlash // Pass this signal to the parser
36
+ explicitFolder: endsWithSlash
34
37
  }
35
38
  })
36
39
  .filter(line => line.name.length > 0)