oxlint-tui 1.4.0 → 1.5.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 CHANGED
@@ -17,6 +17,8 @@ Configuring linters often involves jumping between your editor, a massive JSON f
17
17
  - **Non-Destructive**: Toggling rules happens entirely in memory. No changes are written to disk, making it safe to experiment without messing up your config or comments.
18
18
  - **Config Aware**: Automatically reads `.oxlintrc.json` to initialize the state, but works perfectly even if no config file exists.
19
19
  - **Details**: View category, scope, fix, default, and type-aware rule parameters at a glance.
20
+ - **Hit Counts**: See exactly how many violations each rule triggers in your codebase. Hits are displayed next to the rule name (e.g., `no-debugger (3)`) and highlighted for clarity.
21
+ - **Run All**: Quickly run every available rule (even those turned off) to see what else might be lurking in your code.
20
22
  - **View Docs**: Press <kbd>ENTER</kbd> on any rule to open its official documentation in your browser.
21
23
  - **Zero Dependencies**: Written in pure Node.js without any heavy TUI libraries.
22
24
 
@@ -59,8 +61,9 @@ oxlint-tui
59
61
  | **1** | Set selected rule to "off" |
60
62
  | **2** | Set selected rule to "warn" |
61
63
  | **3** | Set selected rule to "error" |
62
- | **x** | Run "oxlint" with the selected rule |
63
- | **r** | Run "oxlint with all enabled rules |
64
+ | **x** | Run linter with the selected rule only |
65
+ | **r** | Run linter with all activated rules |
66
+ | **a** | Run linter with ALL available rules |
64
67
  | **Enter** | Open Rule Documentation in Browser |
65
68
  | **q** / **Esc** | Quit |
