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.1.1 to 0.2.0

__tests__/parser/cjs.test.ts

355

__tests__/project.test.ts

@@ -37,356 +37,1 @@ import { getDependantFiles } from '../src/import';

});
describe('CommonJS import test', () => {
it('should be able to distinguish CommonJS imports', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: 'const express = require(\'express\'); const app = express();',
},
];
const dependants = getDependantFiles(files, 'express', {
module: false,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish imports nested in code', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `function test() {
let a;
if (process.env.NODE_ENV === 'development') {
const b = require('express');
a = b.json();
} else {
a = () => {};
}
return a;
}`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: false,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish actually dependant files', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `function test() {
let a;
if (process.env.NODE_ENV === 'development') {
const b = require('express');
a = b.json();
} else {
a = () => {};
}
return a;
}`,
},
{
name: 'c.js',
path: 'b/c.js',
content: `exports.a = 'b'`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: false,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish false alarms', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `function test() {
let a;
if (process.env.NODE_ENV === 'development') {
const b = require('express');
a = b.json();
} else {
a = () => {};
}
return a;
}`,
},
{
name: 'c.js',
path: 'b/c.js',
content: `const a = "require('express')";`,
},
{
name: 'd.js',
path: 'b/d.js',
content: `const a = require('babel');`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: false,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to parse .mjs correctly', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `function test() {
let a;
if (process.env.NODE_ENV === 'development') {
const b = require('express');
a = b.json();
} else {
a = () => {};
}
return a;
}`,
},
{
name: 'c.mjs',
path: 'b/c.mjs',
content: `import express from 'express';`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: false,
silent: false,
});
expect(dependants.length).toBe(2);
expect(dependants[0].name).toBe('a.js');
expect(dependants[1].name).toBe('c.mjs');
});
it('should be able to parse shebanged files', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `#!/usr/bin/env node
const express = require('express');
const app = express();`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: false,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
});
describe('ESModule import test', () => {
it('should be able to distinguish default imports', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: 'import express from \'express\'; const app = express();',
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish named imports', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: 'import { json } from \'express\'; app.use(json())',
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish unnamed imports', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: 'import \'express\';',
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish all-module import', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: 'import * as express from \'express\';',
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish dynamic imports', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: 'const a = import(\'express\');',
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to distinguish module imports nested in code', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `(async () => {
if (somethingIsTrue) {
await import('express');
}
})();`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
expect(dependants[0].lineNumbers[0]).toBe(3);
});
it('should be able to distinguish false alarms', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `import express from 'express';
function test() {
let a;
if (process.env.NODE_ENV === 'development') {
a = express.json();
} else {
a = () => {};
}
return a;
}`,
},
{
name: 'c.js',
path: 'b/c.js',
content: `const a = "import express from 'express'";`,
},
{
name: 'd.js',
path: 'b/d.js',
content: `import babel from 'babel'`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to parse shebanged files', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `#!/usr/bin/env node
import express from 'express';
const app = express();`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
it('should be able to tolerate CommonJS imports', () => {
const files: ProjectFile[] = [
{
name: 'a.js',
path: 'b/a.js',
content: `const express = require('express');
const app = express();`,
},
];
const dependants = getDependantFiles(files, 'express', {
module: true,
silent: false,
});
expect(dependants.length).toBe(1);
expect(dependants[0].name).toBe('a.js');
});
});

14

bin/cli.js

@@ -21,8 +21,10 @@ "use strict";

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

@@ -29,0 +31,0 @@ })

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDependantFiles = void 0;
const acorn_1 = require("acorn");
const acorn_walk_1 = require("acorn-walk");
function getESModulesImportLines(baseNode, dependency) {
const lines = [];
acorn_walk_1.simple(baseNode, {
ImportExpression(node) {
const importExpr = node;
if (importExpr.source.type === 'Literal' &&
importExpr.source.value === dependency) {
lines.push(node.loc.start.line);
}
},
ImportDeclaration(node) {
const importDec = node;
if (importDec.source.type === 'Literal' &&
importDec.source.value === dependency) {
lines.push(node.loc.start.line);
}
},
CallExpression(node) {
const callExpr = node;
if (callExpr.callee.type === 'Identifier' &&
callExpr.callee.name === 'require' &&
callExpr.arguments[0].type === 'Literal' &&
callExpr.arguments[0].value === dependency) {
lines.push(node.loc.start.line);
}
},
});
return lines;
}
function getCommonJSImportLines(baseNode, dependency) {
const lines = [];
acorn_walk_1.simple(baseNode, {
CallExpression(node) {
const callExpr = node;
if (callExpr.callee.type === 'Identifier' &&
callExpr.callee.name === 'require' &&
callExpr.arguments[0].type === 'Literal' &&
callExpr.arguments[0].value === dependency) {
lines.push(node.loc.start.line);
}
},
});
return lines;
}
const parser_1 = require("./parser");
function getDependantFiles(files, dependency, { module, silent }) {
const baseOptions = {
ecmaVersion: 'latest',
locations: true,
allowHashBang: true,
};
const baseReader = module ? getESModulesImportLines : getCommonJSImportLines;
const dependant = [];
for (const file of files) {
let source = module ? 'module' : 'script';
let reader = baseReader;
if (file.name.endsWith('mjs')) {
reader = getESModulesImportLines;
source = 'module';
}
else if (file.name.endsWith('cjs')) {
reader = getCommonJSImportLines;
source = 'script';
}
const ext = file.name.endsWith('js') ?
module ? 'mjs' : 'cjs' :
file.name.split('.')[1];
try {
const node = acorn_1.parse(file.content, Object.assign(Object.assign({}, baseOptions), { sourceType: source }));
const isDependant = reader(node, dependency);
const parse = parser_1.getParser(ext);
const isDependant = parse(file.content, dependency);
if (isDependant.length) {

@@ -73,0 +15,0 @@ dependant.push({ name: file.name, path: file.path, lineNumbers: isDependant });

@@ -0,1 +1,17 @@

# v0.2.0 (Fri Jul 30 2021)
#### 🚀 Enhancement
- feat: Add TypeScript support [#3](https://github.com/Namchee/dependent/pull/3) ([@Namchee](https://github.com/Namchee))
#### 🐛 Bug Fix
- docs: Fix npm package scope badge [#2](https://github.com/Namchee/dependent/pull/2) ([@Namchee](https://github.com/Namchee))
#### Authors: 1
- Cristopher ([@Namchee](https://github.com/Namchee))
---
# v0.1.1 (Thu Jul 29 2021)

@@ -2,0 +18,0 @@

{
"name": "@namchee/dependent",
"version": "0.1.1",
"version": "0.2.0",
"description": "Simple utility CLI tool to analyze which files are using a Node dependency 🚀",

@@ -29,2 +29,3 @@ "repository": "git@github.com:Namchee/dependent.git",

"ora": "^5.4.1",
"typescript": "^4.3.5",
"yargs": "^17.0.1"

@@ -52,4 +53,3 @@ },

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

@@ -56,0 +56,0 @@ "publishConfig": {

# Dependent
[![NPM package version](https://img.shields.io/npm/v/namchee/dependent)](https://www.npmjs.com/package/@namchee/dependent) [![Code Style: Google](https://img.shields.io/badge/code%20style-google-blueviolet.svg)](https://github.com/google/gts) ![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg) ![Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/namchee/telepon)
[![NPM package version](https://img.shields.io/npm/v/@namchee/dependent)](https://www.npmjs.com/package/@namchee/dependent) [![Code Style: Google](https://img.shields.io/badge/code%20style-google-blueviolet.svg)](https://github.com/google/gts) ![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg) ![Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/namchee/telepon)

@@ -5,0 +5,0 @@ Dependent is a simple utility CLI to find out which files in your JavaScript project is using a certain dependency. 🚀

@@ -23,8 +23,10 @@ import yargs from 'yargs';

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

@@ -31,0 +33,0 @@ })

@@ -1,99 +0,6 @@

import { parse, Options, Node, SourceLocation } from 'acorn';
import { simple } from 'acorn-walk';
import { getParser } from './parser';
import type {
ImportDeclaration,
ImportExpression,
CallExpression,
} from 'estree';
import { DependantFile, ParserOptions, ProjectFile } from './types';
/**
* Analyze ES modules for all imports to `dependency`
*
* @param {Node} baseNode AST representation of the file
* @param {string} dependency Package name
* @returns {number[]} List of line numbers where `dependency`
* is imported.
*/
function getESModulesImportLines(
baseNode: Node,
dependency: string,
): number[] {
const lines: number[] = [];
simple(baseNode, {
ImportExpression(node: Node) {
const importExpr = node as unknown as ImportExpression;
if (
importExpr.source.type === 'Literal' &&
importExpr.source.value === dependency
) {
lines.push((node.loc as SourceLocation).start.line);
}
},
ImportDeclaration(node: Node) {
const importDec = node as unknown as ImportDeclaration;
if (
importDec.source.type === 'Literal' &&
importDec.source.value === dependency
) {
lines.push((node.loc as SourceLocation).start.line);
}
},
CallExpression(node: Node) {
const callExpr = node as unknown as CallExpression;
if (
callExpr.callee.type === 'Identifier' &&
callExpr.callee.name === 'require' &&
callExpr.arguments[0].type === 'Literal' &&
callExpr.arguments[0].value === dependency
) {
lines.push((node.loc as SourceLocation).start.line);
}
},
});
return lines;
}
/**
* Analyze CommonJS modules for all imports to `dependency`
*
* @param {Node} baseNode AST representation of the file
* @param {string} dependency Package name
* @returns {number[]} List of line numbers where `dependency`
* is imported.
*/
function getCommonJSImportLines(
baseNode: Node,
dependency: string,
): number[] {
const lines: number[] = [];
simple(baseNode, {
CallExpression(node: Node) {
const callExpr = node as unknown as CallExpression;
if (
callExpr.callee.type === 'Identifier' &&
callExpr.callee.name === 'require' &&
callExpr.arguments[0].type === 'Literal' &&
callExpr.arguments[0].value === dependency
) {
lines.push((node.loc as SourceLocation).start.line);
}
},
});
return lines;
}
/**
* Analyze all relevant files for imports to `dependency`

@@ -115,30 +22,13 @@ *

): DependantFile[] {
const baseOptions: Options = {
ecmaVersion: 'latest',
locations: true,
allowHashBang: true,
};
const baseReader = module ? getESModulesImportLines : getCommonJSImportLines;
const dependant: DependantFile[] = [];
for (const file of files) {
let source: 'module' | 'script' = module ? 'module' : 'script';
let reader = baseReader;
const ext = file.name.endsWith('js') ?
module ? 'mjs' : 'cjs' :
file.name.split('.')[1];
if (file.name.endsWith('mjs')) {
reader = getESModulesImportLines;
source = 'module';
} else if (file.name.endsWith('cjs')) {
reader = getCommonJSImportLines;
source = 'script';
}
try {
const node: Node = parse(file.content, {
...baseOptions,
sourceType: source,
});
const parse = getParser(ext);
const isDependant = reader(node, dependency);
const isDependant = parse(file.content, dependency);

@@ -145,0 +35,0 @@ if (isDependant.length) {

@@ -25,1 +25,9 @@ export interface ProjectDefinition {

}
/**
* Base function for all file parsers
*/
export type FileParser = (
content: string,
dependency: string,
) => number[];

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