Socket
Socket
Sign inDemoInstall

knip

Package Overview
Dependencies
Maintainers
0
Versions
407
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

knip - npm Package Compare versions

Comparing version 5.22.3 to 5.23.0

3

dist/cli.js

@@ -24,3 +24,3 @@ import picocolors from 'picocolors';

try {
const { report, issues, counters, rules, configurationHints } = await main({
const { report, issues, counters, rules, tagHints, configurationHints } = await main({
cwd,

@@ -48,2 +48,3 @@ tsConfigFile: tsConfig,

counters,
tagHints,
configurationHints,

@@ -50,0 +51,0 @@ noConfigHints,

@@ -9,3 +9,4 @@ import type { CommandLineOptions } from './types/cli.js';

rules: import("./types/issues.js").Rules;
tagHints: Set<import("./types/issues.js").TagHint>;
configurationHints: Set<import("./types/issues.js").ConfigurationHint>;
}>;

@@ -25,3 +25,3 @@ import { watch } from 'node:fs';

import { findMatch } from './util/regex.js';
import { getShouldIgnoreHandler } from './util/tag.js';
import { getShouldIgnoreHandler, getShouldIgnoreTagHandler } from './util/tag.js';
import { augmentWorkspace, getToSourcePathHandler } from './util/to-source-path.js';

@@ -289,8 +289,11 @@ import { createAndPrintTrace, printTrace } from './util/trace.js';

factory.deletePrincipal(principal);
const shouldIgnore = getShouldIgnoreHandler(tags, isProduction);
const shouldIgnore = getShouldIgnoreHandler(isProduction);
const shouldIgnoreTags = getShouldIgnoreTagHandler(tags);
const isIdentifierReferenced = getIsIdentifierReferencedHandler(graph, entryPaths);
const isExportedItemReferenced = (exportedItem) => exportedItem.refs > 0 &&
(typeof chief.config.ignoreExportsUsedInFile === 'object'
? exportedItem.type !== 'unknown' && !!chief.config.ignoreExportsUsedInFile[exportedItem.type]
: chief.config.ignoreExportsUsedInFile);
const ignoreExportsUsedInFile = chief.config.ignoreExportsUsedInFile;
const isExportedItemReferenced = (exportedItem) => exportedItem.refs[1] ||
(exportedItem.refs[0] > 0 &&
(typeof ignoreExportsUsedInFile === 'object'
? exportedItem.type !== 'unknown' && !!ignoreExportsUsedInFile[exportedItem.type]
: ignoreExportsUsedInFile));
const findUnusedExports = async () => {

@@ -318,3 +321,4 @@ if (isReportValues || isReportTypes) {

continue;
if (importsForExport) {
const isIgnored = shouldIgnoreTags(exportedItem.jsDocTags);
if (!isIgnored && importsForExport) {
const { isReferenced, reExportingEntryFile, traceNode } = isIdentifierReferenced(filePath, identifier, isIncludeEntryExports);

@@ -339,6 +343,9 @@ if (reExportingEntryFile) {

continue;
if (member.refs === 0) {
if (member.refs[0] === 0) {
const id = `${identifier}.${member.identifier}`;
const { isReferenced } = isIdentifierReferenced(filePath, id);
const isIgnored = shouldIgnoreTags(member.jsDocTags);
if (!isReferenced) {
if (isIgnored)
continue;
collector.addIssue({

@@ -355,2 +362,9 @@ type: 'enumMembers',

}
else if (isIgnored) {
for (const tagName of exportedItem.jsDocTags) {
if (tags[1].includes(tagName.replace(/^\@/, ''))) {
collector.addTagHint({ type: 'tag', filePath, identifier: id, tagName });
}
}
}
}

@@ -362,2 +376,11 @@ }

for (const member of principal.findUnusedMembers(filePath, members)) {
if (shouldIgnoreTags(member.jsDocTags)) {
const identifier = `${exportedItem.identifier}.${member.identifier}`;
for (const tagName of exportedItem.jsDocTags) {
if (tags[1].includes(tagName.replace(/^\@/, ''))) {
collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
}
}
continue;
}
collector.addIssue({

@@ -383,2 +406,4 @@ type: 'classMembers',

if (!isExportedItemReferenced(exportedItem)) {
if (isIgnored)
continue;
if (!isSkipLibs && principal?.hasExternalReferences(filePath, exportedItem))

@@ -405,2 +430,9 @@ continue;

}
else if (isIgnored) {
for (const tagName of exportedItem.jsDocTags) {
if (tags[1].includes(tagName.replace(/^\@/, ''))) {
collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
}
}
}
}

@@ -534,3 +566,3 @@ }

await findUnusedExports();
const { issues, counters, configurationHints } = collector.getIssues();
const { issues, counters, tagHints, configurationHints } = collector.getIssues();
if (isFix) {

@@ -543,3 +575,3 @@ await fixer.fixIssues(issues);

streamer.clear();
return { report, issues, counters, rules, configurationHints };
return { report, issues, counters, rules, tagHints, configurationHints };
};

@@ -1,2 +0,2 @@

import type { ConfigurationHint, Issue, Rules } from './types/issues.js';
import type { ConfigurationHint, Issue, Rules, TagHint } from './types/issues.js';
type Filters = Partial<{

@@ -18,2 +18,3 @@ dir: string;

private configurationHints;
private tagHints;
private ignorePatterns;

@@ -30,2 +31,3 @@ private isMatch;

addConfigurationHint(issue: ConfigurationHint): void;
addTagHint(issue: TagHint): void;
purge(): import("./types/issues.js").IssueSet;

@@ -35,2 +37,3 @@ getIssues(): {

counters: import("./types/issues.js").Counters;
tagHints: Set<TagHint>;
configurationHints: Set<ConfigurationHint>;

@@ -37,0 +40,0 @@ };

@@ -5,3 +5,3 @@ import picomatch from 'picomatch';

import { relative } from './util/path.js';
const hasHint = (hints, hint) => Array.from(hints).some(item => item.identifier === hint.identifier && item.type === hint.type && item.workspaceName === hint.workspaceName);
const hasConfigurationHint = (hints, hint) => Array.from(hints).some(item => item.identifier === hint.identifier && item.type === hint.type && item.workspaceName === hint.workspaceName);
const isMatch = timerify(picomatch.isMatch, 'isMatch');

@@ -16,2 +16,3 @@ export class IssueCollector {

configurationHints = new Set();
tagHints = new Set();
ignorePatterns = new Set();

@@ -64,6 +65,9 @@ isMatch;

addConfigurationHint(issue) {
if (!hasHint(this.configurationHints, issue)) {
if (!hasConfigurationHint(this.configurationHints, issue)) {
this.configurationHints.add(issue);
}
}
addTagHint(issue) {
this.tagHints.add(issue);
}
purge() {

@@ -79,2 +83,3 @@ const unusedFiles = this.issues.files;

counters: this.counters,
tagHints: this.tagHints,
configurationHints: this.configurationHints,

@@ -81,0 +86,0 @@ };

declare const _default: {
symbols: ({ report, issues, configurationHints, noConfigHints, isShowProgress }: import("../index.js").ReporterOptions) => void;
symbols: ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }: import("../index.js").ReporterOptions) => void;
compact: ({ report, issues, isShowProgress }: import("../index.js").ReporterOptions) => void;

@@ -4,0 +4,0 @@ codeowners: ({ report, issues, isShowProgress, options }: import("../index.js").ReporterOptions) => void;

import type { ReporterOptions } from '../types/issues.js';
declare const _default: ({ report, issues, configurationHints, noConfigHints, isShowProgress }: ReporterOptions) => void;
declare const _default: ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }: ReporterOptions) => void;
export default _default;

@@ -23,3 +23,3 @@ import EasyTable from 'easy-table';

};
export default ({ report, issues, configurationHints, noConfigHints, isShowProgress }) => {
export default ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }) => {
const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;

@@ -56,10 +56,20 @@ let totalIssues = 0;

}
if (!noConfigHints && configurationHints.size > 0) {
logTitle('Configuration issues', configurationHints.size);
for (const hint of configurationHints) {
const { type, workspaceName, identifier } = hint;
const message = `Unused item in ${type}`;
const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : '';
console.warn(picocolors.gray(`${message}${workspace}:`), identifier);
if (!noConfigHints) {
if (configurationHints.size > 0) {
logTitle('Configuration issues', configurationHints.size);
for (const hint of configurationHints) {
const { type, workspaceName, identifier } = hint;
const message = `Unused item in ${type}`;
const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : '';
console.warn(picocolors.gray(`${message}${workspace}:`), identifier);
}
}
if (tagHints.size > 0) {
logTitle('Tag issues', tagHints.size);
for (const hint of tagHints) {
const { filePath, identifier, tagName } = hint;
const message = `Unused tag in ${toRelative(filePath)}:`;
console.warn(picocolors.gray(message), `${identifier} → ${tagName}`);
}
}
}

@@ -66,0 +76,0 @@ if (totalIssues === 0 && isShowProgress) {

@@ -36,3 +36,3 @@ import type ts from 'typescript';

jsDocTags: Tags;
refs: number;
refs: [number, boolean];
fixes: Fixes;

@@ -48,3 +48,3 @@ symbol?: ts.Symbol;

type: SymbolType;
refs: number;
refs: [number, boolean];
fix: Fix;

@@ -51,0 +51,0 @@ symbol?: ts.Symbol;

@@ -59,2 +59,3 @@ export declare enum SymbolType {

counters: Counters;
tagHints: TagHints;
configurationHints: ConfigurationHints;

@@ -78,1 +79,9 @@ noConfigHints: boolean;

};
type TagHints = Set<TagHint>;
export type TagHint = {
type: 'tag';
filePath: string;
identifier: string;
tagName: string;
};
export {};

@@ -26,1 +26,2 @@ import ts from 'typescript';

export declare const isImportSpecifier: (node: ts.Node) => boolean;
export declare const isReferencedInExportedType: (node: ts.Node, symbol: ts.Symbol) => any;

@@ -154,1 +154,10 @@ import ts from 'typescript';

ts.isNamespaceImport(node.parent);
const isExported = (node) => {
if (node.modifiers?.find(mod => mod.kind === ts.SyntaxKind.ExportKeyword))
return true;
return node.parent ? isExported(node.parent) : false;
};
export const isReferencedInExportedType = (node, symbol) => symbol.exportSymbol &&
!(node.transformFlags & ts.TransformFlags.ContainsTypeScript) &&
Boolean(node.parent.transformFlags & ts.TransformFlags.ContainsTypeScript) &&
isExported(node.parent);

@@ -8,4 +8,5 @@ import { isBuiltin } from 'node:module';

import { extname, isInNodeModules } from '../util/path.js';
import { isIdChar } from '../util/regex.js';
import { shouldIgnore } from '../util/tag.js';
import { getAccessMembers, getDestructuredIds, getJSDocTags, getLineAndCharacterOfPosition, getTypeName, isAccessExpression, isConsiderReferencedNS, isDestructuring, isImportSpecifier, } from './ast-helpers.js';
import { getAccessMembers, getDestructuredIds, getJSDocTags, getLineAndCharacterOfPosition, getTypeName, isAccessExpression, isConsiderReferencedNS, isDestructuring, isImportSpecifier, isReferencedInExportedType, } from './ast-helpers.js';
import getDynamicImportVisitors from './visitors/dynamic-imports/index.js';

@@ -32,8 +33,9 @@ import getExportVisitors from './visitors/exports/index.js';

fix: member.fix,
refs: 0,
refs: [0, false],
jsDocTags: getJSDocTags(member.node),
};
};
const isType = (item) => item.type === 'type' || item.type === 'interface' || item.type === 'member';
const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options) => {
const { skipTypeOnly, tags } = options;
const { skipTypeOnly, tags, ignoreExportsUsedInFile } = options;
const internalImports = new Map();

@@ -49,2 +51,3 @@ const externalImports = new Set();

const importedInternalSymbols = new Map();
const referencedSymbolsInExportedTypes = new Set();
const visitors = getVisitors(sourceFile);

@@ -192,3 +195,3 @@ const addInternalImport = (options) => {

fixes: fix ? [fix] : [],
refs: 0,
refs: [0, false],
isReExport,

@@ -289,2 +292,5 @@ });

}
if (ignoreExportsUsedInFile && !isTopLevel && isReferencedInExportedType(node, symbol)) {
referencedSymbolsInExportedTypes.add(symbol.exportSymbol);
}
}

@@ -312,30 +318,36 @@ }

let index = 0;
while (index < sourceFile.text.length && (index = sourceFile.text.indexOf(item.identifier, index)) !== -1) {
const isDeclaration = index === item.pos || index === item.pos + 1;
if (!isDeclaration) {
const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, index));
if (symbol) {
if (item.symbol === symbol) {
item.refs = 1;
break;
}
const declaration = symbol.declarations?.[0];
if (declaration) {
if (item.symbol === declaration.name?.flowNode?.node?.symbol) {
item.refs = 1;
break;
const text = sourceFile.text;
const id = item.identifier;
while (index < text.length && (index = text.indexOf(id, index)) !== -1) {
if (!isIdChar(text.charAt(index - 1)) && !isIdChar(text.charAt(index + id.length))) {
const isDeclaration = index === item.pos || index === item.pos + 1;
if (!isDeclaration) {
const symbol = typeChecker.getSymbolAtLocation(ts.getTokenAtPosition(sourceFile, index));
if (symbol) {
const isInExportedType = referencedSymbolsInExportedTypes.has(symbol);
if (item.symbol === symbol) {
item.refs = [1, isInExportedType];
if (isInExportedType || isType(item))
break;
}
if (ts.isImportSpecifier(declaration) && symbols.has(symbol)) {
item.refs = 1;
break;
const declaration = symbol.declarations?.[0];
if (declaration) {
if (item.symbol === declaration.name?.flowNode?.node?.symbol) {
item.refs = [1, isInExportedType];
break;
}
if (ts.isImportSpecifier(declaration) && symbols.has(symbol)) {
item.refs = [1, isInExportedType];
break;
}
}
symbols.add(symbol);
}
symbols.add(symbol);
}
}
index += item.identifier.length;
index += id.length;
}
};
for (const item of exports.values()) {
if (options.ignoreExportsUsedInFile)
if (ignoreExportsUsedInFile)
setRefs(item);

@@ -342,0 +354,0 @@ for (const member of item.members) {

export declare const toRegexOrString: (value: string | RegExp) => string | RegExp;
export declare const findMatch: (haystack: undefined | (string | RegExp)[], needle: string) => string | RegExp | undefined;
export declare const isIdChar: (text: string) => boolean;
export const toRegexOrString = (value) => typeof value === 'string' && /[*+\\(|{^$]/.test(value) ? new RegExp(value) : value;
export const findMatch = (haystack, needle) => haystack?.find(n => (typeof n === 'string' ? n === needle : n.test(needle)));
const idCharMatch = /[a-zA-Z0-9$_]/;
export const isIdChar = (text) => idCharMatch.test(text);
import type { Tags } from '../types/cli.js';
export declare const splitTags: (rawTags: string[]) => Tags;
export declare const shouldIgnore: (jsDocTags: Set<string>, tags: Tags) => boolean;
export declare const getShouldIgnoreHandler: (tags: Tags, isProduction: boolean) => (jsDocTags: Set<string>) => boolean;
export declare const getShouldIgnoreHandler: (isProduction: boolean) => (jsDocTags: Set<string>) => boolean;
export declare const getShouldIgnoreTagHandler: (tags: Tags) => (jsDocTags: Set<string>) => boolean;

@@ -19,6 +19,6 @@ export const splitTags = (rawTags) => {

};
export const getShouldIgnoreHandler = (tags, isProduction) => (jsDocTags) => jsDocTags.has('@public') ||
export const getShouldIgnoreHandler = (isProduction) => (jsDocTags) => jsDocTags.has('@public') ||
jsDocTags.has('@beta') ||
jsDocTags.has('@alias') ||
shouldIgnore(jsDocTags, tags) ||
(isProduction && jsDocTags.has('@internal'));
export const getShouldIgnoreTagHandler = (tags) => (jsDocTags) => shouldIgnore(jsDocTags, tags);

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

export declare const version = "5.22.3";
export declare const version = "5.23.0";

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

export const version = '5.22.3';
export const version = '5.23.0';

@@ -35,3 +35,3 @@ import { CacheConsultant } from './CacheConsultant.js';

negatedWorkspacePatterns: string[];
enabledPluginsMap: Record<"angular" | "astro" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "drizzle" | "eleventy" | "eslint" | "gatsby" | "githubActions" | "graphqlCodegen" | "husky" | "jest" | "lefthook" | "linthtml" | "lintStaged" | "lockfileLint" | "lostPixel" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nodeTestRunner" | "npmPackageJsonLint" | "nx" | "nyc" | "oclif" | "playwright" | "playwrightCt" | "postcss" | "prettier" | "releaseIt" | "remark" | "remix" | "rollup" | "semanticRelease" | "sentry" | "simpleGitHooks" | "sizeLimit" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vercelOg" | "vite" | "vitest" | "vue" | "webdriverIo" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie", boolean>;
enabledPluginsMap: Record<"astro" | "angular" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "eleventy" | "eslint" | "gatsby" | "husky" | "jest" | "lefthook" | "linthtml" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nx" | "nyc" | "oclif" | "playwright" | "postcss" | "prettier" | "remark" | "remix" | "rollup" | "sentry" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vue" | "vite" | "vitest" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie" | "drizzle" | "githubActions" | "graphqlCodegen" | "lintStaged" | "lockfileLint" | "lostPixel" | "nodeTestRunner" | "npmPackageJsonLint" | "playwrightCt" | "releaseIt" | "semanticRelease" | "simpleGitHooks" | "sizeLimit" | "vercelOg" | "webdriverIo", boolean>;
enabledPlugins: PluginName[];

@@ -56,3 +56,3 @@ enabledPluginsInAncestors: string[];

referencedDependencies: ReferencedDependencies;
enabledPlugins: ("angular" | "astro" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "drizzle" | "eleventy" | "eslint" | "gatsby" | "githubActions" | "graphqlCodegen" | "husky" | "jest" | "lefthook" | "linthtml" | "lintStaged" | "lockfileLint" | "lostPixel" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nodeTestRunner" | "npmPackageJsonLint" | "nx" | "nyc" | "oclif" | "playwright" | "playwrightCt" | "postcss" | "prettier" | "releaseIt" | "remark" | "remix" | "rollup" | "semanticRelease" | "sentry" | "simpleGitHooks" | "sizeLimit" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vercelOg" | "vite" | "vitest" | "vue" | "webdriverIo" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie")[];
enabledPlugins: ("astro" | "angular" | "ava" | "babel" | "capacitor" | "changesets" | "commitizen" | "commitlint" | "cspell" | "cucumber" | "cypress" | "eleventy" | "eslint" | "gatsby" | "husky" | "jest" | "lefthook" | "linthtml" | "markdownlint" | "mocha" | "moonrepo" | "msw" | "netlify" | "next" | "nx" | "nyc" | "oclif" | "playwright" | "postcss" | "prettier" | "remark" | "remix" | "rollup" | "sentry" | "storybook" | "stryker" | "stylelint" | "svelte" | "syncpack" | "tailwind" | "tsup" | "typedoc" | "typescript" | "unbuild" | "unocss" | "vue" | "vite" | "vitest" | "webpack" | "wireit" | "wrangler" | "xo" | "yorkie" | "drizzle" | "githubActions" | "graphqlCodegen" | "lintStaged" | "lockfileLint" | "lostPixel" | "nodeTestRunner" | "npmPackageJsonLint" | "playwrightCt" | "releaseIt" | "semanticRelease" | "simpleGitHooks" | "sizeLimit" | "vercelOg" | "webdriverIo")[];
}>;

@@ -59,0 +59,0 @@ onDispose(): void;

{
"name": "knip",
"version": "5.22.3",
"version": "5.23.0",
"description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",

@@ -5,0 +5,0 @@ "homepage": "https://knip.dev",

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