66
69
 
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import e from"node:fs";import t from"node:zlib";import{fileURLToPath as n}from"node:url";import r from"node:path";import i from"readline";import{execSync as a,spawn as o}from"node:child_process";import{argv as s,exit as c,platform as l,stdin as u,stdout as d}from"node:process";const f={reset:`\x1B[0m`,dim:`\x1B[38;5;242m`,highlight:`\x1B[38;5;111m`,selectedBg:`\x1B[48;5;24m\x1B[38;5;255m\x1B[1m`,borderActive:`\x1B[38;5;111m`,borderInactive:`\x1B[38;5;237m`,error:`\x1B[38;5;203m`,warn:`\x1B[38;5;215m`,success:`\x1B[38;5;114m`,info:`\x1B[38;5;75m`};function p(e,t){if(!e)return[];let n=Math.ceil(e.length/t),r=Array(n);for(let i=0;i<n;i++)r[i]=e.substring(i*t,(i+1)*t);return r}function m(e,t,n,r,i,a,o,s,c,l){let u=l?f.borderActive:f.borderInactive,d=`${u}┌─ ${a.length>r-6?a.substring(0,r-7)+`…`:a} `.padEnd(r+u.length-1,`─`);e.push(`\x1b[${n};${t}H${d}┐${f.reset}`);for(let a=1;a<i-1;a++)e.push(`\x1b[${n+a};${t}H${u}│${` `.repeat(r-2)}│${f.reset}`);e.push(`\x1b[${n+i-1};${t}H${u}└${`─`.repeat(r-2)}┘${f.reset}`);let p=i-2;o.slice(c,c+p).forEach((i,a)=>{let o=c+a,u=typeof i!=`string`,d=u?i.value:i,p=d.length>r-4?d.substring(0,r-5)+`…`:d.padEnd(r-4),m=f.dim;if(u){let e=i;e.configStatus===`error`?m=f.error:e.configStatus===`warn`?m=f.warn:e.isActive&&(m=f.success)}e.push(`\x1b[${n+1+a};${t+2}H`),o===s?e.push(l?`${f.selectedBg}${p}${f.reset}`:`${f.dim}\x1b[7m${p}${f.reset}`):e.push(`${m}${p}${f.reset}`)})}function h(e,t,n,r,i,a){let o=f.borderInactive,s=`${o}┌─ STATS `.padEnd(r+o.length-1,`─`);e.push(`\x1b[${n};${t}H${s}┐${f.reset}`);for(let a=1;a<i-1;a++)e.push(`\x1b[${n+a};${t}H${o}│${` `.repeat(r-2)}│${f.reset}`);e.push(`\x1b[${n+i-1};${t}H${o}└${`─`.repeat(r-2)}┘${f.reset}`);let c={error:0,warn:0,off:0};a.forEach(e=>{e.configStatus===`error`?c.error++:e.configStatus===`warn`?c.warn++:c.off++}),[{label:`Error`,count:c.error,color:f.error},{label:`Warn`,count:c.warn,color:f.warn},{label:`Off`,count:c.off,color:f.dim}].forEach((a,o)=>{if(o<i-2){let i=String(a.count).padStart(3),s=a.label.padEnd(r-8);e.push(`\x1b[${n+1+o};${t+2}H${a.color}${s}${i}${f.reset}`)}})}function g(e,t,n,r,i,a,o){let s=o?f.borderActive:f.borderInactive,c=`${s}┌─ DETAILS `.padEnd(r+s.length-1,`─`);e.push(`\x1b[${n};${t}H${c}┐${f.reset}`);for(let a=1;a<i-1;a++)e.push(`\x1b[${n+a};${t}H${s}│${` `.repeat(r-2)}│${f.reset}`);if(e.push(`\x1b[${n+i-1};${t}H${s}└${`─`.repeat(r-2)}┘${f.reset}`),!a)return;let l=a.configStatus.toUpperCase();l=a.configStatus===`error`?`${f.error}${l}${f.reset}`:a.configStatus===`warn`?`${f.warn}${l}${f.reset}`:`${f.dim}${l}${f.reset}`;let u=[[`Name`,a.value],[`Status`,l],[`Category`,a.category],[`Scope`,a.scope],[`Fix`,a.fix||`N/A`],[`Default`,a.default?`Yes`:`No`],[`Type-aware`,a.type_aware?`Yes`:`No`]],d=0;u.forEach(([r,a])=>{d<i-2&&(e.push(`\x1b[${n+1+d};${t+2}H${f.highlight}${r.padEnd(12)} ${f.reset}${a}`),d++)}),d<i-3&&d++,d<i-2&&(e.push(`\x1b[${n+1+d};${t+2}H${f.reset}Description:${f.reset}`),d++,p((a.description??`N/A`).replace(/\s+/g,` `).trim(),r-6).forEach(r=>{d<i-2&&(e.push(`\x1b[${n+1+d};${t+2}H${f.dim}${r}${f.reset}`),d++)}));let m=Math.max(d+1,i-3);m<i-1&&e.push(`\x1b[${n+1+m};${t+2}HHit ${f.highlight}ENTER${f.reset} to open docs`)}function _(e,t,n){return e<t?e:e>=t+n?e-n+1:t}function v(){if(!w||!w.categories)return;let{columns:e=80,rows:t=24}=d,n=w.categories[w.selectedCategoryIndex],r=w.rulesByCategory[n]||[],i=r[w.selectedRuleIndex],a=t-5,o=Math.floor(e*.2),s=Math.floor(e*.3),c=e-o-s-2,l=a-6,u=[`\x1B[H\x1B[J`];m(u,1,1,o,l,`CATEGORIES`,w.categories,w.selectedCategoryIndex,w.categoryScroll,w.activePane===0),h(u,1,1+l,o,6,r),m(u,o+1,1,s,a,`RULES (${r.length})`,r,w.selectedRuleIndex,w.ruleScroll,w.activePane===1),g(u,o+s+1,1,c,a,i,w.activePane===2);let p=f[w.messageType]||f.reset;u.push(`\x1b[${t-3};2H${p}● ${w.message}${f.reset}`);let _=w.configPath?`Config: ${w.configPath}`:`No config loaded`;u.push(`\x1b[${t-1};2H${f.dim}Arrows/HJKL: Nav | 1-3: Status | R: Lint | X: Run rule | Enter: Docs | Q: Quit | ${_}${f.reset}`),d.write(u.join(``))}const y=`1.42.0`,b={k:{type:`MOVE_UP`},up:{type:`MOVE_UP`},down:{type:`MOVE_DOWN`},j:{type:`MOVE_DOWN`},left:{type:`MOVE_LEFT`},h:{type:`MOVE_LEFT`},right:{type:`MOVE_RIGHT`},l:{type:`MOVE_RIGHT`},return:{type:`OPEN_DOCS`},enter:{type:`OPEN_DOCS`},1:{type:`SET_STATUS`,value:`off`},2:{type:`SET_STATUS`,value:`warn`},3:{type:`SET_STATUS`,value:`error`},q:{type:`EXIT`},r:{type:`RUN_LINT`},x:{type:`RUN_SINGLE_RULE`}},x=r.dirname(n(import.meta.url));function S(){let n=r.join(x,`rule-descriptions.br`);if(e.existsSync(n))return JSON.parse(t.brotliDecompressSync(e.readFileSync(n)).toString())}const C=S();let w={activePane:0,selectedCategoryIndex:0,selectedRuleIndex:0,categoryScroll:0,ruleScroll:0,isLintInProgress:!1,message:`oxlint-tui`,messageType:`dim`,...A()};function T(e,t){w.config||={rules:{}},w.config.rules||(w.config.rules={});try{let n=e.value,r=e.scope===`oxc2`||e.scope===`eslint`?n:`${e.scope}/${n}`,i=w.config.rules,a=Object.keys(i).find(e=>e===r||e===n||e.endsWith(`/${n}`))||r;i[a]=t}catch{w.message=`Failed to update internal state`,w.messageType=`error`}}function E({rule:e=null}={}){if(w.isLintInProgress)return;w.isLintInProgress=!0;let t=e?`${e.scope}/${e.value}`:null,n=e?e.type_aware:Object.values(w.rulesByCategory).flat().some(e=>e.isActive&&e.type_aware===!0);w.message=`Linting`,t&&(w.message+=` [${t}]`),n&&(w.message+=` with --type-aware`),w.message+=`...`,w.messageType=`info`,v();let r=l===`win32`?`npx.cmd`:`npx`,i=[`-q`,`--yes`,`--package`,`oxlint@${y}`];n&&i.push(`--package`,`oxlint-tsgolint@0.11.4`),i.push(`--`,`oxlint`),n&&i.push(`--type-aware`),t?i.push(`-A`,`all`,`-D`,t):w.config&&w.config.rules&&Object.entries(w.config.rules).forEach(([e,t])=>{let n=Array.isArray(t)?t[0]:t;n===`error`?i.push(`-D`,e):n===`warn`?i.push(`-W`,e):n===`off`&&i.push(`-A`,e)});let a=o(r,i),s=``,c=``;a.stdout.on(`data`,e=>{s+=e}),a.stderr.on(`data`,e=>{c+=e}),a.on(`close`,e=>{w.isLintInProgress=!1;let n=(s+c).match(/Found (\d+) warnings? and (\d+) errors?/i);if(n){let e=parseInt(n[2]);w.message=t?`[${t}] Found ${e} issue${e===1?``:`s`}`:n[0],w.messageType=e>0?`error`:`warn`}else if(s.toLowerCase().includes(`finished`)||e===0&&s.length<200)w.message=`Linting passed! 0 issues found.`,t&&(w.message=`[${t}] ${w.message}`),w.messageType=`success`;else{let e=c.split(`
3
- `).filter(e=>!e.includes(`experimental`)&&!e.includes(`Breaking changes`)&&e.trim()!==``).join(` `);w.message=e?`Error: ${e.substring(0,50)}...`:`Lint failed`,w.messageType=`error`}v()})}function D(e){if(!e)return;let{categories:t,rulesByCategory:n,selectedCategoryIndex:r,selectedRuleIndex:i,activePane:a}=w,o=t[r],s=n[o]||[],l=d.rows-8,u=l-7;switch(e.type){case`EXIT`:P(),c(0);return;case`RUN_LINT`:E();return;case`RUN_SINGLE_RULE`:{let e=s[i];e&&E({rule:e});return}case`OPEN_DOCS`:if(a===1){let e=s[i];e&&j(e.docs_url||e.url)}return;case`SET_STATUS`:{if(a!==1||!e.value)return;let t=s[i];if(!t)return;T(t,e.value);let r=[...s];r[i]={...t,configStatus:e.value,isActive:e.value===`error`||e.value===`warn`},w={...w,message:`Rule '${t.value}' set to: ${e.value}`,messageType:`info`,rulesByCategory:{...n,[o]:r}},v();return}case`MOVE_RIGHT`:a!==1&&(w={...w,activePane:a+1},v());return;case`MOVE_LEFT`:a!==0&&(w={...w,activePane:a-1},v());return;case`MOVE_UP`:if(a===0){let e=r===0?t.length-1:r-1;w={...w,selectedCategoryIndex:e,selectedRuleIndex:0,ruleScroll:0,categoryScroll:_(e,w.categoryScroll,u)}}else if(a===1){let e=i===0?s.length-1:i-1;w={...w,selectedRuleIndex:e,ruleScroll:_(e,w.ruleScroll,l)}}v();return;case`MOVE_DOWN`:if(a===0){let e=r===t.length-1?0:r+1;w={...w,selectedCategoryIndex:e,selectedRuleIndex:0,ruleScroll:0,categoryScroll:_(e,w.categoryScroll,u)}}else if(a===1){let e=i===s.length-1?0:i+1;w={...w,selectedRuleIndex:e,ruleScroll:_(e,w.ruleScroll,l)}}v();return}}function O(e,t,n){if(n.rules){let t=n.rules[e];if(t===void 0){let r=Object.keys(n.rules).find(t=>t.endsWith(`/${e}`));r&&(t=n.rules[r])}if(t!==void 0)return Array.isArray(t)?t[0]:t}return n.categories&&n.categories[t]?n.categories[t]:`off`}function k(e){return e.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g,(e,t)=>t?``:e)}function A(){let t=[],n={rules:{},categories:{}},r=null,i=C;i||(w.message=`Error: Couldn't find description.`,w.messageType=`error`);try{let e=a(`npx -q --yes oxlint@${y} --rules --format=json`,{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]});t=JSON.parse(e)}catch{w.message=`Error: Couldn't run 'npx oxlint'.`,w.messageType=`error`,c(1)}let o=s[2];if(o&&e.existsSync(o)?r=o:e.existsSync(`.oxlintrc.json`)&&(r=`.oxlintrc.json`),r)try{n=JSON.parse(k(e.readFileSync(r,`utf8`)))}catch{w.message=`Error: Couldn't parse config.`,w.messageType=`error`}let l={};return t.forEach(e=>{let t=e.category||`Uncategorized`;l[t]||(l[t]=[]);let r=O(e.value,t,n),a=i[e.scope]?.[e.value];l[t].push({...e,description:a,configStatus:r,isActive:r===`error`||r===`warn`})}),{categories:Object.keys(l).toSorted(),rulesByCategory:l,config:n,configPath:r}}function j(e){e&&o(l===`darwin`?`open`:l===`win32`?`explorer`:`xdg-open`,[e],{detached:!0,stdio:`ignore`}).unref()}const M=e=>d.write(e),N=()=>M(`\x1B[?1049h\x1B[?25l`),P=()=>M(`\x1B[?1049l\x1B[?25h`);i.emitKeypressEvents(u),u.isTTY&&u.setRawMode(!0),u.on(`keypress`,(e,t)=>{D(b[t.name]||(t.ctrl&&t.name===`c`?{type:`EXIT`}:b[t.sequence]||null))}),d.on(`resize`,v),N(),v();export{w as state};
2
+ import e from"node:fs";import t from"node:zlib";import{fileURLToPath as n}from"node:url";import r from"node:path";import i from"readline";import{execSync as a,spawn as o}from"node:child_process";import{argv as s,exit as c,platform as l,stdin as u,stdout as d}from"node:process";const f={reset:`\x1B[0m`,dim:`\x1B[38;5;242m`,highlight:`\x1B[38;5;111m`,selectedBg:`\x1B[48;5;24m\x1B[38;5;255m\x1B[1m`,borderActive:`\x1B[38;5;111m`,borderInactive:`\x1B[38;5;237m`,error:`\x1B[38;5;203m`,warn:`\x1B[38;5;215m`,success:`\x1B[38;5;114m`,info:`\x1B[38;5;75m`};function p(e,t){if(!e)return[];let n=Math.ceil(e.length/t),r=Array(n);for(let i=0;i<n;i++)r[i]=e.substring(i*t,(i+1)*t);return r}function m(e,t,n,r,i,a,o,s,c,l){let u=l?f.borderActive:f.borderInactive,d=`${u}┌─ ${a.length>r-6?a.substring(0,r-7)+`…`:a} `.padEnd(r+u.length-1,`─`);e.push(`\x1b[${n};${t}H${d}┐${f.reset}`);for(let a=1;a<i-1;a++)e.push(`\x1b[${n+a};${t}H${u}│${` `.repeat(r-2)}│${f.reset}`);e.push(`\x1b[${n+i-1};${t}H${u}└${`─`.repeat(r-2)}┘${f.reset}`);let p=i-2;o.slice(c,c+p).forEach((i,a)=>{let o=c+a,u=typeof i!=`string`,d=u?i.hits?`${i.value} (${i.hits})`:i.value:i,p=d.length>r-4?d.substring(0,r-5)+`…`:d.padEnd(r-4),m=f.dim;if(u){let e=i;e.configStatus===`error`?m=f.error:e.configStatus===`warn`?m=f.warn:e.isActive&&(m=f.success),e.hits&&e.hits>0&&(m=f.highlight)}e.push(`\x1b[${n+1+a};${t+2}H`),o===s?e.push(l?`${f.selectedBg}${p}${f.reset}`:`${f.dim}\x1b[7m${p}${f.reset}`):e.push(`${m}${p}${f.reset}`)})}function h(e,t,n,r,i,a){let o=f.borderInactive,s=`${o}┌─ STATS `.padEnd(r+o.length-1,`─`);e.push(`\x1b[${n};${t}H${s}┐${f.reset}`);for(let a=1;a<i-1;a++)e.push(`\x1b[${n+a};${t}H${o}│${` `.repeat(r-2)}│${f.reset}`);e.push(`\x1b[${n+i-1};${t}H${o}└${`─`.repeat(r-2)}┘${f.reset}`);let c={error:0,warn:0,off:0};a.forEach(e=>{e.configStatus===`error`?c.error++:e.configStatus===`warn`?c.warn++:c.off++}),[{label:`Error`,count:c.error,color:f.error},{label:`Warn`,count:c.warn,color:f.warn},{label:`Off`,count:c.off,color:f.dim}].forEach((a,o)=>{if(o<i-2){let i=String(a.count).padStart(3),s=a.label.padEnd(r-8);e.push(`\x1b[${n+1+o};${t+2}H${a.color}${s}${i}${f.reset}`)}})}function g(e,t,n,r,i,a,o){let s=o?f.borderActive:f.borderInactive,c=`${s}┌─ DETAILS `.padEnd(r+s.length-1,`─`);e.push(`\x1b[${n};${t}H${c}┐${f.reset}`);for(let a=1;a<i-1;a++)e.push(`\x1b[${n+a};${t}H${s}│${` `.repeat(r-2)}│${f.reset}`);if(e.push(`\x1b[${n+i-1};${t}H${s}└${`─`.repeat(r-2)}┘${f.reset}`),!a)return;let l=a.configStatus.toUpperCase();l=a.configStatus===`error`?`${f.error}${l}${f.reset}`:a.configStatus===`warn`?`${f.warn}${l}${f.reset}`:`${f.dim}${l}${f.reset}`;let u=[[`Name`,a.value],[`Status`,l],[`Category`,a.category],[`Scope`,a.scope],[`Fix`,a.fix||`N/A`],[`Default`,a.default?`Yes`:`No`],[`Type-aware`,a.type_aware?`Yes`:`No`]],d=0;u.forEach(([r,a])=>{d<i-2&&(e.push(`\x1b[${n+1+d};${t+2}H${f.highlight}${r.padEnd(12)} ${f.reset}${a}`),d++)}),d<i-3&&d++,d<i-2&&(e.push(`\x1b[${n+1+d};${t+2}H${f.reset}Description:${f.reset}`),d++,p((a.description??`N/A`).replace(/\s+/g,` `).trim(),r-6).forEach(r=>{d<i-2&&(e.push(`\x1b[${n+1+d};${t+2}H${f.dim}${r}${f.reset}`),d++)}));let m=Math.max(d+1,i-3);m<i-1&&e.push(`\x1b[${n+1+m};${t+2}HHit ${f.highlight}ENTER${f.reset} to open docs`)}function _(e,t,n){return e<t?e:e>=t+n?e-n+1:t}function v(){if(!w||!w.categories)return;let{columns:e=80,rows:t=24}=d,n=w.categories[w.selectedCategoryIndex],r=w.rulesByCategory[n]||[],i=r[w.selectedRuleIndex],a=t-5,o=Math.floor(e*.2),s=Math.floor(e*.3),c=e-o-s-2,l=a-6,u=[`\x1B[H\x1B[J`];m(u,1,1,o,l,`CATEGORIES`,w.categories,w.selectedCategoryIndex,w.categoryScroll,w.activePane===0),h(u,1,1+l,o,6,r),m(u,o+1,1,s,a,`RULES (${r.length})`,r,w.selectedRuleIndex,w.ruleScroll,w.activePane===1),g(u,o+s+1,1,c,a,i,w.activePane===2);let p=f[w.messageType]||f.reset;u.push(`\x1b[${t-3};2H${p}● ${w.message}${f.reset}`);let _=w.configPath?`Config: ${w.configPath}`:`No config loaded`;u.push(`\x1b[${t-1};2H${f.dim}Arrows/HJKL: Nav | 1-3: Status | R: Active | A: All | X: Rule | Enter: Docs | Q: Quit | ${_}${f.reset}`),d.write(u.join(``))}const y=`1.42.0`,b={k:{type:`MOVE_UP`},up:{type:`MOVE_UP`},down:{type:`MOVE_DOWN`},j:{type:`MOVE_DOWN`},left:{type:`MOVE_LEFT`},h:{type:`MOVE_LEFT`},right:{type:`MOVE_RIGHT`},l:{type:`MOVE_RIGHT`},return:{type:`OPEN_DOCS`},enter:{type:`OPEN_DOCS`},1:{type:`SET_STATUS`,value:`off`},2:{type:`SET_STATUS`,value:`warn`},3:{type:`SET_STATUS`,value:`error`},q:{type:`EXIT`},r:{type:`RUN_LINT`},x:{type:`RUN_SINGLE_RULE`},a:{type:`RUN_ALL_RULES`}},x=r.dirname(n(import.meta.url));function S(){let n=r.join(x,`rule-descriptions.br`);if(e.existsSync(n))return JSON.parse(t.brotliDecompressSync(e.readFileSync(n)).toString())}const C=S();let w={activePane:0,selectedCategoryIndex:0,selectedRuleIndex:0,categoryScroll:0,ruleScroll:0,isLintInProgress:!1,message:`oxlint-tui`,messageType:`dim`,...A()};function T(e,t){w.config||={rules:{}},w.config.rules||(w.config.rules={});try{let n=e.value,r=e.scope===`oxc2`||e.scope===`eslint`?n:`${e.scope}/${n}`,i=w.config.rules,a=Object.keys(i).find(e=>e===r||e===n||e.endsWith(`/${n}`))||r;i[a]=t}catch{w.message=`Failed to update internal state`,w.messageType=`error`}}function E({rule:e=null,isRunAll:t=!1}={}){if(w.isLintInProgress)return;w.isLintInProgress=!0;let n=e?`${e.scope}/${e.value}`:null,r=e||t?!0:Object.values(w.rulesByCategory).flat().some(e=>e.isActive&&e.type_aware===!0);w.message=t?`Running all rules`:`Linting`,n&&(w.message+=` [${n}]`),r&&(w.message+=` with --type-aware`),w.message+=`...`,w.messageType=`info`,v();let i=l===`win32`?`npx.cmd`:`npx`,a=[`-q`,`--yes`,`--package`,`oxlint@${y}`];r&&a.push(`--package`,`oxlint-tsgolint@0.11.4`),a.push(`--`,`oxlint`),r&&a.push(`--type-aware`),a.push(`--format=json`),t?a.push(`-A`,`all`,`-W`,`all`):n?a.push(`-A`,`all`,`-D`,n):w.config&&w.config.rules&&Object.entries(w.config.rules).forEach(([e,t])=>{let n=Array.isArray(t)?t[0]:t;n===`error`?a.push(`-D`,e):n===`warn`?a.push(`-W`,e):n===`off`&&a.push(`-A`,e)});let s=o(i,a),c=``,u=``;s.stdout.on(`data`,e=>{c+=e}),s.stderr.on(`data`,e=>{u+=e}),s.on(`close`,e=>{w.isLintInProgress=!1;try{let e=JSON.parse(c||`{}`).diagnostics||[],t={};e.forEach(e=>{let n=e.code;t[n]=(t[n]||0)+1}),Object.keys(w.rulesByCategory).forEach(e=>{w.rulesByCategory[e].forEach(e=>{e.hits=0})}),Object.entries(t).forEach(([e,t])=>{let n;Object.values(w.rulesByCategory).some(t=>(n=t.find(t=>!!(e===t.value||e===`${t.scope}/${t.value}`||e===`${t.scope}(${t.value})`||e.endsWith(`(${t.value})`))),!!n)),n&&(n.hits=t)});let n=e.filter(e=>e.severity===`error`).length,r=e.filter(e=>e.severity===`warning`).length;e.length>0?(w.message=`Found ${r} warning${r===1?``:`s`} and ${n} error${n===1?``:`s`}`,w.messageType=n>0?`error`:`warn`):(w.message=`Linting passed! 0 issues found.`,w.messageType=`success`)}catch{let e=u.split(`
3
+ `).filter(e=>!e.includes(`experimental`)&&!e.includes(`Breaking changes`)&&e.trim()!==``).join(` `);w.message=e?`Error: ${e.substring(0,50)}...`:`Lint failed`,w.messageType=`error`}v()})}function D(e){if(!e)return;let{categories:t,rulesByCategory:n,selectedCategoryIndex:r,selectedRuleIndex:i,activePane:a}=w,o=t[r],s=n[o]||[],l=d.rows-8,u=l-7;switch(e.type){case`EXIT`:P(),c(0);return;case`RUN_LINT`:E();return;case`RUN_ALL_RULES`:E({isRunAll:!0});return;case`RUN_SINGLE_RULE`:{let e=s[i];e&&E({rule:e});return}case`OPEN_DOCS`:if(a===1){let e=s[i];e&&j(e.docs_url||e.url)}return;case`SET_STATUS`:{if(a!==1||!e.value)return;let t=s[i];if(!t)return;T(t,e.value);let r=[...s];r[i]={...t,configStatus:e.value,isActive:e.value===`error`||e.value===`warn`},w={...w,message:`Rule '${t.value}' set to: ${e.value}`,messageType:`info`,rulesByCategory:{...n,[o]:r}},v();return}case`MOVE_RIGHT`:a!==1&&(w={...w,activePane:a+1},v());return;case`MOVE_LEFT`:a!==0&&(w={...w,activePane:a-1},v());return;case`MOVE_UP`:if(a===0){let e=r===0?t.length-1:r-1;w={...w,selectedCategoryIndex:e,selectedRuleIndex:0,ruleScroll:0,categoryScroll:_(e,w.categoryScroll,u)}}else if(a===1){let e=i===0?s.length-1:i-1;w={...w,selectedRuleIndex:e,ruleScroll:_(e,w.ruleScroll,l)}}v();return;case`MOVE_DOWN`:if(a===0){let e=r===t.length-1?0:r+1;w={...w,selectedCategoryIndex:e,selectedRuleIndex:0,ruleScroll:0,categoryScroll:_(e,w.categoryScroll,u)}}else if(a===1){let e=i===s.length-1?0:i+1;w={...w,selectedRuleIndex:e,ruleScroll:_(e,w.ruleScroll,l)}}v();return}}function O(e,t,n){if(n.rules){let t=n.rules[e];if(t===void 0){let r=Object.keys(n.rules).find(t=>t.endsWith(`/${e}`));r&&(t=n.rules[r])}if(t!==void 0)return Array.isArray(t)?t[0]:t}return n.categories&&n.categories[t]?n.categories[t]:`off`}function k(e){return e.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g,(e,t)=>t?``:e)}function A(){let t=[],n={rules:{},categories:{}},r=null,i=C;i||(w.message=`Error: Couldn't find description.`,w.messageType=`error`);try{let e=a(`npx -q --yes oxlint@${y} --rules --format=json`,{encoding:`utf8`,stdio:[`ignore`,`pipe`,`ignore`]});t=JSON.parse(e)}catch{w.message=`Error: Couldn't run 'npx oxlint'.`,w.messageType=`error`,c(1)}let o=s[2];if(o&&e.existsSync(o)?r=o:e.existsSync(`.oxlintrc.json`)&&(r=`.oxlintrc.json`),r)try{n=JSON.parse(k(e.readFileSync(r,`utf8`)))}catch{w.message=`Error: Couldn't parse config.`,w.messageType=`error`}let l={};return t.forEach(e=>{let t=e.category||`Uncategorized`;l[t]||(l[t]=[]);let r=O(e.value,t,n),a=i[e.scope]?.[e.value];l[t].push({...e,description:a,configStatus:r,isActive:r===`error`||r===`warn`})}),{categories:Object.keys(l).toSorted(),rulesByCategory:l,config:n,configPath:r}}function j(e){e&&o(l===`darwin`?`open`:l===`win32`?`explorer`:`xdg-open`,[e],{detached:!0,stdio:`ignore`}).unref()}const M=e=>d.write(e),N=()=>M(`\x1B[?1049h\x1B[?25l`),P=()=>M(`\x1B[?1049l\x1B[?25h`);i.emitKeypressEvents(u),u.isTTY&&u.setRawMode(!0),u.on(`keypress`,(e,t)=>{D(b[t.name]||(t.ctrl&&t.name===`c`?{type:`EXIT`}:b[t.sequence]||null))}),d.on(`resize`,v),N(),v();export{w as state};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-tui",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "A Node TUI Oxlint rules and configuration browser",
5
5
  "keywords": [
6
6
  "cli",