touch-all 0.0.1 → 1.1.10

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.
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Error type for path traversal detection
3
+ */
4
+ export declare class PathTraversalError {
5
+ readonly path: string;
6
+ readonly _tag = "PathTraversalError";
7
+ constructor(path: string);
8
+ toString(): string;
9
+ }
10
+ /**
11
+ * Error type for file system operation failures
12
+ */
13
+ export declare class FsError {
14
+ readonly message: string;
15
+ readonly _tag = "FsError";
16
+ constructor(message: string);
17
+ toString(): string;
18
+ }
@@ -0,0 +1,5 @@
1
+ export interface ParserResultLineItem {
2
+ path: string;
3
+ isFile: boolean;
4
+ }
5
+ export type ParserResult = Array<ParserResultLineItem>;
@@ -0,0 +1,2 @@
1
+ import { Effect } from 'effect';
2
+ export declare const cli: (args: ReadonlyArray<string>) => Effect.Effect<void, import("./_commonErrors").PathTraversalError | import("./_commonErrors").FsError | Error | import("@effect/cli/ValidationError").ValidationError, import("@effect/cli/CliApp").CliApp.Environment>;
@@ -0,0 +1,4 @@
1
+ import { Effect } from 'effect';
2
+ import { type ParserResult } from './_commonTypes';
3
+ import { FsError, PathTraversalError } from './_commonErrors';
4
+ export declare const fileStructureCreator: (items: ParserResult, basePath: string) => Effect.Effect<void, FsError | PathTraversalError>;
@@ -0,0 +1,10 @@
1
+ import { Effect } from 'effect';
2
+ import { PathTraversalError } from './_commonErrors';
3
+ /**
4
+ * Safely normalize a user-supplied path against a base directory.
5
+ * Fails with PathTraversalError if the resolved path escapes the base.
6
+ *
7
+ * @param projectPath string - Path relative to project.
8
+ * @param basePath string - Must be absolute.
9
+ */
10
+ export declare const resolveProjectPathToBase: (projectPath: string, basePath: string) => Effect.Effect<string, PathTraversalError>;
@@ -1,45 +1,6 @@
1
- import { Effect } from 'effect';
2
- import * as _effect_cli_CliApp from '@effect/cli/CliApp';
3
- import * as _effect_cli_ValidationError from '@effect/cli/ValidationError';
4
-
5
- interface ParserResultLineItem {
6
- path: string;
7
- isFile: boolean;
8
- }
9
- type ParserResult = Array<ParserResultLineItem>;
10
-
11
- /**
12
- * Error type for path traversal detection
13
- */
14
- declare class PathTraversalError {
15
- readonly path: string;
16
- readonly _tag = "PathTraversalError";
17
- constructor(path: string);
18
- toString(): string;
19
- }
20
- /**
21
- * Error type for file system operation failures
22
- */
23
- declare class FsError {
24
- readonly message: string;
25
- readonly _tag = "FsError";
26
- constructor(message: string);
27
- toString(): string;
28
- }
29
-
30
- declare const fileStructureCreator: (items: ParserResult, basePath: string) => Effect.Effect<void, FsError | PathTraversalError>;
31
-
32
- /**
33
- * Safely normalize a user-supplied path against a base directory.
34
- * Fails with PathTraversalError if the resolved path escapes the base.
35
- *
36
- * @param projectPath string - Path relative to project.
37
- * @param basePath string - Must be absolute.
38
- */
39
- declare const resolveProjectPathToBase: (projectPath: string, basePath: string) => Effect.Effect<string, PathTraversalError>;
40
-
41
- declare const parserFolderStructure: (treeString: string) => ParserResult;
42
-
43
- declare const cli: (args: ReadonlyArray<string>) => Effect.Effect<void, PathTraversalError | FsError | Error | _effect_cli_ValidationError.ValidationError, _effect_cli_CliApp.CliApp.Environment>;
44
-
45
- export { FsError, type ParserResult, type ParserResultLineItem, PathTraversalError, cli, fileStructureCreator, parserFolderStructure, resolveProjectPathToBase };
1
+ export { fileStructureCreator } from './fsGenerator';
2
+ export { resolveProjectPathToBase } from './fsNormalizator';
3
+ export { PathTraversalError, FsError } from './_commonErrors';
4
+ export type { ParserResult, ParserResultLineItem } from './_commonTypes';
5
+ export { parserFolderStructure } from './parser';
6
+ export { cli } from './cli';
package/dist/lib/index.js CHANGED
@@ -1,31 +1,34 @@
1
1
  // src/fsGenerator.ts
