feat: добавить программный API для использования как библиотеки

- создан src/index.ts с реэкспортом 14 чистых функций и типов
- перенесён findNearestTemplatesDir из completion.ts в templateUtils.ts
- обновлён package.json: main → dist/index.js, добавлено поле types
- добавлен declaration: true в tsconfig.json для генерации .d.ts
- документирован API в README.md и README_RU.md
- версия поднята до 0.2.0
This commit is contained in:
2026-04-02 18:17:32 +03:00
parent 6604becb0c
commit 96d68c5a63
7 changed files with 92 additions and 21 deletions

View File

@@ -117,3 +117,27 @@ export const {{name.pascalCase}} = () => {
| `--overwrite` | Overwrite existing files | | `--overwrite` | Overwrite existing files |
| `--skip-update` | Skip CLI update check | | `--skip-update` | Skip CLI update check |
| `--<variable> <value>` | Custom template variable | | `--<variable> <value>` | Custom template variable |
## Programmatic API
The package can be used as a library:
```typescript
import { buildPlan, writePlan, collectTemplateVariables } from '@gromlab/create';
```
| Function | Description |
|---|---|
| `renderTemplate(input, vars)` | Substitutes variables and modifiers in a string |
| `collectTemplateVariables(templateDir)` | Collects all variable names from a template |
| `listTemplateNames(templatesDir)` | Lists available templates (subdirectories) |
| `findNearestTemplatesDir(startDir)` | Walks up the directory tree looking for `.templates` |
| `readDirRecursive(dir)` | Recursive list of all files in a directory |
| `resolveTemplateContext(templatesDir, name, vars)` | Validates template and variables |
| `buildPlan(templateDir, outDir, vars, files)` | Builds generation plan (source → target) |
| `writePlan(plan, vars, overwrite)` | Writes files to disk according to the plan |
| `getCollisions(plan)` | Lists plan files that already exist on disk |
| `getExistingDirs(outDir, dirs)` | Checks which directories already exist |
| `getTopLevelDirs(outDir, plan)` | Top-level directories from the plan |
| `getRoots(outDir, plan)` | Root paths for summary output |
| `CASE_MODIFIERS` | Case modifier functions dictionary |

View File

@@ -117,3 +117,27 @@ export const {{name.pascalCase}} = () => {
| `--overwrite` | Перезаписать существующие файлы | | `--overwrite` | Перезаписать существующие файлы |
| `--skip-update` | Не проверять обновления CLI | | `--skip-update` | Не проверять обновления CLI |
| `--<переменная> <значение>` | Произвольная переменная шаблона | | `--<переменная> <значение>` | Произвольная переменная шаблона |
## Программный API
Пакет можно использовать как библиотеку:
```typescript
import { buildPlan, writePlan, collectTemplateVariables } from '@gromlab/create';
```
| Функция | Назначение |
|---|---|
| `renderTemplate(input, vars)` | Подставляет переменные и модификаторы в строку |
| `collectTemplateVariables(templateDir)` | Собирает все имена переменных из шаблона |
| `listTemplateNames(templatesDir)` | Список доступных шаблонов (подпапки) |
| `findNearestTemplatesDir(startDir)` | Ищет `.templates` вверх по дереву каталогов |
| `readDirRecursive(dir)` | Рекурсивный список всех файлов в каталоге |
| `resolveTemplateContext(templatesDir, name, vars)` | Валидация шаблона и переменных |
| `buildPlan(templateDir, outDir, vars, files)` | Построение плана генерации (source → target) |
| `writePlan(plan, vars, overwrite)` | Запись файлов на диск по плану |
| `getCollisions(plan)` | Список файлов из плана, которые уже существуют |
| `getExistingDirs(outDir, dirs)` | Проверка существующих директорий |
| `getTopLevelDirs(outDir, plan)` | Директории верхнего уровня из плана |
| `getRoots(outDir, plan)` | Корневые пути для итогового вывода |
| `CASE_MODIFIERS` | Словарь модификаторов регистра |

View File

@@ -1,6 +1,6 @@
{ {
"name": "@gromlab/create", "name": "@gromlab/create",
"version": "0.1.7", "version": "0.2.0",
"description": "Template-based file generator CLI", "description": "Template-based file generator CLI",
"author": "Gromov Sergei", "author": "Gromov Sergei",
"license": "MIT", "license": "MIT",
@@ -27,7 +27,8 @@
"gromlab-create": "dist/cli.js", "gromlab-create": "dist/cli.js",
"create": "dist/cli.js" "create": "dist/cli.js"
}, },
"main": "dist/cli.js", "main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [ "files": [
"dist" "dist"
], ],

View File

@@ -1,30 +1,12 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { collectTemplateVariables, listTemplateNames } from './templateUtils'; import { collectTemplateVariables, listTemplateNames, findNearestTemplatesDir } from './templateUtils';
import { detectRunMode } from './runtime'; import { detectRunMode } from './runtime';
const BIN_NAMES = ['gromlab-create', 'create']; const BIN_NAMES = ['gromlab-create', 'create'];
const COMPLETION_BLOCK_START = '# gromlab-create completion start'; const COMPLETION_BLOCK_START = '# gromlab-create completion start';
const COMPLETION_BLOCK_END = '# gromlab-create completion end'; const COMPLETION_BLOCK_END = '# gromlab-create completion end';
function findNearestTemplatesDir(startDir: string): string | undefined {
let current = path.resolve(startDir);
while (true) {
const candidate = path.join(current, '.templates');
try {
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
return candidate;
}
} catch {
// ignore errors and keep walking up
}
const parent = path.dirname(current);
if (parent === current) break;
current = parent;
}
return undefined;
}
function listTemplates(cwd: string): string[] { function listTemplates(cwd: string): string[] {
const dir = findNearestTemplatesDir(cwd); const dir = findNearestTemplatesDir(cwd);
try { try {

21
src/index.ts Normal file
View File

@@ -0,0 +1,21 @@
// Программный API для @gromlab/create
// CLI точка входа остаётся в cli.ts (bin)
// Генерация
export { buildPlan, writePlan, getCollisions, getExistingDirs, getTopLevelDirs, getRoots } from './plan';
// Шаблоны
export {
renderTemplate,
collectTemplateVariables,
readDirRecursive,
listTemplateNames,
findNearestTemplatesDir,
CASE_MODIFIERS,
} from './templateUtils';
// Валидация
export { resolveTemplateContext, normalizeArgs } from './validation';
// Типы
export type { PlanItem, TemplateContext, ValidationError, ParsedArgs } from './types';

View File

@@ -70,3 +70,21 @@ export function listTemplateNames(templatesDir: string): string[] {
const entries = fs.readdirSync(templatesDir, { withFileTypes: true }); const entries = fs.readdirSync(templatesDir, { withFileTypes: true });
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(); return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
} }
export function findNearestTemplatesDir(startDir: string): string | undefined {
let current = path.resolve(startDir);
while (true) {
const candidate = path.join(current, '.templates');
try {
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
return candidate;
}
} catch {
// ignore errors and keep walking up
}
const parent = path.dirname(current);
if (parent === current) break;
current = parent;
}
return undefined;
}

View File

@@ -6,6 +6,7 @@
"outDir": "dist", "outDir": "dist",
"rootDir": "src", "rootDir": "src",
"strict": true, "strict": true,
"declaration": true,
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"skipLibCheck": true "skipLibCheck": true