npm-deprecated-check
Version:
Check for deprecated packages
3 lines (2 loc) • 12.9 kB
JavaScript
import f from"node:process";import{Option as d,program as u}from"commander";import k,{readFileSync as J,existsSync as R,writeFileSync as U,readdirSync as X,statSync as Z}from"node:fs";import ee from"node:os";import ne,{resolve as b,join as C}from"node:path";import m from"ansis";import B,{coerce as j,major as te,gt as G}from"semver";import{execSync as oe}from"node:child_process";import ae from"yocto-spinner";import{readWantedLockfile as re}from"@pnpm/lockfile-file";import{parseSyml as se}from"@yarnpkg/parsers";const V="1.6.0",ce=ee.homedir(),g=ne.resolve(ce,".ndcrc");function I(){try{const e=J(g,"utf-8");return JSON.parse(e)||{}}catch{return{}}}const h=["gpt-3.5-turbo","gpt-4","gpt-4-turbo","gpt-4o-mini","gpt-4o"],ie="https://api.openai.com/v1";function v(e){console.error(`${m.bgRed(" ERROR ")} ${m.red(e??"")}`)}function l(e){console.log(e??"")}function E(e){console.log(`${m.bgGreen(" OK ")} ${e??""}`)}function O(e){console.warn(`${m.bgYellowBright(" WARN ")} ${m.yellow(e??"")}`)}function de(e,o,t){const n=o.split(".");let a=e;const r=n.length;for(let c=0;c<r-1;c++){const s=n[c];a[s]||(a[s]={}),a=a[s]}a[n[r-1]]=t}function pe(e,o){const t=o.split(".");let n=e;const a=t.length;for(let r=0;r<a-1;r++){const c=t[r];if(!n[c])return;n=n[c]}return n[t[a-1]]}function le(e,o){const t=o.split(".");let n=e;const a=t.length,r=[];for(let c=0;c<a-1;c++){const s=t[c];if(!n[s])return;r.unshift({parent:n,key:s,value:n[s]}),n=n[s]}delete n[t[a-1]];for(const{parent:c,key:s,value:i}of r)Object.keys(i).length||delete c[s]}function F(e){try{return JSON.parse(e)}catch{return}}function me(e){R(g)||U(g,JSON.stringify({latestVersion:V,lastChecked:Date.now()},null,2),"utf-8");let o={};try{const t=J(g,"utf-8");o=JSON.parse(t)}catch{}if(e.get){const t=pe(o,e.get);l(t)}if(e.set){const[t,n]=e.set;t==="openaiModel"&&!h.includes(n)&&(v(`error: option '--openaiModel <value>' argument '${n}' is invalid. Allowed choices are ${h.join(", ")}.`),f.exit(1));let a;Number.isNaN(Number.parseInt(n))?n==="true"?a=!0:n==="false"?a=!1:a=n:a=Number.parseInt(n),de(o,t,a),U(g,JSON.stringify(o,null,2),"utf-8")}e.delete&&(le(o,e.delete),U(g,JSON.stringify(o,null,2),"utf-8")),e.list&&l(JSON.stringify(o,null,2))}const ue={openaiModel:h[0],openaiBaseURL:ie},fe=I();async function ge(e,o){const t=Object.assign(ue,fe,o);if(!t.openaiKey)return null;for(let n=h.indexOf(t.openaiModel);n>-1;n--){const a=h[n],{url:r,req:c}=he(e,t.openaiKey,a,t.openaiBaseURL);try{const s=await fetch(r,c);if(!s.ok){const y=await s.text().catch(()=>"Unknown"),x=F(y),Q=x?void 0:y;throw new Error(x?.error?.message?x?.error?.message:Q||"Unknown error occurred")}const i=(await s.json().catch(()=>null)).choices[0]?.message?.content,p=F(i)||i;return p?.length?p:null}catch(s){l(),O(s),l()}}return null}function he(e,o,t,n){const a=`${n}/chat/completions`,r={method:"post",body:JSON.stringify({messages:[{role:"user",content:`The npm package - ${e} is deprecated, please suggest some alternative packages, only return an array of the package names.`}],model:t},null,2),headers:{Accept:"application/json","Content-Type":"application/json",Authorization:`Bearer ${o}`}};return{url:a,req:r}}function S(e){return oe(e).toString()}let $="";function ve(){if($)return $;try{$=S("npm config get registry").trim()}catch{$="https://registry.npmjs.org/"}return $}const w=ae({text:"Checking\u2026"});let L;function ye(){w.color="green",w.start(),L=setTimeout(()=>{w.color="yellow",L=setTimeout(()=>{w.color="red"},3e4)},3e4)}function ke(){clearTimeout(L),w.stop()}async function P(e,o){const t=Object.keys(e),n=[];let a=!1,r=!1;for(const c of t){ye();const s=await $e(c,e[c],o);if(ke(),n.push(s),s.error&&(r=!0,v(s.error),l()),s.deprecated){if(a=!0,O(`${s.name}@${s.version}: ${s.time}
deprecated: ${s.deprecated}`),s.minimumUpgradeVersion?(l(m.green("minimum upgrade version: ")),l(`[${m.magenta(s.minimumUpgradeVersion)}](https://www.npmjs.com/package/${s.name}/v/${s.minimumUpgradeVersion})`)):l(m.yellow("No upgrade available.")),s.recommend)if(l(m.green("recommended: ")),Array.isArray(s.recommend))for(const i of s.recommend)l(`[${m.magenta(i)}](https://www.npmjs.com/package/${i})`);else l(s.recommend);l(),o.failfast&&f.exit(1)}}return r||E(`All dependencies retrieved successfully.${a?"":" There are no deprecated dependencies."}`),n}const Oe=I();async function $e(e,o,t){let n;try{const i=t.registry||Oe.registry||ve(),p=i.endsWith("/")?i:`${i}/`;if(n=await(await fetch(p+e)).json(),!n)return{name:e,error:`${e}: Could not find the package!`}}catch(i){return{name:e,error:`${e}: ${i.message}`}}if(!n["dist-tags"])return{name:e,error:`${e}: Could not find the package!`};const a=o.version||(o.range?n["dist-tags"][o.range]||B.maxSatisfying(Object.keys(n.versions),o.range||"*")||null:n["dist-tags"].latest?n["dist-tags"].latest:v(`${e}: 'latest' dist-tag does not exist!`));if(!a||!n.versions[a])return{name:e,error:`${e}: Please enter the correct range!`};const r=n.versions[a].deprecated,c=r?await ge(n.name,t):null;let s=null;if(r){const i=B.sort(Object.keys(n.versions));for(let p=i.indexOf(a);p<i.length;p++){const y=i[p];if(!n.versions[y].deprecated){s=y;break}}}return{name:n.name,version:a,time:n.time[a],deprecated:r,recommend:c,minimumUpgradeVersion:s}}function Y(e){return["link:","file:","workspace:"].some(o=>e.startsWith(o))}function we(e){return/^https?:\/\//.test(e)}function be(e){return/\.git$/.test(e)}const je=b("./package-lock.json"),Se=b("./yarn.lock"),Ne=b("./pnpm-lock.yaml");function xe(e){const o=[{path:je,read(){const t=JSON.parse(k.readFileSync(this.path,"utf-8"));let n=t.dependencies,a="";t.lockfileVersion>1&&(n=t.packages,a="node_modules/");const r={};for(const c in e){const s=a+c;n[s]&&(r[c]={version:n[s].version})}return r}},{path:Se,read(){const t=k.readFileSync(this.path,"utf-8"),n=se(t),a={};for(const r in e)n[`${r}@${e[r].range}`]&&(a[r]={version:n[`${r}@${e[r].range}`].version});return a}},{path:Ne,async read(){const t=await re(b(this.path,".."),{ignoreIncompatible:!1});if(t&&t.packages){const n=Object.keys(e),a={};for(const r in t.packages){const c=t.packages[r];n.includes(c.name)&&(a[c.name]={version:c.version})}return a}else return{}}}].filter(t=>k.existsSync(t.path)).sort((t,n)=>k.lstatSync(n.path).mtimeMs-k.lstatSync(t.path).mtimeMs);return o.length>0?o[0].read():{}}function K(e){const o={};for(const t in e){if(e[t].includes("@")){const n=e[t].lastIndexOf("@");e[t]=e[t].slice(n+1)}o[t]={range:e[t]}}return o}function Je(e){if(!R(e))return v("package.json does not exist in the current path, please execute it under the correct project path.");const o=J(e,"utf-8"),{dependencies:t,devDependencies:n}=JSON.parse(o);return{...K(t),...K(n)}}async function W(e){const o=f.cwd(),t=e.deep?_(o):[o];for(const n of t)l(`> ${n}`),await Re(n,e),l()}function _(e,o=[]){const t=C(e,"package.json");R(t)&&o.push(e);let n;try{n=X(e)}catch{return o}for(const a of n){if(a==="node_modules")continue;const r=C(e,a);try{Z(r).isDirectory()&&_(r,o)}catch{}}return o}async function Re(e,o){const t=C(e,"package.json"),n=Je(t);if(n)try{const a=o.ignore?.split(",")||[],r={};for(const i in n){const p=n[i];!a.includes(i)&&!Y(p.range)&&!we(p.range)&&!be(p.range)&&(r[i]=p)}const c=await xe(r),s=Object.assign(r,c);return P(s,o)}catch(a){v(a.message)}}const Ue=/"((?:@[a-z][a-z0-9-_.]*\/)?[a-z][a-z0-9-_.]*)@(\d+\.\d+\.\d+(?:-[a-z0-9-]+(?:\.[a-z0-9-]+)*)?)"/g;function q(e){const{manager:o,...t}=e;try{let n={};if(o==="pnpm")n=JSON.parse(S("pnpm list -g --depth=0 --json")).map(r=>r.dependencies).reduce((r,c)=>Object.assign(r,c),{});else if(o==="yarn"){const r=S("yarn global list --depth=0"),c=Array.from(r.matchAll(Ue),s=>[s[1],s[2]]);for(const s of c){const[i,p]=s;n[i]={version:p}}}else n=JSON.parse(S("npm ls -g --depth=0 --json")).dependencies;const a=e.ignore?.split(",")||[];return P(Object.fromEntries(Object.entries(n).filter(([r,{version:c}])=>!a.includes(r)&&!Y(c))),t)}catch(n){v(n.message)}}const Ce={start:"2015-09-08",lts:"2015-10-12",maintenance:"2017-04-01",end:"2018-04-30",codename:"Argon"},Le={start:"2015-10-29",maintenance:"2016-04-30",end:"2016-06-30"},Pe={start:"2016-04-26",lts:"2016-10-18",maintenance:"2018-04-30",end:"2019-04-30",codename:"Boron"},Ae={start:"2016-10-25",maintenance:"2017-04-30",end:"2017-06-30"},De={start:"2017-05-30",lts:"2017-10-31",maintenance:"2019-01-01",end:"2019-12-31",codename:"Carbon"},Me={start:"2017-10-01",maintenance:"2018-04-01",end:"2018-06-30"},Te={start:"2018-04-24",lts:"2018-10-30",maintenance:"2020-05-19",end:"2021-04-30",codename:"Dubnium"},ze={start:"2018-10-23",maintenance:"2019-04-22",end:"2019-06-01"},Be={start:"2019-04-23",lts:"2019-10-21",maintenance:"2020-11-30",end:"2022-04-30",codename:"Erbium"},Ge={start:"2019-10-22",maintenance:"2020-04-01",end:"2020-06-01"},Ve={start:"2020-04-21",lts:"2020-10-27",maintenance:"2021-10-19",end:"2023-04-30",codename:"Fermium"},Ie={start:"2020-10-20",maintenance:"2021-04-01",end:"2021-06-01"},Ee={start:"2021-04-20",lts:"2021-10-26",maintenance:"2022-10-18",end:"2023-09-11",codename:"Gallium"},Fe={start:"2021-10-19",maintenance:"2022-04-01",end:"2022-06-01"},Ye={start:"2022-04-19",lts:"2022-10-25",maintenance:"2023-10-18",end:"2025-04-30",codename:"Hydrogen"},Ke={start:"2022-10-18",maintenance:"2023-04-01",end:"2023-06-01"},We={start:"2023-04-18",lts:"2023-10-24",maintenance:"2024-10-22",end:"2026-04-30",codename:"Iron"},_e={start:"2023-10-17",maintenance:"2024-04-01",end:"2024-06-01"},qe={start:"2024-04-24",lts:"2024-10-29",maintenance:"2025-10-21",end:"2027-04-30",codename:"Jod"},He={start:"2024-10-16",maintenance:"2025-04-01",end:"2025-06-01"},Qe={start:"2025-05-06",lts:"2025-10-28",maintenance:"2026-10-20",end:"2028-04-30",codename:""},Xe={start:"2025-10-15",maintenance:"2026-04-01",end:"2026-06-01"},Ze={start:"2026-04-22",lts:"2026-10-28",maintenance:"2027-10-20",end:"2029-04-30",codename:""},A={"v0.8":{start:"2012-06-25",end:"2014-07-31"},"v0.10":{start:"2013-03-11",end:"2016-10-31"},"v0.12":{start:"2015-02-06",end:"2016-12-31"},v4:Ce,v5:Le,v6:Pe,v7:Ae,v8:De,v9:Me,v10:Te,v11:ze,v12:Be,v13:Ge,v14:Ve,v15:Ie,v16:Ee,v17:Fe,v18:Ye,v19:Ke,v20:We,v21:_e,v22:qe,v23:He,v24:Qe,v25:Xe,v26:Ze};function en(e){return Object.keys(e).reduce((o,t)=>{const n=j(o),a=j(t);return G(a,n)?t:o})}function N(){const e=j(f.version),o=j(en(A)),t=A[`v${te(e)}`];if(t){const n=new Date(t.end);new Date<n?E(`Your node version (${e}) is supported until ${t.end}.`):O(`Your node version (${e}) is no longer supported since ${t.end}.`)}else G(e,o)?O(`Your node version (${e}) is higher than the latest version ${o}. Please update 'npm-deprecated-check'.`):O(`Your node version (${e}) can't be found in the release schedule. Please update 'npm-deprecated-check'.`);return{version:e,latestVersion:o,releases:A}}function H(e){const{packageName:o,range:t,...n}=e;return P({[o]:{range:t}},n)}const D=new d("--registry <value>","specify registry URL"),M=new d("--openaiKey <value>","recommend alternative packages via ChatGPT"),T=new d("--openaiModel <value>","ChatGPT model").choices(h),z=new d("--openaiBaseURL <value>","override the default base URL for the API");u.version(`npm-deprecated-check ${V}`).usage("<command> [options]"),u.command("node").description("check if used node version is deprecated (reached End Of Life)").action(()=>{N()}),u.command("current").description("check the packages of the current project").addOption(new d("--ignore <value>","ignore specific packages")).addOption(new d("--failfast","exit the program if it has been deprecated")).addOption(new d("--deep","deep inspection for monorepo projects")).addOption(D).addOption(M).addOption(T).addOption(z).action(e=>{N(),W(e)}),u.command("global").description("check global packages, default: npm").addOption(new d("-m, --manager <value>","check specified package manager").choices(["npm","yarn","pnpm"]).default("npm")).addOption(new d("--ignore <value>","ignore specific packages")).addOption(new d("--failfast","exit the program if it has been deprecated")).addOption(D).addOption(M).addOption(T).addOption(z).action(e=>{N(),q(e)}),u.command("package <packageName>").description("check for specified package").addOption(new d("-r, --range <value>","check specified versions")).addOption(new d("--failfast","exit the program if it has been deprecated")).addOption(D).addOption(M).addOption(T).addOption(z).action((e,o)=>{const t={packageName:e,...o};H(t)}),u.command("config").description("inspect and modify the config").addOption(new d("-g, --get <path>","get value from option")).addOption(new d("-s, --set <path> <value>","set option value")).addOption(new d("-d, --delete <path>","delete option from config")).addOption(new d("-l, --list","list all options")).action((e,o)=>{Object.keys(e).length===0&&(o.outputHelp(),f.exit(0));const t={};for(const n in e)n==="set"?t.set=[e.set,o.args[0]]:t[n]=e[n];me(t)}),u.parse(f.argv);export{W as checkCurrent,q as checkGlobal,N as checkNode,H as checkPackage};