touch-all 1.2.0 → 2.0.0

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
@@ -10,7 +10,8 @@ It behaves like `mkdir -p` and `touch` combined, creating directories and files
10
10
 
11
11
  - Accepts tree strings in **box-drawing** (`├──`, `└──`, `│`) or **indentation** (spaces) format
12
12
  - Trailing slash `/` marks a directory; no trailing `/` marks a file
13
- - Inline comments stripped automatically (`# ...` and `// ...`)
13
+ - Inline comments stripped automatically (`# ...`, `// ...`, `<- ...`, `← ...`)
14
+ - Symlink creation with `link-name -> target` syntax
14
15
  - `--dry-run` parses and validates without touching the file system
15
16
  - `--verbose` prints every created path
16
17
  - Path traversal protection — no item can escape the target directory
@@ -66,6 +67,13 @@ touch-all "..." --verbose
66
67
  touch-all "..." -v
67
68
  ```
68
69
 
70
+ - `--yes` , `-y` – skips the confirmation prompt when symlinks point outside the project root. Required in non-interactive environments (scripts, CI).
71
+
72
+ ```bash
73
+ touch-all "..." --yes
74
+ touch-all "..." -y
75
+ ```
76
+
69
77
  - `--completions` – generates a completion script for a specific shell. Supported shells: `sh`, `bash`, `fish`, `zsh`.
70
78
  - `--log-level` – sets the minimum log level for a command. Supported levels: `all`, `trace`, `debug`, `info`, `warning`, `error`, `fatal`, `none`. The default log level is `warning`.
71
79
  - `--help` , `-h` – shows the help documentation for a command.
@@ -104,20 +112,40 @@ my-project/
104
112
 
105
113
  Both formats produce identical results.
106
114
 
107
-
108
115
  ### Rules
109
116
 
110
117
  | Syntax | Meaning |
111
- |-------------------| -------------------------------- |
118
+ | ----------------- | -------------------------------- |
112
119
  | `name/` | directory |
113
120
  | `name` | file |
114
121
  | `dir/sub/` | directory at an explicit subpath |
115
122
  | `dir/sub/file.ts` | file at an explicit subpath |
116
123
  | `... # comment` | ignored (stripped) |
117
124
  | `... // comment` | ignored (stripped) |
125
+ | `... <- comment` | ignored (stripped) |
126
+ | `... ← comment` | ignored (stripped) |
127
+ | `name -> target` | symlink pointing to `target` |
118
128
 
119
129
  Items at the root level (no indentation / no parent) are created directly inside the target directory.
120
130
 
131
+ ### Symlinks
132
+
133
+ Use `link-name -> target` to create a symlink. The target is passed as-is to the OS — use paths relative to the symlink's location, just as you would in a shell.
134
+
135
+ ```
136
+ my-project/
137
+ src/
138
+ index.ts
139
+ utils -> ../shared/utils.ts # symlink to a sibling directory
140
+ shared/
141
+ utils.ts
142
+ ```
143
+
144
+ If `target` ends with `/`, the symlink is created as a directory symlink (relevant on Windows). The link name's suffix is ignored.
145
+
146
+ > [!WARNING]
147
+ > If any symlink target resolves outside the project root (`--path`), `touch-all` will prompt for confirmation before proceeding. Use `--yes` to skip the prompt in scripts or CI.
148
+
121
149
  ## Library API
122
150
 
123
151
  ```bash
@@ -137,21 +165,27 @@ import type { ParserResult, ParserResultLineItem } from 'touch-all'
137
165
 
138
166
  ### `parserFolderStructure(tree: string): ParserResult`
139
167
 
140
- Parses a tree string into a flat list of `type ParserResult = { path: string, isFile: boolean }` items. Pure function, no I/O.
168
+ Parses a tree string into a flat list of items. Pure function, no I/O.
169
+
170
+ Each item is one of:
171
+
172
+ ```ts
173
+ type ParserResultLineItem =
174
+ | { type: 'file'; path: string }
175
+ | { type: 'folder'; path: string }
176
+ | { type: 'symlink'; path: string; target: string }
177
+ ```
141
178
 
142
179
  ```ts
143
180
  const items = parserFolderStructure(`
144
181
  src/
145
182
  index.ts
183
+ link -> ../shared.ts
146
184
  `)
147
185
  // [
148
- // {
149
- // path: 'src',
150
- // isFile: false
151
- // }, {
152
- // path: 'src/index.ts',
153
- // isFile: true
154
- // }
186
+ // { type: 'folder', path: 'src' },
187
+ // { type: 'file', path: 'src/index.ts' },
188
+ // { type: 'symlink', path: 'src/link', target: '../shared.ts' },
155
189
  // ]
156
190
  ```
157
191
 
@@ -166,11 +200,7 @@ import { NodeContext, NodeRuntime } from '@effect/platform-node'
166
200
  const projectDirectory = '/absolute/target/path'
167
201
  const items = parserFolderStructure(tree)
168
202
 
169
- fileStructureCreator(items, projectDirectory)
170
- .pipe(
171
- Effect.provide(NodeContext.layer),
172
- NodeRuntime.runMain,
173
- )
203
+ fileStructureCreator(items, projectDirectory).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain)
174
204
  ```
175
205
 
176
206
  ### `resolveProjectPathToBase(projectPath: string, basePath: string): Effect<string, PathTraversalError>`
@@ -180,13 +210,12 @@ Resolves `projectPath` relative to `basePath` and rejects any path that would es
180
210
  > [!CAUTION]
181
211
  > `projectPath` cannot traverse outside of `basePath`. If `projectPath` is absolute, it treated as relative to `basePath`. If `projectPath` is relative, it is resolved against `basePath`. In either case, if the resulting path is outside of `basePath`, a `PathTraversalError` is thrown.
182
212
 
183
-
184
213
  ### Error types
185
214
 
186
215
  | Class | `_tag` | When thrown |
187
216
  | -------------------- | ---------------------- | --------------------------------------------------------------- |
188
217
  | `PathTraversalError` | `'PathTraversalError'` | Resolved path escapes `basePath`, or `basePath` is not absolute |
189
- | `FsError` | `'FsError'` | `mkdirSync` or `writeFileSync` fails |
218
+ | `FsError` | `'FsError'` | `mkdirSync`, `writeFileSync`, or `symlinkSync` fails |
190
219
 
191
220
  ## License
192
221