@cmorales_/touch 1.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 ADDED
@@ -0,0 +1,103 @@
1
+ # @cmorales/touch
2
+
3
+ Creating complex file structures has never been easier. `@cmorales/touch` is a powerful CLI tool that allows you to generate nested directories and files using a simple and intuitive syntax.
4
+
5
+ ## Installation
6
+
7
+ You can use the package directly via `npx` or install it globally.
8
+
9
+ ### Using `npx` (Recommended)
10
+
11
+ ```bash
12
+ npx @cmorales/touch <structure>
13
+ ```
14
+
15
+ ### Global Installation
16
+
17
+ ```bash
18
+ npm install -g @cmorales/touch
19
+ # or
20
+ pnpm add -g @cmorales/touch
21
+ # or
22
+ bun add -g @cmorales/touch
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ The basic syntax is:
28
+
29
+ ```bash
30
+ touch <input>
31
+ ```
32
+
33
+ Where `<input>` describes the file and folder structure you want to create.
34
+
35
+ ### Key Features
36
+
37
+ #### 1. Creating Directories
38
+ To create a directory, simply append a `/` to the name.
39
+ - `app/` -> Creates a folder named `app`.
40
+ - `app/models/` -> Creates an `app` folder and a `models` folder inside it.
41
+
42
+ #### 2. Grouping
43
+ You can group multiple items at the same level using `{}`, `[]`, or `()`. All work identically and can be used interchangeably.
44
+
45
+ - **Curly Braces:** `src/{components,utils}`
46
+ - **Square Brackets:** `src/[components,utils]`
47
+ - **Parentheses:** `src/(components,utils)`
48
+
49
+ All the above commands create `src/components` and `src/utils`.
50
+
51
+ #### 3. Nesting
52
+ You can nest groups within groups to build complex trees.
53
+
54
+ ```bash
55
+ src/{components/{Button,Input},utils}
56
+ ```
57
+ Creates:
58
+ - `src/components/Button`
59
+ - `src/components/Input`
60
+ - `src/utils`
61
+
62
+ #### 4. Extensions
63
+ You can specify extensions in two ways:
64
+
65
+ - **Per File:** Directly in the name.
66
+ `src/{index.ts,styles.css}`
67
+
68
+ - **Default Extension:** If you provide an extension at the end of a group, it will be applied to all files in that group that don't already have one.
69
+
70
+ `src/{components,utils}.ts`
71
+ Creates:
72
+ - `src/components.ts`
73
+ - `src/utils.ts`
74
+
75
+ ### Examples
76
+
77
+ **React Component Structure:**
78
+ ```bash
79
+ src/components/Button/{index.tsx,styles.module.css,types.ts}
80
+ ```
81
+
82
+ **MVC Backend Structure:**
83
+ ```bash
84
+ src/{controllers/,models/,routes/,services/}
85
+ ```
86
+
87
+ **Complex Project Setup:**
88
+ ```bash
89
+ my-app/{src/{components/,hooks/,utils/},public/,package.json,README.md}
90
+ ```
91
+
92
+ ## About the Author
93
+
94
+ Created by **Cristian Morales**.
95
+
96
+ - **GitHub:** [Cristian-F-M](https://github.com/Cristian-F-M)
97
+ - **Twitter:** [@Morales_M20](https://x.com/Morales_M20)
98
+
99
+ ## Support
100
+
101
+ If you find this tool useful, you can support my work!
102
+
103
+ <a href="https://www.buymeacoffee.com/cmorales" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ declare const NodeTypes: {
3
+ readonly DIR: "dir";
4
+ readonly FILE: "file";
5
+ };
6
+
7
+ type NodeType = typeof NodeTypes
8
+
9
+ type Node =
10
+ | { type: NodeType['DIR']; name: string; children: Node[] }
11
+ | { type: NodeType['FILE']; name: string }
12
+
13
+ declare function build(tree: Node, base: string, defaultExt?: string): void;
14
+
15
+ declare function parse(input: string): Node;
16
+ declare function getExtension(input: string): string;
17
+
18
+ export { build, getExtension, parse };
package/dist/index.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/builder.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+
7
+ // src/logger.ts
8
+ import pc from "picocolors";
9
+ var log = (msg) => console.log(pc.green(msg));
10
+ var warn = (msg) => console.log(pc.yellow(msg));
11
+ var success = (msg) => console.log(pc.bold(pc.cyan(msg)));
12
+
13
+ // src/builder.ts
14
+ function build(tree, base, defaultExt = "") {
15
+ let files = 0;
16
+ let dirs = 0;
17
+ function walk(node, current) {
18
+ if (node.type === "dir") {
19
+ const dirPath = path.join(current, node.name);
20
+ if (fs.existsSync(dirPath)) warn(`exists: ${dirPath}`);
21
+ if (!fs.existsSync(dirPath)) {
22
+ fs.mkdirSync(dirPath, { recursive: true });
23
+ log(`created: ${dirPath}`);
24
+ dirs++;
25
+ }
26
+ node.children?.forEach((c) => walk(c, dirPath));
27
+ return;
28
+ }
29
+ let file = node.name;
30
+ if (!fs.existsSync(file) && defaultExt && path.extname(file).length <= 1)
31
+ file += defaultExt;
32
+ const filePath = path.join(current, file);
33
+ if (fs.existsSync(filePath)) return warn(`exists: ${filePath}`);
34
+ fs.writeFileSync(filePath, "");
35
+ log(`created: ${filePath}`);
36
+ files++;
37
+ }
38
+ walk(tree, base);
39
+ success(`
40
+ \u2714 Created ${files} files and ${dirs} folders
41
+ `);
42
+ }
43
+
44
+ // src/constants/symbols.ts
45
+ var OPEN_SYMBOLS = {
46
+ "{": "{",
47
+ "(": "(",
48
+ "[": "["
49
+ };
50
+ var CLOSE_SYMBOLS = {
51
+ "}": "}",
52
+ ")": ")",
53
+ "]": "]"
54
+ };
55
+ var SEPARATOR_SYMBOLS = { ",": "," };
56
+ var FOLDER_INDICATOR_SYMBOLS = { "/": "/", "\\": "\\" };
57
+ var OPEN_CLOSE_PAIRS = {
58
+ "(": ")",
59
+ "{": "}",
60
+ "[": "]"
61
+ };
62
+
63
+ // src/parser.ts
64
+ function parse(input) {
65
+ let i = 0;
66
+ function walk() {
67
+ let name = "";
68
+ let char = input[i];
69
+ while (i < input.length && !Object.hasOwn(
70
+ Object.assign(
71
+ {},
72
+ OPEN_SYMBOLS,
73
+ CLOSE_SYMBOLS,
74
+ SEPARATOR_SYMBOLS,
75
+ FOLDER_INDICATOR_SYMBOLS
76
+ ),
77
+ char
78
+ )) {
79
+ name += char;
80
+ char = input[++i];
81
+ }
82
+ name = name.trim();
83
+ if (Object.hasOwn(FOLDER_INDICATOR_SYMBOLS, char ?? "")) {
84
+ i++;
85
+ char = input[i];
86
+ if (!Object.hasOwn(OPEN_SYMBOLS, char ?? "")) {
87
+ const children = [];
88
+ if (i < input.length && !Object.hasOwn(SEPARATOR_SYMBOLS, input[i]) && !Object.hasOwn(CLOSE_SYMBOLS, input[i]))
89
+ children.push(walk());
90
+ return { type: "dir", name, children };
91
+ }
92
+ }
93
+ if (Object.hasOwn(OPEN_SYMBOLS, char ?? "")) {
94
+ const opener = input[i++];
95
+ const closer = OPEN_CLOSE_PAIRS[OPEN_SYMBOLS[opener]];
96
+ const children = [];
97
+ char = input[i];
98
+ while (i < input.length && char !== closer) {
99
+ children.push(walk());
100
+ if (input[i] === ",") i++;
101
+ char = input[i];
102
+ }
103
+ i++;
104
+ return { type: "dir", name, children };
105
+ }
106
+ return { type: "file", name };
107
+ }
108
+ return walk();
109
+ }
110
+ function getExtension(input) {
111
+ let s = [];
112
+ let i = 0;
113
+ const closes = Object.values(CLOSE_SYMBOLS);
114
+ while (s.length <= 1 && i < closes.length) {
115
+ s = input.split(closes[i]);
116
+ i++;
117
+ }
118
+ const ext = s.at(-1);
119
+ return ext ? `${ext}` : "";
120
+ }
121
+ export {
122
+ build,
123
+ getExtension,
124
+ parse
125
+ };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "name": "@cmorales_/touch",
4
+ "module": "/dist/index.js",
5
+ "type": "module",
6
+ "private": false,
7
+ "scripts": {
8
+ "build": "tsup src/index.ts --format esm --dts",
9
+ "test": "vitest"
10
+ },
11
+ "bin": {
12
+ "touch": "dist/index.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "package.json",
18
+ "LICENSE"
19
+ ],
20
+ "devDependencies": {
21
+ "@biomejs/biome": "^2.3.13",
22
+ "@types/bun": "latest",
23
+ "tsup": "^8.5.1",
24
+ "vitest": "^4.0.18"
25
+ },
26
+ "peerDependencies": {
27
+ "typescript": "^5"
28
+ },
29
+ "dependencies": {
30
+ "picocolors": "^1.1.1"
31
+ }
32
+ }