@namchee/dependent
Advanced tools
Comparing version 0.1.1 to 0.2.0
@@ -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'); | ||
}); | ||
}); |
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
53994
20
39
1284
7
+ Addedtypescript@^4.3.5
+ Addedtypescript@4.9.5(transitive)