mejora 2.4.0 → 3.0.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/README.md +53 -52
- package/dist/check-registry-mjn8FfPF.mjs +1 -0
- package/dist/config-C0qrgNuO.mjs +1 -0
- package/dist/hash-BaLFZO2A.mjs +1 -0
- package/dist/index-BNOgTD7N.d.mts +520 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -1
- package/dist/run.mjs +4 -4
- package/dist/workers/check.d.mts +1 -1
- package/dist/workers/check.mjs +1 -1
- package/package.json +1 -1
- package/dist/check-registry-D0NGvrkK.mjs +0 -1
- package/dist/index-BHX-tY_Y.d.mts +0 -427
- package/dist/typescript-BG8_PZiP.mjs +0 -3
package/README.md
CHANGED
|
@@ -122,29 +122,26 @@ Create one of:
|
|
|
122
122
|
- `mejora.config.mjs`
|
|
123
123
|
- `mejora.config.mts`
|
|
124
124
|
|
|
125
|
-
Example:
|
|
126
|
-
|
|
127
125
|
```ts
|
|
128
126
|
import { defineConfig, eslint, regex, typescript } from "mejora";
|
|
129
127
|
|
|
130
128
|
export default defineConfig({
|
|
131
|
-
checks:
|
|
132
|
-
|
|
129
|
+
checks: [
|
|
130
|
+
eslint({
|
|
131
|
+
name: "no-nested-ternary",
|
|
133
132
|
files: ["src/**/*.{ts,tsx,js,jsx}"],
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"no-nested-ternary": "error",
|
|
137
|
-
},
|
|
133
|
+
rules: {
|
|
134
|
+
"no-nested-ternary": "error",
|
|
138
135
|
},
|
|
139
136
|
}),
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
},
|
|
137
|
+
typescript({
|
|
138
|
+
name: "no-implicit-any",
|
|
139
|
+
compilerOptions: {
|
|
140
|
+
noImplicitAny: true,
|
|
145
141
|
},
|
|
146
142
|
}),
|
|
147
|
-
|
|
143
|
+
regex({
|
|
144
|
+
name: "no-todos",
|
|
148
145
|
files: ["src/**/*"],
|
|
149
146
|
patterns: [
|
|
150
147
|
{
|
|
@@ -161,13 +158,11 @@ export default defineConfig({
|
|
|
161
158
|
},
|
|
162
159
|
],
|
|
163
160
|
}),
|
|
164
|
-
|
|
161
|
+
],
|
|
165
162
|
});
|
|
166
163
|
```
|
|
167
164
|
|
|
168
|
-
Each
|
|
169
|
-
|
|
170
|
-
The object key is the check identifier and is used in the baseline.
|
|
165
|
+
Each check’s `name` is used as its ID in the baseline and output.
|
|
171
166
|
|
|
172
167
|
## Supported Checks
|
|
173
168
|
|
|
@@ -199,46 +194,52 @@ The object key is the check identifier and is used in the baseline.
|
|
|
199
194
|
|
|
200
195
|
### Custom Checks
|
|
201
196
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
A custom check is made of two pieces:
|
|
205
|
-
|
|
206
|
-
- A **runner** (registered in `runners`) that knows how to execute your check type
|
|
207
|
-
- A **check config** (declared in `checks`) that includes a matching `type` and any options your runner needs
|
|
197
|
+
Define custom checks using `defineCheck()`:
|
|
208
198
|
|
|
209
199
|
```ts
|
|
210
|
-
import
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
200
|
+
import { defineConfig, defineCheck } from "mejora";
|
|
201
|
+
import { glob, readFile } from "node:fs/promises";
|
|
202
|
+
|
|
203
|
+
const noHardcodedUrls = defineCheck<{ files: string[] }>({
|
|
204
|
+
type: "no-hardcoded-urls",
|
|
205
|
+
async run(config) {
|
|
206
|
+
const violations = [];
|
|
207
|
+
|
|
208
|
+
for await (const file of glob(config.files, { cwd: process.cwd() })) {
|
|
209
|
+
const content = await readFile(file, "utf-8");
|
|
210
|
+
const lines = content.split("\n");
|
|
211
|
+
|
|
212
|
+
for (let i = 0; i < lines.length; i++) {
|
|
213
|
+
const line = lines[i];
|
|
214
|
+
const matches = line.matchAll(/https?:\/\/[^\s'"]+/g);
|
|
215
|
+
|
|
216
|
+
for (const match of matches) {
|
|
217
|
+
violations.push({
|
|
218
|
+
file,
|
|
219
|
+
line: i + 1,
|
|
220
|
+
column: match.index + 1,
|
|
221
|
+
rule: "no-hardcoded-urls",
|
|
222
|
+
message: `Hardcoded URL found: ${match[0]}`,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
229
227
|
|
|
230
|
-
|
|
231
|
-
|
|
228
|
+
return violations;
|
|
229
|
+
},
|
|
230
|
+
});
|
|
232
231
|
|
|
233
232
|
export default defineConfig({
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
type: "custom",
|
|
233
|
+
checks: [
|
|
234
|
+
noHardcodedUrls({
|
|
235
|
+
name: "urls-in-src",
|
|
238
236
|
files: ["src/**/*.ts"],
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
237
|
+
}),
|
|
238
|
+
noHardcodedUrls({
|
|
239
|
+
name: "urls-in-lib",
|
|
240
|
+
files: ["lib/**/*.ts"],
|
|
241
|
+
}),
|
|
242
|
+
],
|
|
242
243
|
});
|
|
243
244
|
```
|
|
244
245
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=class{runners=new Map;static getRequiredTypes(e){return new Set(e.map(e=>e.config.type))}get(e){let t=this.runners.get(e);if(!t)throw Error(`Unknown check type: ${e}`);return t}getTypes(){return new Set(this.runners.keys())}has(e){return this.runners.has(e)}init(e={}){if(e.runners)for(let t of e.runners)this.register(t)}register(e){this.runners.has(e.type)||this.runners.set(e.type,e)}async setup(e){await this.runLifecycle(e,`setup`)}async validate(e){await this.runLifecycle(e,`validate`)}async runLifecycle(e,t){let n=[];for(let r of e){let e=this.get(r)[t]?.();e&&n.push(e)}await Promise.all(n)}};export{e as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createRequire as e}from"node:module";import{pathToFileURL as t}from"node:url";var n=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),r=e(import.meta.url),i=n(((e,t)=>{let n=r(`path`),i=r(`fs`),a=r(`os`),o=r(`url`),s=i.promises.readFile;function c(e,t){return[`package.json`,`.${e}rc.json`,`.${e}rc.js`,`.${e}rc.cjs`,...t?[]:[`.${e}rc.mjs`],`.config/${e}rc`,`.config/${e}rc.json`,`.config/${e}rc.js`,`.config/${e}rc.cjs`,...t?[]:[`.config/${e}rc.mjs`],`${e}.config.js`,`${e}.config.cjs`,...t?[]:[`${e}.config.mjs`]]}function l(e){return n.dirname(e)||n.sep}let u=(e,t)=>JSON.parse(t),d=typeof __webpack_require__==`function`?__non_webpack_require__:r,f=Object.freeze({".js":d,".json":d,".cjs":d,noExt:u});t.exports.defaultLoadersSync=f;let p=async e=>{try{return(await import(o.pathToFileURL(e).href)).default}catch(t){try{return d(e)}catch(e){throw e.code===`ERR_REQUIRE_ESM`||e instanceof SyntaxError&&e.toString().includes(`Cannot use import statement outside a module`)?t:e}}},m=Object.freeze({".js":p,".mjs":p,".cjs":p,".json":u,noExt:u});t.exports.defaultLoaders=m;function h(e,t,r){let i={stopDir:a.homedir(),searchPlaces:c(e,r),ignoreEmptySearchPlaces:!0,cache:!0,transform:e=>e,packageProp:[e],...t,loaders:{...r?f:m,...t.loaders}};return i.searchPlaces.forEach(e=>{let t=n.extname(e)||`noExt`,r=i.loaders[t];if(!r)throw Error(`Missing loader for extension "${e}"`);if(typeof r!=`function`)throw Error(`Loader for extension "${e}" is not a function: Received ${typeof r}.`)}),i}function g(e,t){return typeof e==`string`&&e in t?t[e]:(Array.isArray(e)?e:e.split(`.`)).reduce((e,t)=>e===void 0?e:e[t],t)||null}function _(e){if(!e)throw Error(`load must pass a non-empty string`)}function v(e,t){if(!e)throw Error(`No loader specified for extension "${t}"`);if(typeof e!=`function`)throw Error(`loader is not a function`)}let y=e=>(t,n,r)=>(e&&t.set(n,r),r);t.exports.lilconfig=function(e,t){let{ignoreEmptySearchPlaces:r,loaders:a,packageProp:o,searchPlaces:c,stopDir:u,transform:d,cache:f}=h(e,t??{},!1),p=new Map,m=new Map,b=y(f);return{async search(e=process.cwd()){let t={config:null,filepath:``},m=new Set,h=e;dirLoop:for(;;){if(f){let e=p.get(h);if(e!==void 0){for(let t of m)p.set(t,e);return e}m.add(h)}for(let e of c){let c=n.join(h,e);try{await i.promises.access(c)}catch{continue}let l=String(await s(c)),u=n.extname(e)||`noExt`,d=a[u];if(e===`package.json`){let e=g(o,await d(c,l));if(e!=null){t.config=e,t.filepath=c;break dirLoop}continue}let f=l.trim()===``;if(!(f&&r)){f?(t.isEmpty=!0,t.config=void 0):(v(d,u),t.config=await d(c,l)),t.filepath=c;break dirLoop}}if(h===u||h===l(h))break dirLoop;h=l(h)}let _=t.filepath===``&&t.config===null?d(null):d(t);if(f)for(let e of m)p.set(e,_);return _},async load(e){_(e);let t=n.resolve(process.cwd(),e);if(f&&m.has(t))return m.get(t);let{base:i,ext:c}=n.parse(t),l=c||`noExt`,u=a[l];v(u,l);let p=String(await s(t));if(i===`package.json`)return b(m,t,d({config:g(o,await u(t,p)),filepath:t}));let h={config:null,filepath:t},y=p.trim()===``;return y&&r?b(m,t,d({config:void 0,filepath:t,isEmpty:!0})):(h.config=y?void 0:await u(t,p),b(m,t,d(y?{...h,isEmpty:y,config:void 0}:h)))},clearLoadCache(){f&&m.clear()},clearSearchCache(){f&&p.clear()},clearCaches(){f&&(m.clear(),p.clear())}}},t.exports.lilconfigSync=function(e,t){let{ignoreEmptySearchPlaces:r,loaders:a,packageProp:o,searchPlaces:s,stopDir:c,transform:u,cache:d}=h(e,t??{},!0),f=new Map,p=new Map,m=y(d);return{search(e=process.cwd()){let t={config:null,filepath:``},p=new Set,m=e;dirLoop:for(;;){if(d){let e=f.get(m);if(e!==void 0){for(let t of p)f.set(t,e);return e}p.add(m)}for(let e of s){let s=n.join(m,e);try{i.accessSync(s)}catch{continue}let c=n.extname(e)||`noExt`,l=a[c],u=String(i.readFileSync(s));if(e===`package.json`){let e=g(o,l(s,u));if(e!=null){t.config=e,t.filepath=s;break dirLoop}continue}let d=u.trim()===``;if(!(d&&r)){d?(t.isEmpty=!0,t.config=void 0):(v(l,c),t.config=l(s,u)),t.filepath=s;break dirLoop}}if(m===c||m===l(m))break dirLoop;m=l(m)}let h=t.filepath===``&&t.config===null?u(null):u(t);if(d)for(let e of p)f.set(e,h);return h},load(e){_(e);let t=n.resolve(process.cwd(),e);if(d&&p.has(t))return p.get(t);let{base:s,ext:c}=n.parse(t),l=c||`noExt`,f=a[l];v(f,l);let h=String(i.readFileSync(t));if(s===`package.json`)return u({config:g(o,f(t,h)),filepath:t});let y={config:null,filepath:t},b=h.trim()===``;return b&&r?m(p,t,u({filepath:t,config:void 0,isEmpty:!0})):(y.config=b?void 0:f(t,h),m(p,t,u(b?{...y,isEmpty:b,config:void 0}:y)))},clearLoadCache(){d&&p.clear()},clearSearchCache(){d&&f.clear()},clearCaches(){d&&(p.clear(),f.clear())}}}}))();const a=async e=>{let n=await import(t(e).href);return n&&typeof n==`object`&&`default`in n?n.default:n};function o(e){let t=[],n=new Set;for(let r of e.checks)if(r.__runnerFactory&&!n.has(r.config.type)){n.add(r.config.type);let e=r.__runnerFactory();t.push(e)}return{checks:e.checks,runners:t}}const s=async()=>{let e=await(0,i.lilconfig)(`mejora`,{loaders:{".js":a,".mjs":a,".mts":a,".ts":a},searchPlaces:[`mejora.config.ts`,`mejora.config.mts`,`mejora.config.js`,`mejora.config.mjs`]}).search(process.cwd());if(!e?.config)throw Error(`No configuration file found.`);return e.config};export{s as n,o as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{hash as e}from"node:crypto";const t=/^[A-Za-z]:\//;function n(e=``){return e&&e.replace(/\\/g,`/`).replace(t,e=>e.toUpperCase())}const r=/^[/\\]{2}/,i=/^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/,a=/^[A-Za-z]:$/,o=/^\/([A-Za-z]:)?$/,s=`/`,c=function(e){if(e.length===0)return`.`;e=n(e);let t=e.match(r),i=p(e),o=e[e.length-1]===`/`;return e=f(e,!i),e.length===0?i?`/`:o?`./`:`.`:(o&&(e+=`/`),a.test(e)&&(e+=`/`),t?i?`//${e}`:`//./${e}`:i&&!p(e)?`/${e}`:e)},l=function(...e){let t=``;for(let n of e)if(n)if(t.length>0){let e=t[t.length-1]===`/`,r=n[0]===`/`;e&&r?t+=n.slice(1):t+=e||r?n:`/${n}`}else t+=n;return c(t)};function u(){return typeof process<`u`&&typeof process.cwd==`function`?process.cwd().replace(/\\/g,`/`):`/`}const d=function(...e){e=e.map(e=>n(e));let t=``,r=!1;for(let n=e.length-1;n>=-1&&!r;n--){let i=n>=0?e[n]:u();!i||i.length===0||(t=`${i}/${t}`,r=p(i))}return t=f(t,!r),r&&!p(t)?`/${t}`:t.length>0?t:`.`};function f(e,t){let n=``,r=0,i=-1,a=0,o=null;for(let s=0;s<=e.length;++s){if(s<e.length)o=e[s];else if(o===`/`)break;else o=`/`;if(o===`/`){if(!(i===s-1||a===1))if(a===2){if(n.length<2||r!==2||n[n.length-1]!==`.`||n[n.length-2]!==`.`){if(n.length>2){let e=n.lastIndexOf(`/`);e===-1?(n=``,r=0):(n=n.slice(0,e),r=n.length-1-n.lastIndexOf(`/`)),i=s,a=0;continue}else if(n.length>0){n=``,r=0,i=s,a=0;continue}}t&&(n+=n.length>0?`/..`:`..`,r=2)}else n.length>0?n+=`/${e.slice(i+1,s)}`:n=e.slice(i+1,s),r=s-i-1;i=s,a=0}else o===`.`&&a!==-1?++a:a=-1}return n}const p=function(e){return i.test(e)},m=function(e,t){let n=d(e).replace(o,`$1`).split(`/`),r=d(t).replace(o,`$1`).split(`/`);if(r[0][1]===`:`&&n[0][1]===`:`&&n[0]!==r[0])return r.join(`/`);let i=[...n];for(let e of i){if(r[0]!==e)break;n.shift(),r.shift()}return[...n.map(()=>`..`),...r].join(`/`)},h=function(e){let t=n(e).replace(/\/$/,``).split(`/`).slice(0,-1);return t.length===1&&a.test(t[0])&&(t[0]+=`/`),t.join(`/`)||(p(e)?`/`:`.`)},g=function(e,t){let r=n(e).split(`/`),i=``;for(let e=r.length-1;e>=0;e--){let t=r[e];if(t){i=t;break}}return t&&i.endsWith(t)?i.slice(0,-t.length):i},_=t=>e(`sha256`,t,`hex`);export{l as a,s as c,p as i,g as n,m as o,h as r,d as s,_ as t};
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { Linter } from "eslint";
|
|
2
|
+
import { CompilerOptions } from "typescript";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
interface Issue {
|
|
6
|
+
/**
|
|
7
|
+
* 1-indexed column number for display.
|
|
8
|
+
*/
|
|
9
|
+
column: number;
|
|
10
|
+
/**
|
|
11
|
+
* Relative path from cwd.
|
|
12
|
+
*/
|
|
13
|
+
file: string;
|
|
14
|
+
/**
|
|
15
|
+
* Hash of canonical representation.
|
|
16
|
+
*
|
|
17
|
+
* @example "a1b2c3d4e5f6g7h8i9j0"
|
|
18
|
+
*/
|
|
19
|
+
id: string;
|
|
20
|
+
/**
|
|
21
|
+
* 1-indexed line number for display.
|
|
22
|
+
*/
|
|
23
|
+
line: number;
|
|
24
|
+
/**
|
|
25
|
+
* The message.
|
|
26
|
+
*/
|
|
27
|
+
message: string;
|
|
28
|
+
/**
|
|
29
|
+
* Identifier for the issue (rule name, diagnostic code, etc).
|
|
30
|
+
*
|
|
31
|
+
* @example "no-nested-ternary" (ESLint)
|
|
32
|
+
*
|
|
33
|
+
* @example "TS2345" (TypeScript)
|
|
34
|
+
*/
|
|
35
|
+
rule: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Issue produced by a check runner.
|
|
39
|
+
*/
|
|
40
|
+
type IssueInput = Omit<Issue, "id">;
|
|
41
|
+
type SnapshotType = "items";
|
|
42
|
+
interface RawSnapshot {
|
|
43
|
+
/**
|
|
44
|
+
* Snapshot items (each item represents an issue produced by the check).
|
|
45
|
+
*/
|
|
46
|
+
items: IssueInput[];
|
|
47
|
+
type: SnapshotType;
|
|
48
|
+
}
|
|
49
|
+
interface Snapshot {
|
|
50
|
+
items: Issue[];
|
|
51
|
+
type: SnapshotType;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Configuration for an ESLint check.
|
|
55
|
+
*
|
|
56
|
+
* All ESLint configuration fields (rules, languageOptions, plugins, etc.)
|
|
57
|
+
* are passed directly to ESLint's `overrideConfig` option.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* eslint({
|
|
62
|
+
* name: "no-console",
|
|
63
|
+
* files: ["src/**\/*.{ts,tsx}"],
|
|
64
|
+
* rules: {
|
|
65
|
+
* "no-console": "error",
|
|
66
|
+
* },
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example Advanced with custom parser
|
|
71
|
+
* ```ts
|
|
72
|
+
* eslint({
|
|
73
|
+
* name: "typescript-strict",
|
|
74
|
+
* files: ["src/**\/*.ts"],
|
|
75
|
+
* rules: {
|
|
76
|
+
* "@typescript-eslint/no-explicit-any": "error"
|
|
77
|
+
* },
|
|
78
|
+
* languageOptions: {
|
|
79
|
+
* parser: typescriptParser
|
|
80
|
+
* }
|
|
81
|
+
* })
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
interface ESLintCheckConfig extends Linter.Config {
|
|
85
|
+
/**
|
|
86
|
+
* Concurrency setting for ESLint.
|
|
87
|
+
*
|
|
88
|
+
* @see https://eslint.org/blog/2025/08/multithread-linting/#cli-multithreading-support
|
|
89
|
+
*
|
|
90
|
+
* @default "auto"
|
|
91
|
+
*/
|
|
92
|
+
concurrency?: "auto" | "off" | number;
|
|
93
|
+
/**
|
|
94
|
+
* Glob patterns for files to lint.
|
|
95
|
+
*
|
|
96
|
+
* Passed directly to ESLint's `lintFiles()` method.
|
|
97
|
+
*
|
|
98
|
+
* @example ["src/**\/*.ts", "src/**\/*.tsx"]
|
|
99
|
+
*/
|
|
100
|
+
files: string[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Configuration for a TypeScript diagnostics check.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* typescript({
|
|
108
|
+
* name: "strict",
|
|
109
|
+
* compilerOptions: {
|
|
110
|
+
* noImplicitAny: true,
|
|
111
|
+
* },
|
|
112
|
+
* })
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
interface TypeScriptCheckConfig {
|
|
116
|
+
/**
|
|
117
|
+
* Compiler options to merge with the base tsconfig.
|
|
118
|
+
*
|
|
119
|
+
* These options are merged with (not replacing) the compiler options
|
|
120
|
+
* from your tsconfig file.
|
|
121
|
+
*/
|
|
122
|
+
compilerOptions?: CompilerOptions;
|
|
123
|
+
/**
|
|
124
|
+
* Path to a TypeScript config file.
|
|
125
|
+
*
|
|
126
|
+
* If not provided, mejora will search for the nearest `tsconfig.json`
|
|
127
|
+
* starting from the current working directory.
|
|
128
|
+
*
|
|
129
|
+
* @example "tsconfig.strict.json"
|
|
130
|
+
*/
|
|
131
|
+
tsconfig?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A regex pattern configuration.
|
|
135
|
+
*/
|
|
136
|
+
interface RegexPattern {
|
|
137
|
+
/**
|
|
138
|
+
* Human-readable message for matches.
|
|
139
|
+
*
|
|
140
|
+
* @example "TODO comment found"
|
|
141
|
+
*
|
|
142
|
+
* @example "console.log statement"
|
|
143
|
+
*
|
|
144
|
+
* @example (match) => `Found TODO at line ${match.index + 1}`
|
|
145
|
+
*/
|
|
146
|
+
message?: ((match: RegExpExecArray) => string) | string;
|
|
147
|
+
/**
|
|
148
|
+
* The regex pattern to match.
|
|
149
|
+
*
|
|
150
|
+
* @example /\/\/\s*TODO:/gi
|
|
151
|
+
*
|
|
152
|
+
* @example /console\.log/g
|
|
153
|
+
*/
|
|
154
|
+
pattern: RegExp;
|
|
155
|
+
/**
|
|
156
|
+
* Rule identifier for this pattern.
|
|
157
|
+
* If not provided, uses the pattern source as the rule ID.
|
|
158
|
+
*
|
|
159
|
+
* @example "no-todos"
|
|
160
|
+
*
|
|
161
|
+
* @example "no-console-log"
|
|
162
|
+
*/
|
|
163
|
+
rule?: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Configuration for regex pattern matching check.
|
|
167
|
+
*/
|
|
168
|
+
interface RegexCheckConfig {
|
|
169
|
+
/**
|
|
170
|
+
* Concurrency for processing files.
|
|
171
|
+
*
|
|
172
|
+
* @default 10
|
|
173
|
+
*/
|
|
174
|
+
concurrency?: number;
|
|
175
|
+
/**
|
|
176
|
+
* Array of glob patterns for files to check.
|
|
177
|
+
*
|
|
178
|
+
* @example ["src/**\/*.ts", "lib/**\/*.js"]
|
|
179
|
+
*/
|
|
180
|
+
files: string[];
|
|
181
|
+
/**
|
|
182
|
+
* Array of glob patterns to ignore.
|
|
183
|
+
*
|
|
184
|
+
* @default ["**\/node_modules/**", "**\/dist/**", "**\/.git/**"]
|
|
185
|
+
*/
|
|
186
|
+
ignore?: string[];
|
|
187
|
+
/**
|
|
188
|
+
* Array of regex patterns to match.
|
|
189
|
+
*/
|
|
190
|
+
patterns: RegexPattern[];
|
|
191
|
+
}
|
|
192
|
+
type CustomCheckConfig = Record<string, unknown> & {
|
|
193
|
+
type: string;
|
|
194
|
+
};
|
|
195
|
+
type CheckConfig = (ESLintCheckConfig & {
|
|
196
|
+
type: "eslint";
|
|
197
|
+
}) | (TypeScriptCheckConfig & {
|
|
198
|
+
type: "typescript";
|
|
199
|
+
}) | CustomCheckConfig;
|
|
200
|
+
/**
|
|
201
|
+
* A check object that can be passed to mejora().
|
|
202
|
+
* Created by factory functions like eslint(), typescript(), regex(), or defineCheck().
|
|
203
|
+
*/
|
|
204
|
+
interface Check {
|
|
205
|
+
/**
|
|
206
|
+
* Internal factory for creating the check runner.
|
|
207
|
+
* Used by defineConfig() for auto-registration.
|
|
208
|
+
*
|
|
209
|
+
* @internal
|
|
210
|
+
*/
|
|
211
|
+
__runnerFactory?: () => CheckRunner;
|
|
212
|
+
/**
|
|
213
|
+
* The underlying check configuration.
|
|
214
|
+
*/
|
|
215
|
+
config: CheckConfig;
|
|
216
|
+
/**
|
|
217
|
+
* Unique identifier for this check.
|
|
218
|
+
* Used in baseline tracking and output.
|
|
219
|
+
*/
|
|
220
|
+
id: string;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* mejora configuration.
|
|
224
|
+
*
|
|
225
|
+
* Define checks to run and track for regressions.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* import { defineConfig, eslint, typescript } from "mejora";
|
|
230
|
+
*
|
|
231
|
+
* export default defineConfig({
|
|
232
|
+
* checks: [
|
|
233
|
+
* eslint({
|
|
234
|
+
* name: "no-nested-ternary",
|
|
235
|
+
* files: ["src/**\/*.{ts,tsx}"],
|
|
236
|
+
* rules: {
|
|
237
|
+
* "no-nested-ternary": "error",
|
|
238
|
+
* },
|
|
239
|
+
* }),
|
|
240
|
+
* typescript({
|
|
241
|
+
* name: "strict",
|
|
242
|
+
* compilerOptions: {
|
|
243
|
+
* noImplicitAny: true,
|
|
244
|
+
* },
|
|
245
|
+
* }),
|
|
246
|
+
* ]
|
|
247
|
+
* });
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
interface Config {
|
|
251
|
+
/**
|
|
252
|
+
* Array of checks to run.
|
|
253
|
+
*/
|
|
254
|
+
checks: Check[];
|
|
255
|
+
/**
|
|
256
|
+
* Optional custom check runners.
|
|
257
|
+
*/
|
|
258
|
+
runners?: CheckRunner[];
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Interface that all check runners must implement.
|
|
262
|
+
*
|
|
263
|
+
* Each check type (eslint, typescript, custom) implements this interface
|
|
264
|
+
* to provide consistent lifecycle hooks for execution.
|
|
265
|
+
*/
|
|
266
|
+
interface CheckRunner<TConfig = unknown> {
|
|
267
|
+
/**
|
|
268
|
+
* Execute the check and return a snapshot of issues.
|
|
269
|
+
*
|
|
270
|
+
* @param config - Check-specific configuration
|
|
271
|
+
*
|
|
272
|
+
* @returns Snapshot containing all issues found
|
|
273
|
+
*/
|
|
274
|
+
run(config: TConfig): Promise<RawSnapshot>;
|
|
275
|
+
/**
|
|
276
|
+
* Setup any infrastructure needed for this check.
|
|
277
|
+
* Called once during runner setup, in parallel with other checks.
|
|
278
|
+
*
|
|
279
|
+
* Examples: creating cache directories, initializing compilation state.
|
|
280
|
+
*
|
|
281
|
+
* Optional - not all checks need infrastructure setup.
|
|
282
|
+
*/
|
|
283
|
+
setup?(): Promise<void>;
|
|
284
|
+
/**
|
|
285
|
+
* Unique identifier for this check type.
|
|
286
|
+
* Must match the `type` field in CheckConfig.
|
|
287
|
+
*
|
|
288
|
+
* @example "eslint"
|
|
289
|
+
*
|
|
290
|
+
* @example "typescript"
|
|
291
|
+
*/
|
|
292
|
+
readonly type: string;
|
|
293
|
+
/**
|
|
294
|
+
* Validate that all dependencies for this check are available.
|
|
295
|
+
* Called once during runner setup before any checks execute.
|
|
296
|
+
*
|
|
297
|
+
* Should throw a descriptive error if dependencies are missing.
|
|
298
|
+
*
|
|
299
|
+
* @throws {Error} If required dependencies are not installed
|
|
300
|
+
*/
|
|
301
|
+
validate?(): Promise<void>;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Custom check definition for defineCheck().
|
|
305
|
+
*/
|
|
306
|
+
interface CustomCheckDefinition<TConfig extends Record<string, unknown> = Record<string, unknown>> {
|
|
307
|
+
/**
|
|
308
|
+
* Optional default configuration values.
|
|
309
|
+
* These will be merged with user-provided config.
|
|
310
|
+
*/
|
|
311
|
+
defaults?: Partial<TConfig>;
|
|
312
|
+
/**
|
|
313
|
+
* Execute the check and return violations.
|
|
314
|
+
*
|
|
315
|
+
* @param config - The full check configuration object
|
|
316
|
+
*
|
|
317
|
+
* @returns Array of violations found
|
|
318
|
+
*/
|
|
319
|
+
run(config: TConfig): Promise<IssueInput[]>;
|
|
320
|
+
/**
|
|
321
|
+
* Setup any infrastructure needed for this check type.
|
|
322
|
+
* Called once per type during runner setup, in parallel with other checks.
|
|
323
|
+
*
|
|
324
|
+
* Examples: creating cache directories, initializing compilation state.
|
|
325
|
+
*
|
|
326
|
+
* Optional - not all checks need infrastructure setup.
|
|
327
|
+
*/
|
|
328
|
+
setup?(): Promise<void>;
|
|
329
|
+
/**
|
|
330
|
+
* Unique identifier for this check type.
|
|
331
|
+
* Multiple check instances can share the same type and runner.
|
|
332
|
+
*
|
|
333
|
+
* @example "eslint"
|
|
334
|
+
*
|
|
335
|
+
* @example "typescript"
|
|
336
|
+
*
|
|
337
|
+
* @example "no-hardcoded-urls"
|
|
338
|
+
*/
|
|
339
|
+
type: string;
|
|
340
|
+
/**
|
|
341
|
+
* Validate that all dependencies for this check type are available.
|
|
342
|
+
* Called once per type during runner setup before any checks execute.
|
|
343
|
+
*
|
|
344
|
+
* Should throw a descriptive error if dependencies are missing.
|
|
345
|
+
*
|
|
346
|
+
* @throws {Error} If required dependencies are not installed
|
|
347
|
+
*/
|
|
348
|
+
validate?(): Promise<void>;
|
|
349
|
+
}
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/core/config.d.ts
|
|
352
|
+
/**
|
|
353
|
+
* Define a mejora configuration.
|
|
354
|
+
*
|
|
355
|
+
* @param config - Configuration object
|
|
356
|
+
*
|
|
357
|
+
* @param config.checks - Array of checks to run
|
|
358
|
+
*
|
|
359
|
+
* @returns A mejora configuration object
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* import { defineConfig, eslint, typescript, regex } from "mejora";
|
|
364
|
+
*
|
|
365
|
+
* export default defineConfig({
|
|
366
|
+
* checks: [
|
|
367
|
+
* eslint({
|
|
368
|
+
* name: "no-console",
|
|
369
|
+
* files: ["src/**\/*.ts"],
|
|
370
|
+
* rules: { "no-console": "error" }
|
|
371
|
+
* }),
|
|
372
|
+
* typescript({
|
|
373
|
+
* name: "strict",
|
|
374
|
+
* compilerOptions: { noImplicitAny: true }
|
|
375
|
+
* }),
|
|
376
|
+
* regex({
|
|
377
|
+
* name: "no-todos",
|
|
378
|
+
* files: ["**\/*"],
|
|
379
|
+
* patterns: [{ pattern: /TODO/g }]
|
|
380
|
+
* })
|
|
381
|
+
* ]
|
|
382
|
+
* });
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
declare function defineConfig(config: {
|
|
386
|
+
checks: Check[];
|
|
387
|
+
}): {
|
|
388
|
+
checks: Check[];
|
|
389
|
+
runners: CheckRunner<unknown>[];
|
|
390
|
+
};
|
|
391
|
+
//#endregion
|
|
392
|
+
//#region src/core/define-check.d.ts
|
|
393
|
+
/**
|
|
394
|
+
* Configuration for a check instance, combining the check-specific config
|
|
395
|
+
* with required metadata fields.
|
|
396
|
+
*/
|
|
397
|
+
type UserCheckConfig<TConfig> = TConfig & {
|
|
398
|
+
/**
|
|
399
|
+
* Unique identifier for this check instance.
|
|
400
|
+
* Used in baseline tracking and output.
|
|
401
|
+
*/
|
|
402
|
+
name: string;
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* Define a custom check for use with mejora().
|
|
406
|
+
*
|
|
407
|
+
* This is the primitive used by all checks (built-in and custom) to create
|
|
408
|
+
* reusable check definitions. Multiple check instances with the same type
|
|
409
|
+
* share a single runner for optimal performance.
|
|
410
|
+
*
|
|
411
|
+
* @param definition - Custom check definition with type, run function, and optional hooks.
|
|
412
|
+
*
|
|
413
|
+
* @returns A factory function that creates Check objects with different configurations.
|
|
414
|
+
*
|
|
415
|
+
* @example
|
|
416
|
+
* ```ts
|
|
417
|
+
* import { defineConfig, defineCheck } from "mejora";
|
|
418
|
+
*
|
|
419
|
+
* const noHardcodedUrls = defineCheck<{ files: string[] }>({
|
|
420
|
+
* type: "no-hardcoded-urls",
|
|
421
|
+
* async run(config) {
|
|
422
|
+
* const violations: IssueInput[] = [];
|
|
423
|
+
* // Custom checking logic using config.files
|
|
424
|
+
* return violations;
|
|
425
|
+
* }
|
|
426
|
+
* });
|
|
427
|
+
*
|
|
428
|
+
* export default defineConfig({
|
|
429
|
+
* checks: [
|
|
430
|
+
* noHardcodedUrls({ name: "urls-in-src", files: ["src/**\/*.ts"] }),
|
|
431
|
+
* noHardcodedUrls({ name: "urls-in-lib", files: ["lib/**\/*.ts"] }),
|
|
432
|
+
* ]
|
|
433
|
+
* });
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
declare function defineCheck<TConfig extends Record<string, any> = Record<string, any>>(definition: CustomCheckDefinition<TConfig>): (userConfig: UserCheckConfig<TConfig>) => Check;
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region src/runners/eslint.d.ts
|
|
439
|
+
/**
|
|
440
|
+
* Create an ESLint check for use with mejora().
|
|
441
|
+
*
|
|
442
|
+
* @param config - ESLint check configuration options including name.
|
|
443
|
+
*
|
|
444
|
+
* @returns A Check object for use with mejora().
|
|
445
|
+
*
|
|
446
|
+
* @example
|
|
447
|
+
* ```ts
|
|
448
|
+
* import { defineConfig, eslint } from "mejora";
|
|
449
|
+
*
|
|
450
|
+
* export default defineConfig({
|
|
451
|
+
* checks: [
|
|
452
|
+
* eslint({
|
|
453
|
+
* name: "no-console",
|
|
454
|
+
* files: ["src/**\/*.ts"],
|
|
455
|
+
* rules: { "no-console": "error" }
|
|
456
|
+
* })
|
|
457
|
+
* ]
|
|
458
|
+
* });
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
declare const eslint: (userConfig: ESLintCheckConfig & {
|
|
462
|
+
name: string;
|
|
463
|
+
}) => Check;
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/runners/regex.d.ts
|
|
466
|
+
/**
|
|
467
|
+
* Create a regex check for use with mejora().
|
|
468
|
+
*
|
|
469
|
+
* @param config - Regex check configuration options including name.
|
|
470
|
+
*
|
|
471
|
+
* @returns A Check object for use with mejora().
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```ts
|
|
475
|
+
* import { defineConfig, regex } from "mejora";
|
|
476
|
+
*
|
|
477
|
+
* export default defineConfig({
|
|
478
|
+
* checks: [
|
|
479
|
+
* regex({
|
|
480
|
+
* name: "no-todos",
|
|
481
|
+
* files: ["src/**\/*.ts"],
|
|
482
|
+
* patterns: [
|
|
483
|
+
* { pattern: /TODO/g, message: "TODO comment found" }
|
|
484
|
+
* ]
|
|
485
|
+
* })
|
|
486
|
+
* ]
|
|
487
|
+
* });
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
declare const regex: (userConfig: RegexCheckConfig & {
|
|
491
|
+
name: string;
|
|
492
|
+
}) => Check;
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/runners/typescript.d.ts
|
|
495
|
+
/**
|
|
496
|
+
* Create a TypeScript check for use with mejora().
|
|
497
|
+
*
|
|
498
|
+
* @param config - TypeScript check configuration options including name.
|
|
499
|
+
*
|
|
500
|
+
* @returns A Check object for use with mejora().
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```ts
|
|
504
|
+
* import { defineConfig, typescript } from "mejora";
|
|
505
|
+
*
|
|
506
|
+
* export default defineConfig({
|
|
507
|
+
* checks: [
|
|
508
|
+
* typescript({
|
|
509
|
+
* name: "strict-types",
|
|
510
|
+
* compilerOptions: { noImplicitAny: true }
|
|
511
|
+
* })
|
|
512
|
+
* ]
|
|
513
|
+
* });
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
declare const typescript: (userConfig: TypeScriptCheckConfig & {
|
|
517
|
+
name: string;
|
|
518
|
+
}) => Check;
|
|
519
|
+
//#endregion
|
|
520
|
+
export { defineConfig as a, Config as c, RawSnapshot as d, Snapshot as f, defineCheck as i, Issue as l, regex as n, Check as o, eslint as r, CheckRunner as s, typescript as t, IssueInput as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { CheckRunner, Config, Issue, IssueInput, RawSnapshot, Snapshot, defineConfig,
|
|
1
|
+
import { a as defineConfig, c as Config, d as RawSnapshot, f as Snapshot, i as defineCheck, l as Issue, n as regex, o as Check, r as eslint, s as CheckRunner, t as typescript, u as IssueInput } from "./index-BNOgTD7N.mjs";
|
|
2
|
+
export { Check, CheckRunner, Config, Issue, IssueInput, RawSnapshot, Snapshot, defineCheck, defineConfig, eslint, regex, typescript };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{t as e}from"./config-C0qrgNuO.mjs";import{a as t,c as n,i as r,o as i,s as a,t as o}from"./hash-BaLFZO2A.mjs";import{glob as s,mkdir as c,readFile as l,stat as u,writeFile as d}from"node:fs/promises";import{createReadStream as f}from"node:fs";import{createInterface as p}from"node:readline/promises";function m(e){let{defaults:t,type:n}=e,r=()=>{let t={async run(t){let{type:n,...r}=t;return{items:await e.run(r),type:`items`}},type:n};return e.setup&&(t.setup=e.setup),e.validate&&(t.validate=e.validate),t};return e=>{let{name:i,...a}=e;return{__runnerFactory:r,config:{type:n,...t,...a},id:i}}}function h(){let e=new WeakSet;return(t,n)=>{if(n&&typeof n==`object`){if(e.has(n))return`[Circular]`;if(e.add(n),!Array.isArray(n)){let e=n,t={};for(let n of Object.keys(e).toSorted()){let r=e[n];typeof r==`function`||typeof r==`symbol`||(t[n]=r)}return t}}return n}}function g(e){return o(JSON.stringify(e??null,h()))}function _(e,t=process.cwd()){return a(t,`node_modules`,`.cache`,`mejora`,e)}async function v(e){try{let t=await l(e,`utf8`);return JSON.parse(t)}catch{return{}}}async function y(e,t){try{await d(e,JSON.stringify(t,null,2),`utf8`)}catch{}}async function b(e){try{let t=await u(e);return`${t.mtimeMs}-${t.size}`}catch{return null}}const x=m({async run(e){let{ESLint:t}=await import(`eslint`),n=process.cwd(),r=_(`eslint`,n),a=g(e),{concurrency:o,files:s,...c}=e,l=new Set;if(c.rules)for(let e of Object.keys(c.rules))l.add(e);let u=l.size>0,d=await new t({cache:!0,cacheLocation:`${r}/${a}.eslintcache`,overrideConfig:c,...!u&&{concurrency:o??`auto`},...u&&{ruleFilter:({ruleId:e})=>l.has(e)}}).lintFiles(s),f=[];for(let{filePath:e,messages:t}of d){let r=i(n,e);for(let{column:e,line:n,message:i,ruleId:a}of t)a&&f.push({column:e,file:r,line:n,message:i,rule:a})}return f},async setup(){let e=_(`eslint`,process.cwd()),{mkdir:t}=await import(`node:fs/promises`);await t(e,{recursive:!0})},type:`eslint`,async validate(){try{await import(`eslint`)}catch{throw Error(`eslint check requires "eslint" package to be installed. Run: npm install eslint`)}}}),S=[`**/node_modules/**`,`**/dist/**`,`**/.git/**`];function C(e,t){if(t?.length)return t;let n=e.map(e=>/^([^*]+\/)/.exec(e)?.[1]).filter(e=>e!==void 0);return[...S,...n.flatMap(e=>S.map(t=>t.replace(/^\*\*\//,e)))]}async function w(e,t,n){let r=Array.from({length:e.length}),i=0;return await Promise.all(Array.from({length:Math.min(t,e.length)},async()=>{for(;i<e.length;){let t=i++;r[t]=await n(e[t])}})),r}const T=m({async run(e){let n=process.cwd(),r=Array.isArray(e.files)?e.files:[e.files],i=C(r,e.ignore),a=new Set;for(let e of r){let t=s(e,{cwd:n,exclude:i});for await(let e of t)a.add(e)}let o=[...a],c=e.patterns.map(({message:e,pattern:t,rule:n})=>{let r=t.flags.includes(`g`)?t.flags:`${t.flags}g`;return{message:e,regex:new RegExp(t.source,r),ruleText:n??t.source}}),l=t(_(`regex`,n),`${g(e)}.json`),u=await v(l),d={},m=await w(o,e.concurrency??10,async e=>{let r=t(n,e),i=await b(r);if(!i)return[];let a=u[e];if(a?.hash===i)return d[e]=a,a.items;try{let t=[],n=p({crlfDelay:1/0,input:f(r,{encoding:`utf8`})}),a=0;try{for await(let r of n){a++;for(let n of c){n.regex.lastIndex=0;let i;for(;(i=n.regex.exec(r))!==null;){let r=i.index+1,o=typeof n.message==`function`?n.message(i):n.message??`Pattern matched: ${i[0]}`;t.push({column:r,file:e,line:a,message:o,rule:n.ruleText})}}}}finally{n.close()}return d[e]={hash:i,items:t},t}catch{return[]}}),h=[];for(let e of m)h.push(...e);return await y(l,d),h},async setup(){await c(_(`regex`,process.cwd()),{recursive:!0})},type:`regex`});function E(e,t){return e.replaceAll(/import\("([^"]+)"\)/g,(e,n)=>{try{if(r(n)){let e=i(t,n);if(!e.startsWith(`..`))return`import("${e||`.`}")`}}catch{}return e})}const D=m({async run(e){let{createIncrementalCompilerHost:t,createIncrementalProgram:r,findConfigFile:o,flattenDiagnosticMessageText:s,getPreEmitDiagnostics:c,parseJsonConfigFileContent:l,readConfigFile:u,sys:d,version:f}=await import(`typescript`),p=process.cwd(),m=d.fileExists.bind(d),h=d.readFile.bind(d),v=e.tsconfig?a(e.tsconfig):o(p,m,`tsconfig.json`);if(!v)throw Error(`TypeScript config file not found`);let{config:y,error:b}=u(v,h);if(b){let e=typeof b.messageText==`string`?b.messageText:s(b.messageText,`
|
|
2
|
+
`);throw TypeError(`Failed to read TypeScript config: ${e}`)}let x=l(y,d,p,e.compilerOptions),S=a(_(`typescript`,p),`${g({compilerOptions:e.compilerOptions??{},configPath:v,parsedOptions:x.options,typescriptVersion:f})}.tsbuildinfo`),C={...x.options,incremental:!0,noEmit:!0,skipLibCheck:x.options.skipLibCheck??!0,tsBuildInfoFile:S},w=t(C,d),T=w.writeFile.bind(w);w.writeFile=(e,t,...n)=>{a(e)===S&&T(e,t,...n)};let D=r({host:w,options:C,projectReferences:x.projectReferences??[],rootNames:x.fileNames}),O=c(D.getProgram());D.emit();let k=a(p),A=k+n,j=O.filter(e=>{if(!e.file)return!0;let t=a(e.file.fileName);return t===k||t.startsWith(A)}),M=[];for(let e of j){let t=s(e.messageText,`
|
|
3
|
+
`),n=`TS${e.code}`;if(e.file&&e.start!==void 0){let{character:r,line:a}=e.file.getLineAndCharacterOfPosition(e.start),o=i(p,e.file.fileName);M.push({column:r+1,file:o,line:a+1,message:E(t,p),rule:n})}else M.push({column:0,file:`(global)`,line:0,message:E(t,p),rule:n})}return M},async setup(){let e=_(`typescript`,process.cwd()),{mkdir:t}=await import(`node:fs/promises`);await t(e,{recursive:!0})},type:`typescript`,async validate(){try{await import(`typescript`)}catch{throw Error(`typescript check requires "typescript" package to be installed. Run: npm install typescript`)}}});export{m as defineCheck,e as defineConfig,x as eslint,T as regex,D as typescript};
|
package/dist/run.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
2
|
+
import{n as e}from"./config-C0qrgNuO.mjs";import{n as t,o as n,r,t as i}from"./hash-BaLFZO2A.mjs";import{t as a}from"./check-registry-mjn8FfPF.mjs";import{fileURLToPath as o}from"node:url";import{mkdir as s,readFile as c,writeFile as l}from"node:fs/promises";import{inspect as u,parseArgs as d,styleText as f}from"node:util";import{Worker as p}from"node:worker_threads";import{env as m}from"node:process";const h=e=>t=>f(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(`gray`),ee=h(`underline`),te=h(`yellow`);function ne(e){let t=[e.message];if(e.stack){let n=e.stack.split(`
|
|
3
3
|
`).slice(1).map(e=>y(e.trim())).join(`
|
|
4
4
|
`);t.push(n)}return t.join(`
|
|
5
|
-
`)}function C(...e){return e.map(e=>typeof e==`string`?e:e instanceof Error?ne(e):u(e,{colors:!1,depth:10})).join(` `)}const w={error:(...e)=>{console.error(x(`✖`),C(...e))},log:(...e)=>{console.log(C(...e))},start:(...e)=>{console.log(v(`◐`),C(...e))},success:(...e)=>{console.log(b(`✔`),C(...e))}};function T(e,t){return e.file===t.file?e.line===t.line?e.column-t.column:e.line-t.line:e.file.localeCompare(t.file)}function re(
|
|
5
|
+
`)}function C(...e){return e.map(e=>typeof e==`string`?e:e instanceof Error?ne(e):u(e,{colors:!1,depth:10})).join(` `)}const w={error:(...e)=>{console.error(x(`✖`),C(...e))},log:(...e)=>{console.log(C(...e))},start:(...e)=>{console.log(v(`◐`),C(...e))},success:(...e)=>{console.log(b(`✔`),C(...e))}};function T(e,t){return e.file===t.file?e.line===t.line?e.column-t.column:e.line-t.line:e.file.localeCompare(t.file)}function re(e){let t=Map.groupBy(e,e=>e.signature),n=[];for(let[e,r]of t){r.sort(T);for(let[t,{signature:a,...o}]of r.entries())n.push({...o,id:i(`${e}:${t}`)})}return n}function E(e){return{items:re(e.items.map(e=>({...e,signature:`${e.file} - ${e.rule}: ${e.message}`}))).toSorted(T),type:`items`}}function D(e,t){return e===1?t:`${t}s`}const O=`__unparsable__`;function k(e){return e.replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`[`,`[`).replaceAll(`]`,`]`)}function A(e,t,r){let i=n(t,e);return r?`${i}#L${r}`:i}function j(e,t){return`[${e}](${t})`}function ie(e,t){let n=A(e.file,t,e.line);return`- ${j(e.line?`Line ${e.line}`:e.file,n)} - ${`${e.rule}: ${k(e.message)}`}`}function ae(e){let t=Object.groupBy(e,e=>e.file||O);return Object.entries(t).map(([e,t=[]])=>({filePath:e,items:t})).toSorted((e,t)=>e.filePath===O?1:t.filePath===O?-1:e.filePath.localeCompare(t.filePath))}function oe(e,t){let n=e.length,r=D(n,`issue`),i=[`\n### Other Issues · ${t}\n`];for(let t of e)i.push(`- ${t.rule}: ${k(t.message)}`);return i.push(`\n${n} ${r} in Other Issues`,``),i.join(`
|
|
6
6
|
`)}function M(e,t,n){if(e.filePath===O)return oe(e.items,n);let r=A(e.filePath,t),i=j(e.filePath,r),a=e.items.length,o=D(a,`issue`),s=[`\n### ${i} · ${n}\n`];for(let n of e.items)s.push(ie(n,t));return s.push(`\n${a} ${o} in ${e.filePath}`,``),s.join(`
|
|
7
7
|
`)}function se(e,t,n){let r=t.length,i=D(r,`issue`),a=[`\n## ${e}\n`];if(t.length===0)return a.push(`No issues`),a.join(`
|
|
8
8
|
`);let o=ae(t);for(let t of o)a.push(M(t,n,e));return a.push(`---\n${r} total ${i} for ${e}`),a.join(`
|
|
@@ -16,7 +16,7 @@ import{c as e,d as t,l as n,p as r,u as i}from"./typescript-BG8_PZiP.mjs";import
|
|
|
16
16
|
}`.repeat(t)}`}function fe(e){let t=(e.match(/\{/g)?.length??0)-(e.match(/\}/g)?.length??0);return t===0?e:t<0?V(e,-t):H(e,t)}function pe(e){try{return JSON.parse(e)}catch{return}}function U(e){let t=e.trim();return t.endsWith(`,`)?t.slice(0,-1):t}function me(e){return`{
|
|
17
17
|
"version": 2,
|
|
18
18
|
${U(e)}
|
|
19
|
-
}`}function W(e){if(typeof e!=`object`||!e)throw TypeError(`Baseline must be an object`);if(`checks`in e&&e.checks&&typeof e.checks==`object`)return{checks:e.checks,version:2};let t={};for(let[n,r]of Object.entries(e))n!==`version`&&(t[n]=r);return{checks:t,version:2}}function G(e){try{let t=pe(e.trim());if(t)return W(t);let n=me(fe(U(e)));return W(JSON.parse(n))}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 Map;for(let n of e)for(let[e,{items:r=[]}]of Object.entries(n.checks)){if(r.length===0)continue;let n=t.get(e);n||(n=new Map,t.set(e,n));for(let e of r)n.set(e.id,e)}let n={};for(let[e,r]of t)n[e]={items:[...r.values()].toSorted((e,t)=>e.id.localeCompare(t.id)),type:`items`};return{checks:n,version:2}}function _e(e){let t=he(e),n=[];for(let{ours:e,theirs:r}of t)n.push(G(e),G(r));return ge(n)}var K=class e{baselinePath;constructor(e=`.mejora/baseline.json`){this.baselinePath=e}static create(e){return{checks:e,version:2}}static getEntry(e,t){return e?.checks[t]}static update(t,n,r){let i=t??e.create({}),a=i.checks[n];return B(r,a)?i:{...i,checks:{...i.checks,[n]:r}}}async load(){try{let e=await c(this.baselinePath,`utf8`);if(e.includes(`<<<<<<<`)){w.start(`Merge conflict detected in baseline, auto-resolving...`);let t=_e(e);return await this.save(t,!0),w.success(`Baseline conflict resolved`),t}return await this.resolveMarkdownConflictIfNeeded(e),JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return null;throw e}}async save(e,t=!1){if(le&&!t)return;let n=`${JSON.stringify(e,null,2)}\n`,
|
|
19
|
+
}`}function W(e){if(typeof e!=`object`||!e)throw TypeError(`Baseline must be an object`);if(`checks`in e&&e.checks&&typeof e.checks==`object`)return{checks:e.checks,version:2};let t={};for(let[n,r]of Object.entries(e))n!==`version`&&(t[n]=r);return{checks:t,version:2}}function G(e){try{let t=pe(e.trim());if(t)return W(t);let n=me(fe(U(e)));return W(JSON.parse(n))}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 Map;for(let n of e)for(let[e,{items:r=[]}]of Object.entries(n.checks)){if(r.length===0)continue;let n=t.get(e);n||(n=new Map,t.set(e,n));for(let e of r)n.set(e.id,e)}let n={};for(let[e,r]of t)n[e]={items:[...r.values()].toSorted((e,t)=>e.id.localeCompare(t.id)),type:`items`};return{checks:n,version:2}}function _e(e){let t=he(e),n=[];for(let{ours:e,theirs:r}of t)n.push(G(e),G(r));return ge(n)}var K=class e{baselinePath;constructor(e=`.mejora/baseline.json`){this.baselinePath=e}static create(e){return{checks:e,version:2}}static getEntry(e,t){return e?.checks[t]}static update(t,n,r){let i=t??e.create({}),a=i.checks[n];return B(r,a)?i:{...i,checks:{...i.checks,[n]:r}}}async load(){try{let e=await c(this.baselinePath,`utf8`);if(e.includes(`<<<<<<<`)){w.start(`Merge conflict detected in baseline, auto-resolving...`);let t=_e(e);return await this.save(t,!0),w.success(`Baseline conflict resolved`),t}return await this.resolveMarkdownConflictIfNeeded(e),JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return null;throw e}}async save(e,t=!1){if(le&&!t)return;let n=`${JSON.stringify(e,null,2)}\n`,i=this.baselinePath.replace(`.json`,`.md`),a=N(e,r(this.baselinePath));await s(r(this.baselinePath),{recursive:!0}),await Promise.all([l(this.baselinePath,n,`utf8`),l(i,a,`utf8`)])}async resolveMarkdownConflictIfNeeded(e){let t=this.baselinePath.replace(`.json`,`.md`);try{(await c(t,`utf8`)).includes(`<<<<<<<`)&&(w.start(`Merge conflict detected in markdown report, regenerating...`),await l(t,N(JSON.parse(e),r(this.baselinePath)),`utf8`),w.success(`Markdown report regenerated`))}catch{}}};const ve=o(new URL(`workers/check.mjs`,import.meta.url));var ye=class e{baselineManager;registry;constructor(e,t){this.registry=e,this.baselineManager=new K(t)}static async executeChecksParallel(t,n){try{let r=t.map(async t=>{let r=await e.executeWorker(t.id),i=E(r.snapshot),a=K.getEntry(n,t.id),o=z(i,a);return{baseline:a,checkId:t.id,duration:r.duration,hasImprovement:o.hasImprovement,hasRegression:o.hasRegression,hasRelocation:o.hasRelocation,isInitial:o.isInitial,newIssues:o.newIssues,removedIssues:o.removedIssues,snapshot:i}});return await Promise.all(r)}catch(e){return w.error(`Parallel execution failed:`,e),null}}static async executeWorker(e){return new Promise((t,n)=>{let r=new p(ve,{workerData:{checkId:e}});r.on(`message`,t),r.on(`error`,n),r.on(`exit`,e=>{e!==0&&n(Error(`Worker stopped with exit code ${e}`))})})}static filterChecks=(t,n)=>{let r=n.only?e.resolveRegex(n.only,`--only`):null,i=n.skip?e.resolveRegex(n.skip,`--skip`):null;return!r&&!i?t:t.filter(e=>!(r&&!r.test(e.id)||i?.test(e.id)))};static resolveRegex(e,t){try{return new RegExp(e)}catch{throw Error(`Invalid regex pattern for ${t}: "${e}"`)}}async run(t,n={}){let r=performance.now(),i=await this.baselineManager.load(),a=e.filterChecks(t.checks,n),o=a.length;w.start(`Running ${o} check${o===1?``:`s`}...`);let s=o>1?await e.executeChecksParallel(a,i):await this.executeChecksSequential(a,i);if(!s)return{exitCode:2,hasImprovement:!1,hasRegression:!0,results:[],totalDuration:performance.now()-r};let c=i,l=!1,u=!1,d=!1;for(let e of s)e.hasRegression&&(l=!0),e.hasImprovement&&(u=!0),e.isInitial&&(d=!0),(e.hasImprovement||e.hasRelocation||n.force||e.isInitial)&&(c=K.update(c,e.checkId,{items:e.snapshot.items,type:e.snapshot.type}));return c&&c!==i&&(!l||n.force||d)&&await this.baselineManager.save(c,n.force),{exitCode:l&&!n.force?1:0,hasImprovement:u,hasRegression:l,results:s,totalDuration:performance.now()-r}}async executeChecksSequential(e,t){try{let t=a.getRequiredTypes(e);await Promise.all([this.registry.setup(t),this.registry.validate(t)])}catch(e){return w.error(`Setup failed:`,e),null}let n=[];for(let r of e)try{let e=performance.now(),i=await this.registry.get(r.config.type).run(r.config),a=performance.now()-e,o=E(i),s=K.getEntry(t,r.id),c=z(o,s);n.push({baseline:s,checkId:r.id,duration:a,hasImprovement:c.hasImprovement,hasRegression:c.hasRegression,hasRelocation:c.hasRelocation,isInitial:c.isInitial,newIssues:c.newIssues,removedIssues:c.removedIssues,snapshot:o})}catch(e){return w.error(`Error running check "${r.id}":`,e),null}return n}};function q(e,t){if(!(e===void 0||t===0))return e/t}function be(e){let{results:t,totalDuration:n}=e,r=t.length,i=[],a=[],o=[],s=[],c=0,l=0,u=0,d=0,f=0,p=[];for(let e of t){let t=e.snapshot.items.length;f+=t,e.isInitial?(u+=t,o.push(e.checkId)):(e.hasImprovement&&(c+=e.removedIssues.length,i.push(e.checkId)),e.hasRegression&&(l+=e.newIssues.length,a.push(e.checkId)),!e.hasImprovement&&!e.hasRegression&&(d+=t,s.push(e.checkId))),p.push({checkId:e.checkId,duration:e.duration,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,isInitial:e.isInitial,newIssues:e.newIssues,removedIssues:e.removedIssues,totalIssues:t})}let m={checks:p,exitCode:e.exitCode,hasImprovement:e.hasImprovement,hasRegression:e.hasRegression,summary:{avgDuration:q(n,r),checksRun:r,improvementChecks:i,improvements:c,initial:u,initialChecks:o,regressionChecks:a,regressions:l,totalIssues:f,unchanged:d,unchangedChecks:s},totalDuration:n};return JSON.stringify(m,null,2)}function xe(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 J(e){let t=Math.round(e);return t<1?`<1ms`:xe(t)}const Y=` `,Se=`${Y} `;function Ce(e){return e===`initial`?y(`→`):e===`improvement`?b(`↑`):x(`↓`)}function we(e,n,i){let a=r(e),o=t(e),s=n>0?`:${n}:${i>0?i:1}`:``;return`${a===`.`?``:y(`${a}/`)}${ee(o)}${s?y(s):``}`}function Te(e,t){return[`${Ce(t)} ${we(e.file,e.line,e.column)} ${y(e.rule)}`,e.message]}function X(e,t){let n=[],r=e.slice(0,10);for(let e of r){let[r,i]=Te(e,t);n.push(`${Y}${r}`,`${Se}${i}`)}let i=e.length-r.length;return i>0&&n.push(`${Y}${y(`... and ${i} more`)}`),n}function Ee(e){return e===void 0?[]:[` ${y(`Duration`)} ${J(e)}`]}function De(e){return[` ${y(`Issues`)} ${_(e)}`]}function Z(e){return[...Ee(e.duration),...De(e.snapshot.items.length)]}function Oe(e){if(!e.hasRegression)return[];let t=e.newIssues.length;return[` ${x(t)} new ${D(t,`issue`)} (${D(t,`regression`)}):`,...X(e.newIssues,`regression`)]}function Q(e){if(!e.hasImprovement)return[];let t=e.removedIssues.length;return[` ${b(t)} ${D(t,`issue`)} fixed (${D(t,`improvement`)}):`,...X(e.removedIssues,`improvement`)]}function ke(e,t){let n=t?``:`
|
|
20
20
|
`,r=e.snapshot.items.length,i=[`${n}${g(`ℹ`)} ${e.checkId}:`,` Initial baseline created with ${g(r)} ${D(r,`issue`)}`];return e.snapshot.items.length>0&&i.push(...X(e.snapshot.items,`initial`)),i.push(``,...Z(e)),i}function Ae(e,t){return[`${t?``:`
|
|
21
21
|
`}${e.hasRegression?x(`✖`):b(`✔`)} ${e.checkId}:`,...Oe(e),...Q(e),``,...Z(e)]}function je(e,t){let n=t?``:`
|
|
22
22
|
`,r=e.snapshot.items.length;return e.duration===void 0?[`${n}${S(`ℹ`)} ${e.checkId} (${_(r)})`]:[`${n}${S(`ℹ`)} ${e.checkId} (${_(r)}) ${y(J(e.duration))}`]}function Me(e,t){return e.isInitial?ke(e,t):e.hasRegression||e.hasImprovement?Ae(e,t):je(e,t)}function Ne(e,t,n){return t?g(`✔ Initial baseline created successfully`):e.hasRegression?n?te(`⚠ Regressions detected (forced)`):`${x(`✗ Regressions detected`)} - Run failed`:e.hasImprovement?`${b(`✔ Improvements detected`)} - Baseline updated`:b(`✔ All checks passed`)}function Pe(e,t){let n={hasAnyInitial:!1,totalImprovements:0,totalInitial:0,totalIssues:0,totalRegressions:0};for(let t of e.results){let e=t.snapshot.items.length;if(n.totalIssues+=e,t.isInitial){n.hasAnyInitial=!0,n.totalInitial+=e;continue}let{hasImprovement:r,hasRegression:i}=t;r&&(n.totalImprovements+=t.removedIssues.length),i&&(n.totalRegressions+=t.newIssues.length)}let r=[` ${y(`Improvements`)} ${b(n.totalImprovements)}`,` ${y(`Regressions`)} ${x(n.totalRegressions)}`,` ${y(`Initial`)} ${g(n.totalInitial)}`,` ${y(`Checks`)} ${e.results.length}`,` ${y(`Issues`)} ${_(n.totalIssues)}`],i=q(e.totalDuration,e.results.length);return e.totalDuration!==void 0&&i!==void 0&&r.push(` ${y(`Duration`)} ${J(e.totalDuration)} ${S(`(avg ${J(i)})`)}`),r.push(``,Ne(e,n.hasAnyInitial,t)),r.join(`
|
|
@@ -40,4 +40,4 @@ Examples:
|
|
|
40
40
|
mejora --json
|
|
41
41
|
mejora --only "eslint > *"
|
|
42
42
|
mejora --skip typescript
|
|
43
|
-
`),process.exit(0));try{let
|
|
43
|
+
`),process.exit(0));try{let t=new a,n=await e();t.init(n);let r=await new ye(t).run(n,{force:$.force,json:$.json,only:$.only,skip:$.skip});$.json?w.log(be(r)):(w.log(``),w.log(Fe(r,$.force))),process.exit(r.exitCode)}catch(e){e instanceof Error?w.error(e.message):w.error(e),process.exit(2)}export{};
|
package/dist/workers/check.d.mts
CHANGED
package/dist/workers/check.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{n as e}from"../config-C0qrgNuO.mjs";import{t}from"../check-registry-mjn8FfPF.mjs";import{parentPort as n,workerData as r}from"node:worker_threads";let i=null,a=null;const o=new Map;async function s({checkId:n}){i||(a??=(async()=>{i=await e()})(),await a);let r=i?.checks.find(e=>e.id===n);if(!r)throw Error(`Check not found in config: ${n}`);let s=r.config,c=o.get(s.type);if(!c){c=new t,c.init(i);let e=new Set([s.type]);await Promise.all([c.setup(e),c.validate(e)]),o.set(s.type,c)}let l=performance.now(),u=await c.get(s.type).run(s);return{duration:performance.now()-l,snapshot:u}}if(n){let e=await s(r);n.postMessage(e)}export{s as checkWorker};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{o as e,r as t,t as n}from"./typescript-BG8_PZiP.mjs";var r=class{runners=new Map;static getRequiredTypes(e){return new Set(Object.values(e).map(e=>e.type))}get(e){let t=this.runners.get(e);if(!t)throw Error(`Unknown check type: ${e}`);return t}getTypes(){return new Set(this.runners.keys())}has(e){return this.runners.has(e)}init(r={}){if(this.register(new e),this.register(new n),this.register(new t),r.runners)for(let e of r.runners)this.register(e)}register(e){this.runners.has(e.type)||this.runners.set(e.type,e)}async setup(e){await this.runLifecycle(e,`setup`)}async validate(e){await this.runLifecycle(e,`validate`)}async runLifecycle(e,t){let n=[];for(let r of e){let e=this.get(r)[t]?.();e&&n.push(e)}await Promise.all(n)}};export{r as t};
|
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
import * as eslint from "eslint";
|
|
2
|
-
import { Linter } from "eslint";
|
|
3
|
-
import * as typescript from "typescript";
|
|
4
|
-
import { CompilerOptions } from "typescript";
|
|
5
|
-
|
|
6
|
-
//#region src/types.d.ts
|
|
7
|
-
interface Issue {
|
|
8
|
-
/**
|
|
9
|
-
* 1-indexed column number for display.
|
|
10
|
-
*/
|
|
11
|
-
column: number;
|
|
12
|
-
/**
|
|
13
|
-
* Relative path from cwd.
|
|
14
|
-
*/
|
|
15
|
-
file: string;
|
|
16
|
-
/**
|
|
17
|
-
* Hash of canonical representation.
|
|
18
|
-
*
|
|
19
|
-
* @example "a1b2c3d4e5f6g7h8i9j0"
|
|
20
|
-
*/
|
|
21
|
-
id: string;
|
|
22
|
-
/**
|
|
23
|
-
* 1-indexed line number for display.
|
|
24
|
-
*/
|
|
25
|
-
line: number;
|
|
26
|
-
/**
|
|
27
|
-
* The message.
|
|
28
|
-
*/
|
|
29
|
-
message: string;
|
|
30
|
-
/**
|
|
31
|
-
* Identifier for the issue (rule name, diagnostic code, etc).
|
|
32
|
-
*
|
|
33
|
-
* @example "no-nested-ternary" (ESLint)
|
|
34
|
-
*
|
|
35
|
-
* @example "TS2345" (TypeScript)
|
|
36
|
-
*/
|
|
37
|
-
rule: string;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Issue produced by a check runner.
|
|
41
|
-
*/
|
|
42
|
-
type IssueInput = Omit<Issue, "id">;
|
|
43
|
-
type SnapshotType = "items";
|
|
44
|
-
interface RawSnapshot {
|
|
45
|
-
/**
|
|
46
|
-
* Snapshot items (each item represents an issue produced by the check).
|
|
47
|
-
*/
|
|
48
|
-
items: IssueInput[];
|
|
49
|
-
type: SnapshotType;
|
|
50
|
-
}
|
|
51
|
-
interface Snapshot {
|
|
52
|
-
items: Issue[];
|
|
53
|
-
type: SnapshotType;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Configuration for an ESLint check.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* eslint({
|
|
61
|
-
* files: ["src/**\/*.{ts,tsx}"],
|
|
62
|
-
* overrides: {
|
|
63
|
-
* rules: {
|
|
64
|
-
* "no-nested-ternary": "error",
|
|
65
|
-
* },
|
|
66
|
-
* },
|
|
67
|
-
* })
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
interface ESLintCheckConfig {
|
|
71
|
-
/**
|
|
72
|
-
* Concurrency setting for ESLint.
|
|
73
|
-
*
|
|
74
|
-
* @see https://eslint.org/blog/2025/08/multithread-linting/#cli-multithreading-support
|
|
75
|
-
*
|
|
76
|
-
* @default "auto"
|
|
77
|
-
*/
|
|
78
|
-
concurrency?: "auto" | "off" | number;
|
|
79
|
-
/**
|
|
80
|
-
* Glob patterns for files to lint.
|
|
81
|
-
*
|
|
82
|
-
* Passed directly to ESLint's `lintFiles()` method.
|
|
83
|
-
*
|
|
84
|
-
* @example ["src/**\/*.ts", "src/**\/*.tsx"]
|
|
85
|
-
*/
|
|
86
|
-
files: string[];
|
|
87
|
-
/**
|
|
88
|
-
* ESLint configuration to merge with the base config.
|
|
89
|
-
*
|
|
90
|
-
* This is passed to ESLint's `overrideConfig` option and merged with
|
|
91
|
-
* your existing ESLint configuration.
|
|
92
|
-
*
|
|
93
|
-
* Can be a single config object or an array of config objects.
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* ```ts
|
|
97
|
-
* {
|
|
98
|
-
* rules: {
|
|
99
|
-
* "no-console": "error",
|
|
100
|
-
* },
|
|
101
|
-
* }
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
overrides?: Linter.Config | Linter.Config[];
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Configuration for a TypeScript diagnostics check.
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```ts
|
|
111
|
-
* typescript({
|
|
112
|
-
* overrides: {
|
|
113
|
-
* compilerOptions: {
|
|
114
|
-
* noImplicitAny: true,
|
|
115
|
-
* },
|
|
116
|
-
* },
|
|
117
|
-
* })
|
|
118
|
-
* ```
|
|
119
|
-
*/
|
|
120
|
-
interface TypeScriptCheckConfig {
|
|
121
|
-
/**
|
|
122
|
-
* Compiler options to merge with the base tsconfig.
|
|
123
|
-
*
|
|
124
|
-
* These options are merged with (not replacing) the compiler options
|
|
125
|
-
* from your tsconfig file.
|
|
126
|
-
*/
|
|
127
|
-
overrides?: {
|
|
128
|
-
compilerOptions?: CompilerOptions;
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Path to a TypeScript config file.
|
|
132
|
-
*
|
|
133
|
-
* If not provided, mejora will search for the nearest `tsconfig.json`
|
|
134
|
-
* starting from the current working directory.
|
|
135
|
-
*
|
|
136
|
-
* @example "tsconfig.strict.json"
|
|
137
|
-
*/
|
|
138
|
-
tsconfig?: string;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* A regex pattern configuration.
|
|
142
|
-
*/
|
|
143
|
-
interface RegexPattern {
|
|
144
|
-
/**
|
|
145
|
-
* Human-readable message for matches.
|
|
146
|
-
*
|
|
147
|
-
* @example "TODO comment found"
|
|
148
|
-
*
|
|
149
|
-
* @example "console.log statement"
|
|
150
|
-
*
|
|
151
|
-
* @example (match) => `Found TODO at line ${match.index + 1}`
|
|
152
|
-
*/
|
|
153
|
-
message?: ((match: RegExpExecArray) => string) | string;
|
|
154
|
-
/**
|
|
155
|
-
* The regex pattern to match.
|
|
156
|
-
*
|
|
157
|
-
* @example /\/\/\s*TODO:/gi
|
|
158
|
-
*
|
|
159
|
-
* @example /console\.log/g
|
|
160
|
-
*/
|
|
161
|
-
pattern: RegExp;
|
|
162
|
-
/**
|
|
163
|
-
* Rule identifier for this pattern.
|
|
164
|
-
* If not provided, uses the pattern source as the rule ID.
|
|
165
|
-
*
|
|
166
|
-
* @example "no-todos"
|
|
167
|
-
*
|
|
168
|
-
* @example "no-console-log"
|
|
169
|
-
*/
|
|
170
|
-
rule?: string;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Configuration for regex pattern matching check.
|
|
174
|
-
*/
|
|
175
|
-
interface RegexCheckConfig {
|
|
176
|
-
/**
|
|
177
|
-
* Concurrency for processing files.
|
|
178
|
-
*
|
|
179
|
-
* @default 10
|
|
180
|
-
*/
|
|
181
|
-
concurrency?: number;
|
|
182
|
-
/**
|
|
183
|
-
* Array of glob patterns for files to check.
|
|
184
|
-
*
|
|
185
|
-
* @example ["src/**\/*.ts", "lib/**\/*.js"]
|
|
186
|
-
*/
|
|
187
|
-
files: string[];
|
|
188
|
-
/**
|
|
189
|
-
* Array of glob patterns to ignore.
|
|
190
|
-
*
|
|
191
|
-
* @default ["**\/node_modules/**", "**\/dist/**", "**\/.git/**"]
|
|
192
|
-
*/
|
|
193
|
-
ignore?: string[];
|
|
194
|
-
/**
|
|
195
|
-
* Array of regex patterns to match.
|
|
196
|
-
*/
|
|
197
|
-
patterns: RegexPattern[];
|
|
198
|
-
}
|
|
199
|
-
type CustomCheckConfig = Record<string, unknown> & {
|
|
200
|
-
type: string;
|
|
201
|
-
};
|
|
202
|
-
type CheckConfig$1 = (ESLintCheckConfig & {
|
|
203
|
-
type: "eslint";
|
|
204
|
-
}) | (TypeScriptCheckConfig & {
|
|
205
|
-
type: "typescript";
|
|
206
|
-
}) | CustomCheckConfig;
|
|
207
|
-
/**
|
|
208
|
-
* mejora configuration.
|
|
209
|
-
*
|
|
210
|
-
* Define checks to run and track for regressions.
|
|
211
|
-
*
|
|
212
|
-
* @example
|
|
213
|
-
* ```ts
|
|
214
|
-
* import { defineConfig, eslint, typescript } from "mejora";
|
|
215
|
-
*
|
|
216
|
-
* export default defineConfig({
|
|
217
|
-
* checks: {
|
|
218
|
-
* "eslint > no-nested-ternary": eslint({
|
|
219
|
-
* files: ["src/**\/*.{ts,tsx}"],
|
|
220
|
-
* overrides: {
|
|
221
|
-
* rules: {
|
|
222
|
-
* "no-nested-ternary": "error",
|
|
223
|
-
* },
|
|
224
|
-
* },
|
|
225
|
-
* }),
|
|
226
|
-
* "typescript": typescript({
|
|
227
|
-
* overrides: {
|
|
228
|
-
* compilerOptions: {
|
|
229
|
-
* noImplicitAny: true,
|
|
230
|
-
* },
|
|
231
|
-
* },
|
|
232
|
-
* }),
|
|
233
|
-
* },
|
|
234
|
-
* });
|
|
235
|
-
* ```
|
|
236
|
-
*/
|
|
237
|
-
interface Config {
|
|
238
|
-
/**
|
|
239
|
-
* Check definitions.
|
|
240
|
-
*
|
|
241
|
-
* Each key is a check identifier used in the baseline and output.
|
|
242
|
-
* The identifier can contain any characters.
|
|
243
|
-
*
|
|
244
|
-
* Use `eslint()` and `typescript()` helpers to create check configs.
|
|
245
|
-
*
|
|
246
|
-
* @example
|
|
247
|
-
* ```ts
|
|
248
|
-
* {
|
|
249
|
-
* "eslint > no-console": eslint({ ... }),
|
|
250
|
-
* "typescript": typescriptCheck({ ... }),
|
|
251
|
-
* }
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
|
-
checks: Record<string, CheckConfig$1>;
|
|
255
|
-
/**
|
|
256
|
-
* Runners to register custom check types.
|
|
257
|
-
*
|
|
258
|
-
* Built-in checks (eslint, typescript) are always available.
|
|
259
|
-
*
|
|
260
|
-
* @example
|
|
261
|
-
* ```ts
|
|
262
|
-
* {
|
|
263
|
-
* runners: [myCustomRunner()],
|
|
264
|
-
* checks: {
|
|
265
|
-
* "custom": myCheck({ ... })
|
|
266
|
-
* }
|
|
267
|
-
* }
|
|
268
|
-
* ```
|
|
269
|
-
*/
|
|
270
|
-
runners?: CheckRunner[];
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Interface that all check runners must implement.
|
|
274
|
-
*
|
|
275
|
-
* Each check type (eslint, typescript, custom) implements this interface
|
|
276
|
-
* to provide consistent lifecycle hooks for execution.
|
|
277
|
-
*/
|
|
278
|
-
interface CheckRunner<TConfig = unknown> {
|
|
279
|
-
/**
|
|
280
|
-
* Execute the check and return a snapshot of issues.
|
|
281
|
-
*
|
|
282
|
-
* @param config - Check-specific configuration
|
|
283
|
-
*
|
|
284
|
-
* @returns Snapshot containing all issues found
|
|
285
|
-
*/
|
|
286
|
-
run(config: TConfig): Promise<RawSnapshot>;
|
|
287
|
-
/**
|
|
288
|
-
* Setup any infrastructure needed for this check.
|
|
289
|
-
* Called once during runner setup, in parallel with other checks.
|
|
290
|
-
*
|
|
291
|
-
* Examples: creating cache directories, initializing compilation state.
|
|
292
|
-
*
|
|
293
|
-
* Optional - not all checks need infrastructure setup.
|
|
294
|
-
*/
|
|
295
|
-
setup?(): Promise<void>;
|
|
296
|
-
/**
|
|
297
|
-
* Unique identifier for this check type.
|
|
298
|
-
* Must match the `type` field in CheckConfig.
|
|
299
|
-
*
|
|
300
|
-
* @example "eslint"
|
|
301
|
-
*
|
|
302
|
-
* @example "typescript"
|
|
303
|
-
*/
|
|
304
|
-
readonly type: string;
|
|
305
|
-
/**
|
|
306
|
-
* Validate that all dependencies for this check are available.
|
|
307
|
-
* Called once during runner setup before any checks execute.
|
|
308
|
-
*
|
|
309
|
-
* Should throw a descriptive error if dependencies are missing.
|
|
310
|
-
*
|
|
311
|
-
* @throws {Error} If required dependencies are not installed
|
|
312
|
-
*/
|
|
313
|
-
validate?(): Promise<void>;
|
|
314
|
-
}
|
|
315
|
-
//#endregion
|
|
316
|
-
//#region src/runners/eslint.d.ts
|
|
317
|
-
/**
|
|
318
|
-
* Check runner for ESLint.
|
|
319
|
-
*/
|
|
320
|
-
declare class ESLintCheckRunner implements CheckRunner {
|
|
321
|
-
readonly type = "eslint";
|
|
322
|
-
run(eslintConfig: ESLintCheckConfig): Promise<{
|
|
323
|
-
items: IssueInput[];
|
|
324
|
-
type: "items";
|
|
325
|
-
}>;
|
|
326
|
-
setup(): Promise<void>;
|
|
327
|
-
validate(): Promise<void>;
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Create an ESLint check configuration.
|
|
331
|
-
*
|
|
332
|
-
* @param config - ESLint check configuration options.
|
|
333
|
-
*
|
|
334
|
-
* @returns An ESLint check configuration object.
|
|
335
|
-
*/
|
|
336
|
-
declare function eslintCheck(config: ESLintCheckConfig): {
|
|
337
|
-
concurrency?: "auto" | "off" | number;
|
|
338
|
-
files: string[];
|
|
339
|
-
overrides?: eslint.Linter.Config | eslint.Linter.Config[];
|
|
340
|
-
type: "eslint";
|
|
341
|
-
};
|
|
342
|
-
//#endregion
|
|
343
|
-
//#region src/runners/regex.d.ts
|
|
344
|
-
/**
|
|
345
|
-
* Check runner for regex pattern matching.
|
|
346
|
-
*/
|
|
347
|
-
declare class RegexCheckRunner implements CheckRunner {
|
|
348
|
-
readonly type = "regex";
|
|
349
|
-
run(config: RegexCheckConfig): Promise<{
|
|
350
|
-
items: IssueInput[];
|
|
351
|
-
type: "items";
|
|
352
|
-
}>;
|
|
353
|
-
setup(): Promise<void>;
|
|
354
|
-
}
|
|
355
|
-
declare const regexRunner: () => RegexCheckRunner;
|
|
356
|
-
/**
|
|
357
|
-
* Create a regex check configuration.
|
|
358
|
-
*
|
|
359
|
-
* @param config - Regex check configuration options.
|
|
360
|
-
*
|
|
361
|
-
* @returns A regex check configuration object.
|
|
362
|
-
*/
|
|
363
|
-
declare function regexCheck(config: RegexCheckConfig): {
|
|
364
|
-
concurrency?: number;
|
|
365
|
-
files: string[];
|
|
366
|
-
ignore?: string[];
|
|
367
|
-
patterns: RegexPattern[];
|
|
368
|
-
type: "regex";
|
|
369
|
-
};
|
|
370
|
-
//#endregion
|
|
371
|
-
//#region src/runners/typescript.d.ts
|
|
372
|
-
/**
|
|
373
|
-
* Check runner for TypeScript.
|
|
374
|
-
*/
|
|
375
|
-
declare class TypeScriptCheckRunner implements CheckRunner {
|
|
376
|
-
readonly type = "typescript";
|
|
377
|
-
run(typescriptConfig: TypeScriptCheckConfig): Promise<{
|
|
378
|
-
items: IssueInput[];
|
|
379
|
-
type: "items";
|
|
380
|
-
}>;
|
|
381
|
-
setup(): Promise<void>;
|
|
382
|
-
validate(): Promise<void>;
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* Create a TypeScript check configuration.
|
|
386
|
-
*
|
|
387
|
-
* @param config - TypeScript check configuration options.
|
|
388
|
-
*
|
|
389
|
-
* @returns A TypeScript check configuration object.
|
|
390
|
-
*/
|
|
391
|
-
declare function typescriptCheck(config: TypeScriptCheckConfig): {
|
|
392
|
-
overrides?: {
|
|
393
|
-
compilerOptions?: typescript.CompilerOptions;
|
|
394
|
-
};
|
|
395
|
-
tsconfig?: string;
|
|
396
|
-
type: "typescript";
|
|
397
|
-
};
|
|
398
|
-
//#endregion
|
|
399
|
-
//#region src/core/config.d.ts
|
|
400
|
-
type ExtractRunnerByType<TRunners extends readonly CheckRunner[], TType extends string> = Extract<TRunners[number], {
|
|
401
|
-
type: TType;
|
|
402
|
-
}>;
|
|
403
|
-
type ExtractConfig<TRunner> = TRunner extends CheckRunner<infer C> ? C : never;
|
|
404
|
-
type CheckConfig<TRunners extends readonly CheckRunner[], TType extends TRunners[number]["type"]> = ExtractConfig<ExtractRunnerByType<TRunners, TType>> & {
|
|
405
|
-
type: TType;
|
|
406
|
-
};
|
|
407
|
-
type InternalRunners = readonly [ESLintCheckRunner, TypeScriptCheckRunner, RegexCheckRunner];
|
|
408
|
-
/**
|
|
409
|
-
* Define mejora configuration.
|
|
410
|
-
*
|
|
411
|
-
* @param config - mejora configuration object.
|
|
412
|
-
*
|
|
413
|
-
* @param config.runners - Optional array of custom check runners to register.
|
|
414
|
-
*
|
|
415
|
-
* @param config.checks - Map of check names to their configurations.
|
|
416
|
-
*
|
|
417
|
-
* @returns The provided configuration object.
|
|
418
|
-
*/
|
|
419
|
-
declare function defineConfig<const TRunners extends readonly CheckRunner[]>(config: {
|
|
420
|
-
checks: Record<string, CheckConfig<[...InternalRunners, ...TRunners], [...InternalRunners, ...TRunners][number]["type"]>>;
|
|
421
|
-
runners?: TRunners;
|
|
422
|
-
}): {
|
|
423
|
-
checks: Record<string, CheckConfig<[...InternalRunners, ...TRunners], [...InternalRunners, ...TRunners][number]["type"]>>;
|
|
424
|
-
runners?: TRunners;
|
|
425
|
-
};
|
|
426
|
-
//#endregion
|
|
427
|
-
export { eslintCheck as a, Issue as c, Snapshot as d, regexRunner as i, IssueInput as l, typescriptCheck as n, CheckRunner as o, regexCheck as r, Config as s, defineConfig as t, RawSnapshot as u };
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{createRequire as e}from"node:module";import{pathToFileURL as t}from"node:url";import{glob as n,mkdir as r,readFile as i,stat as a,writeFile as o}from"node:fs/promises";import{hash as s}from"node:crypto";import{createReadStream as c}from"node:fs";import{createInterface as l}from"node:readline/promises";var u=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),d=e(import.meta.url),f=u(((e,t)=>{let n=d(`path`),r=d(`fs`),i=d(`os`),a=d(`url`),o=r.promises.readFile;function s(e,t){return[`package.json`,`.${e}rc.json`,`.${e}rc.js`,`.${e}rc.cjs`,...t?[]:[`.${e}rc.mjs`],`.config/${e}rc`,`.config/${e}rc.json`,`.config/${e}rc.js`,`.config/${e}rc.cjs`,...t?[]:[`.config/${e}rc.mjs`],`${e}.config.js`,`${e}.config.cjs`,...t?[]:[`${e}.config.mjs`]]}function c(e){return n.dirname(e)||n.sep}let l=(e,t)=>JSON.parse(t),u=typeof __webpack_require__==`function`?__non_webpack_require__:d,f=Object.freeze({".js":u,".json":u,".cjs":u,noExt:l});t.exports.defaultLoadersSync=f;let p=async e=>{try{return(await import(a.pathToFileURL(e).href)).default}catch(t){try{return u(e)}catch(e){throw e.code===`ERR_REQUIRE_ESM`||e instanceof SyntaxError&&e.toString().includes(`Cannot use import statement outside a module`)?t:e}}},m=Object.freeze({".js":p,".mjs":p,".cjs":p,".json":l,noExt:l});t.exports.defaultLoaders=m;function h(e,t,r){let a={stopDir:i.homedir(),searchPlaces:s(e,r),ignoreEmptySearchPlaces:!0,cache:!0,transform:e=>e,packageProp:[e],...t,loaders:{...r?f:m,...t.loaders}};return a.searchPlaces.forEach(e=>{let t=n.extname(e)||`noExt`,r=a.loaders[t];if(!r)throw Error(`Missing loader for extension "${e}"`);if(typeof r!=`function`)throw Error(`Loader for extension "${e}" is not a function: Received ${typeof r}.`)}),a}function g(e,t){return typeof e==`string`&&e in t?t[e]:(Array.isArray(e)?e:e.split(`.`)).reduce((e,t)=>e===void 0?e:e[t],t)||null}function _(e){if(!e)throw Error(`load must pass a non-empty string`)}function v(e,t){if(!e)throw Error(`No loader specified for extension "${t}"`);if(typeof e!=`function`)throw Error(`loader is not a function`)}let y=e=>(t,n,r)=>(e&&t.set(n,r),r);t.exports.lilconfig=function(e,t){let{ignoreEmptySearchPlaces:i,loaders:a,packageProp:s,searchPlaces:l,stopDir:u,transform:d,cache:f}=h(e,t??{},!1),p=new Map,m=new Map,b=y(f);return{async search(e=process.cwd()){let t={config:null,filepath:``},m=new Set,h=e;dirLoop:for(;;){if(f){let e=p.get(h);if(e!==void 0){for(let t of m)p.set(t,e);return e}m.add(h)}for(let e of l){let c=n.join(h,e);try{await r.promises.access(c)}catch{continue}let l=String(await o(c)),u=n.extname(e)||`noExt`,d=a[u];if(e===`package.json`){let e=g(s,await d(c,l));if(e!=null){t.config=e,t.filepath=c;break dirLoop}continue}let f=l.trim()===``;if(!(f&&i)){f?(t.isEmpty=!0,t.config=void 0):(v(d,u),t.config=await d(c,l)),t.filepath=c;break dirLoop}}if(h===u||h===c(h))break dirLoop;h=c(h)}let _=t.filepath===``&&t.config===null?d(null):d(t);if(f)for(let e of m)p.set(e,_);return _},async load(e){_(e);let t=n.resolve(process.cwd(),e);if(f&&m.has(t))return m.get(t);let{base:r,ext:c}=n.parse(t),l=c||`noExt`,u=a[l];v(u,l);let p=String(await o(t));if(r===`package.json`)return b(m,t,d({config:g(s,await u(t,p)),filepath:t}));let h={config:null,filepath:t},y=p.trim()===``;return y&&i?b(m,t,d({config:void 0,filepath:t,isEmpty:!0})):(h.config=y?void 0:await u(t,p),b(m,t,d(y?{...h,isEmpty:y,config:void 0}:h)))},clearLoadCache(){f&&m.clear()},clearSearchCache(){f&&p.clear()},clearCaches(){f&&(m.clear(),p.clear())}}},t.exports.lilconfigSync=function(e,t){let{ignoreEmptySearchPlaces:i,loaders:a,packageProp:o,searchPlaces:s,stopDir:l,transform:u,cache:d}=h(e,t??{},!0),f=new Map,p=new Map,m=y(d);return{search(e=process.cwd()){let t={config:null,filepath:``},p=new Set,m=e;dirLoop:for(;;){if(d){let e=f.get(m);if(e!==void 0){for(let t of p)f.set(t,e);return e}p.add(m)}for(let e of s){let s=n.join(m,e);try{r.accessSync(s)}catch{continue}let c=n.extname(e)||`noExt`,l=a[c],u=String(r.readFileSync(s));if(e===`package.json`){let e=g(o,l(s,u));if(e!=null){t.config=e,t.filepath=s;break dirLoop}continue}let d=u.trim()===``;if(!(d&&i)){d?(t.isEmpty=!0,t.config=void 0):(v(l,c),t.config=l(s,u)),t.filepath=s;break dirLoop}}if(m===l||m===c(m))break dirLoop;m=c(m)}let h=t.filepath===``&&t.config===null?u(null):u(t);if(d)for(let e of p)f.set(e,h);return h},load(e){_(e);let t=n.resolve(process.cwd(),e);if(d&&p.has(t))return p.get(t);let{base:s,ext:c}=n.parse(t),l=c||`noExt`,f=a[l];v(f,l);let h=String(r.readFileSync(t));if(s===`package.json`)return u({config:g(o,f(t,h)),filepath:t});let y={config:null,filepath:t},b=h.trim()===``;return b&&i?m(p,t,u({filepath:t,config:void 0,isEmpty:!0})):(y.config=b?void 0:f(t,h),m(p,t,u(b?{...y,isEmpty:b,config:void 0}:y)))},clearLoadCache(){d&&p.clear()},clearSearchCache(){d&&f.clear()},clearCaches(){d&&(p.clear(),f.clear())}}}}))();const p=async e=>{let n=await import(t(e).href);return n&&typeof n==`object`&&`default`in n?n.default:n};function m(e){return e}const h=async()=>{let e=await(0,f.lilconfig)(`mejora`,{loaders:{".js":p,".mjs":p,".mts":p,".ts":p},searchPlaces:[`mejora.config.ts`,`mejora.config.mts`,`mejora.config.js`,`mejora.config.mjs`]}).search(process.cwd());if(!e?.config)throw Error(`No configuration file found.`);return e.config},g=/^[A-Za-z]:\//;function _(e=``){return e&&e.replace(/\\/g,`/`).replace(g,e=>e.toUpperCase())}const v=/^[/\\]{2}/,y=/^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/,b=/^[A-Za-z]:$/,x=/^\/([A-Za-z]:)?$/,S=function(e){if(e.length===0)return`.`;e=_(e);let t=e.match(v),n=D(e),r=e[e.length-1]===`/`;return e=E(e,!n),e.length===0?n?`/`:r?`./`:`.`:(r&&(e+=`/`),b.test(e)&&(e+=`/`),t?n?`//${e}`:`//./${e}`:n&&!D(e)?`/${e}`:e)},C=function(...e){let t=``;for(let n of e)if(n)if(t.length>0){let e=t[t.length-1]===`/`,r=n[0]===`/`;e&&r?t+=n.slice(1):t+=e||r?n:`/${n}`}else t+=n;return S(t)};function w(){return typeof process<`u`&&typeof process.cwd==`function`?process.cwd().replace(/\\/g,`/`):`/`}const T=function(...e){e=e.map(e=>_(e));let t=``,n=!1;for(let r=e.length-1;r>=-1&&!n;r--){let i=r>=0?e[r]:w();!i||i.length===0||(t=`${i}/${t}`,n=D(i))}return t=E(t,!n),n&&!D(t)?`/${t}`:t.length>0?t:`.`};function E(e,t){let n=``,r=0,i=-1,a=0,o=null;for(let s=0;s<=e.length;++s){if(s<e.length)o=e[s];else if(o===`/`)break;else o=`/`;if(o===`/`){if(!(i===s-1||a===1))if(a===2){if(n.length<2||r!==2||n[n.length-1]!==`.`||n[n.length-2]!==`.`){if(n.length>2){let e=n.lastIndexOf(`/`);e===-1?(n=``,r=0):(n=n.slice(0,e),r=n.length-1-n.lastIndexOf(`/`)),i=s,a=0;continue}else if(n.length>0){n=``,r=0,i=s,a=0;continue}}t&&(n+=n.length>0?`/..`:`..`,r=2)}else n.length>0?n+=`/${e.slice(i+1,s)}`:n=e.slice(i+1,s),r=s-i-1;i=s,a=0}else o===`.`&&a!==-1?++a:a=-1}return n}const D=function(e){return y.test(e)},O=function(e,t){let n=T(e).replace(x,`$1`).split(`/`),r=T(t).replace(x,`$1`).split(`/`);if(r[0][1]===`:`&&n[0][1]===`:`&&n[0]!==r[0])return r.join(`/`);let i=[...n];for(let e of i){if(r[0]!==e)break;n.shift(),r.shift()}return[...n.map(()=>`..`),...r].join(`/`)},k=function(e){let t=_(e).replace(/\/$/,``).split(`/`).slice(0,-1);return t.length===1&&b.test(t[0])&&(t[0]+=`/`),t.join(`/`)||(D(e)?`/`:`.`)},A=function(e,t){let n=_(e).split(`/`),r=``;for(let e=n.length-1;e>=0;e--){let t=n[e];if(t){r=t;break}}return t&&r.endsWith(t)?r.slice(0,-t.length):r},j=e=>s(`sha256`,e,`hex`);function M(){let e=new WeakSet;return(t,n)=>{if(n&&typeof n==`object`){if(e.has(n))return`[Circular]`;if(e.add(n),!Array.isArray(n)){let e=n,t={};for(let n of Object.keys(e).toSorted()){let r=e[n];typeof r==`function`||typeof r==`symbol`||(t[n]=r)}return t}}return n}}function N(e){return j(JSON.stringify(e??null,M()))}function P(e,t=process.cwd()){return T(t,`node_modules`,`.cache`,`mejora`,e)}async function F(e){try{let t=await i(e,`utf8`);return JSON.parse(t)}catch{return{}}}async function I(e,t){try{await o(e,JSON.stringify(t,null,2),`utf8`)}catch{}}async function L(e){try{let t=await a(e);return`${t.mtimeMs}-${t.size}`}catch{return null}}var R=class{type=`eslint`;async run(e){let{ESLint:t}=await import(`eslint`),n=process.cwd(),r=P(this.type,n),i=N(e),a=new Set;if(e.overrides){let t=Array.isArray(e.overrides)?e.overrides:[e.overrides];for(let e of t)if(e.rules)for(let t of Object.keys(e.rules))a.add(t)}let o=a.size>0,s=await new t({cache:!0,cacheLocation:`${r}/${i}.eslintcache`,overrideConfig:e.overrides,...!o&&{concurrency:e.concurrency??`auto`},...o&&{ruleFilter:({ruleId:e})=>a.has(e)}}).lintFiles(e.files),c=[];for(let{filePath:e,messages:t}of s){let r=O(n,e);for(let{column:e,line:n,message:i,ruleId:a}of t)a&&c.push({column:e,file:r,line:n,message:i,rule:a})}return{items:c,type:`items`}}async setup(){let e=process.cwd(),t=P(this.type,e),{mkdir:n}=await import(`node:fs/promises`);await n(t,{recursive:!0})}async validate(){try{await import(`eslint`)}catch{throw Error(`${this.type} check requires "eslint" package to be installed. Run: npm install eslint`)}}};function z(e){return{type:`eslint`,...e}}const B=[`**/node_modules/**`,`**/dist/**`,`**/.git/**`];function V(e,t){if(t?.length)return t;let n=e.map(e=>/^([^*]+\/)/.exec(e)?.[1]).filter(e=>e!==void 0);return[...B,...n.flatMap(e=>B.map(t=>t.replace(/^\*\*\//,e)))]}async function H(e,t,n){let r=Array.from({length:e.length}),i=0;return await Promise.all(Array.from({length:Math.min(t,e.length)},async()=>{for(;i<e.length;){let t=i++;r[t]=await n(e[t])}})),r}var U=class{type=`regex`;async run(e){let t=process.cwd(),r=Array.isArray(e.files)?e.files:[e.files],i=V(r,e.ignore),a=new Set;for(let e of r){let r=n(e,{cwd:t,exclude:i});for await(let e of r)a.add(e)}let o=[...a],s=e.patterns.map(({message:e,pattern:t,rule:n})=>{let r=t.flags.includes(`g`)?t.flags:`${t.flags}g`;return{message:e,regex:new RegExp(t.source,r),ruleText:n??t.source}}),u=C(P(this.type,t),`${N(e)}.json`),d=await F(u),f={},p=await H(o,e.concurrency??10,async e=>{let n=C(t,e),r=await L(n);if(!r)return[];let i=d[e];if(i?.hash===r)return f[e]=i,i.items;try{let t=[],i=l({crlfDelay:1/0,input:c(n,{encoding:`utf8`})}),a=0;try{for await(let n of i){a++;for(let r of s){r.regex.lastIndex=0;let i;for(;(i=r.regex.exec(n))!==null;){let n=i.index+1,o=typeof r.message==`function`?r.message(i):r.message??`Pattern matched: ${i[0]}`;t.push({column:n,file:e,line:a,message:o,rule:r.ruleText})}}}}finally{i.close()}return f[e]={hash:r,items:t},t}catch{return[]}}),m=[];for(let e of p)m.push(...e);return await I(u,f),{items:m,type:`items`}}async setup(){let e=process.cwd();await r(P(this.type,e),{recursive:!0})}};const W=()=>new U;function G(e){return{type:`regex`,...e}}function K(e,t){return e.replaceAll(/import\("([^"]+)"\)/g,(e,n)=>{try{if(D(n)){let e=O(t,n);if(!e.startsWith(`..`))return`import("${e||`.`}")`}}catch{}return e})}var q=class{type=`typescript`;async run(e){let{createIncrementalCompilerHost:t,createIncrementalProgram:n,findConfigFile:r,flattenDiagnosticMessageText:i,getPreEmitDiagnostics:a,parseJsonConfigFileContent:o,readConfigFile:s,sys:c,version:l}=await import(`typescript`),u=process.cwd(),d=c.fileExists.bind(c),f=c.readFile.bind(c),p=e.tsconfig?T(e.tsconfig):r(u,d,`tsconfig.json`);if(!p)throw Error(`TypeScript config file not found`);let{config:m,error:h}=s(p,f);if(h){let e=typeof h.messageText==`string`?h.messageText:i(h.messageText,`
|
|
2
|
-
`);throw TypeError(`Failed to read TypeScript config: ${e}`)}let g=o(m,c,u,e.overrides?.compilerOptions),_=T(P(this.type,u),`${N({configPath:p,overrides:e.overrides?.compilerOptions??{},parsedOptions:g.options,typescriptVersion:l})}.tsbuildinfo`),v={...g.options,incremental:!0,noEmit:!0,skipLibCheck:g.options.skipLibCheck??!0,tsBuildInfoFile:_},y=t(v,c),b=y.writeFile.bind(y);y.writeFile=(e,t,...n)=>{T(e)===_&&b(e,t,...n)};let x=n({host:y,options:v,projectReferences:g.projectReferences??[],rootNames:g.fileNames}),S=a(x.getProgram());x.emit();let C=T(u),w=C+`/`,E=S.filter(e=>{if(!e.file)return!0;let t=T(e.file.fileName);return t===C||t.startsWith(w)}),D=[];for(let e of E){let t=i(e.messageText,`
|
|
3
|
-
`),n=`TS${e.code}`;if(e.file&&e.start!==void 0){let{character:r,line:i}=e.file.getLineAndCharacterOfPosition(e.start),a=O(u,e.file.fileName);D.push({column:r+1,file:a,line:i+1,message:K(t,u),rule:n})}else D.push({column:0,file:`(global)`,line:0,message:K(t,u),rule:n})}return{items:D,type:`items`}}async setup(){let e=process.cwd(),t=P(this.type,e),{mkdir:n}=await import(`node:fs/promises`);await n(t,{recursive:!0})}async validate(){try{await import(`typescript`)}catch{throw Error(`${this.type} check requires "typescript" package to be installed. Run: npm install typescript`)}}};function J(e){return{type:`typescript`,...e}}export{W as a,j as c,O as d,m as f,G as i,A as l,J as n,R as o,h as p,U as r,z as s,q as t,k as u};
|