create-fs-structure 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/LICENSE +22 -0
- package/README.md +43 -0
- package/bin/cli.cjs +139 -0
- package/bin/index.cjs +4 -0
- package/bin/package.json +4 -0
- package/lib/createStructure.cjs +154 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
```text
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2025 Maxim Bulavchikov
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Create Structure
|
|
2
|
+
|
|
3
|
+
CLI tool to create directory and file structure from a text scheme.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install globally
|
|
9
|
+
npm install -g create-structure
|
|
10
|
+
|
|
11
|
+
# Or use with npx
|
|
12
|
+
npx create-structure scheme.txt ./my-project
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
```bash
|
|
17
|
+
# Create structure from scheme.ts (or .txt) in current directory
|
|
18
|
+
create-structure
|
|
19
|
+
|
|
20
|
+
# Specify scheme file and target directory
|
|
21
|
+
create-structure ./my-scheme.txt ./my-project
|
|
22
|
+
|
|
23
|
+
# Dry run (show what would be created)
|
|
24
|
+
create-structure --dry-run scheme.txt ./my-project
|
|
25
|
+
|
|
26
|
+
# Show help
|
|
27
|
+
create-structure --help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Scheme Format
|
|
31
|
+
```bash
|
|
32
|
+
# Create a text (.ts or .txt) file with your desired structure:
|
|
33
|
+
|
|
34
|
+
text
|
|
35
|
+
├── components/
|
|
36
|
+
│ ├── Button/
|
|
37
|
+
│ │ ├── Button.tsx
|
|
38
|
+
│ │ └── index.ts
|
|
39
|
+
│ └── Header.tsx
|
|
40
|
+
├── utils/
|
|
41
|
+
│ └── helpers.ts
|
|
42
|
+
└── package.json
|
|
43
|
+
```
|
package/bin/cli.cjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { createStructureFromScheme, parseLine } = require('../lib/createStructure.cjs');
|
|
3
|
+
const { existsSync, statSync, readFileSync } = require('fs');
|
|
4
|
+
const { join, resolve } = require('path');
|
|
5
|
+
|
|
6
|
+
// Функция для вывода справки
|
|
7
|
+
function showHelp() {
|
|
8
|
+
console.log(`
|
|
9
|
+
Использование: node create-structure.js [ПУТЬ_К_СХЕМЕ] [ЦЕЛЕВАЯ_ПАПКА]
|
|
10
|
+
|
|
11
|
+
Аргументы:
|
|
12
|
+
ПУТЬ_К_СХЕМЕ Путь к файлу схемы (по умолчанию: ./scheme.ts)
|
|
13
|
+
ЦЕЛЕВАЯ_ПАПКА Путь к папке для создания структуры (по умолчанию: текущая папка)
|
|
14
|
+
|
|
15
|
+
Особенности:
|
|
16
|
+
- Если файл уже существует, он не будет перезаписан
|
|
17
|
+
- Если папка уже существует, скрипт продолжит работу внутри нее
|
|
18
|
+
- Несуществующие родительские папки будут созданы автоматически
|
|
19
|
+
- Файлы получают базовое содержимое в зависимости от расширения
|
|
20
|
+
|
|
21
|
+
Примеры:
|
|
22
|
+
node create-structure.js # Использует scheme.ts и создает в текущей папке
|
|
23
|
+
node create-structure.js ./my-scheme.txt # Использует my-scheme.txt в текущей папке
|
|
24
|
+
node create-structure.js scheme.ts ./my-app # Использует scheme.ts, создает в ./my-app
|
|
25
|
+
node create-structure.js ../scheme.txt ../proj # Использует схему на уровень выше
|
|
26
|
+
|
|
27
|
+
Поддерживаемые форматы схемы:
|
|
28
|
+
├── components/ # Папка
|
|
29
|
+
│ ├── Button.tsx # Файл
|
|
30
|
+
└── utils/ # Папка
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function dryRun(schemeFile, targetDir) {
|
|
35
|
+
console.log(`📄 Схема: ${schemeFile}`);
|
|
36
|
+
console.log(`📁 Целевая папка: ${resolve(targetDir)}`);
|
|
37
|
+
|
|
38
|
+
if (!existsSync(schemeFile)) {
|
|
39
|
+
console.error(`❌ Файл схемы не найден: ${schemeFile}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const content = readFileSync(schemeFile, "utf8");
|
|
44
|
+
const lines = content.split("\n");
|
|
45
|
+
|
|
46
|
+
console.log("\n📋 Планируемые действия:");
|
|
47
|
+
|
|
48
|
+
const pathStack = [targetDir];
|
|
49
|
+
let minLevel = Infinity;
|
|
50
|
+
const parsedLines = [];
|
|
51
|
+
|
|
52
|
+
// Парсим все строки
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
if (!line.trim()) continue;
|
|
55
|
+
const parsed = parseLine(line);
|
|
56
|
+
if (!parsed) continue;
|
|
57
|
+
if (parsed.level < minLevel) minLevel = parsed.level;
|
|
58
|
+
parsedLines.push(parsed);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Симулируем создание
|
|
62
|
+
for (const parsed of parsedLines) {
|
|
63
|
+
const normalizedLevel = parsed.level - minLevel;
|
|
64
|
+
while (pathStack.length > normalizedLevel + 1) {
|
|
65
|
+
pathStack.pop();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const currentPath = pathStack.slice(1).join("/");
|
|
69
|
+
const fullPath = join(targetDir, currentPath, parsed.name);
|
|
70
|
+
|
|
71
|
+
if (existsSync(fullPath)) {
|
|
72
|
+
const stats = statSync(fullPath);
|
|
73
|
+
const type = stats.isDirectory() ? "папка" : "файл";
|
|
74
|
+
console.log(
|
|
75
|
+
` ⏭️ Будет пропущен (уже существует): ${fullPath} (${type})`
|
|
76
|
+
);
|
|
77
|
+
} else {
|
|
78
|
+
const type = parsed.isDir ? "папка" : "файл";
|
|
79
|
+
console.log(` ✅ Будет создан: ${fullPath} (${type})`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (parsed.isDir) {
|
|
83
|
+
pathStack.push(parsed.name);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(
|
|
88
|
+
"\n🔍 Проверка завершена. Для создания файлов запустите без флага --dry-run"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function main() {
|
|
93
|
+
const args = process.argv.slice(2);
|
|
94
|
+
|
|
95
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
96
|
+
showHelp();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
101
|
+
console.log("create-structure v1.0.0");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const dryRunFlag = args.includes("--dry-run");
|
|
106
|
+
if (dryRunFlag) {
|
|
107
|
+
console.log("🔍 Режим проверки (dry run) - файлы не будут созданы");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const filteredArgs = args.filter(
|
|
111
|
+
(arg) => !arg.startsWith("--") && !arg.startsWith("-")
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
let schemeFile = "scheme.ts";
|
|
115
|
+
let targetDir = ".";
|
|
116
|
+
|
|
117
|
+
if (filteredArgs.length === 1) {
|
|
118
|
+
const arg = filteredArgs[0];
|
|
119
|
+
if (existsSync(arg) && statSync(arg).isFile()) {
|
|
120
|
+
schemeFile = arg;
|
|
121
|
+
} else {
|
|
122
|
+
targetDir = arg;
|
|
123
|
+
}
|
|
124
|
+
} else if (filteredArgs.length >= 2) {
|
|
125
|
+
schemeFile = filteredArgs[0];
|
|
126
|
+
targetDir = filteredArgs[1];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (dryRunFlag) {
|
|
130
|
+
dryRun(schemeFile, targetDir);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Запускаем создание структуры
|
|
135
|
+
createStructureFromScheme(schemeFile, targetDir);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Запуск
|
|
139
|
+
main();
|
package/bin/index.cjs
ADDED
package/bin/package.json
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } = require('fs');
|
|
2
|
+
const { join, dirname, extname, basename, resolve } = require('path');
|
|
3
|
+
|
|
4
|
+
function parseLine(line) {
|
|
5
|
+
// Удаляем символы дерева, оставляем только отступы
|
|
6
|
+
let indent = 0;
|
|
7
|
+
|
|
8
|
+
// Считаем отступы до первого не-дерева символа
|
|
9
|
+
for (let i = 0; i < line.length; i++) {
|
|
10
|
+
const char = line[i];
|
|
11
|
+
if (
|
|
12
|
+
char === " " ||
|
|
13
|
+
char === "│" ||
|
|
14
|
+
char === "├" ||
|
|
15
|
+
char === "└" ||
|
|
16
|
+
char === "─"
|
|
17
|
+
) {
|
|
18
|
+
indent++;
|
|
19
|
+
} else {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Каждые 4 символа = 1 уровень
|
|
25
|
+
const level = Math.floor(indent / 4);
|
|
26
|
+
|
|
27
|
+
// Получаем чистую строку
|
|
28
|
+
const clean = line.slice(indent).trim();
|
|
29
|
+
if (!clean) return null;
|
|
30
|
+
|
|
31
|
+
const isDir = clean.endsWith("/");
|
|
32
|
+
const name = isDir ? clean.slice(0, -1) : clean;
|
|
33
|
+
|
|
34
|
+
return { level, isDir, name };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createStructureFromScheme(schemeFile, targetDir = ".") {
|
|
38
|
+
try {
|
|
39
|
+
if (!existsSync(schemeFile)) {
|
|
40
|
+
throw new Error(`Файл схемы не найден: ${schemeFile}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const content = readFileSync(schemeFile, "utf8");
|
|
44
|
+
const lines = content.split("\n");
|
|
45
|
+
|
|
46
|
+
const pathStack = [targetDir];
|
|
47
|
+
let createdCount = 0;
|
|
48
|
+
let skippedCount = 0;
|
|
49
|
+
|
|
50
|
+
console.log(`📄 Чтение схемы из: ${schemeFile}`);
|
|
51
|
+
console.log(`📁 Создание структуры в: ${resolve(targetDir)}\n`);
|
|
52
|
+
|
|
53
|
+
// Сначала найдем минимальный отступ, чтобы нормализовать уровни
|
|
54
|
+
let minLevel = Infinity;
|
|
55
|
+
const parsedLines = [];
|
|
56
|
+
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
if (!line.trim()) continue;
|
|
59
|
+
|
|
60
|
+
// Парсим строку
|
|
61
|
+
const parsed = parseLine(line);
|
|
62
|
+
if (!parsed) continue;
|
|
63
|
+
|
|
64
|
+
// Запоминаем минимальный уровень
|
|
65
|
+
if (parsed.level < minLevel) {
|
|
66
|
+
minLevel = parsed.level;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
parsedLines.push(parsed);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Обрабатываем каждую строку
|
|
73
|
+
for (const parsed of parsedLines) {
|
|
74
|
+
// Нормализуем уровень (вычитаем минимальный)
|
|
75
|
+
const normalizedLevel = parsed.level - minLevel;
|
|
76
|
+
|
|
77
|
+
// Обновляем стек пути - это КРИТИЧЕСКИ важная часть!
|
|
78
|
+
while (pathStack.length > normalizedLevel + 1) {
|
|
79
|
+
pathStack.pop();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Строим полный путь
|
|
83
|
+
const currentPath = pathStack.slice(1).join("/");
|
|
84
|
+
const fullPath = join(targetDir, currentPath, parsed.name);
|
|
85
|
+
|
|
86
|
+
// Создаем элемент
|
|
87
|
+
if (parsed.isDir) {
|
|
88
|
+
try {
|
|
89
|
+
if (existsSync(fullPath)) {
|
|
90
|
+
console.log(`✓ Папка уже существует: ${fullPath}`);
|
|
91
|
+
skippedCount++;
|
|
92
|
+
} else {
|
|
93
|
+
mkdirSync(fullPath, { recursive: true });
|
|
94
|
+
console.log(`✓ Создана новая папка: ${fullPath}`);
|
|
95
|
+
createdCount++;
|
|
96
|
+
}
|
|
97
|
+
// Добавляем папку в стек для следующего уровня
|
|
98
|
+
pathStack.push(parsed.name);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(`✗ Ошибка создания папки ${fullPath}:`, err.message);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
try {
|
|
104
|
+
// Создаем родительскую папку если нужно
|
|
105
|
+
const parentDir = dirname(fullPath);
|
|
106
|
+
if (!existsSync(parentDir)) {
|
|
107
|
+
mkdirSync(parentDir, { recursive: true });
|
|
108
|
+
console.log(`✓ Создана родительская папка: ${parentDir}`);
|
|
109
|
+
createdCount++;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Проверяем существование файла
|
|
113
|
+
if (existsSync(fullPath)) {
|
|
114
|
+
console.log(`✓ Файл уже существует: ${fullPath}`);
|
|
115
|
+
skippedCount++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Создаем файл с базовым содержимым
|
|
120
|
+
const ext = extname(parsed.name);
|
|
121
|
+
let content = "";
|
|
122
|
+
|
|
123
|
+
if (ext === ".tsx" || ext === ".ts") {
|
|
124
|
+
const componentName = basename(parsed.name, ext);
|
|
125
|
+
content = `// ${parsed.name}\n// Auto-generated\n\nexport const ${componentName} = () => {\n return (\n <div>\n {/* Content */}\n </div>\n );\n}\n`;
|
|
126
|
+
} else if (ext === ".css") {
|
|
127
|
+
content = `/* ${parsed.name} */\n/* Auto-generated */\n\n.container {\n /* Styles */\n}\n`;
|
|
128
|
+
} else {
|
|
129
|
+
content = `// ${parsed.name}\n// Auto-generated\n`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
writeFileSync(fullPath, content);
|
|
133
|
+
console.log(`✓ Создан новый файл: ${fullPath}`);
|
|
134
|
+
createdCount++;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error(`✗ Ошибка создания файла ${fullPath}:`, err.message);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`\n📊 Результат:`);
|
|
142
|
+
console.log(` ✅ Создано новых элементов: ${createdCount}`);
|
|
143
|
+
console.log(` ⏭️ Пропущено существующих: ${skippedCount}`);
|
|
144
|
+
console.log(`\n🎉 Структура успешно создана в ${resolve(targetDir)}`);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error("❌ Ошибка:", error.message);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = {
|
|
152
|
+
parseLine,
|
|
153
|
+
createStructureFromScheme
|
|
154
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-fs-structure",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to create directory and file structure from a text scheme",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-structure": "./bin/cli.cjs"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"scaffolding",
|
|
15
|
+
"generator",
|
|
16
|
+
"cli",
|
|
17
|
+
"structure",
|
|
18
|
+
"filesystem"
|
|
19
|
+
],
|
|
20
|
+
"author": "Your Name <your.email@example.com>",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/your-username/create-structure.git"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/your-username/create-structure/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/your-username/create-structure#readme",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=14.0.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {},
|
|
34
|
+
"devDependencies": {}
|
|
35
|
+
}
|