ebely 0.0.2 → 0.0.4

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,34 @@
1
+ ### Быстрый старт из шаблона
2
+
3
+ Склонировать готовый тест-проект (как `create-react-app`):
4
+
5
+ ```sh
6
+ npx ebely create my-tests # в новую/пустую папку
7
+ # или
8
+ npx ebely create # в текущую папку
9
+ ```
10
+
11
+ Затем:
12
+
13
+ ```sh
14
+ cd my-tests
15
+ pnpm install
16
+ # отредактируй ebely/ebely.ts (url, путь к swagger) под свой бэкенд
17
+ pnpm run client:generate
18
+ pnpm test
19
+ ```
20
+
21
+ ### Алгоритм действий для пользователя библиотекой
22
+
23
+
24
+ #### 1. Установить библиотеку
25
+ `pnpm add -D ebely`
26
+
27
+ #### 2. Один раз разложить скиллы в проект
28
+ `npx ebely skills`
29
+
30
+ #### 3. Открыть проект в Claude Code и вызвать настройку
31
+ `/ebely-setup ` # настроит конфиг, сгенерирует клиент, стор, хуки
32
+
33
+ #### 4. Дальше при написании любых тестов
34
+ `/ebely-write-tests ` # опишет сценарий — тесты напишутся сами
package/bin/ebely.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { cp, mkdir, readdir, stat } from 'node:fs/promises'
2
+ import { cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
3
3
  import { existsSync } from 'node:fs'
4
- import { dirname, join, resolve } from 'node:path'
4
+ import { basename, dirname, join, resolve, sep } from 'node:path'
5
5
  import { fileURLToPath } from 'node:url'
6
6
 
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url))
8
8
  const pkgRoot = resolve(__dirname, '..')
9
9
  const skillsSrc = join(pkgRoot, 'skills')
10
+ const templateSrc = join(pkgRoot, 'clone', 'tests')
10
11
 
11
12
  async function listSkills() {
12
13
  const entries = await readdir(skillsSrc, { withFileTypes: true })
@@ -38,10 +39,101 @@ async function installSkills({ toUser }) {
38
39
  )
39
40
  }
40
41
 
