@namchee/dependent
Advanced tools
Comparing version 0.14.1 to 0.15.0
#!/usr/bin/env node | ||
import e from"ora";import t from"chalk";import n from"yargs";import{resolve as o,basename as r}from"path";import{existsSync as s,readFileSync as i}from"fs";import{spawn as a}from"child_process";import c from"glob";import{pathToFileURL as l}from"url";import p from"typescript";import{table as d}from"table";const u=n(process.argv.slice(2)).scriptName("dependent").command("$0 <package> [files...]","Analyze package usage in your project directory.").usage("Usage: $0 <package> [files...]").positional("package",{alias:"p",type:"string",description:"Package name to be analyzed."}).positional("files",{alias:"f",type:"string",description:"Files to be analyzed in glob pattern relative to the current project directory.",default:["!(node_modules)/**/*.js","!(node_modules)/**/*.mjs","!(node_modules)/**/*.ts","!(node_modules)/**/*.jsx","!(node_modules)/**/*.tsx","!(node_modules)/**/*.vue","!(node_modules)/**/*.svelte","*.js","*.mjs","*.ts","*.jsx","*.tsx","*.vue","*.svelte"]}).options({silent:{alias:"s",describe:"Skip all unreadable and unparseable files instead of throwing errors",type:"boolean",default:!1,demandOption:!1},table:{alias:"t",describe:"Print the output in table format",type:"boolean",default:!1,demandOption:!1},precheck:{alias:"c",describe:"Check if the package is installed",type:"boolean",default:!0,demandOption:!1},include:{describe:"Type of possible dependant to be scanned for dependency. Defaults to files and scripts",type:"array",default:["scripts","files"],demandOption:!1}});function m(e,t){return new Promise(((n,o)=>{const r=a(e,t);r.stdout.on("data",(e=>n(e))),r.on("error",(()=>o(new Error("Command not found"))))}))}const f={npm:"ls",pnpm:"ls",yarn:"why"};function h(){var e,t,n,r,a,c;const l=o(process.cwd(),"package.json");if(!s(l))throw new Error("The current project directory is not a NodeJS-based project");try{const o=JSON.parse(i(l,"utf-8"));return{name:null!==(e=o.name)&&void 0!==e?e:"",executables:null!==(t=o.bin)&&void 0!==t?t:{},scripts:null!==(n=o.scripts)&&void 0!==n?n:{},dependencies:null!==(r=o.dependencies)&&void 0!==r?r:{},devDependencies:null!==(a=o.devDependencies)&&void 0!==a?a:{},peerDependencies:null!==(c=o.peerDependencies)&&void 0!==c?c:{}}}catch(e){const{message:t}=e;throw new Error(`Failed to read package.json: ${t}`)}}async function g(e){let t=function(){const e=o(process.cwd(),"package-lock.json");if(s(e))return"npm";const t=o(process.cwd(),"yarn.lock");return s(t)?"yarn":"pnpm"}();const n=f[t];/^win/.test(process.platform)&&(t=`${t}.cmd`);const r=await m(t,[n,e]);if(!(r.includes(e)&&0!==r.lastIndexOf(e)))throw new Error(`Package ${e} has not been installed in this project`)}function y(e){const t=e.split("/");return e.startsWith("@")?t.slice(0,2).join("/"):t[0]}let w,v,j;function b(e,t,n){const o=[];return e.walk(t,{enter(e){var t,r,s;switch(e.type){case"ImportDeclaration":{const r=e;"Literal"===r.source.type&&y(null===(t=r.source.value)||void 0===t?void 0:t.toString())===n&&o.push(e.loc.start.line);break}case"ImportExpression":{const t=e;"Literal"===t.source.type&&y(null===(r=t.source.value)||void 0===r?void 0:r.toString())===n&&o.push(e.loc.start.line);break}case"CallExpression":{const t=e;"Identifier"===t.callee.type&&"require"===t.callee.name&&"Literal"===t.arguments[0].type&&y(null===(s=t.arguments[0].value)||void 0===s?void 0:s.toString())===n&&o.push(e.loc.start.line);break}}}}),o}async function S(e,t,n){v||await async function(e){if(v)return;const t=["typescript","lib","typescript.js"],n=[o(process.cwd(),"node_modules",...t),...e.map((e=>o(e,...t)))].map((e=>import(l(e).toString()))),r=await Promise.allSettled(n);for(let e=0;e<r.length;e++){const t=r[e];if("fulfilled"===t.status)return void(v=t.value.default)}throw new Error("No TypeScript parsers available")}(n);return function(e,t){const n=[],o=r=>{switch(r.kind){case p.SyntaxKind.ImportDeclaration:{const o=r.moduleSpecifier;o.kind===p.SyntaxKind.StringLiteral&&y(o.getText().slice(1,-1))===t&&n.push(e.getLineAndCharacterOfPosition(r.getStart()).line+1);break}case p.SyntaxKind.CallExpression:{const o=r,{expression:s}=o,i=o.arguments,a=s.kind===p.SyntaxKind.ImportKeyword&&1===i.length&&i[0].kind===p.SyntaxKind.StringLiteral&&y(i[0].getText().slice(1,-1))===t,c=s.kind===p.SyntaxKind.Identifier&&"require"===s.getText()&&1===i.length&&i[0].kind===p.SyntaxKind.StringLiteral&&y(i[0].getText().slice(1,-1))===t;(a||c)&&n.push(e.getLineAndCharacterOfPosition(r.getStart()).line+1);break}}v.forEachChild(r,o)};return o(e),n}(v.createSourceFile("",e,v.ScriptTarget.Latest,!0,v.ScriptKind.TSX),t)}const k={js:S,mjs:S,cjs:S,jsx:S,ts:S,vue:async function(e,t,n){var r;j||await async function(e){if(j)return;const t=["@vue","compiler-sfc","dist","compiler-sfc.cjs.js"],n=[o(process.cwd(),"node_modules","vue","compiler-sfc","index.js"),o(process.cwd(),"node_modules",...t),...e.map((e=>o(e,...t)))].map((e=>import(l(e).toString()))),r=await Promise.allSettled(n);for(let e=0;e<r.length;e++){const t=r[e];if("fulfilled"===t.status)return void(j=t.value.default)}throw new Error("No Vue 3 parser available")}(n);const s=j.parse(e),i=null!==(r=s.descriptor.script)&&void 0!==r?r:s.descriptor.scriptSetup;if(i){const e=i.loc.start.line;return(await S(i.content,t,n)).map((t=>t+e-1))}return[]},svelte:async function(e,t,n){w||await async function(e){if(w)return;const t=["svelte","compiler.js"],n=[o(process.cwd(),"node_modules",...t),...e.map((e=>o(e,...t)))].map((e=>import(l(e).toString()))),r=await Promise.allSettled(n);for(let e=0;e<r.length;e++){const t=r[e];if("fulfilled"===t.status)return void(w=t.value.default)}throw new Error("No Svelte compiler available")}(n);const r=w.parse(e);return[...b(w,r.instance,t),...b(w,r.module,t)]}};async function x(){try{return(await m(/^win/.test(process.platform)?"npm.cmd":"npm",["root","--global"])).toString().trim()}catch(e){if("Command not found"===e.message)return"";throw e}}async function $(){try{const e=await m(/^win/.test(process.platform)?"yarn.cmd":"yarn",["global","dir"]);return o(e.toString().trim(),"node_modules")}catch(e){if("Command not found"===e.message)return"";throw e}}async function E(){try{return(await m(/^win/.test(process.platform)?"pnpm.cmd":"pnpm",["root","--global"])).toString().trim()}catch(e){if("Command not found"===e.message)return"";throw e}}async function O(e,n,{silent:o}){const r=(await Promise.allSettled([x(),$(),E()])).map((e=>"fulfilled"===e.status?e.value:"")),s=e.map((async e=>{try{const t=function(e){return e.name.split(".").pop()}(e),o=function(e){if(!(e in k))throw new Error(`.${e} files are currently not supported`);return k[e]}(t),s=await o(e.content,n,r);return s.length?{name:e.name,path:e.path,lineNumbers:s}:null}catch(t){const n=t;throw new Error(`Failed to parse ${e.path}: ${n.message}`)}}));if(!o){return(await Promise.all(s)).filter((e=>null!==e))}const i=await Promise.allSettled(s),a=[];for(const e of i)"rejected"===e.status?console.log(t.yellow(e.reason)):e.value&&a.push(e.value);return a}function F(e){const{scripts:t}=h(),{executables:n}=function(e){var t,n,r,a,c,l;const p=o(process.cwd(),"node_modules",e,"package.json");if(!s(p))throw new Error("The current project directory is not a NodeJS-based project");try{const e=JSON.parse(i(p,"utf-8"));return{name:null!==(t=e.name)&&void 0!==t?t:"",executables:null!==(n=e.bin)&&void 0!==n?n:{},scripts:null!==(r=e.scripts)&&void 0!==r?r:{},dependencies:null!==(a=e.dependencies)&&void 0!==a?a:{},devDependencies:null!==(c=e.devDependencies)&&void 0!==c?c:{},peerDependencies:null!==(l=e.peerDependencies)&&void 0!==l?l:{}}}catch(e){const{message:t}=e;throw new Error(`Failed to read package.json: ${t}`)}}(e),r=[];if(Object.keys(n).length){const e=`(${Object.keys(n).join("|")})`;for(const[n,o]of Object.entries(t))o.match(e)&&r.push(n)}return r}const C={js:"JavaScript Files",cjs:"CommonJS Files",mjs:"ESModule Files",jsx:"JavaScript Extended Files",ts:"TypeScript Files",tsx:"TypeScript Extended Files",vue:"Vue's Single File Component",svelte:"Svelte's Single File Component"};function P(e,t){const n=e.path.split("/").length,o=t.path.split("/").length;return n>o?1:n<o?-1:e.name>t.name?1:-1}function T(e,n,o){return t.cyanBright(`${{files:"📁",scripts:"📜"}[e]} There are ${o} ${e} in this project that depends on '${n}'`)}function _(e){const t=[];for(const[n,o]of Object.entries(e)){const e=C[n];if(o.length){const n=`📜 ${e}`,r=o.map((e=>[e.name,e.path,e.lineNumbers.join(", ")])),s=d([["Filename","Path","Line Number"],...r]);t.push(n+"\n"+s)}}return t.join("\n").slice(0,-1)}function D(e){const n=[];for(const[o,r]of Object.entries(e)){const e=C[o];if(r.length){const o=`📜 ${e}`,s=r.map((({name:e,path:n,lineNumbers:o})=>t.cyan(` └── ${e}:${o.join(", ")} → ${n}`)));n.push(o+"\n"+s.join("\n"))}}return n.join("\n\n")}function N(e){return e.map((e=>t.cyan(` └── ${e}`))).join("\n")}function L(e){return d([["Script"],e.map((e=>[e]))]).slice(0,-1)}function K(e,t,{format:n}){const o=[T("files",t,e.length)],r={lines:D,table:_},s=function(e){const t={js:[],cjs:[],mjs:[],jsx:[],ts:[],tsx:[],vue:[],svelte:[]};for(const n of e)t[n.name.split(".").pop()].push(n);for(const e of Object.values(t))e.sort(P);return t}(e);return e.length&&o.push(r[n](s)),o.join("\n\n")}(async()=>{const n=u.parseSync(),o=e().start();try{const e=n.package;o.text=t.greenBright("Scanning project directory...");const{silent:s,table:a,precheck:l,include:p}=n;l&&(o.text=t.greenBright("Checking package installation..."),function(e,t){if(!(Object.keys(t.dependencies||{}).includes(e)||Object.keys(t.devDependencies||{}).includes(e)||Object.keys(t.peerDependencies||{}).includes(e)))throw new Error(`Package ${e} is not defined in this project`)}(e,h()),await g(e));const d=function(e,t){const n=c.sync(`{${e.join(",")}}`,{silent:!0}),o=[];for(const e of n)try{const t=r(e),n=i(e,"utf-8");o.push({name:t,path:e,content:n})}catch(n){if(t)continue;throw new Error(`Failed to read ${e}`)}return o}(n.files,s);o.text=t.greenBright("Analyzing project for dependency...");const u=[];if(p.includes("files")){const t=await O(d,e,{silent:s});u.push(K(t,e,{format:a?"table":"lines"}))}if(p.includes("scripts")){const t=F(e);u.push(function(e,t,{format:n}){const o=[T("scripts",t,e.length)],r={lines:N,table:L};return e.length&&o.push(r[n](e)),o.join("\n")}(t,e,{format:a?"table":"lines"}))}o.succeed(t.greenBright("Analysis completed successfully")),console.log(u.join("\n\n"))}catch(e){const n=e;o.fail(t.redBright(n.message)),console.log(t.cyanBright("Terminating..."))}})(); | ||
import e from"ora";import t from"chalk";import n from"yargs";import{resolve as o,basename as s}from"path";import{existsSync as r,readFileSync as i}from"fs";import{spawn as a}from"child_process";import c from"glob";import l from"typescript";import{pathToFileURL as d}from"url";import{table as p}from"table";const u=n(process.argv.slice(2)).scriptName("dependent").command("$0 <package> [files...]","Analyze package usage in your project directory.").usage("Usage: $0 <package> [files...]").positional("package",{alias:"p",type:"string",description:"Package name to be analyzed."}).positional("files",{alias:"f",type:"string",description:"Files to be analyzed in glob pattern relative to the current project directory.",default:["!(node_modules)/**/*.js","!(node_modules)/**/*.mjs","!(node_modules)/**/*.ts","!(node_modules)/**/*.jsx","!(node_modules)/**/*.tsx","!(node_modules)/**/*.vue","!(node_modules)/**/*.svelte","!(node_modules)/**/*.astro","*.js","*.mjs","*.ts","*.jsx","*.tsx","*.vue","*.svelte","*.astro"]}).options({silent:{alias:"s",describe:"Skip all unreadable and unparseable files instead of throwing errors",type:"boolean",default:!1,demandOption:!1},table:{alias:"t",describe:"Print the output in table format",type:"boolean",default:!1,demandOption:!1},precheck:{alias:"c",describe:"Check if the package is installed",type:"boolean",default:!0,demandOption:!1},include:{describe:"Type of possible dependant to be scanned for dependency. Defaults to files and scripts",type:"array",default:["scripts","files"],demandOption:!1}});function m(e,t){return new Promise(((n,o)=>{const s=a(e,t);s.stdout.on("data",(e=>n(e))),s.on("error",(()=>o(new Error("Command not found"))))}))}const f={npm:"ls",pnpm:"ls",yarn:"why"};function h(){var e,t,n,s,a,c;const l=o(process.cwd(),"package.json");if(!r(l))throw new Error("The current project directory is not a NodeJS-based project");try{const o=JSON.parse(i(l,"utf-8"));return{name:null!==(e=o.name)&&void 0!==e?e:"",executables:null!==(t=o.bin)&&void 0!==t?t:{},scripts:null!==(n=o.scripts)&&void 0!==n?n:{},dependencies:null!==(s=o.dependencies)&&void 0!==s?s:{},devDependencies:null!==(a=o.devDependencies)&&void 0!==a?a:{},peerDependencies:null!==(c=o.peerDependencies)&&void 0!==c?c:{}}}catch(e){const{message:t}=e;throw new Error(`Failed to read package.json: ${t}`)}}function g(e){var t,n,s,a,c,l;const d=o(process.cwd(),"node_modules",e,"package.json");if(!r(d))throw new Error(`Cannot find metadata for dependency ${e}`);try{const e=JSON.parse(i(d,"utf-8"));return{name:null!==(t=e.name)&&void 0!==t?t:"",executables:null!==(n=e.bin)&&void 0!==n?n:{},scripts:null!==(s=e.scripts)&&void 0!==s?s:{},dependencies:null!==(a=e.dependencies)&&void 0!==a?a:{},devDependencies:null!==(c=e.devDependencies)&&void 0!==c?c:{},peerDependencies:null!==(l=e.peerDependencies)&&void 0!==l?l:{}}}catch(e){const{message:t}=e;throw new Error(`Failed to read package.json: ${t}`)}}async function w(e){let t=function(){const e=o(process.cwd(),"package-lock.json");if(r(e))return"npm";const t=o(process.cwd(),"yarn.lock");return r(t)?"yarn":"pnpm"}();const n=f[t];/^win/.test(process.platform)&&(t=`${t}.cmd`);const s=await m(t,[n,e]);if(!(s.includes(e)&&0!==s.lastIndexOf(e)))throw new Error(`Package ${e} has not been installed in this project`)}function y(e){const t=e.split("/");return e.startsWith("@")?t.slice(0,2).join("/"):t[0]}function v(e){return e.replace(/^([\^~><>=])+/,"")}async function j(e,t){return function(e,t){const n=[],o=s=>{switch(s.kind){case l.SyntaxKind.ImportDeclaration:{const o=s.moduleSpecifier;o.kind===l.SyntaxKind.StringLiteral&&y(o.getText().slice(1,-1))===t&&n.push(e.getLineAndCharacterOfPosition(s.getStart()).line+1);break}case l.SyntaxKind.CallExpression:{const o=s,{expression:r}=o,i=o.arguments,a=r.kind===l.SyntaxKind.ImportKeyword&&1===i.length&&i[0].kind===l.SyntaxKind.StringLiteral&&y(i[0].getText().slice(1,-1))===t,c=r.kind===l.SyntaxKind.Identifier&&"require"===r.getText()&&1===i.length&&i[0].kind===l.SyntaxKind.StringLiteral&&y(i[0].getText().slice(1,-1))===t;(a||c)&&n.push(e.getLineAndCharacterOfPosition(s.getStart()).line+1);break}}l.forEachChild(s,o)};return o(e),n}(l.createSourceFile("",e,l.ScriptTarget.Latest,!0,l.ScriptKind.TSX),t)}let b,S,k,x,$=[];async function _(){try{return(await m(/^win/.test(process.platform)?"npm.cmd":"npm",["root","--global"])).toString().trim()}catch(e){if("Command not found"===e.message)return"";throw e}}async function E(){try{const e=await m(/^win/.test(process.platform)?"yarn.cmd":"yarn",["global","dir"]);return o(e.toString().trim(),"node_modules")}catch(e){if("Command not found"===e.message)return"";throw e}}async function O(){try{return(await m(/^win/.test(process.platform)?"pnpm.cmd":"pnpm",["root","--global"])).toString().trim()}catch(e){if("Command not found"===e.message)return"";throw e}}async function F(){if($.length)return $;const e=await Promise.allSettled([_(),E(),O()]);return $=e.map((e=>"fulfilled"===e.status?e.value:"")).filter(Boolean),$}function P(e,t){const n=[];return S.walk(e,{enter(e){var o,s,r;switch(e.type){case"ImportDeclaration":{const s=e;"Literal"===s.source.type&&y(null===(o=s.source.value)||void 0===o?void 0:o.toString())===t&&n.push(e.loc.start.line);break}case"ImportExpression":{const o=e;"Literal"===o.source.type&&y(null===(s=o.source.value)||void 0===s?void 0:s.toString())===t&&n.push(e.loc.start.line);break}case"CallExpression":{const o=e;"Identifier"===o.callee.type&&"require"===o.callee.name&&"Literal"===o.arguments[0].type&&y(null===(r=o.arguments[0].value)||void 0===r?void 0:r.toString())===t&&n.push(e.loc.start.line);break}}}}),n}function C(){try{const{dependencies:e}=g("astro");return[".pnpm",`@astrojs+compiler@${v(e["@astrojs/compiler"])}`,"node_modules"]}catch(e){return[]}}async function D(e){if(k)return;const t=["@astrojs","compiler","node","index.js"],n=["@astrojs","compiler","dist","node","index.js"],s=C(),r=[o(process.cwd(),"node_modules",...t),o(process.cwd(),"node_modules",...n)];s.length&&r.push(o(process.cwd(),"node_modules",...s,...t),o(process.cwd(),"node_modules",...s,...n));const i=[...r,...e.map((e=>o(e,...t)))].map((e=>import(d(e).toString()))),a=await Promise.allSettled(i);for(let e=0;e<a.length;e++){const t=a[e];if("fulfilled"===t.status)return void(k=t.value)}throw new Error("No Astro parser available")}async function T(e){if(x)return;const t=["@astrojs","compiler","browser","utils.js"],n=["@astrojs","compiler","dist","browser","utils.js"],s=C(),r=[o(process.cwd(),"node_modules",...t),o(process.cwd(),"node_modules",...n)];s.length&&r.push(o(process.cwd(),"node_modules",...s,...t),o(process.cwd(),"node_modules",...s,...n));const i=[...r,...e.map((e=>o(e,...t)))].map((e=>import(d(e).toString()))),a=await Promise.allSettled(i);for(let e=0;e<a.length;e++){const t=a[e];if("fulfilled"===t.status)return void(x=t.value)}throw new Error("Failed to load Astro utility")}const N={js:j,mjs:j,cjs:j,jsx:j,ts:j,vue:async function(e,t){var n;if(!b)throw new Error("Vue compiler has not been loaded yet");const o=b.parse(e),s=null!==(n=o.descriptor.script)&&void 0!==n?n:o.descriptor.scriptSetup;if(s){const e=s.loc.start.line;return(await j(s.content,t)).map((t=>t+e-1))}return[]},svelte:async function(e,t){if(!S)throw new Error("Svelte compiler has not been loaded yet");const n=S.parse(e);return[...P(n.instance,t),...P(n.module,t)]},astro:async function(e,t){if(!k||!x)throw new Error("Astro compiler has not been loaded yet");return async function(e,t){const n=[];return await x.walk(e,(async e=>{if(x.is.frontmatter(e)){const o=await j(e.value,t);n.push(...o)}})),n}((await k.parse(e)).ast,t)}},B={vue:async function(){if(b)return;const e=await F(),t=["@vue","compiler-sfc","dist","compiler-sfc.cjs.js"],n=["vue","compiler-sfc","index.js"],s=function(){try{const{dependencies:e}=g("vue");return[".pnpm",`@vue+compiler-sfc@${v(e["@vue/compiler-sfc"])}`,"node_modules"]}catch(e){return[]}}(),r=[o(process.cwd(),"node_modules",...n)];s.length&&r.push(o(process.cwd(),"node_modules",...s,...t));const i=[...r,o(process.cwd(),"node_modules",...t),...e.map((e=>o(e,...n))),...e.map((e=>o(e,...t)))].map((e=>import(d(e).toString()))),a=await Promise.allSettled(i);for(let e=0;e<a.length;e++){const t=a[e];if("fulfilled"===t.status)return void(b=t.value.default)}throw new Error("No Vue 3 parser available")},svelte:async function(){if(S)return;const e=await F(),t=["svelte","compiler.js"],n=["svelte","src","compiler","index.js"],s=[o(process.cwd(),"node_modules",...n),o(process.cwd(),"node_modules",...t),...e.map((e=>o(e,...n))),...e.map((e=>o(e,...t)))].map((e=>import(d(e).toString()))),r=await Promise.allSettled(s);for(let e=0;e<r.length;e++){const t=r[e];if("fulfilled"===t.status)return void(S=t.value.default)}throw new Error("No Svelte compiler available")},astro:async function(){const e=await F();await Promise.allSettled([D(e),T(e)])}};function L(e){return e.name.split(".").pop()}const A={js:"JavaScript Files",cjs:"CommonJS Files",mjs:"ESModule Files",jsx:"JavaScript Extended Files",ts:"TypeScript Files",tsx:"TypeScript Extended Files",vue:"Vue Single File Component",svelte:"Svelte Single File Component",astro:"Astro Components"};async function K(e,n,{silent:o}){e=e.filter((e=>!e.name.endsWith(".d.ts"))),await async function(e){const t=[...new Set(e.map((e=>L(e))))].map((e=>{try{const t=function(e){return e in B?B[e]:null}(e);return t?t():null}catch(t){throw new Error(`Failed to load compiler for ${A[e]}: ${t.message}`)}})).filter(Boolean);await Promise.all(t)}(e);const s=e.map((async e=>{try{const t=function(e){if(!(e in N))throw new Error(`.${e} files are currently not supported`);return N[e]}(L(e)),o=await t(e.content,n);return o.length?{name:e.name,path:e.path,lineNumbers:o}:null}catch(t){const n=t;throw new Error(`Failed to parse ${e.path}: ${n.message}`)}}));if(!o){return(await Promise.all(s)).filter(Boolean)}const r=await Promise.allSettled(s),i=[];for(const e of r)"rejected"===e.status?console.log(t.yellow(e.reason)):e.value&&i.push(e.value);return i}function I(e,t){const n=e.path.split("/").length,o=t.path.split("/").length;return n>o?1:n<o?-1:e.name>t.name?1:-1}function J(e,n,o){return t.cyanBright(`${{files:"📁",scripts:"📜"}[e]} There are ${o} ${e} in this project that depends on '${n}'`)}function z(e){const t=[];for(const[n,o]of Object.entries(e)){const e=A[n];if(o.length){const n=`📝 ${e}`,s=o.map((e=>[e.name,e.path,e.lineNumbers.join(", ")])),r=p([["Filename","Path","Line Number"],...s]);t.push(n+"\n"+r)}}return t.join("\n").slice(0,-1)}function V(e){const n=[];for(const[o,s]of Object.entries(e)){const e=A[o];if(s.length){const o=`📜 ${e}`,r=s.map((({name:e,path:n,lineNumbers:o})=>t.cyan(` └── ${e}:${o.join(", ")} → ${n}`)));n.push(o+"\n"+r.join("\n"))}}return n.join("\n\n")}function q(e){return e.map((e=>t.cyan(` └── ${e}`))).join("\n")}function W(e){return p([["Script"],e.map((e=>[e]))]).slice(0,-1)}function M(e,t,{format:n}){const o=[J("files",t,e.length)],s={lines:V,table:z},r=function(e){const t={js:[],cjs:[],mjs:[],jsx:[],ts:[],tsx:[],vue:[],svelte:[],astro:[]};for(const n of e)t[n.name.split(".").pop()].push(n);for(const e of Object.values(t))e.sort(I);return t}(e);return e.length&&o.push(s[n](r)),o.join("\n\n")}(async()=>{const n=u.parseSync(),o=e().start();try{const e=n.package;o.text=t.greenBright("Scanning project directory...");const{silent:r,table:a,precheck:l,include:d}=n;l&&(o.text=t.greenBright("Checking package installation..."),function(e,t){if(!(Object.keys(t.dependencies||{}).includes(e)||Object.keys(t.devDependencies||{}).includes(e)||Object.keys(t.peerDependencies||{}).includes(e)))throw new Error(`Package ${e} is not defined in this project`)}(e,h()),await w(e));const p=function(e,t){const n=c.sync(`{${e.join(",")}}`,{silent:!0}),o=[];for(const e of n)try{const t=s(e),n=i(e,"utf-8");o.push({name:t,path:e,content:n})}catch(n){if(t)continue;throw new Error(`Failed to read ${e}`)}return o}(n.files,r);o.text=t.greenBright("Analyzing project for dependency...");const u=[];if(d.includes("files")){const t=await K(p,e,{silent:r});u.push(M(t,e,{format:a?"table":"lines"}))}if(d.includes("scripts")){const t=function(e){const{scripts:t}=h(),{executables:n}=g(e),o=[];if(Object.keys(n).length){const e=`(${Object.keys(n).join("|")})`;for(const[n,s]of Object.entries(t))s.match(e)&&o.push(n)}return o}(e);u.push(function(e,t,{format:n}){const o=[J("scripts",t,e.length)],s={lines:q,table:W};return e.length&&o.push(s[n](e)),o.join("\n")}(t,e,{format:a?"table":"lines"}))}o.succeed(t.greenBright("Analysis completed successfully")),console.log(u.join("\n\n"))}catch(e){const n=e;o.fail(t.redBright(n.message)),console.log(t.cyanBright("Terminating..."))}})(); |
@@ -0,1 +1,13 @@ | ||
# v0.15.0 (Sat Aug 12 2023) | ||
#### 🚀 Enhancement | ||
- feat: add astro support [#62](https://github.com/Namchee/dependent/pull/62) ([@Namchee](https://github.com/Namchee)) | ||
#### Authors: 1 | ||
- Cristopher ([@Namchee](https://github.com/Namchee)) | ||
--- | ||
# v0.14.1 (Sun Aug 06 2023) | ||
@@ -2,0 +14,0 @@ |
{ | ||
"name": "@namchee/dependent", | ||
"version": "0.14.1", | ||
"version": "0.15.0", | ||
"description": "Simple utility CLI tool to analyze which files are using a Node dependency 🚀", | ||
@@ -32,2 +32,3 @@ "repository": "git@github.com:Namchee/dependent.git", | ||
"devDependencies": { | ||
"@astrojs/compiler": "^1.6.3", | ||
"@auto-it/conventional-commits": "^10.37.0", | ||
@@ -34,0 +35,0 @@ "@auto-it/npm": "^10.37.0", |
@@ -28,3 +28,4 @@ # Dependent | ||
5. Vue Single File Components, `.vue` (`.js` and `.ts` scripts only) | ||
6. Svelte Single File Components, `.svelte` (`.js` script only) | ||
6. Svelte Single File Components, `.svelte` | ||
7. Astro Components, `.astro` | ||
@@ -31,0 +32,0 @@ More language support are incoming! Submit your language support ideas [here](https://github.com/Namchee/dependent/issues/new/choose) |
@@ -30,2 +30,3 @@ import yargs from 'yargs'; | ||
'!(node_modules)/**/*.svelte', | ||
'!(node_modules)/**/*.astro', | ||
'*.js', | ||
@@ -38,2 +39,3 @@ '*.mjs', | ||
'*.svelte', | ||
'*.astro', | ||
], | ||
@@ -40,0 +42,0 @@ }) |
@@ -12,4 +12,5 @@ /** | ||
tsx: 'TypeScript Extended Files', | ||
vue: 'Vue\'s Single File Component', | ||
svelte: 'Svelte\'s Single File Component', | ||
vue: 'Vue Single File Component', | ||
svelte: 'Svelte Single File Component', | ||
astro: 'Astro Components' | ||
}; |
import chalk from 'chalk'; | ||
import { getParser } from '@/service/parser'; | ||
import { getParser, getCompiler } from '@/service/parser'; | ||
@@ -12,3 +12,3 @@ import type { | ||
import { getFileExtension } from '@/utils/file'; | ||
import { getGlobalNPMPath, getGlobalYarnPath, getGlobalPnpmPath } from '@/utils/global'; | ||
import { FILE_TYPES } from '@/constant/files'; | ||
@@ -20,9 +20,6 @@ export async function getDependantFiles( | ||
): Promise<DependantFile[]> { | ||
const managerPaths = await Promise.allSettled([ | ||
getGlobalNPMPath(), | ||
getGlobalYarnPath(), | ||
getGlobalPnpmPath(), | ||
]); | ||
// Quickfix, please remove when glob pattern is found | ||
files = files.filter(file => !file.name.endsWith('.d.ts')); | ||
const globs = managerPaths.map(result => result.status === 'fulfilled' ? result.value : ''); | ||
await loadCompilers(files); | ||
@@ -35,3 +32,3 @@ const dependants: Promise<DependantFile | null>[] = files.map( | ||
const parse = getParser(ext); | ||
const dependants = await parse(file.content, dependency, globs); | ||
const dependants = await parse(file.content, dependency); | ||
@@ -56,3 +53,3 @@ if (dependants.length) { | ||
const results = await Promise.all(dependants); | ||
return results.filter(val => val !== null) as DependantFile[]; | ||
return results.filter(Boolean) as DependantFile[]; | ||
} | ||
@@ -75,1 +72,25 @@ | ||
} | ||
async function loadCompilers(files: ProjectFile[]) { | ||
const compilers = [ | ||
...new Set( | ||
files.map(file => getFileExtension(file)) | ||
), | ||
].map((ext: string) => { | ||
try { | ||
const compiler = getCompiler(ext); | ||
if (compiler) { | ||
return compiler(); | ||
} | ||
return null; | ||
} catch (err) { | ||
const error = err as Error; | ||
throw new Error( | ||
`Failed to load compiler for ${FILE_TYPES[ext as keyof typeof FILE_TYPES]}: ${error.message}`, | ||
); | ||
} | ||
}).filter(Boolean); | ||
await Promise.all(compilers); | ||
} |
@@ -48,2 +48,3 @@ import chalk from 'chalk'; | ||
svelte: [], | ||
astro: [], | ||
}; | ||
@@ -88,3 +89,3 @@ | ||
if (extFiles.length) { | ||
const header = `📜 ${alias}`; | ||
const header = `📝 ${alias}`; | ||
@@ -91,0 +92,0 @@ const entries = extFiles.map(file => ([ |
@@ -81,3 +81,3 @@ import { resolve } from 'path'; | ||
throw new Error( | ||
'The current project directory is not a NodeJS-based project', | ||
`Cannot find metadata for dependency ${dependency}`, | ||
); | ||
@@ -84,0 +84,0 @@ } |
@@ -1,6 +0,7 @@ | ||
import { FileParser } from '@/types'; | ||
import { CompilerLoader, FileParser } from '@/types'; | ||
import { getSvelteImportLines } from '@/service/parser/svelte'; | ||
import { getTSImportLines } from '@/service/parser/ts'; | ||
import { getVueImportLines } from '@/service/parser/vue'; | ||
import { getVueImportLines, loadVueCompiler } from '@/service/parser/vue'; | ||
import { getSvelteImportLines, loadSvelteCompiler } from '@/service/parser/svelte'; | ||
import { getAstroImportLines, loadAstroCompiler } from '@/service/parser/astro'; | ||
@@ -19,4 +20,11 @@ /** | ||
svelte: getSvelteImportLines, | ||
astro: getAstroImportLines, | ||
}; | ||
const COMPILER_MAP: Record<string, CompilerLoader> = { | ||
vue: loadVueCompiler, | ||
svelte: loadSvelteCompiler, | ||
astro: loadAstroCompiler, | ||
}; | ||
/** | ||
@@ -37,1 +45,5 @@ * Get the parser function for a file extension | ||
} | ||
export function getCompiler(ext: string): CompilerLoader | null { | ||
return ext in COMPILER_MAP ? COMPILER_MAP[ext] : null; | ||
} |
@@ -1,6 +0,18 @@ | ||
import { describe, it, expect } from 'vitest'; | ||
import { describe, it, expect, beforeAll } from 'vitest'; | ||
import { getSvelteImportLines } from '@/service/parser/svelte'; | ||
import { getSvelteImportLines, loadSvelteCompiler } from '@/service/parser/svelte'; | ||
describe('Svelte parser test', () => { | ||
beforeAll(async () => { | ||
await loadSvelteCompiler(); | ||
}); | ||
it('should be able to parse empty file', async () => { | ||
const content = ``; | ||
const result = await getSvelteImportLines(content, 'lodash'); | ||
expect(result.length).toBe(0); | ||
}); | ||
it('should be able to parse ES module import', async () => { | ||
@@ -19,3 +31,3 @@ const content = `<script> | ||
const result = await getSvelteImportLines(content, 'lodash', []); | ||
const result = await getSvelteImportLines(content, 'lodash'); | ||
@@ -39,3 +51,3 @@ expect(result.length).toBe(1); | ||
const result = await getSvelteImportLines(content, 'lodash', []); | ||
const result = await getSvelteImportLines(content, 'lodash'); | ||
@@ -59,3 +71,3 @@ expect(result.length).toBe(1); | ||
const result = await getSvelteImportLines(content, 'lodash', []); | ||
const result = await getSvelteImportLines(content, 'lodash'); | ||
@@ -79,3 +91,3 @@ expect(result.length).toBe(1); | ||
const result = await getSvelteImportLines(content, 'lodash', []); | ||
const result = await getSvelteImportLines(content, 'lodash'); | ||
@@ -100,3 +112,3 @@ expect(result.length).toBe(1); | ||
const result = await getSvelteImportLines(content, 'foo', []); | ||
const result = await getSvelteImportLines(content, 'foo'); | ||
@@ -124,3 +136,3 @@ expect(result.length).toBe(1); | ||
const result = await getSvelteImportLines(content, 'foo', []); | ||
const result = await getSvelteImportLines(content, 'foo'); | ||
@@ -145,3 +157,3 @@ expect(result.length).toBe(1); | ||
const result = await getSvelteImportLines(content, 'baz', []); | ||
const result = await getSvelteImportLines(content, 'baz'); | ||
@@ -165,3 +177,3 @@ expect(result.length).toBe(0); | ||
const result = await getSvelteImportLines(content, 'bar', []); | ||
const result = await getSvelteImportLines(content, 'bar'); | ||
@@ -168,0 +180,0 @@ expect(result.length).toBe(1); |
@@ -14,6 +14,7 @@ import { resolve } from 'path'; | ||
import { getRootPackage } from '@/utils/package'; | ||
import { getGlobs } from '@/utils/global'; | ||
let compiler: typeof import('svelte/compiler'); | ||
async function loadSvelteCompiler(globs: string[]): Promise<void> { | ||
export async function loadSvelteCompiler(): Promise<void> { | ||
// Do not load the compiler twice | ||
@@ -24,6 +25,12 @@ if (compiler) { | ||
const compilerPath = ['svelte', 'compiler.js']; | ||
const globs = await getGlobs(); | ||
const oldCompilerPath = ['svelte', 'compiler.js']; | ||
const newCompilerPath = ['svelte', 'src', 'compiler', 'index.js']; | ||
const paths = [ | ||
resolve(process.cwd(), 'node_modules', ...compilerPath), | ||
...globs.map(path => resolve(path, ...compilerPath)), | ||
resolve(process.cwd(), 'node_modules', ...newCompilerPath), | ||
resolve(process.cwd(), 'node_modules', ...oldCompilerPath), | ||
...globs.map(path => resolve(path, ...newCompilerPath)), | ||
...globs.map(path => resolve(path, ...oldCompilerPath)), | ||
]; | ||
@@ -56,3 +63,2 @@ | ||
export function parseNode( | ||
svelte: typeof compiler, | ||
sourceNode: BaseNode, | ||
@@ -63,3 +69,3 @@ dependency: string, | ||
svelte.walk(sourceNode, { | ||
compiler.walk(sourceNode, { | ||
enter(node) { | ||
@@ -133,6 +139,5 @@ switch (node.type) { | ||
dependency: string, | ||
globs: string[], | ||
): Promise<number[]> { | ||
if (!compiler) { | ||
await loadSvelteCompiler(globs); | ||
throw new Error('Svelte compiler has not been loaded yet'); | ||
} | ||
@@ -142,5 +147,5 @@ | ||
return [ | ||
...parseNode(compiler, node.instance as BaseNode, dependency), | ||
...parseNode(compiler, node.module as BaseNode, dependency), | ||
...parseNode(node.instance as BaseNode, dependency), | ||
...parseNode(node.module as BaseNode, dependency), | ||
]; | ||
} |
@@ -6,6 +6,14 @@ import { describe, it, expect } from 'vitest'; | ||
describe('TypeScript parser test', () => { | ||
it('should be able to parse empty file', async () =>{ | ||
const content = ``; | ||
const dependant = await getTSImportLines(content, 'express'); | ||
expect(dependant.length).toBe(0); | ||
}); | ||
it('should be able to parse ES modules import', async () =>{ | ||
const content = `import express from 'express';`; | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -19,3 +27,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -29,3 +37,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -39,3 +47,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -49,3 +57,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -59,3 +67,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -69,3 +77,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -76,15 +84,6 @@ expect(dependant.length).toBe(1); | ||
it('should be able to parse combined default and named imports', async () =>{ | ||
const content = `import defaultExport, { export1 } from 'express';`; | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
expect(dependant.length).toBe(1); | ||
expect(dependant[0]).toBe(1); | ||
}); | ||
it('should be able to parse combined default and named imports in separated imports', async () =>{ | ||
const content = `import defaultExport from 'express';\nimport { foo } from 'express';`; | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -98,3 +97,3 @@ expect(dependant.length).toBe(2); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -108,3 +107,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -118,3 +117,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -128,3 +127,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -140,3 +139,3 @@ expect(dependant.length).toBe(1); | ||
const dependant = await getTSImportLines(content, 'express', []); | ||
const dependant = await getTSImportLines(content, 'express'); | ||
@@ -154,3 +153,3 @@ expect(dependant.length).toBe(1); | ||
const dependants = await getTSImportLines(content, 'express', []); | ||
const dependants = await getTSImportLines(content, 'express'); | ||
expect(dependants.length).toBe(1); | ||
@@ -167,3 +166,3 @@ expect(dependants[0]).toBe(3); | ||
const dependants = await getTSImportLines(content, 'express', []); | ||
const dependants = await getTSImportLines(content, 'express'); | ||
expect(dependants.length).toBe(1); | ||
@@ -178,3 +177,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'windicss', []); | ||
const dependants = await getTSImportLines(content, 'windicss'); | ||
expect(dependants.length).toBe(1); | ||
@@ -189,3 +188,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'windicss', []); | ||
const dependants = await getTSImportLines(content, 'windicss'); | ||
expect(dependants.length).toBe(0); | ||
@@ -210,3 +209,3 @@ }); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(1); | ||
@@ -232,3 +231,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(2); | ||
@@ -255,3 +254,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(2); | ||
@@ -271,3 +270,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(1); | ||
@@ -286,3 +285,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(1); | ||
@@ -301,3 +300,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(1); | ||
@@ -322,3 +321,3 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'b', []); | ||
const dependants = await getTSImportLines(content, 'b'); | ||
expect(dependants.length).toBe(1); | ||
@@ -343,3 +342,3 @@ expect(dependants[0]).toBe(6); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(0); | ||
@@ -359,3 +358,3 @@ }); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(1); | ||
@@ -374,3 +373,3 @@ expect(dependants[0]).toBe(3); | ||
const dependants = await getTSImportLines(content, 'react', []); | ||
const dependants = await getTSImportLines(content, 'react'); | ||
expect(dependants.length).toBe(1); | ||
@@ -385,3 +384,4 @@ expect(dependants[0]).toBe(1); | ||
const dependants = await getTSImportLines(content, 'windicss', []); | ||
const dependants = await getTSImportLines(content, 'windicss'); | ||
expect(dependants.length).toBe(1); | ||
@@ -388,0 +388,0 @@ expect(dependants[0]).toBe(1); |
@@ -1,4 +0,1 @@ | ||
import { resolve } from 'path'; | ||
import { pathToFileURL } from 'url'; | ||
import { getRootPackage } from '@/utils/package'; | ||
@@ -9,32 +6,2 @@ | ||
let compiler: typeof import('typescript'); | ||
async function loadTSCompiler(globs: string[]): Promise<void> { | ||
// Do not load the compiler twice | ||
if (compiler) { | ||
return; | ||
} | ||
const compilerPath = ['typescript', 'lib', 'typescript.js']; | ||
const paths = [ | ||
resolve(process.cwd(), 'node_modules', ...compilerPath), | ||
...globs.map(path => resolve(path, ...compilerPath)), | ||
]; | ||
const imports = paths.map(path => import(pathToFileURL(path).toString())); | ||
const compilerImports = await Promise.allSettled(imports); | ||
for (let i = 0; i < compilerImports.length; i++) { | ||
const fileModule = compilerImports[i]; | ||
if (fileModule.status === 'fulfilled') { | ||
compiler = fileModule.value.default as typeof import('typescript'); | ||
return; | ||
} | ||
} | ||
throw new Error('No TypeScript parsers available'); | ||
} | ||
/** | ||
@@ -102,3 +69,3 @@ * Parse TypeScript node for imports to `dependency` | ||
compiler.forEachChild(node, walk); | ||
ts.forEachChild(node, walk); | ||
} | ||
@@ -122,14 +89,9 @@ | ||
dependency: string, | ||
globs: string[], | ||
): Promise<number[]> { | ||
if (!compiler) { | ||
await loadTSCompiler(globs); | ||
} | ||
const node = compiler.createSourceFile( | ||
const node = ts.createSourceFile( | ||
'', | ||
content, | ||
compiler.ScriptTarget.Latest, | ||
ts.ScriptTarget.Latest, | ||
true, | ||
compiler.ScriptKind.TSX, | ||
ts.ScriptKind.TSX, | ||
); | ||
@@ -136,0 +98,0 @@ |
@@ -1,6 +0,37 @@ | ||
import { describe, it, expect } from 'vitest'; | ||
import { describe, it, expect, beforeEach, vi, afterEach, beforeAll } from 'vitest'; | ||
import { getVueImportLines } from '@/service/parser/vue'; | ||
import { getVueImportLines, loadVueCompiler } from '@/service/parser/vue'; | ||
import * as pkgUtils from '@/service/package'; | ||
describe('Vue parser test', () => { | ||
beforeAll(async () => { | ||
await loadVueCompiler(); | ||
}); | ||
beforeEach(() => { | ||
vi.spyOn(pkgUtils, 'resolveDependencyPackageJSON').mockImplementation(() => ({ | ||
name: 'vue', | ||
executables: {}, | ||
scripts: {}, | ||
dependencies: { | ||
'@vue/compiler-sfc': '^3.3.4', | ||
}, | ||
devDependencies: {}, | ||
peerDependencies: {}, | ||
})); | ||
}); | ||
afterEach(() => { | ||
vi.restoreAllMocks(); | ||
}); | ||
it('should be able to parse empty files', async () => { | ||
const content = ``; | ||
const result = await getVueImportLines(content, 'vue'); | ||
expect(result.length).toBe(0); | ||
}); | ||
it('should be able to parse ES module import', async () => { | ||
@@ -25,3 +56,3 @@ const content = `<script> | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -51,3 +82,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -77,3 +108,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -103,3 +134,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -130,3 +161,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'foo', []); | ||
const result = await getVueImportLines(content, 'foo'); | ||
@@ -160,3 +191,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'baz', []); | ||
const result = await getVueImportLines(content, 'baz'); | ||
@@ -187,3 +218,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'baz', []); | ||
const result = await getVueImportLines(content, 'baz'); | ||
@@ -212,3 +243,3 @@ expect(result.length).toBe(0); | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -238,3 +269,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -265,3 +296,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'vue', []); | ||
const result = await getVueImportLines(content, 'vue'); | ||
@@ -284,3 +315,3 @@ expect(result.length).toBe(2); | ||
const result = await getVueImportLines(content, 'lodash', []); | ||
const result = await getVueImportLines(content, 'lodash'); | ||
@@ -316,3 +347,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'lodash', []); | ||
const result = await getVueImportLines(content, 'lodash'); | ||
@@ -352,3 +383,3 @@ expect(result.length).toBe(1); | ||
const result = await getVueImportLines(content, 'vue-class-component', []); | ||
const result = await getVueImportLines(content, 'vue-class-component'); | ||
@@ -355,0 +386,0 @@ expect(result.length).toBe(1); |
@@ -6,5 +6,25 @@ import { resolve } from 'path'; | ||
import { getActualVersion } from '@/utils/package'; | ||
import { getGlobs } from '@/utils/global'; | ||
import { resolveDependencyPackageJSON } from '@/service/package'; | ||
let compiler: typeof import('@vue/compiler-sfc'); | ||
export async function loadVueCompiler(globs: string[]): Promise<void> { | ||
function getPnpmCompilerPath(): string[] { | ||
try { | ||
const { dependencies } = resolveDependencyPackageJSON('vue'); | ||
const compilerVersion = dependencies['@vue/compiler-sfc']; | ||
return [ | ||
'.pnpm', | ||
`@vue+compiler-sfc@${getActualVersion(compilerVersion)}`, | ||
'node_modules', | ||
] | ||
} catch (err) { | ||
return []; | ||
} | ||
} | ||
export async function loadVueCompiler(): Promise<void> { | ||
// Do not load the compiler twice | ||
@@ -15,3 +35,5 @@ if (compiler) { | ||
const oldCompilerPath = [ | ||
const globs = await getGlobs(); | ||
const flatCompilerPath = [ | ||
'@vue', | ||
@@ -30,6 +52,16 @@ 'compiler-sfc', | ||
const pnpmCompilerPath = getPnpmCompilerPath(); | ||
const nonGlobs = [resolve(process.cwd(), 'node_modules', ...newCompilerPath)]; | ||
if (pnpmCompilerPath.length) { | ||
nonGlobs.push( | ||
resolve(process.cwd(), 'node_modules', ...pnpmCompilerPath, ...flatCompilerPath) | ||
) | ||
} | ||
const paths = [ | ||
resolve(process.cwd(), 'node_modules', ...newCompilerPath), | ||
resolve(process.cwd(), 'node_modules', ...oldCompilerPath), | ||
...globs.map(path => resolve(path, ...oldCompilerPath)), | ||
...nonGlobs, | ||
resolve(process.cwd(), 'node_modules', ...flatCompilerPath), | ||
...globs.map(path => resolve(path, ...newCompilerPath)), | ||
...globs.map(path => resolve(path, ...flatCompilerPath)), | ||
]; | ||
@@ -63,6 +95,5 @@ | ||
dependency: string, | ||
globs: string[], | ||
): Promise<number[]> { | ||
if (!compiler) { | ||
await loadVueCompiler(globs); | ||
throw new Error('Vue compiler has not been loaded yet'); | ||
} | ||
@@ -76,4 +107,4 @@ | ||
const lines = await getTSImportLines(script.content, dependency, globs); | ||
// -1, since the `<script>` block shouldn't count | ||
const lines = await getTSImportLines(script.content, dependency); | ||
return lines.map(line => line + startingLine - 1); | ||
@@ -80,0 +111,0 @@ } |
@@ -50,7 +50,8 @@ /** | ||
dependency: string, | ||
globs: string[], | ||
) => Promise<number[]>; | ||
export type CompilerLoader = () => Promise<void>; | ||
export interface LoggerConfig { | ||
format: 'lines' | 'table'; | ||
} |
@@ -5,2 +5,4 @@ import { resolve } from 'path'; | ||
let globs: string[] = []; | ||
/** | ||
@@ -11,3 +13,3 @@ * Get the current npm global installation path | ||
*/ | ||
export async function getGlobalNPMPath(): Promise<string> { | ||
async function getGlobalNPMPath(): Promise<string> { | ||
try { | ||
@@ -36,3 +38,3 @@ const path = await executeCommand( | ||
*/ | ||
export async function getGlobalYarnPath(): Promise<string> { | ||
async function getGlobalYarnPath(): Promise<string> { | ||
try { | ||
@@ -61,3 +63,3 @@ const path = await executeCommand( | ||
*/ | ||
export async function getGlobalPnpmPath(): Promise<string> { | ||
async function getGlobalPnpmPath(): Promise<string> { | ||
try { | ||
@@ -80,1 +82,19 @@ const path = await executeCommand( | ||
} | ||
export async function getGlobs(): Promise<string[]> { | ||
if (globs.length) { | ||
return globs; | ||
} | ||
const managerPaths = await Promise.allSettled([ | ||
getGlobalNPMPath(), | ||
getGlobalYarnPath(), | ||
getGlobalPnpmPath(), | ||
]); | ||
globs = managerPaths | ||
.map(result => result.status === 'fulfilled' ? result.value : '') | ||
.filter(Boolean); | ||
return globs; | ||
} |
import { describe, expect, it } from 'vitest'; | ||
import { getRootPackage } from './package'; | ||
import { getActualVersion, getRootPackage } from './package'; | ||
@@ -33,1 +33,38 @@ describe('getRootPackage', () => { | ||
}); | ||
describe('getInstalledVersion', () => { | ||
it('should replace nothing', () => { | ||
const version = '1.6.3'; | ||
const result = getActualVersion(version); | ||
expect(result).toBe('1.6.3'); | ||
}); | ||
it('should replace ^', () => { | ||
const version = '^1.6.3'; | ||
const result = getActualVersion(version); | ||
expect(result).toBe('1.6.3'); | ||
}); | ||
it('should replace ~', () => { | ||
const version = '~1.6.3'; | ||
const result = getActualVersion(version); | ||
expect(result).toBe('1.6.3'); | ||
}); | ||
it('should replace <', () => { | ||
const version = '<1.6.3'; | ||
const result = getActualVersion(version); | ||
expect(result).toBe('1.6.3'); | ||
}); | ||
it('should replace <> with =', () => { | ||
const version = '>=1.6.3'; | ||
const result = getActualVersion(version); | ||
expect(result).toBe('1.6.3'); | ||
}); | ||
}) |
@@ -17,1 +17,5 @@ /** | ||
} | ||
export function getActualVersion(semver: string): string { | ||
return semver.replace(/^([\^~><>=])+/, ''); | ||
} |
173332
51
2355
158
30