Socket
Socket
Sign inDemoInstall

@namchee/dependent

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@namchee/dependent - npm Package Compare versions

Comparing version 0.7.1 to 0.7.2

25

__tests__/cli.test.ts

@@ -0,3 +1,11 @@

import { jest } from '@jest/globals';
import { cli } from './../src/cli';
jest.useFakeTimers();
afterEach(() => {
jest.clearAllTimers();
});
describe('CLI test', () => {

@@ -10,16 +18,2 @@ it('should be able to accomodate simple usage', () => {

it('should be able to parse module option', () => {
const args = cli.parseSync('express --module');
expect(args.package).toBe('express');
expect(args.module).toBe(true);
});
it('should be able to parse script option', () => {
const args = cli.parseSync('express --script');
expect(args.package).toBe('express');
expect(args.script).toBe(true);
});
it('should be able to parse file pattern option', () => {

@@ -55,3 +49,3 @@ const args = cli.parseSync('express src/**/*.js');

it('should be able to accomodate complex usage', () => {
const args = cli.parseSync('express src/**/*.js bin/**/*.js --module');
const args = cli.parseSync('express src/**/*.js bin/**/*.js');

@@ -61,4 +55,3 @@ expect(args.package).toBe('express');

expect(args.files).toContain('bin/**/*.js');
expect(args.module).toBe(true);
});
});

@@ -0,3 +1,11 @@

import { jest } from '@jest/globals';
import { getJSImportLines } from '../../src/parser/js';
jest.useFakeTimers();
afterEach(() => {
jest.clearAllTimers();
});
describe('ESModule import test', () => {

@@ -103,1 +111,168 @@ it('should be able to parse default imports', () => {

});
describe('React JSX test', () => {
it('should be able to parse default imports', () => {
const content = `import react from 'react';
function Home() {
return <h1>Hello World</h1>;
}
export default Home;`
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse default imports', () => {
const content = `import { useState } from 'react';
function Home() {
const [ping, setPing] = useState(0);
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse aliased imports', () => {
const content = `import { useState as a } from 'react';
function Home() {
const [ping, setPing] = a(0);
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse namespace imports', () => {
const content = `import * as React from 'react';
function Home() {
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse unnamed imports', () => {
const content = `import 'react';
function Home() {
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse dynamic imports', () => {
const content = `const a = import('react');
function Home() {
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse imports nested in code', () => {
const content = `import * as React from 'react';
function Home() {
const isTest = () => {
if (isDevelopment) {
const a = require('b');
}
}
return <h1>Hello world</h1>;
}
export default Home;`;
const dependants = getJSImportLines(content, 'b');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(6);
});
it('should be able to distinguish false alarms', () => {
const content = `const react = "import * as React from 'react';";
function Home() {
const isTest = () => {
if (isDevelopment) {
const a = require('b');
}
}
return <h1>Hello world</h1>;
}
export default Home;`;
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(0);
});
it('should be able to tolerate CommonJS imports', () => {
const content = `import express from 'express';
const react = require('react');
function Home() {
return <h1>Hello world</h1>;
}
export default Home;`;
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(3);
});
it('should be able to parse class-based components', () => {
const content = `import * as React from 'react';
export class Welcome extends React.Component {
render() {
return <h1>Hello world</h1>;
}
}`;
const dependants = getJSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to detect nested modules', () => {
const content = `import { defineConfig } from 'windicss/helpers';
export default defineConfig({});`;
const dependants = getJSImportLines(content, 'windicss');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
});

@@ -0,3 +1,11 @@

import { jest } from '@jest/globals';
import { getTSImportLines } from '../../src/parser/ts';
jest.useFakeTimers();
afterEach(() => {
jest.clearAllTimers();
});
describe('TypeScript parser test', () => {

@@ -121,1 +129,187 @@ it('should be able to parse ES modules import', () => {

});
describe('React TSX test', () => {
it('should be able to parse default imports', () => {
const content = `import React from 'react';
export type HomeProps = {
foo: string;
bar: 'foo';
};
function Home({ foo }: React.PropsWithoutRef<HomeProps>): JSX.Element {
return <h1>{foo}</h1>;
}
export default Home;`;
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse default imports', () => {
const content = `import * as React from 'react';
import { useState } from 'react';
export type HomeProps = {
foo: string;
bar: 'foo';
};
function Home({ foo }: React.PropsWithoutRef<HomeProps>): JSX.Element {
const [baz, setBaz] = useState(foo);
return <h1>{foo}</h1>;
}
export default Home;`;
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(2);
expect(dependants[0]).toBe(1);
expect(dependants[1]).toBe(2);
});
it('should be able to parse aliased imports', () => {
const content = `import * as React from 'react';
import { useState as a } from 'react';
export type HomeProps = {
foo: string;
bar: 'foo';
};
function Home({ foo }: React.PropsWithoutRef<HomeProps>): JSX.Element {
const [baz, setBaz] = a(foo);
return <h1>{foo}</h1>;
}
export default Home;`
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(2);
expect(dependants[0]).toBe(1);
expect(dependants[1]).toBe(2);
});
it('should be able to parse namespace imports', () => {
const content = `import * as React from 'react';
function Home(): JSX.Element {
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse unnamed imports', () => {
const content = `import 'react';
function Home(): JSX.Element {
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse dynamic imports', () => {
const content = `const a = import('react');
function Home(): JSX.Element {
return <h1>{ping}</h1>;
}
export default Home;`
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to parse imports nested in code', () => {
const content = `import * as React from 'react';
function Home(): JSX.Element {
const isTest = () => {
if (isDevelopment) {
const a = require('b');
}
}
return <h1>Hello world</h1>;
}
export default Home;`;
const dependants = getTSImportLines(content, 'b');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(6);
});
it('should be able to distinguish false alarms', () => {
const content = `const react = "import * as React from 'react';";
function Home(): JSX.Element {
const isTest = () => {
if (isDevelopment) {
const a = require('b');
}
}
return <h1>Hello world</h1>;
}
export default Home;`;
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(0);
});
it('should be able to tolerate CommonJS imports', () => {
const content = `import express from 'express';
const react = require('react');
function Home(): JSX.Element {
return <h1>Hello world</h1>;
}
export default Home;`;
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(3);
});
it('should be able to parse class-based components', () => {
const content = `import * as React from 'react';
export class Welcome extends React.Component {
render() {
return <h1>Hello world</h1>;
}
}`;
const dependants = getTSImportLines(content, 'react');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
it('should be able to detect nested modules', () => {
const content = `import { defineConfig } from 'windicss/helpers';
export default defineConfig({});`;
const dependants = getTSImportLines(content, 'windicss');
expect(dependants.length).toBe(1);
expect(dependants[0]).toBe(1);
});
});

@@ -0,4 +1,12 @@

import { jest } from '@jest/globals';
import { getDependantFiles } from '../src/import';
import { ProjectFile } from './../src/types';
jest.useFakeTimers();
afterEach(() => {
jest.clearAllTimers();
});
describe('Parser tolerance test', () => {

@@ -5,0 +13,0 @@ it('should throw an error when silent is false', () => {

#!/usr/bin/env node
import e from"ora";import t from"chalk";import n from"yargs";import{hideBin as r}from"yargs/helpers";import{resolve as s,basename as i}from"path";import{existsSync as o,readFileSync as a}from"fs";import{spawn as c}from"child_process";import l from"glob";import{parse as p,Parser as d}from"acorn";import{simple as u,base as g}from"acorn-walk";import h from"typescript";import m from"acorn-jsx";function f(e,t,n,r){return new(n||(n=Promise))((function(s,i){function o(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,a)}c((r=r.apply(e,t||[])).next())}))}const y=n(r(process.argv)).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|__tests__|test)/**/*!(.spec|test).js","!(node_modules|__tests__|test)/**/*!(.spec|test).mjs","!(node_modules|__tests__|test)/**/*!(.spec|test).ts","!(node_modules|__tests__|test)/**/*!(.spec|test).jsx","!(node_modules|__tests__|test)/**/*!(.spec|test).tsx","*!(.spec|test).js","*!(.spec|test).mjs","*!(.spec|test).ts","*!(.spec|test).jsx","*!(.spec|test).tsx"]}).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}});const x=d.extend(m());const S={js:function(e,t){return function(e,t){const n=[];return u(e,{ImportExpression(e){var r;const s=e;"Literal"===s.source.type&&(null===(r=s.source.value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)},ImportDeclaration(e){var r;const s=e;"Literal"===s.source.type&&(null===(r=s.source.value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)},CallExpression(e){var r;const s=e;"Identifier"===s.callee.type&&"require"===s.callee.name&&"Literal"===s.arguments[0].type&&(null===(r=s.arguments[0].value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)}}),n}(p(e,{ecmaVersion:"latest",locations:!0,allowHashBang:!0,sourceType:"module"}),t)},ts:function(e,t){return function(e,t){const n=[],r=s=>{switch(s.kind){case h.SyntaxKind.ImportDeclaration:{const r=s.moduleSpecifier;r.kind===h.SyntaxKind.StringLiteral&&r.getText().slice(1,-1).startsWith(t)&&n.push(e.getLineAndCharacterOfPosition(s.getStart()).line+1);break}case h.SyntaxKind.CallExpression:{const r=s,i=r.expression,o=r.arguments,a=i.kind===h.SyntaxKind.ImportKeyword&&1===o.length&&o[0].kind===h.SyntaxKind.StringLiteral&&o[0].getText().slice(1,-1).startsWith(t),c=i.kind===h.SyntaxKind.Identifier&&"require"===i.getText()&&1===o.length&&o[0].kind===h.SyntaxKind.StringLiteral&&o[0].getText().slice(1,-1).startsWith(t);(a||c)&&n.push(e.getLineAndCharacterOfPosition(s.getStart()).line+1);break}}h.forEachChild(s,r)};return r(e),n}(h.createSourceFile("",e,h.ScriptTarget.Latest,!0),t)},jsx:function(e,t){return function(e,t){const n=[];return u(e,{ImportExpression(e){var r;const s=e;"Literal"===s.source.type&&(null===(r=s.source.value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)},ImportDeclaration(e){var r;const s=e;"Literal"===s.source.type&&(null===(r=s.source.value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)},CallExpression(e){var r;const s=e;"Identifier"===s.callee.type&&"require"===s.callee.name&&"Literal"===s.arguments[0].type&&(null===(r=s.arguments[0].value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)}},Object.assign(Object.assign({},g),{JSXElement:()=>{}})),n}(x.parse(e,{ecmaVersion:"latest",locations:!0,allowHashBang:!0,sourceType:"module"}),t)},tsx:function(e,t){return function(e,t){const n=[],r=s=>{switch(s.kind){case h.SyntaxKind.ImportDeclaration:{const r=s.moduleSpecifier;r.kind===h.SyntaxKind.StringLiteral&&r.getText().slice(1,-1).startsWith(t)&&n.push(e.getLineAndCharacterOfPosition(s.getStart()).line+1);break}case h.SyntaxKind.CallExpression:{const r=s,i=r.expression,o=r.arguments,a=i.kind===h.SyntaxKind.ImportKeyword&&1===o.length&&o[0].kind===h.SyntaxKind.StringLiteral&&o[0].getText().slice(1,-1).startsWith(t),c=i.kind===h.SyntaxKind.Identifier&&"require"===i.getText()&&1===o.length&&o[0].kind===h.SyntaxKind.StringLiteral&&o[0].getText().slice(1,-1).startsWith(t);(a||c)&&n.push(e.getLineAndCharacterOfPosition(s.getStart()).line+1);break}}h.forEachChild(s,r)};return r(e),n}(h.createSourceFile("",e,h.ScriptTarget.Latest,!0,h.ScriptKind.TSX),t)}};function v(e){if(!(e in S))throw new Error(`.${e} files are currently not supported`);return S[e]}function k(e,n,r){console.log("\n"+t.cyanBright(`📦 There are ${e.length} files in this project that depends on '${n}'`)),e.length&&(r?function(e){const t=e.map((e=>({"File name":e.name,"File path":e.path,"Line number":e.lineNumbers.join(", ")})));console.table(t)}(e):function(e){e.forEach((({name:e,path:n,lineNumbers:r})=>{console.log(t.cyan(` └── ${e}:${r.join(", ")} → ${n}`))}))}(e))}f(void 0,void 0,void 0,(function*(){const n=y.parseSync(),r=e().start();try{const e=n.package;r.text=t.greenBright("Scanning project directory...");const p=function(){const e=s(process.cwd(),"package.json");if(!o(e))throw new Error("The current project directory is not a NodeJS-based project");try{const t=JSON.parse(a(e,"utf-8"));return{name:t.name,dependencies:t.dependencies,devDependencies:t.devDependencies,peerDependencies:t.peerDependencies}}catch(e){throw new Error("Invalid package.json schema")}}(),d=n.silent,u=n.table;r.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,p),yield function(e){return new Promise(((t,n)=>{c(/^win/.test(process.platform)?"npm.cmd":"npm",["ls",e]).stdout.on("data",(r=>{r.includes(e)&&0!==r.lastIndexOf(e)?t():n(new Error(`Package ${e} is not installed in this project`))}))}))}(e),r.text=t.greenBright("Analyzing package dependency...");const g=function(e,t,{silent:n}){const r=[];for(const s of e){let e=s.name.split(".").pop();"mjs"===e&&(e="js");try{const n=v(e)(s.content,t);n.length&&r.push({name:s.name,path:s.path,lineNumbers:n})}catch(e){const t=e;if(n)continue;throw new Error(`Failed to parse ${s.path}: ${t.message}`)}}return r}(function(e,t){const n=l.sync(`{${e.join(",")}}`,{silent:!0}),r=[];for(const e of n)try{const t=i(e),n=a(e,"utf-8");r.push({name:t,path:e,content:n})}catch(n){if(t)continue;throw new Error(`Failed to read ${e}`)}return r}(n.files,d),e,{silent:d});r.succeed(t.greenBright("Analysis completed successfully")),k(g,e,u)}catch(e){const n=e;r.fail(t.redBright(n.message)),console.log(t.cyanBright("Terminating..."))}}));
import e from"ora";import t from"chalk";import n from"yargs";import r,{resolve as o,basename as i}from"path";import{existsSync as s,readFileSync as a}from"fs";import{spawn as c}from"child_process";import l from"glob";import{Parser as p}from"acorn";import{simple as d,base as u}from"acorn-walk";import m from"acorn-jsx";import g from"global-dirs";const f=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","*.js","*.mjs","*.ts","*.jsx","*.tsx"]}).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}});const h=p.extend(m());function y(e,t){return function(e,t){const n=[];return d(e,{ImportExpression(e){var r;const o=e;"Literal"===o.source.type&&(null===(r=o.source.value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)},ImportDeclaration(e){var r;const o=e;"Literal"===o.source.type&&(null===(r=o.source.value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)},CallExpression(e){var r;const o=e;"Identifier"===o.callee.type&&"require"===o.callee.name&&"Literal"===o.arguments[0].type&&(null===(r=o.arguments[0].value)||void 0===r?void 0:r.toString().startsWith(t))&&n.push(e.loc.start.line)}},Object.assign(Object.assign({},u),{JSXElement:()=>{}})),n}(h.parse(e,{ecmaVersion:"latest",locations:!0,allowHashBang:!0,sourceType:"module"}),t)}let b;try{const e=["typescript","lib","typescript.js"],t=new URL(r.posix.resolve("node_modules",...e),import.meta.url),n=new URL(r.posix.resolve(g.npm.packages,...e),import.meta.url),o=new URL(r.posix.resolve(g.yarn.packages,...e),import.meta.url),i=await Promise.allSettled([import(t.toString()),import(n.toString()),import(o.toString())]);for(const e of i)if("fulfilled"===e.status){b=e.value.default;break}}catch(e){}function j(e,t){if(!b)throw new Error("No typescript parsers available");return function(e,t){const n=[],r=o=>{switch(o.kind){case b.SyntaxKind.ImportDeclaration:{const r=o.moduleSpecifier;10===r.kind&&r.getText().slice(1,-1).startsWith(t)&&n.push(e.getLineAndCharacterOfPosition(o.getStart()).line+1);break}case b.SyntaxKind.CallExpression:{const r=o,i=r.expression,s=r.arguments,a=i.kind===b.SyntaxKind.ImportKeyword&&1===s.length&&s[0].kind===b.SyntaxKind.StringLiteral&&s[0].getText().slice(1,-1).startsWith(t),c=i.kind===b.SyntaxKind.Identifier&&"require"===i.getText()&&1===s.length&&s[0].kind===b.SyntaxKind.StringLiteral&&s[0].getText().slice(1,-1).startsWith(t);(a||c)&&n.push(e.getLineAndCharacterOfPosition(o.getStart()).line+1);break}}b.forEachChild(o,r)};return r(e),n}(b.createSourceFile("",e,b.ScriptTarget.Latest,!0,b.ScriptKind.TSX),t)}const k={js:y,ts:j,jsx:y,tsx:j};function x(e){if(!(e in k))throw new Error(`.${e} files are currently not supported`);return k[e]}function w(e,n,r){console.log("\n"+t.cyanBright(`📦 There are ${e.length} files in this project that depends on '${n}'`)),e.length&&(r?function(e){const t=e.map((e=>({"File name":e.name,"File path":e.path,"Line number":e.lineNumbers.join(", ")})));console.table(t)}(e):function(e){e.forEach((({name:e,path:n,lineNumbers:r})=>{console.log(t.cyan(` └── ${e}:${r.join(", ")} → ${n}`))}))}(e))}(async()=>{const n=f.parseSync(),r=e().start();try{const e=n.package;r.text=t.greenBright("Scanning project directory...");const p=function(){const e=o(process.cwd(),"package.json");if(!s(e))throw new Error("The current project directory is not a NodeJS-based project");try{const t=JSON.parse(a(e,"utf-8"));return{name:t.name,dependencies:t.dependencies,devDependencies:t.devDependencies,peerDependencies:t.peerDependencies}}catch(e){throw new Error("Invalid package.json schema")}}(),d=n.silent,u=n.table;r.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,p),await function(e){return new Promise(((t,n)=>{c(/^win/.test(process.platform)?"npm.cmd":"npm",["ls",e]).stdout.on("data",(r=>{r.includes(e)&&0!==r.lastIndexOf(e)?t():n(new Error(`Package ${e} is not installed in this project`))}))}))}(e);const m=function(e,t){const n=l.sync(`{${e.join(",")}}`,{silent:!0}),r=[];for(const e of n)try{const t=i(e),n=a(e,"utf-8");r.push({name:t,path:e,content:n})}catch(n){if(t)continue;throw new Error(`Failed to read ${e}`)}return r}(n.files,d);r.text=t.greenBright("Analyzing package dependency...");const g=function(e,t,{silent:n}){const r=[];for(const o of e){let e=o.name.split(".").pop();"mjs"===e&&(e="js");try{const n=x(e)(o.content,t);n.length&&r.push({name:o.name,path:o.path,lineNumbers:n})}catch(e){const t=e;if(n)continue;throw new Error(`Failed to parse ${o.path}: ${t.message}`)}}return r}(m,e,{silent:d});r.succeed(t.greenBright("Analysis completed successfully")),w(g,e,u)}catch(e){const n=e;r.fail(t.redBright(n.message)),console.log(t.cyanBright("Terminating..."))}})();

@@ -0,1 +1,15 @@

# v0.7.2 (Sun Aug 29 2021)
#### 🐛 Bug Fix
- fix: Reconfigure jest to be able to execute native ES modules tests [#35](https://github.com/Namchee/dependent/pull/35) ([@Namchee](https://github.com/Namchee))
- refactor: Migrate typescript parser into external packages [#32](https://github.com/Namchee/dependent/pull/32) ([@Namchee](https://github.com/Namchee))
- refactor: Unify js extended parsers with base parserr [#30](https://github.com/Namchee/dependent/pull/30) ([@Namchee](https://github.com/Namchee))
#### Authors: 1
- Cristopher ([@Namchee](https://github.com/Namchee))
---
# v0.7.1 (Sun Aug 08 2021)

@@ -2,0 +16,0 @@

@@ -5,8 +5,17 @@ import type { Config } from '@jest/types';

const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
// https://jestjs.io/docs/ecmascript-modules
transform: {},
verbose: true,
transform: {},
// https://kulshekhar.github.io/ts-jest/docs/guides/esm-support/#use-esm-presets; 'manual configuration' didn't work
preset: 'ts-jest/presets/js-with-ts-esm',
extensionsToTreatAsEsm: ['.ts'],
timers: 'fake',
maxWorkers: 2,
globals: {
'ts-jest': {
useESM: true,
},
},
};
export default config;
{
"name": "@namchee/dependent",
"version": "0.7.1",
"version": "0.7.2",
"description": "Simple utility CLI tool to analyze which files are using a Node dependency 🚀",

@@ -21,4 +21,4 @@ "repository": "git@github.com:Namchee/dependent.git",

"build:watch": "rollup -c rollup.config.js -w",
"test": "jest",
"test:watch": "jest --watch"
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch"
},

@@ -31,4 +31,4 @@ "dependencies": {

"glob": "^7.1.7",
"global-dirs": "^3.0.0",
"ora": "^5.4.1",
"typescript": "^4.3.5",
"yargs": "^17.0.1"

@@ -48,2 +48,3 @@ },

"auto": "^10.30.0",
"cross-env": "^7.0.3",
"eslint": "^7.29.0",

@@ -54,3 +55,3 @@ "eslint-config-google": "^0.14.0",

"eslint-plugin-jsdoc": "^35.4.0",
"jest": "^27.0.6",
"jest": "^27.1.0",
"prettier": "^2.3.2",

@@ -63,3 +64,4 @@ "rollup": "^2.56.0",

"stylelint-config-standard": "^22.0.0",
"ts-jest": "^27.0.4"
"ts-jest": "^27.0.5",
"typescript": "^4.3.5"
},

@@ -66,0 +68,0 @@ "engines": {

@@ -39,2 +39,8 @@ # Dependent

Alternatively, you can execute `dependent` without installing it by using `npx`. Below is the example of executing `dependent` with `npx`
```bash
npx @namchee/dependent foo
```
> This package can only be executed on NodeJS 12 or later.

@@ -41,0 +47,0 @@

@@ -13,5 +13,4 @@ import typescript from 'rollup-plugin-typescript2';

output: [
// will drop this later
{ file: 'bin/index.js', format: 'es' },
],
}
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';

@@ -7,3 +6,3 @@ /**

*/
export const cli = yargs(hideBin(process.argv))
export const cli = yargs(process.argv.slice(2))
.scriptName('dependent')

@@ -26,12 +25,12 @@ .command(

default: [
'!(node_modules|__tests__|test)/**/*!(.spec|test).js',
'!(node_modules|__tests__|test)/**/*!(.spec|test).mjs',
'!(node_modules|__tests__|test)/**/*!(.spec|test).ts',
'!(node_modules|__tests__|test)/**/*!(.spec|test).jsx',
'!(node_modules|__tests__|test)/**/*!(.spec|test).tsx',
'*!(.spec|test).js',
'*!(.spec|test).mjs',
'*!(.spec|test).ts',
'*!(.spec|test).jsx',
'*!(.spec|test).tsx',
'!(node_modules)/**/*.js',
'!(node_modules)/**/*.mjs',
'!(node_modules)/**/*.ts',
'!(node_modules)/**/*.jsx',
'!(node_modules)/**/*.tsx',
'*.js',
'*.mjs',
'*.ts',
'*.jsx',
'*.tsx',
],

@@ -38,0 +37,0 @@ })

import { getParser } from './parser';
import { DependantFile, ParserOptions, ProjectFile } from './types';
import type { DependantFile, ParserOptions, ProjectFile } from './types';

@@ -11,4 +11,2 @@ /**

* @param {ParserOptions} options Parsing options
* @param {boolean} options.module `true` if all files should
* be parsed as ES modules, `false` otherwise.
* @param {boolean} options.silent `true` if the parser

@@ -34,3 +32,2 @@ * should ignore invalid files, `false` otherwise.

const parse = getParser(ext);
const isDependant = parse(file.content, dependency);

@@ -40,3 +37,3 @@

dependant.push(
{ name: file.name, path: file.path, lineNumbers: isDependant }
{ name: file.name, path: file.path, lineNumbers: isDependant },
);

@@ -43,0 +40,0 @@ }

@@ -15,3 +15,2 @@ #!/usr/bin/env node

const args = cli.parseSync();
const spinner = ora().start();

@@ -33,5 +32,6 @@

const files = getProjectFiles(args.files, silent);
spinner.text = chalk.greenBright('Analyzing package dependency...');
const files = getProjectFiles(args.files, silent);
const dependant = getDependantFiles(

@@ -38,0 +38,0 @@ files,

@@ -5,4 +5,2 @@ import { FileParser } from '../types';

import { getTSImportLines } from './ts';
import { getJSXImportLines } from './jsx';
import { getTSXImportLines } from './tsx';

@@ -13,7 +11,7 @@ /**

*/
const PARSER_MAP: Record<string, FileParser> = {
const PARSER_FUNCTIONS: Record<string, FileParser> = {
js: getJSImportLines,
ts: getTSImportLines,
jsx: getJSXImportLines,
tsx: getTSXImportLines,
jsx: getJSImportLines,
tsx: getTSImportLines,
};

@@ -30,7 +28,7 @@

export function getParser(ext: string): FileParser {
if (!(ext in PARSER_MAP)) {
if (!(ext in PARSER_FUNCTIONS)) {
throw new Error(`.${ext} files are currently not supported`);
}
return PARSER_MAP[ext];
return PARSER_FUNCTIONS[ext];
}

@@ -1,4 +0,6 @@

import { parse } from 'acorn';
import { simple } from 'acorn-walk';
import { Parser } from 'acorn';
import { simple, base } from 'acorn-walk';
import jsx from 'acorn-jsx';
import type { Node } from 'acorn';

@@ -12,2 +14,4 @@ import type {

const parser = Parser.extend(jsx());
/**

@@ -62,2 +66,7 @@ * Parse native JavaScript nodes for imports to `dependency`

},
}, {
...base,
JSXElement: () => {
// empty
},
});

@@ -80,3 +89,3 @@

): number[] {
const node: Node = parse(content, {
const node: Node = parser.parse(content, {
ecmaVersion: 'latest',

@@ -83,0 +92,0 @@ locations: true,

@@ -1,7 +0,48 @@

import ts from 'typescript';
import globalDirectories from 'global-dirs';
import path from 'path';
import type {
SourceFile,
Node,
ImportDeclaration,
CallExpression,
} from 'typescript';
let ts: typeof import('typescript');
try {
const basePath = ['typescript', 'lib', 'typescript.js'];
const localPath = new URL(
path.posix.resolve('node_modules', ...basePath),
import.meta.url,
);
const npmPath = new URL(
path.posix.resolve(globalDirectories.npm.packages, ...basePath),
import.meta.url,
);
const yarnPath = new URL(
path.posix.resolve(globalDirectories.yarn.packages, ...basePath),
import.meta.url,
);
const imports = await Promise.allSettled([
import(localPath.toString()),
import(npmPath.toString()),
import(yarnPath.toString()),
]);
for (const impor of imports) {
if (impor.status === 'fulfilled') {
ts = impor.value.default as typeof import('typescript');
break;
}
}
} catch (err) {
/* ignore for now */
}
/**
* Parse TypeScript node for imports to `dependency`
*
* @param {ts.SourceFile} sourceNode AST representation of the file
* @param {SourceFile} sourceNode AST representation of the file
* @param {string} dependency Package name

@@ -12,3 +53,3 @@ * @returns {number[]} List of line numbers where `dependency`

function parseNode(
sourceNode: ts.SourceFile,
sourceNode: SourceFile,
dependency: string,

@@ -18,10 +59,10 @@ ): number[] {

const walk = (node: ts.Node) => {
const walk = (node: Node) => {
switch (node.kind) {
case ts.SyntaxKind.ImportDeclaration: {
const specifier = (node as ts.ImportDeclaration)
const specifier = (node as ImportDeclaration)
.moduleSpecifier;
if (
specifier.kind === ts.SyntaxKind.StringLiteral &&
specifier.kind === 10 &&
specifier.getText().slice(1, -1).startsWith(dependency)

@@ -38,3 +79,3 @@ ) {

case ts.SyntaxKind.CallExpression: {
const callExpr = node as ts.CallExpression;
const callExpr = node as CallExpression;

@@ -85,2 +126,6 @@ const expression = callExpr.expression;

): number[] {
if (!ts) {
throw new Error('No typescript parsers available');
}
const node = ts.createSourceFile(

@@ -91,2 +136,3 @@ '',

true,
ts.ScriptKind.TSX,
);

@@ -93,0 +139,0 @@

@@ -5,3 +5,4 @@ {

"outDir": "./bin",
"module": "es2020"
"target": "es2017",
"module": "esnext"
},

@@ -12,4 +13,5 @@ "include": [

"exclude": [
"node_modules"
"node_modules",
"**/*.test.ts"
]
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc