Skip to content

Instantly share code, notes, and snippets.

@yamadayuki
Last active December 10, 2018 06:08
Show Gist options
  • Save yamadayuki/a7f7524d999c347877af1dde15dcdeb7 to your computer and use it in GitHub Desktop.
Save yamadayuki/a7f7524d999c347877af1dde15dcdeb7 to your computer and use it in GitHub Desktop.
check npm dependencies
#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const madge = require("madge");
const { parse } = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const { isImportDeclaration, isImportSpecifier } = require("@babel/types");
const chalk = require("chalk");
const { log, error } = console;
const cwdPath = process.cwd();
const options = {
sourceType: "module",
presets: [
["@babel/preset-env", { modules: false, useBuiltIns: "entry" }],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
"jsx",
"classProperties",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-json-strings",
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
"react-hot-loader/babel",
[
"react-css-modules",
{
filetypes: { ".scss": { syntax: "postcss-scss" } },
webpackHotModuleReloading: true,
generateScopedName: "[name]--[local]",
handleMissingStyleName: "warn",
},
],
["babel-plugin-styled-components", { ssr: true }],
],
};
const checkDep = (filepath, dirname) => {
try {
const buf = fs.readFileSync(path.join(dirname, filepath), "utf8");
const ast = parse(buf.toString(), options);
const deps = [];
traverse(ast, {
enter(path) {
if (isImportDeclaration(path.node)) {
// Check only the npm package import
if (!/^\./.test(path.node.source.value)) {
deps.push(path.node.source.value);
}
}
},
});
return deps;
} catch (err) {
error({ filepath, err });
return [];
}
};
const checkDependencies = (targetFile, deps = []) => {
const targetPath = path.resolve(cwdPath, targetFile);
const targetDirname = path.dirname(targetFile);
madge(targetPath, {
fileExtensions: ["js", "jsx"],
excludeRegExp: [/.+\.s?css$/],
}).then(res => {
const dependencies = res.obj();
const npmDependencies = Object.keys(dependencies)
.map(key => dependencies[key])
.reduce((acc, value) => acc.concat(value), [])
.filter((val, i, self) => self.indexOf(val) === i)
.map(p => checkDep(p, targetDirname))
.reduce((acc, value) => acc.concat(value), [])
.filter((val, i, self) => self.indexOf(val) === i)
.sort();
log(npmDependencies.join("\n"));
if (deps.length > 0) {
log();
const npmd = new Set(npmDependencies);
const intersection = new Set(deps.filter(d => npmd.has(d)));
if (intersection.size > 0) {
log(chalk.blue("included"));
log(
" " +
[...intersection]
.map(d => {
return chalk.yellow(d);
})
.join(", ")
);
} else {
log(chalk.green("not included"));
}
}
});
};
checkDependencies(process.argv[2], process.argv.slice(3));
{
"name": "npm-dependencies",
"version": "1.0.0",
"bin": "./index.js",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.2.0",
"@babel/parser": "^7.2.0",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/plugin-proposal-json-strings": "^7.2.0",
"@babel/plugin-proposal-object-rest-spread": "^7.2.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-syntax-import-meta": "^7.2.0",
"@babel/preset-env": "^7.2.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
"@babel/traverse": "^7.1.6",
"@babel/types": "^7.2.0",
"babel-plugin-styled-components": "^1.9.2",
"chalk": "^2.4.1",
"madge": "^3.3.0",
"react-css-modules": "^4.7.8",
"react-hot-loader": "^4.3.12"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment