ts-unused-exports
Advanced tools
Comparing version 6.1.2 to 6.2.0
@@ -0,1 +1,12 @@ | ||
## [6.2.0] - 11 May 2020 | ||
### Changed | ||
- (Internal) Using TypeScript 3.8 | ||
### Added | ||
- Handle dynamic imports within a TSX div or fragment | ||
- Add basic support for export * as namespace. | ||
## [6.1.2] - 1 Apr 2020 | ||
@@ -2,0 +13,0 @@ |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var util_1 = require("./parser/util"); | ||
var isExportArray = function (e) { | ||
@@ -39,6 +40,21 @@ return e.startsWith('[') && e.endsWith(']'); | ||
}; | ||
var processImportsOfExportedAsNamespace = function (file, exportMap) { | ||
/* Basic support for export-as-namespace. | ||
* If a file is exported as a namespace, and that namespace is imported, | ||
* then we mark *all* exports of that file as used. | ||
* A more accurate analysis would require scanning for all usages of the exports, via that namespace. | ||
*/ | ||
file.pathsExportedAsNamespace.forEach(function (exportedAsNamespace) { | ||
var _a; | ||
var what = (_a = exportMap[exportedAsNamespace]) === null || _a === void 0 ? void 0 : _a.exports; | ||
if (what) { | ||
Object.keys(what).forEach(function (exported) { return what[exported].usageCount++; }); | ||
} | ||
}); | ||
}; | ||
var processImports = function (file, exportMap) { | ||
processImportsOfExportedAsNamespace(file, exportMap); | ||
Object.keys(file.imports).forEach(function (key) { | ||
var _a; | ||
var ex = exportMap[key] && exportMap[key].exports; | ||
var _a, _b; | ||
var ex = (_a = exportMap[key]) === null || _a === void 0 ? void 0 : _a.exports; | ||
// Handle imports from an index file | ||
@@ -49,3 +65,3 @@ if (!ex && key === '.') { | ||
var indexKey = indexCandidates[c]; | ||
ex = ((_a = exportMap[indexKey]) === null || _a === void 0 ? void 0 : _a.exports) || undefined; | ||
ex = ((_b = exportMap[indexKey]) === null || _b === void 0 ? void 0 : _b.exports) || undefined; | ||
if (ex) | ||
@@ -92,3 +108,3 @@ break; | ||
delete fileExports.exports[ex]; | ||
var exports = (_a = exportMap[ex.slice(2)]) === null || _a === void 0 ? void 0 : _a.exports; | ||
var exports = (_a = exportMap[util_1.cleanRelativePath(ex)]) === null || _a === void 0 ? void 0 : _a.exports; | ||
if (exports) { | ||
@@ -121,7 +137,7 @@ Object.keys(exports) | ||
var filterFiles = function (files, extraOptions) { | ||
var _a, _b; | ||
if (!((_a = extraOptions) === null || _a === void 0 ? void 0 : _a.ignoreFilesRegex)) { | ||
var _a; | ||
if (!(extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.ignoreFilesRegex)) { | ||
return files; | ||
} | ||
var regexes = (_b = extraOptions.ignoreFilesRegex) === null || _b === void 0 ? void 0 : _b.map(function (rex) { return new RegExp(rex); }); | ||
var regexes = (_a = extraOptions.ignoreFilesRegex) === null || _a === void 0 ? void 0 : _a.map(function (rex) { return new RegExp(rex); }); | ||
var shouldIgnoreFile = function (fileName) { | ||
@@ -128,0 +144,0 @@ return regexes.some(function (reg) { |
@@ -16,3 +16,2 @@ "use strict"; | ||
var tsconfig = _a[0], tsFiles = _a.slice(1); | ||
var _b, _c, _d; | ||
if (!argsParser_1.hasValidArgs(showError, tsconfig, tsFiles)) { | ||
@@ -33,3 +32,3 @@ showError(usage_1.USAGE); | ||
var options = argsParser_1.extractOptionsFromFiles(tsFiles).options; | ||
if ((_b = options) === null || _b === void 0 ? void 0 : _b.showLineNumber) { | ||
if (options === null || options === void 0 ? void 0 : options.showLineNumber) { | ||
files.forEach(function (path) { | ||
@@ -46,7 +45,7 @@ analysis_1[path].forEach(function (unusedExport) { | ||
} | ||
if ((_c = options) === null || _c === void 0 ? void 0 : _c.exitWithCount) { | ||
if (options === null || options === void 0 ? void 0 : options.exitWithCount) { | ||
// Max allowed exit code is 127 (single signed byte) | ||
return exitWith(Math.min(127, files.length)); | ||
} | ||
var maxIssues = ((_d = options) === null || _d === void 0 ? void 0 : _d.maxIssues) || 0; | ||
var maxIssues = (options === null || options === void 0 ? void 0 : options.maxIssues) || 0; | ||
return exitWith(files.length <= maxIssues | ||
@@ -53,0 +52,0 @@ ? ExitCode.NoUnusedExportsFound |
@@ -6,4 +6,5 @@ import * as ts from 'typescript'; | ||
what: string[]; | ||
isExportStarAs?: boolean; | ||
} | ||
export declare const getFromText: (moduleSpecifier: string) => string; | ||
export declare const getFrom: (moduleSpecifier: ts.Expression) => string; |
@@ -5,2 +5,3 @@ "use strict"; | ||
var common_1 = require("./common"); | ||
var util_1 = require("./util"); | ||
var namespaceBlacklist_1 = require("./namespaceBlacklist"); | ||
@@ -15,27 +16,6 @@ // Parse Dynamic Imports | ||
} | ||
function isWithArguments(node) { | ||
var myInterface = node; | ||
return !!myInterface.arguments; | ||
} | ||
// A whitelist, to over-ride namespaceBlacklist. | ||
// | ||
// We need to search some structures that would not have a namespace. | ||
var whitelist = [ts.SyntaxKind.MethodDeclaration]; | ||
var runForChildren = function (next, fun) { | ||
next | ||
.getChildren() | ||
.filter(function (c) { return !namespaceBlacklist_1.namespaceBlacklist.includes(c.kind) || whitelist.includes(c.kind); }) | ||
.forEach(function (node) { return fun(node); }); | ||
}; | ||
var recurseIntoChildren = function (next, fun) { | ||
var alsoProcessChildren = fun(next); | ||
if (alsoProcessChildren) { | ||
runForChildren(next, function (node) { return recurseIntoChildren(node, fun); }); | ||
} | ||
return alsoProcessChildren; | ||
}; | ||
var parseDereferencedLambdaParamsToTypes = function (paramName, lambda) { | ||
var types = []; | ||
var usagePrefix = paramName + "."; | ||
recurseIntoChildren(lambda, function (child) { | ||
util_1.recurseIntoChildren(lambda, function (child) { | ||
if (child.getText().startsWith(usagePrefix)) { | ||
@@ -82,39 +62,76 @@ var usage = child.getText().substring(usagePrefix.length); | ||
}; | ||
recurseIntoChildren(node, function (child) { | ||
if (child.kind === ts.SyntaxKind.ArrowFunction) { | ||
processLambda(child); | ||
var firstArrow = util_1.findFirstChildOfKind(node, ts.SyntaxKind.ArrowFunction); | ||
if (firstArrow) { | ||
processLambda(firstArrow); | ||
} | ||
return what; | ||
}; | ||
var addImportViaLambda = function (node, from, addImport) { | ||
var whatFromLambda = findLambdasWithDereferencing(node); | ||
var what = ['default'].concat(whatFromLambda); | ||
addImport({ | ||
from: common_1.getFromText(from), | ||
what: what, | ||
}); | ||
return whatFromLambda.length !== 0; | ||
}; | ||
var tryParseExpression = function (expr, addImport) { | ||
if (expr.getText().startsWith('import')) { | ||
var callExpression = util_1.findFirstChildOfKind(expr, ts.SyntaxKind.CallExpression); | ||
if (!(callExpression === null || callExpression === void 0 ? void 0 : callExpression.getText().startsWith('import'))) { | ||
return false; | ||
} | ||
return true; | ||
var syntaxListWithFrom = util_1.findFirstChildOfKind(callExpression, ts.SyntaxKind.SyntaxList); | ||
if (!syntaxListWithFrom) { | ||
return false; | ||
} | ||
var from = syntaxListWithFrom.getText(); | ||
return addImportViaLambda(expr, from, addImport); | ||
} | ||
return false; | ||
}; | ||
var handleImportWithJsxAttributes = function (attributes, addImport) { | ||
attributes.properties.forEach(function (prop) { | ||
if (ts.isJsxAttribute(prop)) { | ||
if (prop.initializer && | ||
ts.isJsxExpression(prop.initializer) && | ||
prop.initializer.expression) { | ||
tryParseExpression(prop.initializer.expression, addImport); | ||
} | ||
} | ||
}); | ||
return what; | ||
}; | ||
var handleImportWithinExpression = function (node, addImport) { | ||
var expr = node; | ||
while (isWithExpression(expr)) { | ||
var newExpr = expr.expression; | ||
if (!tryParseExpression(newExpr, addImport)) { | ||
if (ts.isJsxElement(newExpr) || ts.isJsxFragment(newExpr)) { | ||
var jsxExpressions = util_1.findAllChildrenOfKind(newExpr, ts.SyntaxKind.JsxExpression); | ||
jsxExpressions.forEach(function (j) { | ||
var jsxExpr = j; | ||
if (jsxExpr.expression) { | ||
tryParseExpression(jsxExpr.expression, addImport); | ||
} | ||
}); | ||
} | ||
var selfClosingElements = util_1.findAllChildrenOfKind(newExpr, ts.SyntaxKind.JsxSelfClosingElement); | ||
selfClosingElements.forEach(function (elem) { | ||
if (ts.isJsxSelfClosingElement(elem)) { | ||
handleImportWithJsxAttributes(elem.attributes, addImport); | ||
} | ||
}); | ||
} | ||
if (isWithExpression(newExpr)) { | ||
expr = newExpr; | ||
} | ||
else { | ||
break; | ||
} | ||
} | ||
}; | ||
exports.addDynamicImports = function (node, addImport) { | ||
var addImportsInAnyExpression = function (node) { | ||
var getArgumentFrom = function (node) { | ||
if (isWithArguments(node)) { | ||
return node.arguments[0].getText(); | ||
} | ||
}; | ||
if (isWithExpression(node)) { | ||
var expr = node; | ||
while (isWithExpression(expr)) { | ||
var newExpr = expr.expression; | ||
if (newExpr.getText() === 'import') { | ||
var importing = getArgumentFrom(expr); | ||
if (!!importing) { | ||
var what = ['default'].concat(findLambdasWithDereferencing(node)); | ||
addImport({ | ||
from: common_1.getFromText(importing), | ||
what: what, | ||
}); | ||
} | ||
} | ||
if (isWithExpression(newExpr)) { | ||
expr = newExpr; | ||
} | ||
else { | ||
break; | ||
} | ||
} | ||
handleImportWithinExpression(node, addImport); // can be a ParenthesizedExpression with a JSX element inside it | ||
} | ||
@@ -124,4 +141,4 @@ return true; | ||
// Recurse, since dynamic imports can occur at nested levels within the code | ||
recurseIntoChildren(node, addImportsInAnyExpression); | ||
util_1.recurseIntoChildren(node, addImportsInAnyExpression); | ||
}; | ||
//# sourceMappingURL=dynamic.js.map |
import * as ts from 'typescript'; | ||
import { LocationInFile, ExtraCommandLineOptions } from '../types'; | ||
import { ExtraCommandLineOptions, LocationInFile } from '../types'; | ||
import { FromWhat } from './common'; | ||
@@ -4,0 +4,0 @@ export declare const extractExportStatement: (decl: ts.ExportDeclaration) => string[]; |
@@ -7,5 +7,17 @@ "use strict"; | ||
var extractAliasFirstFromElements = function (elements) { return elements.map(function (e) { return e.name.text; }); }; | ||
var extractPropertyOrAliasFirstFromElements = function (elements) { return elements.map(function (e) { return (e.propertyName || e.name).text; }); }; | ||
var extractFromBindingsWith = function (bindings, extract) { | ||
if (ts.isNamedExports(bindings)) | ||
return extract(bindings.elements); | ||
return [bindings.name.text]; | ||
}; | ||
var extractAliasFirstFromBindings = function (bindings) { | ||
return extractFromBindingsWith(bindings, extractAliasFirstFromElements); | ||
}; | ||
var extractPropertyOrAliasFromBindings = function (bindings) { | ||
return extractFromBindingsWith(bindings, extractPropertyOrAliasFirstFromElements); | ||
}; | ||
exports.extractExportStatement = function (decl) { | ||
return decl.exportClause | ||
? extractAliasFirstFromElements(decl.exportClause.elements) | ||
? extractAliasFirstFromBindings(decl.exportClause) | ||
: []; | ||
@@ -17,7 +29,7 @@ }; | ||
? // The alias 'name' or the original type is exported | ||
extractAliasFirstFromElements(exportClause.elements) | ||
extractAliasFirstFromBindings(exportClause) | ||
: common_1.STAR; | ||
var whatImported = exportClause | ||
? // The original type 'propertyName' is imported | ||
exportClause.elements.map(function (e) { return (e.propertyName || e.name).text; }) | ||
extractPropertyOrAliasFromBindings(exportClause) | ||
: common_1.STAR; | ||
@@ -32,2 +44,3 @@ return { | ||
what: whatImported, | ||
isExportStarAs: (exportClause === null || exportClause === void 0 ? void 0 : exportClause.kind) === ts.SyntaxKind.NamespaceExport, | ||
}, | ||
@@ -65,4 +78,3 @@ }; | ||
var shouldNodeTypeBeIgnored = function (node, extraOptions) { | ||
var _a; | ||
var allowUnusedTypes = !!((_a = extraOptions) === null || _a === void 0 ? void 0 : _a.allowUnusedTypes); | ||
var allowUnusedTypes = !!(extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.allowUnusedTypes); | ||
if (!allowUnusedTypes) | ||
@@ -69,0 +81,0 @@ return false; |
@@ -0,3 +1,3 @@ | ||
import * as ts from 'typescript'; | ||
import * as tsconfigPaths from 'tsconfig-paths'; | ||
import * as ts from 'typescript'; | ||
import { FromWhat } from './common'; | ||
@@ -4,0 +4,0 @@ import { Imports } from '../types'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var common_1 = require("./common"); | ||
var path_1 = require("path"); | ||
var fs_1 = require("fs"); | ||
var common_1 = require("./common"); | ||
var util_1 = require("./util"); | ||
@@ -7,0 +7,0 @@ // Parse Imports |
@@ -8,2 +8,3 @@ "use strict"; | ||
var import_1 = require("./import"); | ||
var util_1 = require("./util"); | ||
var comment_1 = require("./comment"); | ||
@@ -29,3 +30,7 @@ var nodeProcessor_1 = require("./nodeProcessor"); | ||
var tsconfigPathsMatcher = (!!paths && tsconfigPaths.createMatchPath(baseDir, paths)) || undefined; | ||
var pathsExportedAsNamespace = []; | ||
var addImport = function (fw) { | ||
if (fw.isExportStarAs) { | ||
pathsExportedAsNamespace.push(util_1.cleanRelativePath(fw.from)); | ||
} | ||
return import_1.addImportCore(fw, rootDir, path, imports, baseDir, baseUrl, tsconfigPathsMatcher); | ||
@@ -48,2 +53,3 @@ }; | ||
exportLocations: exportLocations, | ||
pathsExportedAsNamespace: pathsExportedAsNamespace, | ||
}; | ||
@@ -57,4 +63,3 @@ }; | ||
var baseUrl = _a.baseUrl, filePaths = _a.files, paths = _a.paths; | ||
var _b; | ||
var includeDeclarationFiles = !((_b = extraOptions) === null || _b === void 0 ? void 0 : _b.excludeDeclarationFiles); | ||
var includeDeclarationFiles = !(extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.excludeDeclarationFiles); | ||
var files = filePaths | ||
@@ -61,0 +66,0 @@ .filter(function (p) { return includeDeclarationFiles || !p.includes('.d.'); }) |
@@ -45,5 +45,4 @@ "use strict"; | ||
.forEach(function (name) { | ||
var _a; | ||
addExport(namespace.namespace + name, node); | ||
if ((_a = extraOptions) === null || _a === void 0 ? void 0 : _a.searchNamespaces) { | ||
if (extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.searchNamespaces) { | ||
// performance: halves the time taken on large codebase (150k loc) | ||
@@ -68,3 +67,2 @@ var isNamespace = node | ||
if (namespace === void 0) { namespace = ''; } | ||
var _a; | ||
var kind = node.kind; | ||
@@ -92,3 +90,3 @@ var processSubNode = function (subNode, namespace) { | ||
// so for performance should only be done when necessary. | ||
if ((_a = extraOptions) === null || _a === void 0 ? void 0 : _a.searchNamespaces) { | ||
if (extraOptions === null || extraOptions === void 0 ? void 0 : extraOptions.searchNamespaces) { | ||
imports_from_namespace_1.addImportsFromNamespace(node, imports, addImport); | ||
@@ -95,0 +93,0 @@ } |
@@ -0,1 +1,6 @@ | ||
import ts = require('typescript'); | ||
export declare function isUnique<T>(value: T, index: number, self: T[]): boolean; | ||
export declare function cleanRelativePath(path: string): string; | ||
export declare function recurseIntoChildren(next: ts.Node, fun: (node: ts.Node) => boolean): boolean; | ||
export declare function findAllChildrenOfKind(node: ts.Node, kind: ts.SyntaxKind): ts.Node[]; | ||
export declare function findFirstChildOfKind(node: ts.Node, kind: ts.SyntaxKind): ts.Node | null; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ts = require("typescript"); | ||
var namespaceBlacklist_1 = require("./namespaceBlacklist"); | ||
function isUnique(value, index, self) { | ||
@@ -7,2 +9,56 @@ return self.indexOf(value) === index; | ||
exports.isUnique = isUnique; | ||
function cleanRelativePath(path) { | ||
return path.slice(2); | ||
} | ||
exports.cleanRelativePath = cleanRelativePath; | ||
// A whitelist, to over-ride namespaceBlacklist. | ||
// | ||
// We need to search some structures that would not have a namespace. | ||
var whitelist = [ | ||
ts.SyntaxKind.MethodDeclaration, | ||
ts.SyntaxKind.PropertyAssignment, | ||
ts.SyntaxKind.JsxElement, | ||
ts.SyntaxKind.JsxSelfClosingElement, | ||
]; | ||
function runForChildren(next, fun) { | ||
next | ||
.getChildren() | ||
.filter(function (c) { return !namespaceBlacklist_1.namespaceBlacklist.includes(c.kind) || whitelist.includes(c.kind); }) | ||
.forEach(function (node) { | ||
fun(node); | ||
}); | ||
} | ||
function recurseIntoChildren(next, fun) { | ||
var alsoProcessChildren = fun(next); | ||
if (alsoProcessChildren) { | ||
runForChildren(next, function (node) { return recurseIntoChildren(node, fun); }); | ||
} | ||
return alsoProcessChildren; | ||
} | ||
exports.recurseIntoChildren = recurseIntoChildren; | ||
function findAllChildrenOfKind(node, kind) { | ||
var childrenFound = []; | ||
var innerFindFirstChildOfKind = function (childNode) { | ||
if (childNode.kind === kind) { | ||
childrenFound.push(childNode); | ||
} | ||
return true; | ||
}; | ||
recurseIntoChildren(node, innerFindFirstChildOfKind); | ||
return childrenFound; | ||
} | ||
exports.findAllChildrenOfKind = findAllChildrenOfKind; | ||
function findFirstChildOfKind(node, kind) { | ||
var childFound = null; | ||
var innerFindFirstChildOfKind = function (childNode) { | ||
if (!childFound && childNode.kind === kind) { | ||
childFound = childNode; | ||
return false; | ||
} | ||
return true; | ||
}; | ||
recurseIntoChildren(node, innerFindFirstChildOfKind); | ||
return childFound; | ||
} | ||
exports.findFirstChildOfKind = findFirstChildOfKind; | ||
//# sourceMappingURL=util.js.map |
@@ -10,2 +10,3 @@ export interface Imports { | ||
exportLocations: LocationInFile[]; | ||
pathsExportedAsNamespace: string[]; | ||
} | ||
@@ -12,0 +13,0 @@ export interface LocationInFile { |
{ | ||
"name": "ts-unused-exports", | ||
"version": "6.1.2", | ||
"version": "6.2.0", | ||
"description": "ts-unused-exports finds unused exported symbols in your Typescript project", | ||
@@ -42,14 +42,14 @@ "main": "lib/app.js", | ||
"@istanbuljs/nyc-config-typescript": "^1.0.1", | ||
"@types/node": "^13.1.6", | ||
"@typescript-eslint/eslint-plugin": "^2.15.0", | ||
"@typescript-eslint/parser": "^2.15.0", | ||
"codecov": "^3.6.1", | ||
"coveralls": "^3.0.9", | ||
"@types/node": "^13.9.8", | ||
"@typescript-eslint/eslint-plugin": "^2.26.0", | ||
"@typescript-eslint/parser": "^2.26.0", | ||
"codecov": "^3.6.5", | ||
"coveralls": "^3.0.11", | ||
"eslint": "^6.8.0", | ||
"eslint-config-prettier": "^6.9.0", | ||
"eslint-config-prettier": "^6.10.1", | ||
"eslint-plugin-prettier": "^3.1.2", | ||
"nyc": "^15.0.0", | ||
"pickled-cucumber": "^2.0.13", | ||
"pickled-cucumber": "^2.0.14", | ||
"prettier": "^1.19.1", | ||
"ts-node": "^8.6.1" | ||
"ts-node": "^8.8.1" | ||
}, | ||
@@ -59,4 +59,4 @@ "dependencies": { | ||
"tsconfig-paths": "^3.9.0", | ||
"typescript": "^3.7.4" | ||
"typescript": "^3.8.3" | ||
} | ||
} |
@@ -62,3 +62,3 @@ # ts-unused-exports | ||
| `exitWithCount` | Set the process exit code to be the count of files that have unused exports. | `--exitWithCount` | | ||
| `ignoreFiles` | Ignore files with filenames that match the given regex. Use this to exclude groups of files - for example test files and their utilities. | `--ignoreFiles=\.(spec|test)` | | ||
| `ignoreFiles` | Ignore files with filenames that match the given regex. Use this to exclude groups of files - for example test files and their utilities. | `--ignoreFiles=.*spec` | | ||
| `ignoreProductionFiles` | Only scan **test** files (so ignore non-test 'production' files). | `--ignoreProductionFiles` | | ||
@@ -65,0 +65,0 @@ | `ignoreTestFiles` | Only scan **production** files (ignore all test files, like `spec.ts(x)` or `test.ts(x)` or `TestUtils.ts`). Use this to detect production code that is only used in tests (so is dead code). Note: this will NOT detect unused exports in test code - for that, you can run `ts-unused-exports` separately with the `--ignoreProductionFiles` option. | `--ignoreTestFiles` | |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
112427
1495
Updatedtypescript@^3.8.3