mejora 1.4.2 → 1.4.3
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 +57 -81
- package/dist/run.mjs +12 -12
- package/package.json +18 -3
package/README.md
CHANGED
|
@@ -1,30 +1,58 @@
|
|
|
1
1
|
# mejora
|
|
2
2
|
|
|
3
|
-
Prevent regressions
|
|
3
|
+
> Prevent regressions. Allow improvement.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
![actions][actions-badge]
|
|
6
|
+
[![version][version-badge]][package]
|
|
7
|
+
[![downloads][downloads-badge]][npmtrends]
|
|
8
|
+
[![Install Size][install-size-badge]][packagephobia]
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
`mejora` runs [checks](#supported-checks), compares them to a stored baseline, and fails only when things get worse.
|
|
8
11
|
|
|
9
|
-
##
|
|
12
|
+
## Behavior
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
`mejora` asks: _“Did this regress?”_
|
|
14
|
+
Each check produces a snapshot.
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
Snapshots are compared against a baseline.
|
|
15
17
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- CI enforcement without blocking progress
|
|
18
|
+
- New items are regressions and fail the run
|
|
19
|
+
- Removed items are improvements and pass the run
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Snapshots currently use the `items` format:
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
```json
|
|
24
|
+
{ "type": "items", "items": ["file.ts:12", "file.ts:45"] }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Order does not matter.
|
|
28
|
+
|
|
29
|
+
The baseline represents the last accepted state and should be committed to the repository.
|
|
30
|
+
|
|
31
|
+
Default location:
|
|
32
|
+
|
|
33
|
+
```txt
|
|
34
|
+
.mejora/baseline.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
When a run produces fewer items than the baseline, the run passes and the baseline is updated automatically.
|
|
38
|
+
|
|
39
|
+
Regressions fail the run.
|
|
40
|
+
|
|
41
|
+
`mejora --force` updates the baseline even when regressions are present.
|
|
42
|
+
|
|
43
|
+
### Output
|
|
44
|
+
|
|
45
|
+
Output is non-interactive and deterministic.
|
|
46
|
+
|
|
47
|
+
- Plain text by default
|
|
48
|
+
- Markdown output for human-friendly review and navigation
|
|
49
|
+
- `--json` produces structured output for CI and automation
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
### Exit Codes
|
|
52
|
+
|
|
53
|
+
- `0` pass or improvement
|
|
54
|
+
- `1` regression detected or baseline out of sync
|
|
55
|
+
- `2` configuration or runtime error
|
|
28
56
|
|
|
29
57
|
## Installation
|
|
30
58
|
|
|
@@ -105,7 +133,7 @@ export default defineConfig({
|
|
|
105
133
|
Each entry in `checks` is an explicit check.
|
|
106
134
|
The object key is the check identifier and is used in the baseline.
|
|
107
135
|
|
|
108
|
-
## Supported
|
|
136
|
+
## Supported Checks
|
|
109
137
|
|
|
110
138
|
### ESLint
|
|
111
139
|
|
|
@@ -126,50 +154,7 @@ The object key is the check identifier and is used in the baseline.
|
|
|
126
154
|
> [!NOTE]
|
|
127
155
|
> `typescript` (^5.0.0) is required as a peer dependency when using the TypeScript check
|
|
128
156
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
### Items
|
|
132
|
-
|
|
133
|
-
```json
|
|
134
|
-
{ "type": "items", "items": ["file.ts:12", "file.ts:45"] }
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
Fails if new items appear.
|
|
138
|
-
Order does not matter.
|
|
139
|
-
|
|
140
|
-
## Baseline
|
|
141
|
-
|
|
142
|
-
Default location:
|
|
143
|
-
|
|
144
|
-
```txt
|
|
145
|
-
.mejora/baseline.json
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Example:
|
|
149
|
-
|
|
150
|
-
```json
|
|
151
|
-
{
|
|
152
|
-
"version": 1,
|
|
153
|
-
"checks": {
|
|
154
|
-
"eslint > no-nested-ternary": {
|
|
155
|
-
"type": "items",
|
|
156
|
-
"items": ["src/a.ts:1"]
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
The baseline represents the last accepted state.
|
|
163
|
-
|
|
164
|
-
## Improvements and baseline updates
|
|
165
|
-
|
|
166
|
-
When a run produces fewer items than the baseline, the run passes and the baseline is updated automatically to reflect the improvement.
|
|
167
|
-
|
|
168
|
-
This includes items that no longer exist in the codebase.
|
|
169
|
-
|
|
170
|
-
Improvements are reported separately from regressions so progress is visible.
|
|
171
|
-
|
|
172
|
-
## CI behavior
|
|
157
|
+
## CI
|
|
173
158
|
|
|
174
159
|
When running in CI, mejora does not write the baseline.
|
|
175
160
|
|
|
@@ -177,23 +162,6 @@ Instead, it compares the committed baseline to the expected results from the cur
|
|
|
177
162
|
|
|
178
163
|
If there is any difference between the committed baseline and the expected results, the run fails.
|
|
179
164
|
|
|
180
|
-
## Force mode
|
|
181
|
-
|
|
182
|
-
`mejora --force` updates the baseline even when regressions are present.
|
|
183
|
-
|
|
184
|
-
## Exit codes
|
|
185
|
-
|
|
186
|
-
- `0` pass or improvement
|
|
187
|
-
- `1` regression detected or baseline out of sync
|
|
188
|
-
- `2` configuration or runtime error
|
|
189
|
-
|
|
190
|
-
## Output
|
|
191
|
-
|
|
192
|
-
- Default output is plain text
|
|
193
|
-
- No prompts
|
|
194
|
-
- No interactive behavior
|
|
195
|
-
- `--json` produces structured, deterministic output
|
|
196
|
-
|
|
197
165
|
## Merge Conflicts
|
|
198
166
|
|
|
199
167
|
mejora automatically resolves conflicts in both `baseline.json` and `baseline.md`:
|
|
@@ -207,13 +175,21 @@ $ git status
|
|
|
207
175
|
# Just run mejora - both files are auto-resolved
|
|
208
176
|
$ mejora
|
|
209
177
|
Merge conflict detected in baseline, auto-resolving...
|
|
210
|
-
|
|
178
|
+
✔ Baseline conflict resolved
|
|
211
179
|
|
|
212
180
|
# Commit the resolved files
|
|
213
181
|
$ git add .mejora/
|
|
214
182
|
$ git commit -m "Merge feature branch"
|
|
215
183
|
```
|
|
216
184
|
|
|
217
|
-
##
|
|
185
|
+
## Credits
|
|
186
|
+
|
|
187
|
+
- `mejora` is inspired by [betterer](https://phenomnomnominal.github.io/betterer/).
|
|
218
188
|
|
|
219
|
-
|
|
189
|
+
[actions-badge]: https://img.shields.io/github/actions/workflow/status/jimmy-guzman/mejora/cd.yml?style=flat-square&logo=github-actions
|
|
190
|
+
[version-badge]: https://img.shields.io/npm/v/mejora?style=flat-square&logo=npm
|
|
191
|
+
[package]: https://www.npmjs.com/package/mejora
|
|
192
|
+
[downloads-badge]: https://img.shields.io/npm/dm/mejora?style=flat-square&logo=npm
|
|
193
|
+
[npmtrends]: https://www.npmtrends.com/mejora
|
|
194
|
+
[packagephobia]: https://packagephobia.com/result?p=mejora
|
|
195
|
+
[install-size-badge]: https://img.shields.io/badge/dynamic/json?url=https://packagephobia.com/v2/api.json%3Fp=mejora&query=$.install.pretty&label=install%20size&style=flat-square&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDggMTA4Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzAwNjgzOCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzMyZGU4NSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGZpbGw9InVybCgjYSkiIGQ9Ik0yMS42NjcgNzMuODA5VjMzLjg2N2wyOC4zMy0xNi4xODggMjguMzM3IDE2LjE4OFY2Ni4xM0w0OS45OTcgODIuMzIxIDM1IDczLjc1VjQxLjYwNGwxNC45OTctOC41N0w2NSA0MS42MDR2MTYuNzg4bC0xNS4wMDMgOC41NzEtMS42NjMtLjk1di0xNi42NzJsOC4zODItNC43OTItNi43MTktMy44MzgtOC4zMyA0Ljc2M1Y2OS44OGw4LjMzIDQuNzYyIDIxLjY3LTEyLjM4M1YzNy43MzdsLTIxLjY3LTEyLjM3OS0yMS42NjMgMTIuMzc5djM5Ljg4TDQ5Ljk5NyA5MCA4NSA3MFYzMEw0OS45OTcgMTAgMTUgMzB2NDB6Ii8+PC9zdmc+
|
package/dist/run.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,c as t,n,r,s as i}from"./config-c3IzQqLC.mjs";import{dirname as a,relative as o}from"node:path";import*as s from"node:util";import{inspect as c,parseArgs as l,styleText as u}from"node:util";import{mkdir as d,readFile as f,writeFile as p}from"node:fs/promises";import{env as m}from"node:process";const h=e=>t=>u(e,typeof t==`number`?t.toString():t),g=h(`blue`),_=h(`bold`),
|
|
3
|
-
`,r=e.snapshot.items.length,i=[`${n}${e.checkId}:`,` Initial baseline created with ${g(r)} ${
|
|
4
|
-
`}${e.checkId}:`,...
|
|
5
|
-
`,r=e.snapshot.items.length;return e.duration===void 0?[`${n}${e.checkId} (${r})`]:[`${n}${e.checkId} (${r}) ${
|
|
6
|
-
`)}function
|
|
7
|
-
`)}const
|
|
2
|
+
import{a as e,c as t,n,r,s as i}from"./config-c3IzQqLC.mjs";import{dirname as a,relative as o}from"node:path";import*as s from"node:util";import{inspect as c,parseArgs as l,styleText as u}from"node:util";import{mkdir as d,readFile as f,writeFile as p}from"node:fs/promises";import{env as m}from"node:process";const h=e=>t=>u(e,typeof t==`number`?t.toString():t),g=h(`blue`),_=h(`bold`),v=h(`cyan`),y=h(`dim`),b=h(`green`),x=h(`red`),S=h(`bgRed`),ee=h(`black`),C=h(`gray`);function w(e){if(e<1e3)return`${e}ms`;let t=e/1e3;if(t<60)return t%1==0?`${t}s`:`${t.toFixed(1)}s`;let n=e/6e4;if(n<60)return n%1==0?`${n}m`:`${n.toFixed(1)}m`;let r=e/36e5;return r%1==0?`${r}h`:`${r.toFixed(1)}h`}function T(e){let t=Math.round(e);return t<1?`<1ms`:w(t)}function E(e,t){return e===1?t:`${t}s`}function D(e){return e.reduce((e,t)=>e+t.snapshot.items.length,0)}function O(e){let t=[],n=[],r=[],i=[];for(let a of e)a.isInitial?i.push(a):(a.hasImprovement&&t.push(a),a.hasRegression&&n.push(a),!a.hasImprovement&&!a.hasRegression&&r.push(a));return{improvements:t,initial:i,regressions:n,unchanged:r}}function te(e){let{improvements:t,initial:n,regressions:r,unchanged:i}=O(e.results),a=D(e.results),o=e.totalDuration!==void 0&&e.results.length>0?e.totalDuration/e.results.length:void 0,s={checks:e.results.map(e=>({checkId:e.checkId,duration:e.duration,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,isInitial:e.isInitial,newItems:e.newItems,removedItems:e.removedItems,totalIssues:e.snapshot.items.length||0})),exitCode:e.exitCode,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,summary:{avgDuration:o,checksRun:e.results.length,improvementChecks:t.map(e=>e.checkId),improvements:t.length,initial:n.length,initialChecks:n.map(e=>e.checkId),regressionChecks:r.map(e=>e.checkId),regressions:r.length,totalIssues:a,unchanged:i.length,unchangedChecks:i.map(e=>e.checkId)},totalDuration:e.totalDuration};return JSON.stringify(s,null,2)}function k(e,t=10){let n=[],r=e.slice(0,t);for(let e of r)n.push(` ${y(e)}`);let i=e.length-t;return i>0&&n.push(` ${y(`... and ${i} more`)}`),n}function ne(e,t){let n=t?``:`
|
|
3
|
+
`,r=e.snapshot.items.length,i=[`${n}${e.checkId}:`,` Initial baseline created with ${g(r)} ${E(r,`issue`)}`];e.snapshot.items.length>0&&i.push(...k(e.snapshot.items)),i.push(``),e.duration!==void 0&&i.push(` ${y(`Duration`)} ${T(e.duration)}`);let a=`${e.snapshot.items.length}`;return i.push(` ${y(`Issues`)} ${_(a)}`),i}function re(e){if(!e.hasRegression)return[];let t=e.newItems.length;return[` ${x(t)} new ${E(t,`issue`)} (${E(t,`regression`)}):`,...k(e.newItems)]}function ie(e){if(!e.hasImprovement)return[];let t=e.removedItems.length;return[` ${b(t)} ${E(t,`issue`)} fixed (${E(t,`improvement`)}):`,...k(e.removedItems)]}function A(e,t){let n=[`${t?``:`
|
|
4
|
+
`}${e.checkId}:`,...re(e),...ie(e),``];e.duration!==void 0&&n.push(` ${y(`Duration`)} ${T(e.duration)}`);let r=`${e.snapshot.items.length}`;return n.push(` ${y(`Issues`)} ${_(r)}`),n}function j(e,t){let n=t?``:`
|
|
5
|
+
`,r=e.snapshot.items.length;return e.duration===void 0?[`${n}${e.checkId} (${_(r)})`]:[`${n}${e.checkId} (${_(r)}) ${y(T(e.duration))}`]}function M(e,t){return e.isInitial?ne(e,t):e.hasRegression||e.hasImprovement?A(e,t):j(e,t)}function ae(e){let t=e.results.some(e=>e.isInitial),{improvements:{length:n},initial:{length:r},regressions:{length:i},unchanged:{length:a}}=O(e.results),o=[` ${y(`Improvements`)} ${n>0?b(n):n}`,` ${y(`Regressions`)} ${i>0?x(i):i}`,` ${y(`Unchanged`)} ${a}`,` ${y(`Initial`)} ${r>0?g(r):r}`,` ${y(`Checks`)} ${e.results.length}`],s=D(e.results);if(o.push(` ${y(`Issues`)} ${_(s)}`),e.totalDuration!==void 0&&e.results.length>0){let t=e.totalDuration/e.results.length;o.push(` ${y(`Duration`)} ${T(e.totalDuration)} ${C(`(avg ${T(t)})`)}`)}return o.push(``),t?o.push(g(`✔ Initial baseline created successfully`)):e.hasRegression?o.push(`${x(`✗ Regressions detected`)} - Run failed`):e.hasImprovement?o.push(`${b(`✔ Improvements detected`)} - Baseline updated`):o.push(b(`✔ All checks passed`)),o.join(`
|
|
6
|
+
`)}function oe(e){let t=[];for(let n=0;n<e.results.length;n++){let r=e.results[n];t.push(...M(r,n===0))}return t.length>0&&t.push(``),t.push(ae(e)),t.join(`
|
|
7
|
+
`)}const se=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],N=`\x1B[?25h`,P=`\r\x1B[K`,F={succeed:`✔`,fail:`✖`,warn:`!`,info:`ℹ`},I={succeed:`green`,fail:`red`,warn:`yellow`,info:`blue`,spinner:`cyan`},L=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531],[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]],R=/(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F)(?:\u200d(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F))*/uy,z=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/y,B=/\p{M}+/uy;function V(e,t){let n=0,r,i=t.length-1;if(e<t[0][0]||e>t[i][1])return!1;for(;i>=n;)if(r=Math.floor((n+i)/2),e>t[r][1])n=r+1;else if(e<t[r][0])i=r-1;else return!0;return!1}function H(e){return e===0||e<32||e>=127&&e<160||V(e,L)?0:e>=4352&&(e<=4447||e==9001||e==9002||e>=11904&&e<=42191&&e!=12351||e>=44032&&e<=55203||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65135||e>=65280&&e<=65376||e>=65504&&e<=65510||e>=131072&&e<=196605||e>=196608&&e<=262141)?2:1}function U(e){let t=0,n=0;for(let r=0;r<e.length;r++){if(n=e[r].charCodeAt(0),n>=32&&n<127){t++;continue}if(z.lastIndex=r,z.test(e)){r=z.lastIndex-1;continue}if(R.lastIndex=r,R.test(e)){r=R.lastIndex-1,t+=2;continue}if(B.lastIndex=r,B.test(e)){r=B.lastIndex-1;continue}t+=H(n)}return t}function W(e,t){let n=e.split(`
|
|
8
8
|
`).length;if(t===1/0)return n;for(let r of e.split(`
|
|
9
9
|
`))n+=Math.max(Math.ceil(U(r)/t)-1,0);return n}var G=class{text;onChange;onFinish;finished=!1;newLineEnding=!0;constructor(e){this.text=e}setText(e){this.text=e,typeof this.onChange==`function`&&this.onChange()}finish(){!this.finished&&typeof this.onFinish==`function`&&this.onFinish(),this.finished=!0}output(){return this.text}disableNewLineEnding(){this.newLineEnding=!1}};const K=new class{hideCursor;components=[];lastLinesAmt=0;terminalWidth=1/0;finishedComponents=0;outputBuffer=``;constructor(e=!0){this.hideCursor=e}addComponent(e){this.components.push(e),e.onChange=this.render.bind(this),e.onFinish=this.onComponentFinish.bind(this),process.stdout.getWindowSize&&(this.terminalWidth=process.stdout.getWindowSize()[0]),this.render()}onComponentFinish(){this.finishedComponents++,this.finishedComponents===this.components.length&&(this._reset(),process.stdout.write(N))}removeComponent(e){this.components=this.components.filter(t=>t!==e),e.onChange=void 0,e.finished&&this.finishedComponents--,this.render()}render(){if(this.outputBuffer=``,this.clear(),this.components.length===0){process.stdout.write(this.outputBuffer),this.hideCursor&&process.stdout.write(N),this.lastLinesAmt=0;return}this.hideCursor&&(this.outputBuffer+=`\x1B[?25l`);let e=``,t=!0;for(let n=0;n<this.components.length;n++){let r=this.components[n];e+=r.output()+(n!==this.components.length-1||r.newLineEnding?`
|
|
10
|
-
`:``),r.finished||(t=!1)}this.lastLinesAmt=W(e,this.terminalWidth),this.outputBuffer+=e,t&&(this._reset(),this.outputBuffer+=N),process.stdout.write(this.outputBuffer)}clear(){for(let e=0;e<this.lastLinesAmt-1;e++)this.outputBuffer+=P+`\x1B[1A`;this.outputBuffer+=P}_reset(){this.components=[],this.lastLinesAmt=0,this.terminalWidth=1/0,this.finishedComponents=0}};var
|
|
10
|
+
`:``),r.finished||(t=!1)}this.lastLinesAmt=W(e,this.terminalWidth),this.outputBuffer+=e,t&&(this._reset(),this.outputBuffer+=N),process.stdout.write(this.outputBuffer)}clear(){for(let e=0;e<this.lastLinesAmt-1;e++)this.outputBuffer+=P+`\x1B[1A`;this.outputBuffer+=P}_reset(){this.components=[],this.lastLinesAmt=0,this.terminalWidth=1/0,this.finishedComponents=0}};var ce=class{running=!1;text=``;currentSymbol;symbolFormatter;interval;frameIndex=0;symbols;frames;component=new G(``);colors;constructor(e=``,{disableNewLineEnding:t,colors:n,frames:r=se,symbols:i={}}={}){this.symbols={...F,...i},typeof n==`object`?this.colors={...I,...n}:n!==!1&&(this.colors=I),t===!0&&this.component.disableNewLineEnding(),typeof e==`string`&&(e={text:e}),delete e.symbol,this.setDisplay(e,!1),this.frames=r,this.currentSymbol=r[0]}start(e=50){if(this.running)throw Error(`Spinner is already running.`);this.component.finished&&(this.component.finished=!1),this.interval=setInterval(this.tick.bind(this),e),this.running=!0,this.currentSymbol=this.frames[0],this.tick(),K.addComponent(this.component),this.addListeners()}tick(){this.currentSymbol=this.format(this.frames[this.frameIndex++],`spinner`),this.frameIndex===this.frames.length&&(this.frameIndex=0),this.refresh()}onProcessExit=e=>{this.stop();let t;t=e===`SIGTERM`?143:e===`SIGINT`?130:Number(e),process.exit(t)};addListeners(){process.once(`SIGTERM`,this.onProcessExit),process.once(`SIGINT`,this.onProcessExit),process.once(`exit`,this.onProcessExit)}clearListeners(){process.off(`SIGTERM`,this.onProcessExit),process.off(`SIGINT`,this.onProcessExit),process.off(`exit`,this.onProcessExit)}refresh(){let e=this.currentSymbol;this.symbolFormatter&&(e=this.symbolFormatter(e));let t=(e?e+` `:``)+this.text;this.component.setText(t)}setDisplay(e={},t=!0){typeof e.symbol==`string`&&(typeof e.symbolType==`string`?this.currentSymbol=this.format(e.symbol,e.symbolType):this.currentSymbol=e.symbol),typeof e.text==`string`&&this.setText(e.text,!1),e.symbolFormatter&&(this.symbolFormatter=e.symbolFormatter),t&&this.refresh(),typeof e.symbol==`string`&&this.end()}setText(e,t=!0){this.text=this.format(e,`text`),this.running&&t&&this.refresh()}succeed(e){typeof e==`string`?this.setDisplay({text:e,symbol:this.symbols.succeed,symbolType:`succeed`}):this.setDisplay({...e,symbol:this.symbols.succeed,symbolType:`succeed`})}fail(e){typeof e==`string`?this.setDisplay({text:e,symbol:this.symbols.fail,symbolType:`fail`}):this.setDisplay({...e,symbol:this.symbols.fail,symbolType:`fail`})}warn(e){typeof e==`string`?this.setDisplay({text:e,symbol:this.symbols.warn,symbolType:`warn`}):this.setDisplay({...e,symbol:this.symbols.warn,symbolType:`warn`})}info(e){typeof e==`string`?this.setDisplay({text:e,symbol:this.symbols.info,symbolType:`info`}):this.setDisplay({...e,symbol:this.symbols.info,symbolType:`info`})}stop(){this.end(!1)}end(e=!0){clearInterval(this.interval),this.clearListeners(),e?this.component.finish():K.removeComponent(this.component),this.running=!1}format(e,t){if(this.colors===void 0)return e;let n=this.colors[t];return n&&s.styleText!==void 0?s.styleText(n,e):e}};const q=e=>e in m&&m[e]!==`0`&&m[e]!==`false`;var J=q(`CI`)||q(`CONTINUOUS_INTEGRATION`);function le(e,t){let n=e;for(let e=0;e<t&&n.includes(`}`);e++){let e=n.lastIndexOf(`}`);n=n.slice(0,e)+n.slice(e+1)}return n}function ue(e,t){return`${e}\n${` }`.repeat(t)}`}function de(e){let t=0,n=0;for(let r of e)r===`{`&&t++,r===`}`&&n++;if(t===n)return e;let r=Math.abs(t-n);return n>t?le(e,r):ue(e,r)}function fe(e){let t=e.trim();return de(t.endsWith(`,`)?t.slice(0,-1):t)}function pe(e){return`{
|
|
11
11
|
"version": 1,
|
|
12
12
|
${e}
|
|
13
|
-
}`}function
|
|
13
|
+
}`}function me(e){if(e.checks)return e;let t={};for(let[n,r]of Object.entries(e))n!==`version`&&(t[n]=r);return{checks:t,version:1}}function Y(e){try{let t=pe(fe(e));return me(JSON.parse(t))}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to parse baseline during conflict resolution: ${t}`,{cause:e})}}function he(e){let t=[...e.matchAll(/<<<<<<< .*\n([\s\S]*?)\n=======\n([\s\S]*?)\n>>>>>>> .*$/gm)];if(t.length===0)throw Error(`Could not parse conflict markers in baseline`);return t.map(([,e=``,t=``])=>({ours:e,theirs:t}))}function ge(e){let t=new Set;for(let n of e)for(let e of Object.keys(n.checks))t.add(e);return t}function _e(e,t){let n=new Set;for(let r of e){let e=r.checks[t];if(e?.items)for(let t of e.items)n.add(t)}return[...n].toSorted()}function ve(e){let t={checks:{},version:1},n=ge(e);for(let r of n)t.checks[r]={items:_e(e,r),type:`items`};return t}function ye(e){let t=he(e),n=[];for(let{ours:e,theirs:r}of t)n.push(Y(e),Y(r));return ve(n)}function be(e,t){let n=`# Mejora Baseline
|
|
14
14
|
|
|
15
15
|
`,r=Object.entries(e.checks);for(let e=0;e<r.length;e++){let[i,{items:a=[]}]=r[e],s=e===r.length-1;if(n+=`## ${i}\n\n`,a.length===0)n+=`No issues
|
|
16
16
|
`;else for(let e of a){let[r,...i]=e.split(` - `),a=r?.split(`:`),s=a?.[0],c=a?.[1];if(s){let e=o(t,s),a=c?`${e}#L${c}`:e,l=i.length>0?` - ${i.join(` - `)}`:``;n+=`- [${r}](${a})${l}\n`}}s||(n+=`
|
|
17
|
-
`)}return n}const
|
|
18
|
-
`).slice(1).map(e=>
|
|
17
|
+
`)}return n}const xe=(e,t)=>{if(!t)return!1;let n=e.items??[],r=t.items??[];if(n.length!==r.length)return!1;let i=n.toSorted(),a=r.toSorted();return i.every((e,t)=>e===a[t])};function Se(e){let t=[e.message];if(e.stack){let n=e.stack.split(`
|
|
18
|
+
`).slice(1).map(e=>y(e.trim())).join(`
|
|
19
19
|
`);t.push(n)}return t.join(`
|
|
20
|
-
`)}function X(...e){return e.map(e=>typeof e==`string`?e:e instanceof Error?
|
|
20
|
+
`)}function X(...e){return e.map(e=>typeof e==`string`?e:e instanceof Error?Se(e):c(e,{colors:!1,depth:10})).join(` `)}const Z={error:(...e)=>{console.error(S(ee(` ERROR `)),X(...e))},log:(...e)=>{console.log(X(...e))},start:(...e)=>{console.log(v(`◐`),X(...e))},success:(...e)=>{console.log(b(`✔`),X(...e))}};var Q=class e{baselinePath;constructor(e=`.mejora/baseline.json`){this.baselinePath=e}static create(e){return{checks:e,version:1}}static getEntry(e,t){return e?.checks[t]}static update(t,n,r){let i=t??e.create({}),a=i.checks[n];return xe(r,a)?i:{...i,checks:{...i.checks,[n]:r}}}async load(){try{let e=await f(this.baselinePath,`utf8`);if(e.includes(`<<<<<<<`)){Z.start(`Merge conflict detected in baseline, auto-resolving...`);let t=ye(e);return await this.save(t,!0),Z.success(`Baseline conflict resolved`),t}return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return null;throw e}}async save(e,t=!1){if(J&&!t)return;let n=`${JSON.stringify(e,null,2)}\n`,r=this.baselinePath.replace(`.json`,`.md`),i=be(e,a(this.baselinePath));await d(a(this.baselinePath),{recursive:!0}),await Promise.all([p(this.baselinePath,n,`utf8`),p(r,i,`utf8`)])}};function Ce(){return{hasImprovement:!1,hasRegression:!1,isInitial:!0,newItems:[],removedItems:[]}}function we(e,t){return{hasImprovement:t.length>0,hasRegression:e.length>0,isInitial:!1,newItems:e.toSorted(),removedItems:t.toSorted()}}function Te(e,t){let n=[];for(let r of e)t.has(r)||n.push(r);return n}function Ee(e,t){let n=[];for(let r of t)e.has(r)||n.push(r);return n}function De(e,t){let n=new Set(e.items),r=new Set(t.items);return we(Te(n,r),Ee(n,r))}function Oe(e,t){return t?De(e,t):Ce()}var ke=class n{baselineManager;constructor(e){this.baselineManager=new Q(e)}static filterChecks(e,t){let r={...e};if(t.only){let e=n.resolveRegex(t.only,`--only`);r=Object.fromEntries(Object.entries(r).filter(([t])=>e.test(t)))}if(t.skip){let e=n.resolveRegex(t.skip,`--skip`);r=Object.fromEntries(Object.entries(r).filter(([t])=>!e.test(t)))}return r}static resolveRegex(e,t){try{return new RegExp(e)}catch{throw Error(`Invalid regex pattern for ${t}: "${e}"`)}}static async runCheck(n){return n.type===`eslint`?(await t(),i(n)):(await e(),r(n))}async run(e,t={}){let r=performance.now(),i=await this.baselineManager.load(),a=[],o=!1,s=!1,c=!1,l=i,u=n.filterChecks(e.checks,t);for(let[e,d]of Object.entries(u)){let u=t.json?null:new ce(`Running ${e}...`);try{u?.start();let r=performance.now(),f=await n.runCheck(d),p=performance.now()-r,m=Q.getEntry(i,e),h=Oe(f,m),g={baseline:m,checkId:e,duration:p,hasImprovement:h.hasImprovement,hasRegression:h.hasRegression,isInitial:h.isInitial,newItems:h.newItems,removedItems:h.removedItems,snapshot:f};a.push(g),h.hasRegression&&(o=!0),h.hasImprovement&&(s=!0),h.isInitial&&(c=!0),(h.hasImprovement||t.force||h.isInitial)&&(l=Q.update(l,e,{items:f.items,type:f.type})),u?.succeed(`${e} complete`)}catch(t){return u?.fail(`${e} failed`),Z.error(`Error running check "${e}":`,t),{exitCode:2,hasImprovement:!1,hasRegression:!0,results:a,totalDuration:performance.now()-r}}}l&&l!==i&&(!o||t.force||c)&&await this.baselineManager.save(l,t.force);let d=0;o&&!t.force&&(d=1);let f=performance.now()-r;return{exitCode:d,hasImprovement:s,hasRegression:o,results:a,totalDuration:f}}};const{values:$}=l({allowPositionals:!1,options:{force:{default:!1,short:`f`,type:`boolean`},help:{short:`h`,type:`boolean`},json:{default:!1,type:`boolean`},only:{type:`string`},skip:{type:`string`}},strict:!0});$.help&&(Z.log(`
|
|
21
21
|
mejora - Prevent regressions by allowing only improvement
|
|
22
22
|
|
|
23
23
|
Usage:
|
|
@@ -36,4 +36,4 @@ Examples:
|
|
|
36
36
|
mejora --json
|
|
37
37
|
mejora --only "eslint > *"
|
|
38
38
|
mejora --skip typescript
|
|
39
|
-
`),process.exit(0));try{let e=new
|
|
39
|
+
`),process.exit(0));try{let e=new ke,t=await n(),r=await e.run(t,{force:$.force,json:$.json,only:$.only,skip:$.skip});$.json?Z.log(te(r)):(Z.log(``),Z.log(oe(r))),process.exit(r.exitCode)}catch(e){e instanceof Error?Z.error(e.message):Z.error(e),process.exit(2)}export{};
|
package/package.json
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mejora",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "",
|
|
5
|
-
"keywords": [
|
|
3
|
+
"version": "1.4.3",
|
|
4
|
+
"description": "Prevent regressions. Allow improvement.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"regression",
|
|
7
|
+
"baseline",
|
|
8
|
+
"ci",
|
|
9
|
+
"continuous-integration",
|
|
10
|
+
"linting",
|
|
11
|
+
"eslint",
|
|
12
|
+
"typescript",
|
|
13
|
+
"quality",
|
|
14
|
+
"code-quality",
|
|
15
|
+
"developer-tools",
|
|
16
|
+
"cli",
|
|
17
|
+
"testing",
|
|
18
|
+
"incremental",
|
|
19
|
+
"legacy-code"
|
|
20
|
+
],
|
|
6
21
|
"repository": {
|
|
7
22
|
"type": "git",
|
|
8
23
|
"url": "git+https://github.com/jimmy-guzman/mejora.git"
|