From b53248e13401caf327aab097cf8fa2ecfda53da1 Mon Sep 17 00:00:00 2001 From: "S.Gromov" Date: Tue, 27 Jan 2026 15:18:43 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=20=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D0=B0=D1=81=D0=BE=D0=BC=20create=20=D0=B8=20=D0=BA=D0=BE?= =?UTF-8?q?=D1=80=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D1=8B=D0=B5=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Обновлены примеры в README и FEATURES с использованием алиаса create - Исправлены подсказки help на основной бин gromlab-create - Возвращены внутренние вызовы автодополнения на gromlab-create --- README.md | 21 ++++++--- docs/ru/FEATURES.md | 6 +-- package.json | 2 +- src/args.ts | 7 +-- src/completion.ts | 101 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 119 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 215b99a..08fd7a5 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,28 @@ npm i -g @gromlab/create ## Автодополнение -Сгенерируйте скрипт и подключите его в оболочке: +Установка вместе с автодополнением (одной командой): +**bash** ```bash -gromlab-create completion --shell bash +npm i -g @gromlab/create && create install-autocomplete --shell bash && source ~/.bashrc +``` + +**zsh** +```bash +npm i -g @gromlab/create && create install-autocomplete --shell zsh && source ~/.zshrc +``` + +**fish** +```bash +npm i -g @gromlab/create && create install-autocomplete --shell fish && exec fish ``` ## Использование ```bash -npx @gromlab/create <шаблон> <имя> [путь] [опции] +create <шаблон> <имя> [путь] [опции] ``` Если `[путь]` не указан, файлы создаются в директории, где запущен CLI. @@ -34,10 +45,10 @@ npx @gromlab/create <шаблон> <имя> [путь] [опции] ```bash # Создать компонент из шаблона -npx @gromlab/create component Button +create component Button # Указать папку вывода позиционно -npx @gromlab/create component Button src/components +create component Button src/components ``` ## Шаблоны diff --git a/docs/ru/FEATURES.md b/docs/ru/FEATURES.md index 0ae1189..2295d6c 100644 --- a/docs/ru/FEATURES.md +++ b/docs/ru/FEATURES.md @@ -42,9 +42,10 @@ ## Автодополнение -- Генерация скрипта автодополнения для bash/zsh/fish: `gromlab-create completion --shell `. +- Генерация скрипта автодополнения для bash/zsh/fish: `create completion --shell `. - Автодополнение доступно только для глобальной установки CLI. - Автодополнение шаблонов из ближайшей `.templates/` (по дереву вверх) и переменных `--` выбранного шаблона (кроме `name`). +- Команда `install-autocomplete` устанавливает автодополнение в конфигурацию оболочки (bash/zsh) или файл fish‑completions. ## Обновления CLI @@ -62,8 +63,7 @@ ## Бины -- Основной бин: `gromlab-create`. -- Дополнительный алиас: `create`. +- Основной бин: `gromlab-create` (в примерах используется алиас `create`). ## Служебные команды diff --git a/package.json b/package.json index 455d12b..55919e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gromlab/create", - "version": "0.1.3", + "version": "0.1.4", "description": "Template-based file generator CLI", "license": "MIT", "publishConfig": { diff --git a/src/args.ts b/src/args.ts index 99f1970..a0b1cb6 100644 --- a/src/args.ts +++ b/src/args.ts @@ -3,7 +3,7 @@ import { ParsedArgs } from './types'; export function printHelp() { const lines = [ 'Использование:', - ' npx @gromlab/create <шаблон> <имя> [путь] [опции]', + ' gromlab-create <шаблон> <имя> [путь] [опции]', '', 'Аргументы:', ' <шаблон> Имя шаблона', @@ -12,6 +12,7 @@ export function printHelp() { '', 'Команды:', ' completion --shell Сгенерировать скрипт автодополнения', + ' install-autocomplete [--shell ] Установить автодополнение', '', 'Опции:', ' -- Переменная шаблона (поддерживается любой --key )', @@ -20,8 +21,8 @@ export function printHelp() { ' -h, --help Показать эту справку', '', 'Примеры:', - ' npx @gromlab/create component Button', - ' npx @gromlab/create component Button src/components' + ' gromlab-create component Button', + ' gromlab-create component Button src/components' ]; console.log(lines.join('\n')); } diff --git a/src/completion.ts b/src/completion.ts index 838496f..e42f445 100644 --- a/src/completion.ts +++ b/src/completion.ts @@ -4,6 +4,8 @@ import { collectTemplateVariables, listTemplateNames } from './templateUtils'; import { detectRunMode } from './runtime'; const BIN_NAMES = ['gromlab-create', 'create']; +const COMPLETION_BLOCK_START = '# gromlab-create completion start'; +const COMPLETION_BLOCK_END = '# gromlab-create completion end'; function findNearestTemplatesDir(startDir: string): string | undefined { let current = path.resolve(startDir); @@ -205,9 +207,69 @@ function resolveShell(args: string[]): string | undefined { return candidate; } +function normalizeShellName(shell?: string): string | undefined { + if (!shell) return undefined; + let normalized = shell.trim().toLowerCase(); + if (normalized === 'zh') normalized = 'zsh'; + if (normalized === 'bash' || normalized === 'zsh' || normalized === 'fish') return normalized; + return undefined; +} + +function detectShellFromEnv(): string | undefined { + const envShell = process.env.SHELL ?? ''; + const base = path.basename(envShell).toLowerCase(); + if (base === 'bash' || base === 'zsh' || base === 'fish') return base; + return undefined; +} + function printCompletionUsage() { console.log('Использование:'); console.log(' gromlab-create completion --shell '); + console.log(' gromlab-create install-autocomplete [--shell ]'); +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function installForBashOrZsh(shell: 'bash' | 'zsh') { + const home = process.env.HOME ?? ''; + const rcFile = shell === 'bash' + ? path.join(home, '.bashrc') + : path.join(home, '.zshrc'); + const sourceLine = `source <(gromlab-create completion --shell ${shell})`; + const block = `${COMPLETION_BLOCK_START}\n${sourceLine}\n${COMPLETION_BLOCK_END}`; + const start = escapeRegExp(COMPLETION_BLOCK_START); + const end = escapeRegExp(COMPLETION_BLOCK_END); + const blockRegex = new RegExp(`${start}[\\s\\S]*?${end}`, 'm'); + + let content = ''; + try { + if (fs.existsSync(rcFile)) { + content = fs.readFileSync(rcFile, 'utf8'); + } + } catch { + content = ''; + } + + if (blockRegex.test(content)) { + content = content.replace(blockRegex, block); + } else { + const prefix = content.length > 0 && !content.endsWith('\n') ? '\n' : ''; + content = `${content}${prefix}${block}\n`; + } + + fs.writeFileSync(rcFile, content, 'utf8'); + console.log(`Автодополнение установлено в ${rcFile}`); +} + +function installForFish() { + const home = process.env.HOME ?? ''; + const dir = path.join(home, '.config', 'fish', 'completions'); + const filePath = path.join(dir, 'gromlab-create.fish'); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(filePath, buildFishCompletion(), 'utf8'); + console.log(`Автодополнение установлено в ${filePath}`); } export function handleInternalCommand(args: string[], cwd: string = process.cwd()): boolean { @@ -234,19 +296,17 @@ export function handleInternalCommand(args: string[], cwd: string = process.cwd( return true; } - const shell = resolveShell(rest); + const shell = normalizeShellName(resolveShell(rest)); if (!shell) { printCompletionUsage(); process.exitCode = 1; return true; } - let normalized = shell.trim().toLowerCase(); - if (normalized === 'zh') normalized = 'zsh'; let script = ''; - if (normalized === 'bash') script = buildBashCompletion(); - if (normalized === 'zsh') script = buildZshCompletion(); - if (normalized === 'fish') script = buildFishCompletion(); + if (shell === 'bash') script = buildBashCompletion(); + if (shell === 'zsh') script = buildZshCompletion(); + if (shell === 'fish') script = buildFishCompletion(); if (!script) { console.error('Неизвестный shell. Доступно: bash, zsh, fish.'); @@ -258,5 +318,34 @@ export function handleInternalCommand(args: string[], cwd: string = process.cwd( return true; } + if (command === 'install-autocomplete' || command === 'install-shell') { + if (detectRunMode() !== 'global') { + console.error('Автодополнение доступно только для глобальной установки CLI.'); + process.exitCode = 1; + return true; + } + + const explicitShell = normalizeShellName(resolveShell(rest)); + const shell = explicitShell ?? detectShellFromEnv(); + if (!shell) { + console.error('Не удалось определить shell. Укажите --shell .'); + process.exitCode = 1; + return true; + } + + if (shell === 'bash' || shell === 'zsh') { + installForBashOrZsh(shell); + return true; + } + if (shell === 'fish') { + installForFish(); + return true; + } + + console.error('Неизвестный shell. Доступно: bash, zsh, fish.'); + process.exitCode = 1; + return true; + } + return false; }