Comparing version 3.0.1 to 3.1.0
@@ -35,2 +35,30 @@ import * as path from "path"; | ||
test("analyze line-report directory [style]", async () => { | ||
const directory = path.resolve(__dirname, "../fixtures/line-report"); | ||
const analysis = await analyze({ | ||
directory, | ||
relativePaths: true, | ||
components: ["div"], | ||
prop: "style", | ||
}); | ||
expect(analysis).toMatchSnapshot({ | ||
directory: expect.any(String), | ||
elapsedTime: expect.any(Number), | ||
}); | ||
}); | ||
test("analyze line-report directory [none]", async () => { | ||
const directory = path.resolve(__dirname, "../fixtures/line-report"); | ||
const analysis = await analyze({ | ||
directory, | ||
relativePaths: true, | ||
components: ["div"], | ||
prop: "none", | ||
}); | ||
expect(analysis).toMatchSnapshot({ | ||
directory: expect.any(String), | ||
elapsedTime: expect.any(Number), | ||
}); | ||
}); | ||
test("analyze basic directory", async () => { | ||
@@ -89,2 +117,3 @@ const directory = path.resolve(__dirname, "../fixtures/basic"); | ||
directory, | ||
files: ["main.js"], | ||
components: ["div"], | ||
@@ -99,1 +128,16 @@ prop: "a", | ||
}); | ||
test("analyze multiple prop types in prop-usage directory", async () => { | ||
const directory = path.resolve(__dirname, "../fixtures/prop-usage"); | ||
const analysis = await analyze({ | ||
directory, | ||
files: ["multiplePropTypes.js"], | ||
components: ["div"], | ||
prop: "value", | ||
relativePaths: true, | ||
}); | ||
expect(analysis).toMatchSnapshot({ | ||
directory: expect.any(String), | ||
elapsedTime: expect.any(Number), | ||
}); | ||
}); |
@@ -0,1 +1,5 @@ | ||
# v3.1.0 | ||
- Added `propValue` field `LineInfo` | ||
# v3.0.1 | ||
@@ -2,0 +6,0 @@ |
{ | ||
"name": "jsx-info", | ||
"version": "3.0.1", | ||
"version": "3.1.0", | ||
"description": "displays a report of JSX component and prop usage", | ||
@@ -11,3 +11,3 @@ "main": "dist/src/api.js", | ||
"preversion": "npm run eslint && npm run prettier && npm test", | ||
"postversion": "git push && git push --tags && npm publish", | ||
"postversion": "git push --follow-tags && npm publish", | ||
"start": "tsc --watch", | ||
@@ -14,0 +14,0 @@ "upgrade": "npm-check --update", |
import { ParserPlugin } from "@babel/parser"; | ||
import { JSXElement } from "@babel/types"; | ||
/** Options passed to `analyze` */ | ||
@@ -35,5 +36,33 @@ export interface AnalyzeOptions { | ||
} | ||
/** | ||
* Prop values are tagged with a `type` field to facilitate understanding and | ||
* extensibility as the project grows. | ||
* | ||
* - **none:** The prop wasn't used at the given location. | ||
* | ||
* - **expression:** Expression that can't be evaluated by scanning the code | ||
* (e.g. array, object, function call, etc.). | ||
* | ||
* - **literal:** Literal value such as a string, boolean, number, etc. (NOTE: | ||
* `undefined` is technically a variable, but I think most people expect it to | ||
* be a literal). | ||
* | ||
* More types may be added in the future, if requested. I can see use cases for | ||
* adding arrays and objects as values, though those have the potential to have | ||
* expressions embedded within them. I still have too many questions about how | ||
* to handle composite values like that, so I'm just not supporting them yet. | ||
*/ | ||
export declare type PropValue = { | ||
type: "none"; | ||
} | { | ||
type: "expression"; | ||
} | { | ||
type: "literal"; | ||
text: string; | ||
value: number | string | boolean | undefined | null; | ||
}; | ||
/** Information about a line where a prop was used */ | ||
export interface LineInfo { | ||
propCode: string; | ||
propValue: PropValue; | ||
prettyCode: string; | ||
@@ -70,1 +99,10 @@ startLoc: SourceLocation; | ||
export declare function analyze({ babelPlugins, components, directory, files, gitignore, ignore, onFile, prop, relativePaths, }: AnalyzeOptions): Promise<Analysis>; | ||
export interface ParseOptions { | ||
typescript?: boolean; | ||
babelPlugins?: ParserPlugin[]; | ||
onlyComponents?: string[]; | ||
onComponent?: (options: { | ||
componentName: string; | ||
node: JSXElement; | ||
}) => void; | ||
} |
@@ -88,2 +88,4 @@ "use strict"; | ||
propCode: code.slice(node.start || 0, node.end || -1), | ||
// JSXElement doesn't contain prop value | ||
propValue: { type: "none" }, | ||
startLoc: node.loc.start, | ||
@@ -117,15 +119,14 @@ endLoc: node.loc.end, | ||
} | ||
if (!match(prop.propValue)) { | ||
if (prop.propValue.type === "literal" && | ||
!match(prop.propValue.text)) { | ||
continue; | ||
} | ||
if ((!wantPropKey || prop.propName === wantPropKey) && | ||
match(prop.propValue)) { | ||
reporter.addProp(componentName, prop.propName, { | ||
propCode: prop.propCode, | ||
startLoc: prop.startLoc, | ||
endLoc: prop.endLoc, | ||
prettyCode: formatPrettyCode(code, prop.startLoc.line, prop.endLoc.line), | ||
filename: processFilename(filename), | ||
}); | ||
} | ||
reporter.addProp(componentName, prop.propName, { | ||
propCode: prop.propCode, | ||
propValue: prop.propValue, | ||
startLoc: prop.startLoc, | ||
endLoc: prop.endLoc, | ||
prettyCode: formatPrettyCode(code, prop.startLoc.line, prop.endLoc.line), | ||
filename: processFilename(filename), | ||
}); | ||
} | ||
@@ -176,21 +177,23 @@ } | ||
} | ||
const EXPRESSION = Symbol("formatPropValue.EXPRESSION"); | ||
function formatPropValue(value) { | ||
if (value === null) { | ||
return "true"; | ||
return { type: "literal", value: true, text: String(true) }; | ||
} | ||
if (!value) { | ||
return EXPRESSION; | ||
} | ||
switch (value.type) { | ||
// TODO: Should we interpret anything else here? | ||
// TODO: NaN, Infinity, -Infinity | ||
case "Identifier": | ||
if (value.name === "undefined") { | ||
return { type: "literal", value: undefined, text: String(undefined) }; | ||
} | ||
return { type: "expression" }; | ||
case "BooleanLiteral": | ||
case "StringLiteral": | ||
return value.value; | ||
case "NumericLiteral": | ||
return { type: "literal", value: value.value, text: String(value.value) }; | ||
case "NullLiteral": | ||
return { type: "literal", value: null, text: String(null) }; | ||
case "JSXExpressionContainer": | ||
return formatPropValue(value.expression); | ||
case "NumericLiteral": | ||
case "BooleanLiteral": | ||
return String(value.value); | ||
default: | ||
return EXPRESSION; | ||
return { type: "expression" }; | ||
} | ||
@@ -197,0 +200,0 @@ } |
{ | ||
"name": "jsx-info", | ||
"version": "3.0.1", | ||
"version": "3.1.0", | ||
"description": "displays a report of JSX component and prop usage", | ||
@@ -11,3 +11,3 @@ "main": "dist/src/api.js", | ||
"preversion": "npm run eslint && npm run prettier && npm test", | ||
"postversion": "git push && git push --tags && npm publish", | ||
"postversion": "git push --follow-tags && npm publish", | ||
"start": "tsc --watch", | ||
@@ -14,0 +14,0 @@ "upgrade": "npm-check --update", |
@@ -45,5 +45,33 @@ import { parse as babelParse, ParserPlugin } from "@babel/parser"; | ||
/** | ||
* Prop values are tagged with a `type` field to facilitate understanding and | ||
* extensibility as the project grows. | ||
* | ||
* - **none:** The prop wasn't used at the given location. | ||
* | ||
* - **expression:** Expression that can't be evaluated by scanning the code | ||
* (e.g. array, object, function call, etc.). | ||
* | ||
* - **literal:** Literal value such as a string, boolean, number, etc. (NOTE: | ||
* `undefined` is technically a variable, but I think most people expect it to | ||
* be a literal). | ||
* | ||
* More types may be added in the future, if requested. I can see use cases for | ||
* adding arrays and objects as values, though those have the potential to have | ||
* expressions embedded within them. I still have too many questions about how | ||
* to handle composite values like that, so I'm just not supporting them yet. | ||
*/ | ||
export type PropValue = | ||
| { type: "none" } | ||
| { type: "expression" } | ||
| { | ||
type: "literal"; | ||
text: string; | ||
value: number | string | boolean | undefined | null; | ||
}; | ||
/** Information about a line where a prop was used */ | ||
export interface LineInfo { | ||
propCode: string; | ||
propValue: PropValue; | ||
prettyCode: string; | ||
@@ -147,2 +175,4 @@ startLoc: SourceLocation; | ||
propCode: code.slice(node.start || 0, node.end || -1), | ||
// JSXElement doesn't contain prop value | ||
propValue: { type: "none" }, | ||
startLoc: node.loc.start, | ||
@@ -161,3 +191,3 @@ endLoc: node.loc.end, | ||
let wantPropKey = searchProp; | ||
let match = (_value: string | symbol): boolean => true; | ||
let match = (_value: string): boolean => true; | ||
if (searchProp.includes("!=")) { | ||
@@ -179,21 +209,20 @@ const index = searchProp.indexOf("!="); | ||
} | ||
if (!match(prop.propValue)) { | ||
continue; | ||
} | ||
if ( | ||
(!wantPropKey || prop.propName === wantPropKey) && | ||
match(prop.propValue) | ||
prop.propValue.type === "literal" && | ||
!match(prop.propValue.text) | ||
) { | ||
reporter.addProp(componentName, prop.propName, { | ||
propCode: prop.propCode, | ||
startLoc: prop.startLoc, | ||
endLoc: prop.endLoc, | ||
prettyCode: formatPrettyCode( | ||
code, | ||
prop.startLoc.line, | ||
prop.endLoc.line | ||
), | ||
filename: processFilename(filename), | ||
}); | ||
continue; | ||
} | ||
reporter.addProp(componentName, prop.propName, { | ||
propCode: prop.propCode, | ||
propValue: prop.propValue, | ||
startLoc: prop.startLoc, | ||
endLoc: prop.endLoc, | ||
prettyCode: formatPrettyCode( | ||
code, | ||
prop.startLoc.line, | ||
prop.endLoc.line | ||
), | ||
filename: processFilename(filename), | ||
}); | ||
} | ||
@@ -244,22 +273,23 @@ } | ||
const EXPRESSION = Symbol("formatPropValue.EXPRESSION"); | ||
function formatPropValue(value: Node | null): string | symbol { | ||
function formatPropValue(value: Node | null): PropValue { | ||
if (value === null) { | ||
return "true"; | ||
return { type: "literal", value: true, text: String(true) }; | ||
} | ||
if (!value) { | ||
return EXPRESSION; | ||
} | ||
switch (value.type) { | ||
// TODO: Should we interpret anything else here? | ||
// TODO: NaN, Infinity, -Infinity | ||
case "Identifier": | ||
if (value.name === "undefined") { | ||
return { type: "literal", value: undefined, text: String(undefined) }; | ||
} | ||
return { type: "expression" }; | ||
case "BooleanLiteral": | ||
case "StringLiteral": | ||
return value.value; | ||
case "NumericLiteral": | ||
return { type: "literal", value: value.value, text: String(value.value) }; | ||
case "NullLiteral": | ||
return { type: "literal", value: null, text: String(null) }; | ||
case "JSXExpressionContainer": | ||
return formatPropValue(value.expression); | ||
case "NumericLiteral": | ||
case "BooleanLiteral": | ||
return String(value.value); | ||
default: | ||
return EXPRESSION; | ||
return { type: "expression" }; | ||
} | ||
@@ -283,3 +313,3 @@ } | ||
function createProp(attributeNode: Node) { | ||
function createProp(attributeNode: Node): string { | ||
return getAttributeName(attributeNode); | ||
@@ -299,7 +329,7 @@ } | ||
function createComponent(componentNode: JSXElement) { | ||
function createComponent(componentNode: JSXElement): string { | ||
return getDottedName(componentNode.openingElement.name); | ||
} | ||
interface ParseOptions { | ||
export interface ParseOptions { | ||
typescript?: boolean; | ||
@@ -410,3 +440,3 @@ babelPlugins?: ParserPlugin[]; | ||
addProp(componentName: string, propName: string, line: LineInfo) { | ||
addProp(componentName: string, propName: string, line: LineInfo): void { | ||
this._incrementProp(componentName, propName); | ||
@@ -413,0 +443,0 @@ this._ensureLines(componentName, propName); |
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
119745
43
2180