knip 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,16 +79,18 @@ Please read on if you think you have too many results: [too many false positives
79
79
  knip [options]
80
80
 
81
81
  Options:
82
- -c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
83
- -t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
84
- --dir Working directory (default: current working directory)
85
- --include Report only listed issue group(s) (see below)
86
- --exclude Exclude issue group(s) from report (see below)
87
- --dev Include `devDependencies` in report(s) (default: false)
88
- --no-progress Don't show dynamic progress updates
89
- --max-issues Maximum number of issues before non-zero exit code (default: 0)
90
- --reporter Select reporter: symbols, compact (default: symbols)
91
- --jsdoc Enable JSDoc parsing, with options: public (default: disabled)
82
+ -c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
83
+ -t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
84
+ --dir Working directory (default: current working directory)
85
+ --include Report only listed issue group(s) (see below)
86
+ --exclude Exclude issue group(s) from report (see below)
87
+ --ignore Ignore files matching this glob pattern (can be set multiple times)
88
+ --no-gitignore Don't use .gitignore
89
+ --dev Include `devDependencies` in report(s)
90
+ --no-progress Don't show dynamic progress updates
91
+ --max-issues Maximum number of issues before non-zero exit code (default: 0)
92
+ --reporter Select reporter: symbols, compact (default: symbols)
93
+ --jsdoc Enable JSDoc parsing, with options: public
92
94
 
93
95
  Issue groups: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
94
96
 
@@ -97,6 +99,7 @@ Examples:
97
99
  $ knip
98
100
  $ knip --dir packages/client --include files
99
101
  $ knip -c ./knip.js --reporter compact --jsdoc public
102
+ $ knip --ignore 'lib/**/*.ts' --ignore build
100
103
 
101
104
  More info: https://github.com/webpro/knip
102
105
  ```
package/dist/cli.js CHANGED
@@ -9,10 +9,11 @@ const node_util_1 = require("node:util");
9
9
  const typescript_1 = __importDefault(require("typescript"));
10
10
  const help_1 = require("./help");
11
11
  const config_1 = require("./util/config");
12
- const path_1 = require("./util/path");
12
+ const fs_1 = require("./util/fs");
13
+ const ignore_1 = require("./util/ignore");
13
14
  const reporters_1 = __importDefault(require("./reporters"));
14
15
  const _1 = require(".");
15
- const { values: { help, dir, config: configFilePath = 'knip.json', tsConfig: tsConfigFilePath, include = [], exclude = [], dev: isDev = false, 'no-progress': noProgress = false, reporter = 'symbols', jsdoc = [], 'max-issues': maxIssues = '0', }, } = (0, node_util_1.parseArgs)({
16
+ const { values: { help, dir, config: configFilePath = 'knip.json', tsConfig: tsConfigFilePath, include = [], exclude = [], dev: isDev = false, 'no-progress': noProgress = false, ignore = [], 'no-gitignore': isNoGitIgnore = false, reporter = 'symbols', jsdoc = [], 'max-issues': maxIssues = '0', }, } = (0, node_util_1.parseArgs)({
16
17
  options: {
17
18
  help: { type: 'boolean' },
18
19
  config: { type: 'string', short: 'c' },
@@ -22,6 +23,8 @@ const { values: { help, dir, config: configFilePath = 'knip.json', tsConfig: tsC
22
23
  exclude: { type: 'string', multiple: true },
23
24
  dev: { type: 'boolean' },
24
25
  'max-issues': { type: 'string' },
26
+ ignore: { type: 'string', multiple: true },
27
+ 'no-gitignore': { type: 'boolean' },
25
28
  'no-progress': { type: 'boolean' },
26
29
  reporter: { type: 'string' },
27
30
  jsdoc: { type: 'string', multiple: true },
@@ -36,8 +39,8 @@ const workingDir = dir ? node_path_1.default.resolve(dir) : cwd;
36
39
  const isShowProgress = noProgress === false ? process.stdout.isTTY && typeof process.stdout.cursorTo === 'function' : !noProgress;
37
40
  const printReport = reporter in reporters_1.default ? reporters_1.default[reporter] : require(node_path_1.default.join(workingDir, reporter));
38
41
  const main = async () => {
39
- const localConfigurationPath = await (0, path_1.findFile)(workingDir, configFilePath);
40
- const manifestPath = await (0, path_1.findFile)(workingDir, 'package.json');
42
+ const localConfigurationPath = await (0, fs_1.findFile)(workingDir, configFilePath);
43
+ const manifestPath = await (0, fs_1.findFile)(workingDir, 'package.json');
41
44
  const localConfiguration = localConfigurationPath && require(localConfigurationPath);
42
45
  const manifest = manifestPath && require(manifestPath);
43
46
  if (!localConfigurationPath && !manifest.knip) {
@@ -53,7 +56,7 @@ const main = async () => {
53
56
  }
54
57
  const report = (0, config_1.resolveIncludedIssueGroups)(include, exclude, resolvedConfig);
55
58
  let tsConfigPaths = [];
56
- const tsConfigPath = await (0, path_1.findFile)(workingDir, tsConfigFilePath ?? 'tsconfig.json');
59
+ const tsConfigPath = await (0, fs_1.findFile)(workingDir, tsConfigFilePath ?? 'tsconfig.json');
57
60
  if (tsConfigFilePath && !tsConfigPath) {
58
61
  console.error(`Unable to find ${tsConfigFilePath}\n`);
59
62
  (0, help_1.printHelp)();
@@ -70,6 +73,11 @@ const main = async () => {
70
73
  process.exit(1);
71
74
  }
72
75
  }
76
+ const ignorePatterns = ignore.map(ignore_1.convertPattern);
77
+ if (!isNoGitIgnore) {
78
+ const patterns = await (0, ignore_1.readIgnorePatterns)(cwd, workingDir);
79
+ patterns.forEach(pattern => ignorePatterns.push(pattern));
80
+ }
73
81
  const config = {
74
82
  workingDir,
75
83
  report,
@@ -78,6 +86,7 @@ const main = async () => {
78
86
  isDev: typeof resolvedConfig.dev === 'boolean' ? resolvedConfig.dev : isDev,
79
87
  tsConfigFilePath,
80
88
  tsConfigPaths,
89
+ ignorePatterns,
81
90
  isShowProgress,
82
91
  jsDocOptions: {
83
92
  isReadPublicTag: jsdoc.includes('public'),
package/dist/help.js CHANGED
@@ -5,16 +5,18 @@ const printHelp = () => {
5
5
  console.log(`knip [options]
6
6
 
7
7
  Options:
8
- -c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
9
- -t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
10
- --dir Working directory (default: current working directory)
11
- --include Report only listed issue group(s) (see below)
12
- --exclude Exclude issue group(s) from report (see below)
13
- --dev Include \`devDependencies\` in report(s) (default: false)
14
- --no-progress Don't show dynamic progress updates
15
- --max-issues Maximum number of issues before non-zero exit code (default: 0)
16
- --reporter Select reporter: symbols, compact (default: symbols)
17
- --jsdoc Enable JSDoc parsing, with options: public (default: disabled)
8
+ -c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
9
+ -t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
10
+ --dir Working directory (default: current working directory)
11
+ --include Report only listed issue group(s) (see below)
12
+ --exclude Exclude issue group(s) from report (see below)
13
+ --ignore Ignore files matching this glob pattern (can be set multiple times)
14
+ --no-gitignore Don't use .gitignore
15
+ --dev Include \`devDependencies\` in report(s)
16
+ --no-progress Don't show dynamic progress updates
17
+ --max-issues Maximum number of issues before non-zero exit code (default: 0)
18
+ --reporter Select reporter: symbols, compact (default: symbols)
19
+ --jsdoc Enable JSDoc parsing, with options: public
18
20
 
19
21
  Issue groups: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
20
22
 
@@ -23,6 +25,7 @@ Examples:
23
25
  $ knip
24
26
  $ knip --dir packages/client --include files
25
27
  $ knip -c ./knip.js --reporter compact --jsdoc public
28
+ $ knip --ignore 'lib/**/*.ts' --ignore build
26
29
 
27
30
  More info: https://github.com/webpro/knip`);