2
2
  import { Effect as Effect2 } from "effect";
3
- import path from "path";
4
- import fs from "fs";
3
+ import path from "node:path";
4
+ import fs from "node:fs";
5
5
 
6
6
  // src/fsNormalizator.ts
7
- import { resolve, relative, sep } from "path";
7
+ import { resolve, relative, sep } from "node:path";
8
8
  import { Effect } from "effect";
9
9
 
10
10
  // src/_commonErrors.ts
11
- var PathTraversalError = class {
12
- constructor(path2) {
13
- this.path = path2;
14
- }
11
+ class PathTraversalError {
12
+ path;
15
13
  _tag = "PathTraversalError";
14
+ constructor(path) {
15
+ this.path = path;
16
+ }
16
17
  toString() {
17
18
  return `${this._tag}: The path cannot be used ${this.path}`;
18
19
  }
19
- };
20
- var FsError = class {
20
+ }
21
+
22
+ class FsError {
23
+ message;
24
+ _tag = "FsError";
21
25
  constructor(message) {
22
26
  this.message = message;
23
27
  }
24
- _tag = "FsError";
25
28
  toString() {
26
29
  return `${this._tag}: ${this.message}`;
27
30
  }
28
- };
31
+ }
29
32
 
30
33
  // src/fsNormalizator.ts
31
34
  var resolveProjectPathToBase = (projectPath, basePath) => {
@@ -45,35 +48,30 @@ var fileStructureCreator = (items, basePath) => Effect2.gen(function* (_) {
45
48
  const fullPath = yield* _(resolveProjectPathToBase(item.path, basePath));
46
49
  if (item.isFile) {
47
50
  const dir = path.dirname(fullPath);
48
- yield* _(
49
- Effect2.try({
50
- try: () => fs.mkdirSync(dir, { recursive: true }),
51
- catch: (error) => new FsError(`Failed to create directory ${dir}: ${String(error)}`)
52
- })
53
- );
54
- yield* _(
55
- Effect2.try({
56
- try: () => fs.writeFileSync(fullPath, ""),
57
- catch: (error) => new FsError(`Failed to create file ${fullPath}: ${String(error)}`)
58
- })
59
- );
51
+ yield* _(Effect2.try({
52
+ try: () => fs.mkdirSync(dir, { recursive: true }),
53
+ catch: (error) => new FsError(`Failed to create directory ${dir}: ${String(error)}`)
54
+ }));
55
+ yield* _(Effect2.try({
56
+ try: () => fs.writeFileSync(fullPath, ""),
57
+ catch: (error) => new FsError(`Failed to create file ${fullPath}: ${String(error)}`)
58
+ }));
60
59
  yield* _(Effect2.logInfo(` Created file: ${item.path}`));
61
60
  } else {
62
- yield* _(
63
- Effect2.try({
64
- try: () => fs.mkdirSync(fullPath, { recursive: true }),
65
- catch: (error) => new FsError(`Failed to create directory ${fullPath}: ${String(error)}`)
66
- })
67
- );
61
+ yield* _(Effect2.try({
62
+ try: () => fs.mkdirSync(fullPath, { recursive: true }),
63
+ catch: (error) => new FsError(`Failed to create directory ${fullPath}: ${String(error)}`)
64
+ }));
68
65
  yield* _(Effect2.logInfo(` Created directory: ${item.path}`));
69
66
  }
70
67
  }
71
- yield* _(Effect2.logInfo("\n\u2713 Structure created successfully!"));
68
+ yield* _(Effect2.logInfo(`
69
+ ✓ Structure created successfully!`));
72
70
  });
73
-
74
71
  // src/parser.ts
