Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

jsx-info

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jsx-info - npm Package Compare versions

Comparing version 1.5.0 to 1.6.0

src/formatPrettyCode.js

5

CHANGELOG.md

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

# v1.6.0
- Adds `--report lines --prop <PROP[=value]>` option
- Adds config file support
# v1.5.0

@@ -2,0 +7,0 @@

3

package.json
{
"name": "jsx-info",
"version": "1.5.0",
"version": "1.6.0",
"description": "displays a report of JSX component and prop usage",

@@ -32,2 +32,3 @@ "bin": "src/jsx-info.js",

"commander": "^2.20.0",
"cosmiconfig": "^5.2.1",
"globby": "^9.2.0",

@@ -34,0 +35,0 @@ "ora": "^3.4.0"

@@ -52,2 +52,29 @@ # jsx-info

Find `<Button kind="primary">`
$ npx jsx-info --report lines --prop kind=primary Button
Find all uses of the prop `id`
$ npx jsx-info --report lines --prop id
## Configuration
In order to avoid repeating command line arguments as often, `jsx-info` supports
reading command line argument defaults from a configuration file. You can either
put defaults in a `.jsx-info.json` file or under a key named `"jsx-info"` in
your `package.json` file.
Either way, your configuration should be JSON that looks like this, where every
key is optional:
```json
{
"babelPlugins": ["decorators-legacy", "pipelineOperator"],
"directory": "src",
"ignore": ["**/__test__", "legacy/**"],
"files": ["**/*.{js,jsx,tsx}"]
}
```
## Note

@@ -54,0 +81,0 @@

@@ -0,4 +1,7 @@

const path = require("path");
const program = require("commander");
const cosmiconfig = require("cosmiconfig");
const pkg = require("../package.json");
const { print, printError } = require("./printer");

@@ -10,3 +13,3 @@ function listOption(x, acc = []) {

program.name(pkg.name);
program.name("jsx-info");
program.version(pkg.version, "-v, --version");

@@ -23,2 +26,3 @@ program

.option("--no-color", "force disable color")
.option("--no-config", "disable config file")
.option("--no-gitignore", "disable reading .gitignore files")

@@ -41,4 +45,4 @@ .option(

"--files <PATTERN>",
"glob pattern used to find input files",
"**/*.{js,jsx,tsx}"
"glob pattern used to find input files (repeatable)",
listOption
)

@@ -51,28 +55,51 @@ .option(

.option(
"--report <usage|props>",
"--report <usage|props|lines>",
"specify reports to show (repeatable)",
listOption
)
.option(
"--prop <PROP>",
"which prop to search for when running `--report lines`"
);
program.on("--help", () => {
// eslint-disable-next-line no-console
console.log(`
print(`
Examples:
# Display info for every component
$ ${pkg.name}
$ npx jsx-info
# Display info only for <div> and <Tab.Container>
$ ${pkg.name} div Tab.Container
$ npx jsx-info div Tab.Container
# See lines where className prop was used on div component
$ npx jsx-info --report lines --prop className div
# See lines where \`id\` prop was used on any component
$ npx jsx-info --report lines --prop id
# See lines where kind prop was used with value "primary" on Button component
$ npx jsx-info --report lines --prop kind=primary Button
# Ignore any folder named at any depth named \`__test__\`,
# as well as \`packages/legacy\`
$ ${pkg.name} --ignore '**/__test__' --ignore packages/legacy
$ npx jsx-info --ignore '**/__test__' --ignore packages/legacy
# Enable Babel plugins
$ ${
pkg.name
} --add-babel-plugin decorators-legacy --add-babel-plugin pipelineOperator
$ npx jsx-info --add-babel-plugin decorators-legacy --add-babel-plugin pipelineOperator
Documentation can be found at https://github.com/wavebeem/jsx-info
# Example .jsx-info.json config file
{
"babelPlugins": ["decorators-legacy", "pipelineOperator"],
"directory": "src",
"ignore": ["**/__test__", "legacy/**"],
"files": ["**/*.{js,jsx,tsx}"]
}
# Find <Button kind="primary">
$ npx jsx-info --report lines --prop kind=primary Button
# Find all uses of the prop \`id\`
$ npx jsx-info --report lines --prop id
Full documentation can be found at https://github.com/wavebeem/jsx-info
`);

@@ -83,10 +110,46 @@ });

function getConfig() {
if (!program.config) {
return {};
}
try {
const explorer = cosmiconfig("jsx-info", {
searchPlaces: ["package.json", ".jsx-info.json"]
});
const result = explorer.searchSync();
if (result) {
print(`Loaded configuration from ${result.filepath}\n`);
if (result.config.directory) {
result.config.directory = path.resolve(
result.filepath,
"..",
result.config.directory
);
}
return result.config;
}
} catch (err) {
printError("failed to parse config file");
}
return {};
}
const config = getConfig();
function concat(a, b) {
return [...(a || []), ...(b || [])];
}
exports.prop = program.prop;
exports.components = program.args;
exports.showProgress = program.progress;
exports.babelPlugins = program.addBabelPlugin;
exports.directory = program.directory;
exports.babelPlugins = concat(config.babelPlugins, program.addBabelPlugin);
exports.directory = program.directory || config.directory;
exports.gitignore = program.gitignore;
exports.ignore = program.ignore || [];
exports.files = program.files;
exports.ignore = concat(program.ignore, config.ignore);
exports.files = concat(program.files, config.files);
if (exports.files.length === 0) {
exports.files = ["**/*.{js,jsx,tsx}"];
}
exports.sort = program.sort || "usage";
exports.report = program.report || ["usage", "props"];

@@ -7,3 +7,3 @@ const globby = require("globby");

const searchForFiles = async ({ patterns, gitignore, directory, ignore }) => {
return await globby(patterns, {
return await globby(patterns || "**/*.{js,jsx,tsx}", {
absolute: true,

@@ -10,0 +10,0 @@ onlyFiles: true,

@@ -10,5 +10,5 @@ const {

babelPlugins,
ignore
ignore,
prop
} = require("./cli");
const sleep = require("./sleep");
const parse = require("./parse");

@@ -18,4 +18,14 @@ const Reporter = require("./reporter");

const codeSource = require("./code-source");
const { formatPrettyCode } = require("./formatPrettyCode");
async function sleep() {
return new Promise(resolve => {
setImmediate(resolve);
});
}
async function main() {
if (!prop && report.includes("lines")) {
throw new Error("`--prop` argument required for `lines` report");
}
const timeStart = Date.now();

@@ -38,11 +48,49 @@ if (showProgress) {

// spinner library assumes the event loop will be ticking periodically
await sleep(0);
await sleep();
}
try {
parse(codeSource.codeFromFile(filename), {
const code = codeSource.codeFromFile(filename);
parse(code, {
babelPlugins,
typescript: filename.endsWith(".tsx") || filename.endsWith(".ts"),
onlyComponents: components,
onComponent: component => reporter.addComponent(component),
onProp: (component, prop) => reporter.addProp(component, prop)
onComponent: componentName => {
reporter.addComponent(componentName);
},
onProp: ({
componentName,
propName,
propCode,
startLoc,
endLoc,
propValue
}) => {
if (prop) {
let wantPropKey = prop;
let wantPropValue = undefined;
if (prop.includes("=")) {
const index = prop.indexOf("=");
const key = prop.slice(0, index);
const val = prop.slice(index + 1);
wantPropKey = key;
wantPropValue = val;
}
if (propName !== wantPropKey) {
return;
}
if (wantPropValue !== undefined && propValue !== wantPropValue) {
return;
}
}
const prettyCode = formatPrettyCode(code, startLoc.line, endLoc.line);
reporter.addProp({
componentName,
propName,
propCode,
startLoc,
endLoc,
prettyCode,
filename
});
}
});

@@ -62,5 +110,3 @@ } catch (error) {

printer.print(
printer.styleHeading(
`Scanned ${filenames.length} files in ${totalTime.toFixed(1)} seconds`
)
`Scanned ${filenames.length} files in ${totalTime.toFixed(1)} seconds`
);

@@ -73,2 +119,5 @@ if (report.includes("usage")) {

}
if (report.includes("lines")) {
reporter.reportLinesUsage();
}
reporter.reportErrors();

@@ -78,5 +127,10 @@ }

main().catch(err => {
// eslint-disable-next-line no-console
console.error(err);
if (process.env.DEBUG === "true") {
// eslint-disable-next-line no-console
console.error(err);
} else {
// eslint-disable-next-line no-console
console.error(err.message);
}
process.exit(1);
});
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const { formatPropValue } = require("./formatPropValue");
function createProp(attributeNode) {

@@ -56,3 +58,5 @@ function getAttributeName(attributeNode) {

function doReportComponent(component) {
if (onlyComponents.length === 0) return true;
if (onlyComponents.length === 0) {
return true;
}
return onlyComponents.indexOf(component) !== -1;

@@ -75,7 +79,19 @@ }

const node = path.node;
const component = createComponent(node);
if (doReportComponent(component)) {
onComponent(component);
const componentName = createComponent(node);
if (doReportComponent(componentName)) {
onComponent(componentName);
for (const propNode of node.openingElement.attributes) {
onProp(component, createProp(propNode));
const propCode = code.slice(propNode.start, propNode.end);
const propName = createProp(propNode);
const startLoc = propNode.loc.start;
const endLoc = propNode.loc.end;
const propValue = formatPropValue(propNode.value);
onProp({
componentName,
propName,
propCode,
startLoc,
endLoc,
propValue
});
}

@@ -82,0 +98,0 @@ }

@@ -40,2 +40,6 @@ const chalk = require("chalk");

const styleLinenos = (...args) => {
return chalk.bold(...args);
};
const spinner = ora();

@@ -46,4 +50,8 @@

// eslint-disable-next-line no-console
const printError = console.error.bind(console);
exports.styleComponentName = styleComponentName;
exports.stylePropName = stylePropName;
exports.styleLinenos = styleLinenos;
exports.styleError = styleError;

@@ -55,1 +63,2 @@ exports.styleNumber = styleNumber;

exports.print = print;
exports.printError = printError;

@@ -20,10 +20,16 @@ const printer = require("./printer");

_sortMap(map) {
_sortMapHelper(map) {
const entries = Array.from(map.entries());
const sortTypes = {
usage: (a, b) => b[1] - a[1],
alphabatical: (a, b) => {
if (b[0] < a[0]) return 1;
else if (b[0] > a[0]) return -1;
else return 0;
usage: (a, b) => {
return b[1] - a[1];
},
alphabetical: (a, b) => {
if (b[0] < a[0]) {
return 1;
}
if (b[0] > a[0]) {
return -1;
}
return 0;
}

@@ -49,6 +55,8 @@ };

addProp(componentName, propName) {
addProp({ componentName, propName, prettyCode, startLoc, filename }) {
const props = this._componentProps.get(componentName) || new Map();
const prevPropUsage = props.get(propName) || 0;
props.set(propName, prevPropUsage + 1);
const prop = props.get(propName) || { usage: 0, lines: [] };
prop.usage++;
prop.lines.push({ prettyCode, startLoc, filename });
props.set(propName, prop);
this._componentProps.set(componentName, props);

@@ -84,3 +92,5 @@ }

const totalComponentsCount = this._components.size;
if (!totalComponentsCount) return;
if (totalComponentsCount === 0) {
return;
}
const totalComponentUsageCount = this._sumValues(this._components);

@@ -92,3 +102,3 @@ printer.print(

);
const pairs = this._sortMap(this._components);
const pairs = this._sortMapHelper(this._components);
const maxDigits = getMaxDigits(pairs.values());

@@ -105,3 +115,4 @@ for (const [componentName, count] of pairs) {

reportPropUsage() {
for (const [componentName, props] of this._sortMap(this._componentProps)) {
const sorted = this._sortMapHelper(this._components);
for (const [componentName] of sorted) {
const componentUsage = this._components.get(componentName);

@@ -117,8 +128,15 @@ printer.print(

);
const pairs = this._sortMap(props);
const pairs = this._sortMapHelper(
new Map(
[...(this._componentProps.get(componentName) || new Map())].map(x => {
x[1] = x[1].usage;
return x;
})
)
);
const maxDigits = getMaxDigits(pairs.values());
for (const [propName, count] of pairs) {
for (const [propName, usage] of pairs) {
printer.print(
" " + printer.styleNumber(count.toString().padStart(maxDigits)),
" " + printer.textMeter(componentUsage, count),
" " + printer.styleNumber(usage.toString().padStart(maxDigits)),
" " + printer.textMeter(componentUsage, usage),
" " + printer.stylePropName(propName)

@@ -128,5 +146,29 @@ );

}
printer.print(`
Tip: Want to see where the className prop was used on the <div> component?
npx jsx-info --report lines --prop className div
`);
}
reportLinesUsage() {
// TODO: Does it make sense to sort the output here somehow?
for (const [componentName, props] of this._componentProps) {
for (const [, /* propName */ data] of props) {
for (const lineData of data.lines) {
const { filename, startLoc, prettyCode } = lineData;
const { line, column } = startLoc;
const styledComponentName = printer.styleComponentName(componentName);
printer.print(
printer.styleHeading(
`${styledComponentName} ${filename}:${line}:${column}`
)
);
printer.print(prettyCode);
}
}
}
}
}
module.exports = Reporter;

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