42
+ const STANDALONE_TSCONFIG = `{
43
+ "compilerOptions": {
44
+ "target": "es2022",
45
+ "module": "ESNext",
46
+ "moduleResolution": "Bundler",
47
+ "lib": ["es2022", "dom", "dom.iterable"],
48
+ "types": ["node"],
49
+ "strict": true,
50
+ "noUncheckedIndexedAccess": true,
51
+ "esModuleInterop": true,
52
+ "skipLibCheck": true,
53
+ "verbatimModuleSyntax": true,
54
+ "resolveJsonModule": true,
55
+ "moduleDetection": "force",
56
+ "allowJs": true,
57
+ "noEmit": true
58
+ },
59
+ "include": ["ebely", "tests"]
60
+ }
61
+ `
62
+
63
+ async function ownVersion() {
64
+ const pkg = JSON.parse(await readFile(join(pkgRoot, 'package.json'), 'utf8'))
65
+ return pkg.version
66
+ }
67
+
68
+ /** Имя npm-пакета из имени папки: lowercase, без недопустимых символов. */
69
+ function toPackageName(name) {
70
+ const cleaned = name
71
+ .toLowerCase()
72
+ .replace(/[^a-z0-9._-]+/g, '-')
73
+ .replace(/^[-_.]+|[-_.]+$/g, '')
74
+ return cleaned || 'ebely-tests'
75
+ }
76
+
77
+ async function createProject({ dir }) {
78
+ const target = resolve(process.cwd(), dir ?? '.')
79
+
80
+ if (!existsSync(templateSrc)) {
81
+ console.error(`ebely: шаблон не найден по пути ${templateSrc}`)
82
+ process.exit(1)
83
+ }
84
+
85
+ // Существующая непустая папка — отказ, чтобы не затереть чужие файлы.
86
+ if (existsSync(target)) {
87
+ const entries = await readdir(target)
88
+ if (entries.length > 0) {
89
+ console.error(
90
+ `ebely: папка «${target}» не пуста.\n` +
91
+ `Укажи несуществующую или пустую папку, либо очисти эту.`,
92
+ )
93
+ process.exit(1)
94
+ }
95
+ } else {
96
+ await mkdir(target, { recursive: true })
97
+ }
98
+
99
+ // Копируем шаблон целиком, кроме node_modules.
100
+ await cp(templateSrc, target, {
101
+ recursive: true,
102
+ filter: (src) => !src.split(sep).includes('node_modules'),
103
+ })
104
+
105
+ // package.json: имя по папке + реальная версия ebely вместо workspace:*.
106
+ const version = await ownVersion()
107
+ const pkgPath = join(target, 'package.json')
108
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
109
+ pkg.name = toPackageName(basename(target))
110
+ pkg.dependencies = { ...pkg.dependencies, ebely: `^${version}` }
111
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
112
+
113
+ // Самодостаточный tsconfig (в шаблоне он расширяет конфиг монорепо).
114
+ await writeFile(join(target, 'tsconfig.json'), STANDALONE_TSCONFIG)
115
+
116
+ // Базовый .gitignore.
117
+ await writeFile(join(target, '.gitignore'), 'node_modules\n')
118
+
119
+ const shown = dir && dir !== '.' ? dir : '.'
120
+ console.log(
121
+ `\nГотово. Шаблон ebely создан в «${target}».\n\n` +
122
+ `Дальше:\n` +
123
+ (shown === '.' ? '' : ` cd ${shown}\n`) +
124
+ ` pnpm install # установить зависимости\n` +
125
+ ` # отредактируй ebely/ebely.ts (url, путь к swagger) под свой бэкенд\n` +
126
+ ` pnpm run client:generate # сгенерировать типизированный клиент\n` +
127
+ ` pnpm test # запустить пример тестов\n`,
128
+ )
129
+ }
130
+
41
131
  function help() {
42
132
  console.log(`ebely — CLI
43
133
 
44
134
  Использование:
135
+ npx ebely create [dir] Склонировать шаблон тест-проекта в папку
136
+ (без аргумента или «.» — в текущую папку)
45
137
  npx ebely skills Установить скиллы в .claude/skills проекта (рекомендуется)
46
138
  npx ebely skills --user Установить скиллы глобально в ~/.claude/skills
47
139
  npx ebely help Показать эту справку
@@ -54,6 +146,9 @@ function help() {
54
146
  const [cmd, ...rest] = process.argv.slice(2)
55
147
 
56
148
  switch (cmd) {
149
+ case 'create':
150
+ await createProject({ dir: rest.find((a) => !a.startsWith('-')) })
151
+ break
57
152
  case 'skills':
58
153
  await installSkills({ toUser: rest.includes('--user') })
59
154
  break
@@ -0,0 +1,21 @@
1
+ import type { EbelyConfig } from "ebely";
2
+ import { generateClient } from 'ebely'
3
+ import { UserStore } from "./userStore";
4
+ import { WorldStore } from "./worldStore";
5
+ import { hooks } from "./hooks";
6
+
7
+ export const ebely = {
8
+ userStore: UserStore, // внутренние переменные одного пользователя
9
+ worldStore: WorldStore, // внутренние переменные/сценарии всего приложения
10
+
11
+ url: 'http://localhost:3000', // url бекенда, который нужно тестировать
12
+ swagger: { pathToFile: 'swagger.json', }, // путь к swagger-схеме
13
+ generateClientTo: 'ebely/generated.ts', // путь к генерируемому клиенту
14
+ hooks, // before/after-хуки (объявлены в ./hooks.ts, типизированы)
15
+ mode: 'test', // test - не-2xx статусы не бросаются.
16
+ } satisfies EbelyConfig
17
+
18
+ // запустить в другом файле для генерации клиента
19
+ export const generateEbelyClient = async () => {
20
+ await generateClient(ebely)
21
+ }
@@ -0,0 +1,3 @@
1
+ import { generateEbelyClient } from "./ebely";
2
+
3
+ generateEbelyClient();