28
31
  };
package/dist/types.d.ts CHANGED
@@ -20,11 +20,11 @@ export declare type Issues = {
20
20
  nsTypes: UnusedExportIssues;
21
21
  duplicates: UnusedExportIssues;
22
22
  };
23
- export declare type IssueType = keyof Issues;
23
+ declare type IssueType = keyof Issues;
24
24
  export declare type ProjectIssueType = Extract<IssueType, 'files' | 'dependencies' | 'devDependencies'>;
25
25
  export declare type SymbolIssueType = Exclude<IssueType, ProjectIssueType>;
26
26
  export declare type IssueGroup = 'files' | 'dependencies' | 'unlisted' | 'exports' | 'nsExports' | 'types' | 'nsTypes' | 'duplicates';
27
- export declare type BaseLocalConfiguration = {
27
+ declare type BaseLocalConfiguration = {
28
28
  entryFiles: string[];
29
29
  projectFiles: string[];
30
30
  };
@@ -46,6 +46,7 @@ export declare type Configuration = LocalConfiguration & {
46
46
  isDev: boolean;
47
47
  tsConfigFilePath: undefined | string;
48
48
  tsConfigPaths: string[];
49
+ ignorePatterns: string[];
49
50
  isShowProgress: boolean;
50
51
  jsDocOptions: {
51
52
  isReadPublicTag: boolean;
@@ -0,0 +1,2 @@
1
+ export declare const isFile: (filePath: string) => Promise<boolean>;
2
+ export declare const findFile: (cwd: string, fileName: string) => Promise<string | undefined>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.findFile = exports.isFile = void 0;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const isFile = async (filePath) => {
10
+ try {
11
+ const stats = await promises_1.default.stat(filePath);
12
+ return stats.isFile();
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ };
18
+ exports.isFile = isFile;
19
+ const findFile = async (cwd, fileName) => {
20
+ const filePath = node_path_1.default.join(cwd, fileName);
21
+ if (await (0, exports.isFile)(filePath)) {
22
+ return filePath;
23
+ }
24
+ else {
25
+ const parentDir = node_path_1.default.resolve(cwd, '..');
26
+ return parentDir === '/' ? undefined : (0, exports.findFile)(parentDir, fileName);
27
+ }
28
+ };
29
+ exports.findFile = findFile;
@@ -0,0 +1,2 @@
1
+ export declare const convertPattern: (pattern: string) => string;
2
+ export declare const readIgnorePatterns: (cwd: string, workingDir: string) => Promise<string[]>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readIgnorePatterns = exports.convertPattern = void 0;
7
+ const promises_1 = __importDefault(require("node:fs/promises"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const fs_1 = require("./fs");
10
+ const path_1 = require("./path");
11
+ const convertPattern = (pattern) => (pattern.startsWith('!') ? pattern.substring(1) : `!${pattern}`);
12
+ exports.convertPattern = convertPattern;
13
+ const readIgnoreFile = async (filePath) => {
14
+ let contents = '';
15
+ try {
16
+ contents = (await promises_1.default.readFile(filePath)).toString();
17
+ }
18
+ catch (error) {
19
+ }
20
+ return contents.split(/\r?\n/).filter(line => line && !line.startsWith('#'));
21
+ };
22
+ const traverseDirs = async (rootDir, currentDir, patterns = []) => {
23
+ const gitIgnorePath = node_path_1.default.join(currentDir, '.gitignore');
24
+ const parentDir = node_path_1.default.resolve(currentDir, '..');
25
+ if (await (0, fs_1.isFile)(gitIgnorePath)) {
26
+ (await readIgnoreFile(gitIgnorePath))
27
+ .map(pattern => (0, path_1.addWorkingDirToPattern)(currentDir, pattern))
28
+ .forEach(pattern => patterns.push(pattern));
29
+ }
30
+ if (rootDir === currentDir || parentDir === '/')
31
+ return patterns;
32
+ return traverseDirs(rootDir, parentDir, patterns);
33
+ };
34
+ const readIgnorePatterns = async (cwd, workingDir) => {
35
+ const patterns = await traverseDirs(cwd, workingDir);
36
+ return patterns.map(exports.convertPattern);
37
+ };
38
+ exports.readIgnorePatterns = readIgnorePatterns;
@@ -1 +1,3 @@
1
- export declare const findFile: (cwd: string, fileName: string) => Promise<string | undefined>;
1
+ import type { Configuration } from '../types';
2
+ export declare const addWorkingDirToPattern: (workingDir: string, pattern: string) => string;
3
+ export declare const resolvePaths: (configuration: Configuration, patterns: string[]) => string[];
package/dist/util/path.js CHANGED
@@ -3,26 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.findFile = void 0;
7
- const promises_1 = __importDefault(require("node:fs/promises"));
6
+ exports.resolvePaths = exports.addWorkingDirToPattern = void 0;
8
7
  const node_path_1 = __importDefault(require("node:path"));
9
- const isFile = async (filePath) => {
10
- try {
11
- const stats = await promises_1.default.stat(filePath);
12
- return stats.isFile();
13
- }
14
- catch {
15
- return false;
16
- }
8
+ const addWorkingDirToPattern = (workingDir, pattern) => {
9
+ if (pattern.startsWith('!'))
10
+ return '!' + node_path_1.default.join(workingDir, pattern.slice(1));
11
+ return node_path_1.default.join(workingDir, pattern);
17
12
  };
18
- const findFile = async (cwd, fileName) => {
19
- const filePath = node_path_1.default.join(cwd, fileName);
20
- if (await isFile(filePath)) {
21
- return filePath;
22
- }
23
- else {
24
- const parentDir = node_path_1.default.resolve(cwd, '..');
25
- return parentDir === '/' ? undefined : (0, exports.findFile)(parentDir, fileName);
26
- }
13
+ exports.addWorkingDirToPattern = addWorkingDirToPattern;
14
+ const resolvePaths = (configuration, patterns) => {
15
+ const { workingDir } = configuration;
16
+ return patterns.map(pattern => (0, exports.addWorkingDirToPattern)(workingDir, pattern));
27
17
  };
28
- exports.findFile = findFile;
18
+ exports.resolvePaths = resolvePaths;
@@ -1,5 +1,5 @@
1
1
  import { Project } from 'ts-morph';
2
2
  import type { SourceFile } from 'ts-morph';
3
3
  import type { Configuration } from '../types';
4
- export declare const createProject: (configuration: Configuration, paths?: string | string[]) => Promise<Project>;
4
+ export declare const createProject: (configuration: Configuration, paths?: string[]) => Promise<Project>;
5
5
  export declare const partitionSourceFiles: (projectFiles: SourceFile[], productionFiles: SourceFile[]) => SourceFile[][];
@@ -1,28 +1,20 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.partitionSourceFiles = exports.createProject = void 0;
7
- const node_path_1 = __importDefault(require("node:path"));
8
4
  const ts_morph_1 = require("ts-morph");
9
- const resolvePaths = (cwd, patterns) => {
10
- return [patterns].flat().map(pattern => {
11
- if (pattern.startsWith('!'))
12
- return '!' + node_path_1.default.join(cwd, pattern.slice(1));
13
- return node_path_1.default.join(cwd, pattern);
14
- });
15
- };
5
+ const path_1 = require("./path");
16
6
  const createProject = async (configuration, paths) => {
17
- const { tsConfigFilePath, workingDir } = configuration;
7
+ const { tsConfigFilePath, ignorePatterns } = configuration;
18
8
  const tsConfig = tsConfigFilePath ? { tsConfigFilePath } : { compilerOptions: { allowJs: true } };
19
9
  const workspace = new ts_morph_1.Project({
20
10
  ...tsConfig,
21
11
  skipAddingFilesFromTsConfig: true,
22
12
  skipFileDependencyResolution: true,
23
13
  });
24
- if (paths)
25
- workspace.addSourceFilesAtPaths(resolvePaths(workingDir, paths));
14
+ if (paths) {
15
+ const resolvedPaths = (0, path_1.resolvePaths)(configuration, paths);
16
+ workspace.addSourceFilesAtPaths([...resolvedPaths, ...ignorePatterns]);
17
+ }
26
18
  return workspace;
27
19
  };
28
20
  exports.createProject = createProject;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Find unused files and exports in your TypeScript project",
5
5
  "keywords": [
6
6
  "find",