@7n/rules-lang-js 0.2.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/CHANGELOG.md +9 -0
- package/package.json +53 -0
- package/skills/taze/SKILL.fragment.md +67 -0
- package/taze/diff.mjs +110 -0
- package/taze/docs/diff.md +22 -0
- package/taze/docs/provider.md +22 -0
- package/taze/provider.mjs +130 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Перший реліз: EcosystemProvider npm/bun для taze-оркестратора `@7n/rules` (extension-point `taze`, контракт `@7n/rules/plugin-api`) — фаза 5a spec lang-plugins-extraction: JS-екосистема стала таким самим плагіном, як Rust/Python, ядро — двигун без мовної специфіки. Бекап package.json воркспейсів, bump через `bunx taze -w -r latest` + `bun install`, детермінований `collectTazeDiff` (semver caret-класифікація), CLI `n-rules taze diff` — через handler плагіна. Автодетект — за кореневим `package.json`
|
|
8
|
+
|
|
9
|
+
All notable changes to this project will be documented in this file.
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@7n/rules-lang-js",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Плагін @7n/rules: JS/npm/bun-екосистема — taze-провайдер (package.json, bunx taze)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"javascript",
|
|
7
|
+
"bun",
|
|
8
|
+
"taze",
|
|
9
|
+
"n-rules-plugin"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/nitra/7n-rules#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/nitra/7n-rules/issues"
|
|
14
|
+
},
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"author": "v@nitra.ai",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/nitra/7n-rules.git",
|
|
20
|
+
"directory": "plugins/lang-js"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"files": [
|
|
24
|
+
"taze",
|
|
25
|
+
"skills",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"!**/*.test.mjs",
|
|
28
|
+
"!**/*_test.rego",
|
|
29
|
+
"!**/fixtures/**",
|
|
30
|
+
"!**/tests/**"
|
|
31
|
+
],
|
|
32
|
+
"n-rules": {
|
|
33
|
+
"capabilities": [
|
|
34
|
+
"lang:js"
|
|
35
|
+
],
|
|
36
|
+
"contributes": {
|
|
37
|
+
"rules": false,
|
|
38
|
+
"handlers": {
|
|
39
|
+
"taze": "./taze/provider.mjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@7n/rules": ">=1.24.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"bun": ">=1.3",
|
|
51
|
+
"node": ">=24"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
## npm/bun-гілка (`@7n/rules-lang-js`)
|
|
2
|
+
|
|
3
|
+
Екосистемна гілка JavaScript/npm/bun для taze — виконує кроки 1–8 скелета.
|
|
4
|
+
|
|
5
|
+
### Детекція і передумови
|
|
6
|
+
|
|
7
|
+
Кореневий `package.json`. Потрібні `bun` і `bunx`; чисте робоче дерево (без незакомічених змін у `package.json` / `bun.lock` / `node_modules`).
|
|
8
|
+
|
|
9
|
+
### Крок 1 — стартовий стан
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cp package.json package.json.taze-bak
|
|
13
|
+
cp bun.lock bun.lock.taze-bak
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
(У monorepo — також усі `*/package.json` воркспейсів. Файли тимчасові, видаляються в кінці.)
|
|
17
|
+
|
|
18
|
+
### Крок 2 — оновлення
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bunx taze -w -r latest
|
|
22
|
+
bun install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- `-w` — записати нові версії у `package.json`.
|
|
26
|
+
- `-r` — рекурсивно по всіх воркспейсах.
|
|
27
|
+
- `latest` — піднімати навіть major.
|
|
28
|
+
|
|
29
|
+
### Крок 3 — major-оновлення
|
|
30
|
+
|
|
31
|
+
> **Не порівнюй `package.json` вручну.** Класифікацію semver несе CLI — детерміновано, по всіх воркспейсах.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
n-rules taze diff
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Друкує компактний JSON: `{ "major": [{workspace, pkg, from, to}], "minorPatch": <N>, "totalChanged": <N> }`. `major` — залежності зі зміною найлівішої ненульової компоненти semver (`1.x→2.x`, `0.4.x→0.5.x`, `0.0.3→0.0.4`); саме вони йдуть у кроки 4–6. Покриває **прямі** залежності `package.json` (root + воркспейси); транзитивні (`bun.lock`) — за потреби окремо.
|
|
38
|
+
|
|
39
|
+
### Крок 4 — breaking changes
|
|
40
|
+
|
|
41
|
+
CHANGELOG/Releases репозиторію модуля (поле `repository` у `node_modules/<name>/package.json`); якщо неінформативно — `diff -r` між кешованою старою версією (`~/.bun/install/cache/<name>@<old-version>/`) і новою (`node_modules/<name>/`) по `dist/` / `*.d.ts` / entry-points з `exports`.
|
|
42
|
+
|
|
43
|
+
### Крок 5 — сумісність з кодом
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
rg -n "<імпорт|функція|опція>" --type ts --type js --type vue
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Крок 6 — перевірки після правок
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx @7n/rules lint
|
|
53
|
+
bun run typecheck # якщо є
|
|
54
|
+
bun test # якщо є
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Крок 7 — прибирання
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
rm package.json.taze-bak bun.lock.taze-bak
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
(І решту бекапів воркспейсів, якщо створювались.)
|
|
64
|
+
|
|
65
|
+
### Крок 8 — звіт
|
|
66
|
+
|
|
67
|
+
Окрема секція **npm/bun-пакети** (оновлено / major / зрефакторено / потребує ручного втручання), у **Стан перевірок** — `lint` / `typecheck` / `test`.
|
package/taze/diff.mjs
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** @see ./docs/diff.md */
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { isBreaking, parseVersion } from '@7n/rules/plugin-api'
|
|
7
|
+
import { getMonorepoPackageRootDirs } from '@7n/rules/scripts/lib/workspaces.mjs'
|
|
8
|
+
|
|
9
|
+
/** Поля package.json із залежностями, які порівнюємо. */
|
|
10
|
+
const DEP_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
|
|
11
|
+
|
|
12
|
+
/** Дефолтний суфікс бекапу, який створює крок 1 скіла (`cp package.json package.json.taze-bak`). */
|
|
13
|
+
const DEFAULT_BACKUP_SUFFIX = '.taze-bak'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Порівнює два package.json-обʼєкти й повертає зміни залежностей.
|
|
17
|
+
* @param {object} oldPkg розпарсений старий package.json (бекап)
|
|
18
|
+
* @param {object} newPkg розпарсений новий package.json
|
|
19
|
+
* @param {string} workspace мітка воркспейсу (`.` для кореня)
|
|
20
|
+
* @returns {{major: Array<{workspace:string, pkg:string, from:string, to:string}>, minorPatch:number}} зміни
|
|
21
|
+
*/
|
|
22
|
+
export function diffPackageJson(oldPkg, newPkg, workspace) {
|
|
23
|
+
const major = []
|
|
24
|
+
let minorPatch = 0
|
|
25
|
+
for (const field of DEP_FIELDS) {
|
|
26
|
+
const oldDeps = oldPkg?.[field]
|
|
27
|
+
const newDeps = newPkg?.[field]
|
|
28
|
+
if (!oldDeps || !newDeps) continue
|
|
29
|
+
for (const [pkg, from] of Object.entries(oldDeps)) {
|
|
30
|
+
const to = newDeps[pkg]
|
|
31
|
+
if (to === undefined || to === from) continue
|
|
32
|
+
const fromV = parseVersion(from)
|
|
33
|
+
const toV = parseVersion(to)
|
|
34
|
+
if (fromV && toV && isBreaking(fromV, toV)) {
|
|
35
|
+
major.push({ workspace, pkg, from, to })
|
|
36
|
+
} else {
|
|
37
|
+
minorPatch += 1
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { major, minorPatch }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Читає JSON-файл або повертає null, якщо файл відсутній / невалідний.
|
|
46
|
+
* @param {string} path абсолютний шлях
|
|
47
|
+
* @returns {Promise<object|null>} розпарсений обʼєкт або null
|
|
48
|
+
*/
|
|
49
|
+
async function readJsonOrNull(path) {
|
|
50
|
+
if (!existsSync(path)) return null
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(await readFile(path, 'utf8'))
|
|
53
|
+
} catch {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Збирає diff по всьому монорепо: для кожного воркспейсу порівнює
|
|
60
|
+
* `<ws>/package.json` з `<ws>/package.json<backupSuffix>`.
|
|
61
|
+
* @param {string} cwd корінь репозиторію
|
|
62
|
+
* @param {string} [backupSuffix] суфікс бекап-файлу
|
|
63
|
+
* @returns {Promise<{major: Array<{workspace:string, pkg:string, from:string, to:string}>, minorPatch:number, totalChanged:number, comparedWorkspaces:number}>} агрегований diff
|
|
64
|
+
*/
|
|
65
|
+
export async function collectTazeDiff(cwd, backupSuffix = DEFAULT_BACKUP_SUFFIX) {
|
|
66
|
+
const roots = await getMonorepoPackageRootDirs(cwd)
|
|
67
|
+
const major = []
|
|
68
|
+
let minorPatch = 0
|
|
69
|
+
let comparedWorkspaces = 0
|
|
70
|
+
for (const ws of roots) {
|
|
71
|
+
const dir = join(cwd, ws)
|
|
72
|
+
const oldPkg = await readJsonOrNull(join(dir, `package.json${backupSuffix}`))
|
|
73
|
+
const newPkg = await readJsonOrNull(join(dir, 'package.json'))
|
|
74
|
+
if (!oldPkg || !newPkg) continue
|
|
75
|
+
comparedWorkspaces += 1
|
|
76
|
+
const res = diffPackageJson(oldPkg, newPkg, ws)
|
|
77
|
+
major.push(...res.major)
|
|
78
|
+
minorPatch += res.minorPatch
|
|
79
|
+
}
|
|
80
|
+
return { major, minorPatch, totalChanged: major.length + minorPatch, comparedWorkspaces }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const USAGE = 'Usage: n-rules taze diff [--backup-suffix <suffix>]'
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* CLI: `n-rules taze diff` друкує компактний JSON зі списком major-оновлень і
|
|
87
|
+
* лічбою minor/patch. Read-only.
|
|
88
|
+
* @param {string[]} args аргументи після `taze`
|
|
89
|
+
* @param {string} [cwd] корінь репозиторію (ін'єкція для тестів)
|
|
90
|
+
* @returns {Promise<number>} exit code
|
|
91
|
+
*/
|
|
92
|
+
export async function runTazeCli(args, cwd = process.cwd()) {
|
|
93
|
+
if (args[0] !== 'diff') {
|
|
94
|
+
console.error(USAGE)
|
|
95
|
+
return 1
|
|
96
|
+
}
|
|
97
|
+
const flagAt = args.indexOf('--backup-suffix')
|
|
98
|
+
const backupSuffix = flagAt === -1 ? DEFAULT_BACKUP_SUFFIX : args[flagAt + 1]
|
|
99
|
+
if (!backupSuffix) {
|
|
100
|
+
console.error(USAGE)
|
|
101
|
+
return 1
|
|
102
|
+
}
|
|
103
|
+
const diff = await collectTazeDiff(cwd, backupSuffix)
|
|
104
|
+
if (diff.comparedWorkspaces === 0) {
|
|
105
|
+
console.error(`✗ Не знайдено жодного package.json${backupSuffix} — спершу зроби бекап (крок 1 скіла).`)
|
|
106
|
+
return 1
|
|
107
|
+
}
|
|
108
|
+
process.stdout.write(`${JSON.stringify(diff)}\n`)
|
|
109
|
+
return 0
|
|
110
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: diff.mjs
|
|
4
|
+
resource: plugins/lang-js/taze/diff.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: e9789318
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
Детермінований semver-diff npm/bun-гілки taze: порівнює package.json кожного воркспейсу з `.taze-bak`-бекапом і класифікує зміни залежностей на major vs minor/patch за caret-правилом (`isBreaking`/`parseVersion` з `@7n/rules/plugin-api`).
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
- **diffPackageJson** — порівнює два розпарсені package.json по dependencies/devDependencies/peerDependencies/optionalDependencies; не-semver специфікатори (workspace:*, git-url) ігноруються.
|
|
16
|
+
- **collectTazeDiff** — агрегує diff по root + воркспейсах монорепо (`getMonorepoPackageRootDirs` ядра); воркспейс без бекапу пропускається.
|
|
17
|
+
- **runTazeCli** — CLI-обгортка (`n-rules taze diff`): друкує компактний JSON `{ major, minorPatch, totalChanged }`.
|
|
18
|
+
|
|
19
|
+
## Гарантії поведінки
|
|
20
|
+
|
|
21
|
+
- Read-only: лише читає файли, нічого не пише.
|
|
22
|
+
- Відсутній або невалідний бекап/маніфест — пропуск без винятку.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: provider.mjs
|
|
4
|
+
resource: plugins/lang-js/taze/provider.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 8749ccfb
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Огляд
|
|
10
|
+
|
|
11
|
+
EcosystemProvider npm/bun для taze-оркестратора ядра (фаза 5a spec lang-plugins-extraction: ядро — двигун без мовної специфіки, JS-екосистема — такий самий плагін, як Rust/Python). Контракт `@7n/rules/plugin-api`, реєструється маніфестом `n-rules.contributes.handlers.taze`. Реекспортує `runTazeCli` для CLI `n-rules taze diff` ядра.
|
|
12
|
+
|
|
13
|
+
## Поведінка
|
|
14
|
+
|
|
15
|
+
- **buildDependencyPrompt** — промпт ОДНОГО ізольованого виклику раннера для одного major-запису (кроки 4-6 SKILL.md; без кроків 1-3/7/8 — їх виконує оркестратор).
|
|
16
|
+
- **backupWorkspacePackageFiles** / **cleanupWorkspaceBackups** — бекап і прибирання package.json кожного воркспейсу монорепо (`.taze-bak`).
|
|
17
|
+
- **jsProvider** — detect за кореневим package.json; available перевіряє bun; bump — `bunx taze -w -r latest` + `bun install` (провал кидає з exit-кодом і stderr); diff — `collectTazeDiff` з мапінгом workspace → manifest (контракт порту); cleanup — прибирання бекапів воркспейсів.
|
|
18
|
+
|
|
19
|
+
## Гарантії поведінки
|
|
20
|
+
|
|
21
|
+
- Виконує файлові операції (бекапи) і запускає зовнішні команди (`bunx`, `bun`) — НЕ read-only.
|
|
22
|
+
- Без кореневого package.json detect повертає порожньо — тиша у звіті.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** @see ./docs/provider.md */
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { copyFile, rm } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { getMonorepoPackageRootDirs } from '@7n/rules/scripts/lib/workspaces.mjs'
|
|
7
|
+
|
|
8
|
+
import { collectTazeDiff } from './diff.mjs'
|
|
9
|
+
|
|
10
|
+
// CLI `n-rules taze diff` ядра резолвить цей handler-модуль і кличе runTazeCli.
|
|
11
|
+
export { runTazeCli } from './diff.mjs'
|
|
12
|
+
|
|
13
|
+
/** Суфікс бекапу — той самий, що й у `diff.mjs`/кроці 1 SKILL.md. */
|
|
14
|
+
const BACKUP_SUFFIX = '.taze-bak'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Промпт ОДНОГО ітеративного виклику для npm/bun-пакета (кроки 4-6 SKILL.md)
|
|
18
|
+
* для ОДНОГО major-запису. Кроки 1-3/7/8 виконує оркестратор ядра
|
|
19
|
+
* детерміновано, без LLM.
|
|
20
|
+
* @param {{manifest: string, pkg: string, from: string, to: string}} entry запис major-diff (з `collectTazeDiff`, workspace → manifest)
|
|
21
|
+
* @returns {string} готовий промпт
|
|
22
|
+
*/
|
|
23
|
+
export function buildDependencyPrompt({ manifest, pkg, from, to }) {
|
|
24
|
+
return [
|
|
25
|
+
'# Major-оновлення одного пакета: перевірка сумісності й рефакторинг',
|
|
26
|
+
'',
|
|
27
|
+
`Пакет \`${pkg}\` у воркспейсі \`${manifest}\`: **${from} → ${to}** — вже застосовано в package.json/bun.lock (кроки 1-3 виконано детерміновано, без тебе). Твоя задача — лише breaking-changes-перевірка й, за потреби, рефакторинг.`,
|
|
28
|
+
'',
|
|
29
|
+
'## Кроки',
|
|
30
|
+
`1. Зібрати breaking changes цього оновлення: CHANGELOG/Releases репозиторію модуля (поле \`repository\` у \`node_modules/${pkg}/package.json\`), або git/diff між закешованою старою версією (\`~/.bun/install/cache/${pkg}@<стара-версія>/\`) і новою (\`node_modules/${pkg}/\`).`,
|
|
31
|
+
`2. Знайти використання зачепленого API в коді проєкту (\`rg -n\` по імпортах/викликах \`${pkg}\`).`,
|
|
32
|
+
'3. Сумісно — нічого не робити. Несумісно — застосувати міграцію (перейменувати імпорт, оновити сигнатуру виклику, замінити видалену опцію еквівалентом).',
|
|
33
|
+
'4. Якщо були правки — запусти `npx @7n/rules lint`, typecheck/test якщо є в проєкті.',
|
|
34
|
+
'5. Нетривіальна/неоднозначна міграція — не вгадуй, залиш TODO-коментар із посиланням на CHANGELOG.',
|
|
35
|
+
'',
|
|
36
|
+
'У відповіді одним абзацом підсумуй: сумісно / зрефакторено (які файли) / TODO (чому).'
|
|
37
|
+
].join('\n')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Бекапить package.json кожного воркспейсу (крок 1 SKILL.md) — потрібно для
|
|
42
|
+
* класифікації major/minor через `collectTazeDiff` після bump-у.
|
|
43
|
+
* @param {string} cwd корінь репо
|
|
44
|
+
* @param {{ getMonorepoPackageRootDirs?: (cwd: string) => Promise<string[]>, copyFile?: (src: string, dest: string) => Promise<void> }} [deps] інжекти
|
|
45
|
+
* @returns {Promise<string[]>} відносні шляхи воркспейсів, що мали package.json
|
|
46
|
+
*/
|
|
47
|
+
export async function backupWorkspacePackageFiles(cwd, deps = {}) {
|
|
48
|
+
const getRoots = deps.getMonorepoPackageRootDirs ?? getMonorepoPackageRootDirs
|
|
49
|
+
const copy = deps.copyFile ?? copyFile
|
|
50
|
+
const roots = await getRoots(cwd)
|
|
51
|
+
const backedUp = []
|
|
52
|
+
for (const ws of roots) {
|
|
53
|
+
const pkgPath = join(cwd, ws, 'package.json')
|
|
54
|
+
if (!existsSync(pkgPath)) continue
|
|
55
|
+
await copy(pkgPath, `${pkgPath}${BACKUP_SUFFIX}`)
|
|
56
|
+
backedUp.push(ws)
|
|
57
|
+
}
|
|
58
|
+
return backedUp
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Прибирає бекапи package.json усіх воркспейсів (крок 7 SKILL.md).
|
|
63
|
+
* @param {string} cwd корінь репо
|
|
64
|
+
* @param {{ getMonorepoPackageRootDirs?: (cwd: string) => Promise<string[]>, rm?: (path: string, opts?: object) => Promise<void> }} [deps] інжекти
|
|
65
|
+
* @returns {Promise<void>}
|
|
66
|
+
*/
|
|
67
|
+
export async function cleanupWorkspaceBackups(cwd, deps = {}) {
|
|
68
|
+
const getRoots = deps.getMonorepoPackageRootDirs ?? getMonorepoPackageRootDirs
|
|
69
|
+
const remove = deps.rm ?? rm
|
|
70
|
+
for (const ws of await getRoots(cwd)) {
|
|
71
|
+
await remove(join(cwd, ws, `package.json${BACKUP_SUFFIX}`), { force: true })
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Виконує детерміновану команду npm-гілки, кидає з exit-кодом+stderr при провалі.
|
|
77
|
+
* @param {string} cmd бінарник
|
|
78
|
+
* @param {string[]} args аргументи
|
|
79
|
+
* @param {string} cwd робочий каталог
|
|
80
|
+
* @param {import('@7n/rules/plugin-api').SpawnFn} spawnFn spawnSync-сумісний виклик
|
|
81
|
+
* @returns {void}
|
|
82
|
+
*/
|
|
83
|
+
function runCommand(cmd, args, cwd, spawnFn) {
|
|
84
|
+
const result = spawnFn(cmd, args, { cwd, encoding: 'utf8' })
|
|
85
|
+
if (result.status !== 0) {
|
|
86
|
+
throw new Error(`${cmd} ${args.join(' ')} → exit ${result.status}: ${result.stderr || result.stdout}`)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* EcosystemProvider npm/bun для taze-оркестратора ядра (фаза 5a spec
|
|
92
|
+
* lang-plugins-extraction: ядро — двигун без мовної специфіки, JS-екосистема —
|
|
93
|
+
* такий самий плагін, як Rust/Python). Контракт `@7n/rules/plugin-api`,
|
|
94
|
+
* реєструється маніфестом (`n-rules.contributes.handlers.taze`).
|
|
95
|
+
* `diff.major` записи мапляться workspace → manifest (контракт порту).
|
|
96
|
+
* @type {import('@7n/rules/plugin-api').EcosystemProvider}
|
|
97
|
+
*/
|
|
98
|
+
export const jsProvider = {
|
|
99
|
+
id: 'js-bun',
|
|
100
|
+
title: 'npm/bun-пакети',
|
|
101
|
+
manifestNoun: 'package.json',
|
|
102
|
+
skillSection: 'npm/bun-гілкою SKILL.md',
|
|
103
|
+
detect: cwd => (existsSync(join(cwd, 'package.json')) ? ['package.json'] : []),
|
|
104
|
+
available: spawnFn =>
|
|
105
|
+
spawnFn('bun', ['--version'], { encoding: 'utf8' }).status === 0
|
|
106
|
+
? { ok: true, reason: null }
|
|
107
|
+
: { ok: false, reason: '`bun` не встановлено (https://bun.sh) — npm/bun-гілка потребує bun/bunx' },
|
|
108
|
+
backup: async (cwd, manifests, deps) => {
|
|
109
|
+
await backupWorkspacePackageFiles(cwd, deps)
|
|
110
|
+
},
|
|
111
|
+
bump: (cwd, manifests, { spawnFn, log }) => {
|
|
112
|
+
log('⬆️ bunx taze -w -r latest...')
|
|
113
|
+
runCommand('bunx', ['taze', '-w', '-r', 'latest'], cwd, spawnFn)
|
|
114
|
+
log('📥 bun install...')
|
|
115
|
+
runCommand('bun', ['install'], cwd, spawnFn)
|
|
116
|
+
return Promise.resolve()
|
|
117
|
+
},
|
|
118
|
+
diff: async cwd => {
|
|
119
|
+
const diff = await collectTazeDiff(cwd)
|
|
120
|
+
return {
|
|
121
|
+
major: diff.major.map(({ workspace, pkg, from, to }) => ({ manifest: workspace, pkg, from, to })),
|
|
122
|
+
minorPatch: diff.minorPatch,
|
|
123
|
+
totalChanged: diff.totalChanged
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
promptFor: buildDependencyPrompt,
|
|
127
|
+
cleanup: (cwd, manifests, deps) => cleanupWorkspaceBackups(cwd, deps)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default jsProvider
|