75
72
  var parserFolderStructure = (treeString) => {
76
- const lines = treeString.split("\n");
73
+ const lines = treeString.split(`
74
+ `);
77
75
  const result = [];
78
76
  const pathStack = [];
79
77
  let indentSize = 0;
@@ -81,17 +79,21 @@ var parserFolderStructure = (treeString) => {
81
79
  for (const line of lines) {
82
80
  const [p01 = "", _comment01] = line.split("#");
83
81
  const [p02 = "", _comment02] = p01.split("//");
84
- const p03 = p02.replace(/[│├└─\s]/g, " ");
82
+ const p03 = p02.replace(/[│├└─\t]/g, " ");
85
83
  const cleanLine = p03.trim();
86
- if (!cleanLine) continue;
87
- if (cleanLine === "/") continue;
88
- if (cleanLine.startsWith("./")) continue;
89
- if (cleanLine.endsWith("../")) continue;
84
+ if (!cleanLine)
85
+ continue;
86
+ if (cleanLine === "/")
87
+ continue;
88
+ if (cleanLine.startsWith("./"))
89
+ continue;
90
+ if (cleanLine.endsWith("../"))
91
+ continue;
90
92
  const indent = countLeadingSpaces(p03);
91
93
  indentSize = indentSize === 0 && indent > 0 ? indent : indentSize;
92
94
  const level = indentSize === 0 ? 0 : indent / indentSize;
93
- if (previousIndentLevel > level) {
94
- pathStack.splice(level, previousIndentLevel - level);
95
+ if (previousIndentLevel >= level) {
96
+ pathStack.splice(level, pathStack.length - level);
95
97
  }
96
98
  previousIndentLevel = level;
97
99
  const isFile = !cleanLine.endsWith("/");
@@ -112,68 +114,62 @@ function countLeadingSpaces(str) {
112
114
  const match = str.match(/^[\s]*/);
113
115
  return match ? match[0].length : 0;
114
116
  }
115
-
116
117
  // src/cli.ts
117
118
  import { Args, Command, Options } from "@effect/cli";
118
- import { Console, Effect as Effect3, Logger, LogLevel } from "effect";
119
- var treeArg = Args.text({ name: "tree" }).pipe(
120
- Args.withDescription("Multiline string representing the directory tree structure")
121
- );
122
- var pathOption = Options.directory("path").pipe(
123
- Options.withAlias("p"),
124
- Options.withDefault("."),
125
- Options.withDescription("Target folder path (defaults to current directory)")
126
- );
127
- var dryRunOption = Options.boolean("dry-run").pipe(
128
- Options.withAlias("n"),
129
- Options.withDefault(false),
130
- Options.withDescription("Skip the top-level directory if there is only one")
131
- );
132
- var verboseOption = Options.boolean("verbose").pipe(
133
- Options.withAlias("v"),
134
- Options.withDefault(false),
135
- Options.withDescription("Log to console extra information about creating a directory tree")
136
- );
119
+ import { Console, Effect as Effect3, Logger, LogLevel, Option } from "effect";
120
+ import { createInterface } from "node:readline";
121
+ var treeArg = Args.text({ name: "tree" }).pipe(Args.withDescription("Multiline string representing the directory tree structure"), Args.optional);
122
+ var pathOption = Options.directory("path").pipe(Options.withAlias("p"), Options.withDefault("."), Options.withDescription("Target folder path (defaults to current directory)"));
123
+ var dryRunOption = Options.boolean("dry-run").pipe(Options.withAlias("n"), Options.withDefault(false), Options.withDescription("Skip the top-level directory if there is only one"));
124
+ var verboseOption = Options.boolean("verbose").pipe(Options.withAlias("v"), Options.withDefault(false), Options.withDescription("Log to console extra information about creating a directory tree"));
137
125
  var command = Command.make("touch-all", {
138
126
  tree: treeArg,
139
127
  path: pathOption,
140
128
  dryRun: dryRunOption,
141
129
  verbose: verboseOption
142
- }).pipe(
143
- Command.withDescription("Create directory structure from a tree representation"),
144
- Command.withHandler(({ tree, path: targetPath, dryRun = false, verbose }) => {
145
- const program = Effect3.gen(function* (_) {
146
- if (dryRun) {
147
- yield* _(Effect3.logInfo("Running in dry mode. No one file system node will be created."));
148
- }
149
- yield* _(Effect3.logInfo("Parsing tree structure..."));
150
- const items = parserFolderStructure(tree);
151
- if (items.length === 0) {
152
- yield* _(Console.error("No valid items found in the tree structure"));
153
- yield* _(Console.error(items));
154
- yield* _(Console.error(tree));
155
- return yield* _(Effect3.fail(new Error("Invalid tree structure")));
156
- }
157
- yield* _(Effect3.logInfo(`Found ${items.length} items to create`));
158
- yield* _(Effect3.logInfo(`Found:
130
+ }).pipe(Command.withDescription("Create directory structure from a tree representation"), Command.withHandler(({ tree, path: targetPath, dryRun = false, verbose }) => {
131
+ const readStdin = Effect3.tryPromise({
132
+ try: () => new Promise((resolve2) => {
133
+ const rl = createInterface({ input: process.stdin });
134
+ const lines = [];
135
+ rl.on("line", (line) => lines.push(line));
136
+ rl.on("close", () => resolve2(lines.join(`
137
+ `)));
138
+ }),
139
+ catch: (e) => new Error(`Failed to read stdin: ${String(e)}`)
140
+ });
141
+ const program = Effect3.gen(function* (_) {
142
+ const treeString = Option.isSome(tree) ? tree.value : yield* _(readStdin);
143
+ if (dryRun) {
144
+ yield* _(Effect3.logInfo("Running in dry mode. No one file system node will be created."));
145
+ }
146
+ yield* _(Effect3.logInfo("Parsing tree structure..."));
147
+ const items = parserFolderStructure(treeString);
148
+ if (items.length === 0) {
149
+ yield* _(Console.error("No valid items found in the tree structure"));
150
+ yield* _(Console.error(items));
151
+ yield* _(Console.error(treeString));
152
+ return yield* _(Effect3.fail(new Error("Invalid tree structure")));
153
+ }
154
+ yield* _(Effect3.logInfo(`Found ${items.length} items to create`));
155
+ yield* _(Effect3.logInfo(`Found:
159
156
  ${items.map((i) => `${i.path}
160
157
  `).join("")}`));
161
- if (!dryRun) {
162
- yield* _(fileStructureCreator(items, targetPath));
163
- }
164
- });
165
- return verbose ? program.pipe(Logger.withMinimumLogLevel(LogLevel.Info)) : program;
166
- })
167
- );
158
+ if (!dryRun) {
159
+ yield* _(fileStructureCreator(items, targetPath));
160
+ }
161
+ });
162
+ return verbose ? program.pipe(Logger.withMinimumLogLevel(LogLevel.Info)) : program;
163
+ }));
168
164
  var cli = Command.run(command, {
169
165
  name: "Touch All",
170
166
  version: "0.0.1"
171
167
  });
172
168
  export {
173
- FsError,
174
- PathTraversalError,
175
- cli,
176
- fileStructureCreator,
169
+ resolveProjectPathToBase,
177
170
  parserFolderStructure,
178
- resolveProjectPathToBase
171
+ fileStructureCreator,
172
+ cli,
173
+ PathTraversalError,
174
+ FsError
179
175
  };
@@ -0,0 +1,2 @@
1
+ import type { ParserResult } from './_commonTypes';
2
+ export declare const parserFolderStructure: (treeString: string) => ParserResult;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env node
2
-
3
2
  // src/touch-all.ts
4
3
  import { Effect as Effect4, Logger as Logger2, LogLevel as LogLevel2 } from "effect";
5
4
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
6
5
 
7
6
  // src/cli.ts
8
7
  import { Args, Command, Options } from "@effect/cli";
9
- import { Console, Effect as Effect3, Logger, LogLevel } from "effect";
8
+ import { Console, Effect as Effect3, Logger, LogLevel, Option } from "effect";
9
+ import { createInterface } from "node:readline";
10
10
 
11
11
  // src/parser.ts
12
12
  var parserFolderStructure = (treeString) => {
13
- const lines = treeString.split("\n");
13
+ const lines = treeString.split(`
14
+ `);
14
15
  const result = [];
15
16
  const pathStack = [];
16
17
  let indentSize = 0;
@@ -18,25 +19,29 @@ var parserFolderStructure = (treeString) => {
18
19
  for (const line of lines) {
19
20
  const [p01 = "", _comment01] = line.split("#");
20
21
  const [p02 = "", _comment02] = p01.split("//");
21
- const p03 = p02.replace(/[│├└─\s]/g, " ");
22
+ const p03 = p02.replace(/[│├└─\t]/g, " ");
22
23
  const cleanLine = p03.trim();
23
- if (!cleanLine) continue;
24
- if (cleanLine === "/") continue;
25
- if (cleanLine.startsWith("./")) continue;
26
- if (cleanLine.endsWith("../")) continue;
24
+ if (!cleanLine)
25
+ continue;
26
+ if (cleanLine === "/")
27
+ continue;
28
+ if (cleanLine.startsWith("./"))
29
+ continue;
30
+ if (cleanLine.endsWith("../"))
31
+ continue;
27
32
  const indent = countLeadingSpaces(p03);
28
33
  indentSize = indentSize === 0 && indent > 0 ? indent : indentSize;
29
34
  const level = indentSize === 0 ? 0 : indent / indentSize;
30
- if (previousIndentLevel > level) {
31
- pathStack.splice(level, previousIndentLevel - level);
35
+ if (previousIndentLevel >= level) {
36
+ pathStack.splice(level, pathStack.length - level);
32
37
  }
33
38
  previousIndentLevel = level;
34
39
  const isFile = !cleanLine.endsWith("/");
35
40
  const name = cleanLine.split("/").filter(Boolean);
36
41
  pathStack.push(name);
37
- const path2 = pathStack.flat().join("/");
42
+ const path = pathStack.flat().join("/");
38
43
  result.push({
39
- path: path2,
44
+ path,
40
45
  isFile
41
46
  });
42
47
  if (isFile) {
@@ -52,32 +57,35 @@ function countLeadingSpaces(str) {
52
57
 
53
58
  // src/fsGenerator.ts
54
59
  import { Effect as Effect2 } from "effect";
55
- import path from "path";
56
- import fs from "fs";
60
+ import path from "node:path";
61
+ import fs from "node:fs";
57
62
 
58
63
  // src/fsNormalizator.ts
59
- import { resolve, relative, sep } from "path";
64
+ import { resolve, relative, sep } from "node:path";
60
65
  import { Effect } from "effect";
61
66
 
62
67
  // src/_commonErrors.ts
63
- var PathTraversalError = class {
64
- constructor(path2) {
65
- this.path = path2;
66
- }
68
+ class PathTraversalError {
69
+ path;
67
70
  _tag = "PathTraversalError";
71
+ constructor(path) {
72
+ this.path = path;
73
+ }
68
74
  toString() {
69
75
  return `${this._tag}: The path cannot be used ${this.path}`;
70
76
  }
71
- };
72
- var FsError = class {
77
+ }
78
+
79
+ class FsError {
80
+ message;
81
+ _tag = "FsError";
73
82
  constructor(message) {
74
83
  this.message = message;
75
84
  }
76
- _tag = "FsError";
77
85
  toString() {
78
86
  return `${this._tag}: ${this.message}`;
79
87
  }
80
- };
88
+ }
81
89
 
82
90
  // src/fsNormalizator.ts
83
91
  var resolveProjectPathToBase = (projectPath, basePath) => {
@@ -97,90 +105,75 @@ var fileStructureCreator = (items, basePath) => Effect2.gen(function* (_) {
97
105
  const fullPath = yield* _(resolveProjectPathToBase(item.path, basePath));
98
106
  if (item.isFile) {
99
107
  const dir = path.dirname(fullPath);
100
- yield* _(
101
- Effect2.try({
102
- try: () => fs.mkdirSync(dir, { recursive: true }),
103
- catch: (error) => new FsError(`Failed to create directory ${dir}: ${String(error)}`)
104
- })
105
- );
106
- yield* _(
107
- Effect2.try({
108
- try: () => fs.writeFileSync(fullPath, ""),
109
- catch: (error) => new FsError(`Failed to create file ${fullPath}: ${String(error)}`)
110
- })
111
- );
108
+ yield* _(Effect2.try({
109
+ try: () => fs.mkdirSync(dir, { recursive: true }),
110
+ catch: (error) => new FsError(`Failed to create directory ${dir}: ${String(error)}`)
111
+ }));
112
+ yield* _(Effect2.try({
113
+ try: () => fs.writeFileSync(fullPath, ""),
114
+ catch: (error) => new FsError(`Failed to create file ${fullPath}: ${String(error)}`)
115
+ }));
112
116
  yield* _(Effect2.logInfo(` Created file: ${item.path}`));
113
117
  } else {
114
- yield* _(
115
- Effect2.try({
116
- try: () => fs.mkdirSync(fullPath, { recursive: true }),
117
- catch: (error) => new FsError(`Failed to create directory ${fullPath}: ${String(error)}`)
118
- })
119
- );
118
+ yield* _(Effect2.try({
119
+ try: () => fs.mkdirSync(fullPath, { recursive: true }),
120
+ catch: (error) => new FsError(`Failed to create directory ${fullPath}: ${String(error)}`)
121
+ }));
120
122
  yield* _(Effect2.logInfo(` Created directory: ${item.path}`));
121
123
  }
122
124
  }
123
- yield* _(Effect2.logInfo("\n\u2713 Structure created successfully!"));
125
+ yield* _(Effect2.logInfo(`
126
+ ✓ Structure created successfully!`));
124
127
  });
125
128
 
126
129
  // src/cli.ts
127
- var treeArg = Args.text({ name: "tree" }).pipe(
128
- Args.withDescription("Multiline string representing the directory tree structure")
129
- );
130
- var pathOption = Options.directory("path").pipe(
131
- Options.withAlias("p"),
132
- Options.withDefault("."),
133
- Options.withDescription("Target folder path (defaults to current directory)")
134
- );
135
- var dryRunOption = Options.boolean("dry-run").pipe(
136
- Options.withAlias("n"),
137
- Options.withDefault(false),
138
- Options.withDescription("Skip the top-level directory if there is only one")
139
- );
140
- var verboseOption = Options.boolean("verbose").pipe(
141
- Options.withAlias("v"),
142
- Options.withDefault(false),
143
- Options.withDescription("Log to console extra information about creating a directory tree")
144
- );
130
+ var treeArg = Args.text({ name: "tree" }).pipe(Args.withDescription("Multiline string representing the directory tree structure"), Args.optional);
131
+ var pathOption = Options.directory("path").pipe(Options.withAlias("p"), Options.withDefault("."), Options.withDescription("Target folder path (defaults to current directory)"));
132
+ var dryRunOption = Options.boolean("dry-run").pipe(Options.withAlias("n"), Options.withDefault(false), Options.withDescription("Skip the top-level directory if there is only one"));
133
+ var verboseOption = Options.boolean("verbose").pipe(Options.withAlias("v"), Options.withDefault(false), Options.withDescription("Log to console extra information about creating a directory tree"));
145
134
  var command = Command.make("touch-all", {
146
135
  tree: treeArg,
147
136
  path: pathOption,
148
137
  dryRun: dryRunOption,
149
138
  verbose: verboseOption
150
- }).pipe(
151
- Command.withDescription("Create directory structure from a tree representation"),
152
- Command.withHandler(({ tree, path: targetPath, dryRun = false, verbose }) => {
153
- const program = Effect3.gen(function* (_) {
154
- if (dryRun) {
155
- yield* _(Effect3.logInfo("Running in dry mode. No one file system node will be created."));
156
- }
157
- yield* _(Effect3.logInfo("Parsing tree structure..."));
158
- const items = parserFolderStructure(tree);
159
- if (items.length === 0) {
160
- yield* _(Console.error("No valid items found in the tree structure"));
161
- yield* _(Console.error(items));
162
- yield* _(Console.error(tree));
163
- return yield* _(Effect3.fail(new Error("Invalid tree structure")));
164
- }
165
- yield* _(Effect3.logInfo(`Found ${items.length} items to create`));
166
- yield* _(Effect3.logInfo(`Found:
139
+ }).pipe(Command.withDescription("Create directory structure from a tree representation"), Command.withHandler(({ tree, path: targetPath, dryRun = false, verbose }) => {
140
+ const readStdin = Effect3.tryPromise({
141
+ try: () => new Promise((resolve2) => {
142
+ const rl = createInterface({ input: process.stdin });
143
+ const lines = [];
144
+ rl.on("line", (line) => lines.push(line));
145
+ rl.on("close", () => resolve2(lines.join(`
146
+ `)));
147
+ }),
148
+ catch: (e) => new Error(`Failed to read stdin: ${String(e)}`)
149
+ });
150
+ const program = Effect3.gen(function* (_) {
151
+ const treeString = Option.isSome(tree) ? tree.value : yield* _(readStdin);
152
+ if (dryRun) {
153
+ yield* _(Effect3.logInfo("Running in dry mode. No one file system node will be created."));
154
+ }
155
+ yield* _(Effect3.logInfo("Parsing tree structure..."));
156
+ const items = parserFolderStructure(treeString);
157
+ if (items.length === 0) {
158
+ yield* _(Console.error("No valid items found in the tree structure"));
159
+ yield* _(Console.error(items));
160
+ yield* _(Console.error(treeString));
161
+ return yield* _(Effect3.fail(new Error("Invalid tree structure")));
162
+ }
163
+ yield* _(Effect3.logInfo(`Found ${items.length} items to create`));
164
+ yield* _(Effect3.logInfo(`Found:
167
165
  ${items.map((i) => `${i.path}
168
166
  `).join("")}`));
169
- if (!dryRun) {
170
- yield* _(fileStructureCreator(items, targetPath));
171
- }
172
- });
173
- return verbose ? program.pipe(Logger.withMinimumLogLevel(LogLevel.Info)) : program;
174
- })
175
- );
167
+ if (!dryRun) {
168
+ yield* _(fileStructureCreator(items, targetPath));
169
+ }
170
+ });
171
+ return verbose ? program.pipe(Logger.withMinimumLogLevel(LogLevel.Info)) : program;
172
+ }));
176
173
  var cli = Command.run(command, {
177
174
  name: "Touch All",
178
175
  version: "0.0.1"
179
176
  });
180
177
 
181
178
  // src/touch-all.ts
182
- Effect4.suspend(() => cli(process.argv)).pipe(
183
- Logger2.withMinimumLogLevel(LogLevel2.Warning),
184
- Effect4.provide(NodeContext.layer),
185
- NodeRuntime.runMain
186
- );
179
+ Effect4.suspend(() => cli(process.argv)).pipe(Logger2.withMinimumLogLevel(LogLevel2.Warning), Effect4.provide(NodeContext.layer), NodeRuntime.runMain);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "touch-all",
3
- "version": "0.0.1",
3
+ "version": "1.1.10",
4
4
  "description": "CLI tool to create folder structures from markdown tree representations",
5
5
  "keywords": [
6
6
  "cli",
@@ -11,8 +11,12 @@
11
11
  ],
12
12
  "license": "GPL-3.0-or-later",
13
13
  "author": "Anton Huz <anton@ahuz.dev>",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/anton-huz/touch-all"
17
+ },
14
18
  "bin": {
15
- "touch-all": "./dist/slim/touch-all.js"
19
+ "touch-all": "dist/slim/touch-all.js"
16
20
  },
17
21
  "files": [
18
22
  "dist/bundled",
@@ -27,6 +31,10 @@
27
31
  "import": "./dist/lib/index.js"
28
32
  }
29
33
  },
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "provenance": true
37
+ },
30
38
  "scripts": {
31
39
  "build": "tsup --config .config/tsup.config.ts",
32
40
  "check:lint": "oxlint . -c .config/.oxlintrc.json",
@@ -41,7 +49,8 @@
41
49
  },
42
50
  "devDependencies": {
43
51
  "@total-typescript/tsconfig": "1.0.4",
44
- "@types/node": "22.10.2",
52
+ "@types/bun": "1.3.9",
53
+ "@types/node": "25.3.2",
45
54
  "oxfmt": "0.28.0",
46
55
  "oxlint": "1.43.0",
47
56
  "tsup": "8.5.1",
@@ -50,5 +59,6 @@
50
59
  },
51
60
  "engines": {
52
61
  "node": ">=18"
53
- }
62
+ },
63
+ "url": "https://github.com/anton-huz/touch-all"
54
64
  }