Skip to content

Commit 09fc536

Browse files
committedJul 10, 2024
Minor refactors
1 parent 3c13118 commit 09fc536

File tree

9 files changed

+39
-25
lines changed

9 files changed

+39
-25
lines changed
 

‎packages/knip/src/DependencyDeputy.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { isBuiltin } from 'node:module';
22
import type { Workspace } from './ConfigurationChief.js';
33
import {
4+
DT_SCOPE,
45
IGNORED_DEPENDENCIES,
56
IGNORED_GLOBAL_BINARIES,
67
IGNORED_RUNTIME_DEPENDENCIES,
@@ -256,7 +257,7 @@ export class DependencyDeputy {
256257
if (isPeerDep && peerDepRecs[dependency]) return false;
257258

258259
const [scope, typedDependency] = dependency.split('/');
259-
if (scope === '@types') {
260+
if (scope === DT_SCOPE) {
260261
// The `pkg` dependency already has types included, i.e. this `@types/pkg` is obsolete
261262
if (hasTypesIncluded?.has(typedDependency)) return false;
262263

‎packages/knip/src/ProjectPrincipal.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { PrincipalOptions } from './PrincipalFactory.js';
44
import type { ReferencedDependencies } from './WorkspaceWorker.js';
55
import { getCompilerExtensions } from './compilers/index.js';
66
import type { AsyncCompilers, SyncCompilers } from './compilers/types.js';
7-
import { ANONYMOUS, DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from './constants.js';
7+
import { ANONYMOUS, DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS, PUBLIC_TAG } from './constants.js';
88
import type { DependencyGraph, Export, ExportMember, FileNode, UnresolvedImport } from './types/dependency-graph.js';
99
import type { BoundSourceFile } from './typescript/SourceFile.js';
1010
import type { SourceFileManager } from './typescript/SourceFileManager.js';
@@ -269,6 +269,9 @@ export class ProjectPrincipal {
269269
// Ignore Deno style http import specifiers
270270
if (specifier.startsWith('http')) continue;
271271

272+
// All bets are off after failing to resolve module:
273+
// - either add to external dependencies if it quacks like that so it'll end up as unused or unlisted dependency
274+
// - or maintain unresolved status if not ignored and not foreign
272275
const sanitizedSpecifier = sanitizeSpecifier(specifier);
273276
if (isStartsLikePackageName(sanitizedSpecifier)) {
274277
external.add(sanitizedSpecifier);
@@ -308,15 +311,15 @@ export class ProjectPrincipal {
308311
}
309312

310313
return members.filter(member => {
311-
if (member.jsDocTags.has('@public')) return false;
314+
if (member.jsDocTags.has(PUBLIC_TAG)) return false;
312315
const referencedSymbols = this.findReferences?.(filePath, member.pos) ?? [];
313316
const refs = referencedSymbols.flatMap(refs => refs.references).filter(ref => !ref.isDefinition);
314317
return refs.length === 0;
315318
});
316319
}
317320

318321
public hasExternalReferences(filePath: string, exportedItem: Export) {
319-
if (exportedItem.jsDocTags.has('@public')) return false;
322+
if (exportedItem.jsDocTags.has(PUBLIC_TAG)) return false;
320323

321324
if (!this.findReferences) {
322325
const languageService = ts.createLanguageService(this.backend.languageServiceHost, ts.createDocumentRegistry());

‎packages/knip/src/constants.ts

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ export const DEFAULT_EXTENSIONS = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx',
2222

2323
export const GLOBAL_IGNORE_PATTERNS = ['**/node_modules/**', '.yarn'];
2424

25+
export const PUBLIC_TAG = '@public';
26+
export const INTERNAL_TAG = '@internal';
27+
export const BETA_TAG = '@beta';
28+
export const ALIAS_TAG = '@alias';
29+
30+
export const DT_SCOPE = '@types';
31+
2532
// Binaries that are expected to be globally installed
2633
// In other words, https://www.npmjs.com/package/[name] is NOT the expected dependency
2734
// Package may exist in npm registry, but last publish is at least 6 years ago

‎packages/knip/src/index.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { debugLog, debugLogArray, debugLogObject } from './util/debug.js';
2525
import { addNsValues, addValues, createFileNode } from './util/dependency-graph.js';
2626
import { isFile } from './util/fs.js';
2727
import { _glob, negate } from './util/glob.js';
28-
import { getGitIgnoredFn } from './util/globby.js';
28+
import { getGitIgnoredHandler } from './util/globby.js';
2929
import { getHandler } from './util/handle-dependency.js';
3030
import { getHasStrictlyNsReferences, getType } from './util/has-strictly-ns-references.js';
3131
import { getIsIdentifierReferencedHandler } from './util/is-identifier-referenced.js';
@@ -66,7 +66,7 @@ export const main = async (unresolvedConfiguration: CommandLineOptions) => {
6666
const factory = new PrincipalFactory();
6767
const streamer = new ConsoleStreamer({ isEnabled: isShowProgress });
6868

69-
const isGitIgnored = await getGitIgnoredFn({ cwd, gitignore });
69+
const isGitIgnored = await getGitIgnoredHandler({ cwd, gitignore });
7070
const toSourceFilePath = getToSourcePathHandler(chief);
7171

7272
streamer.cast('Reading workspace configuration(s)...');
@@ -156,12 +156,12 @@ export const main = async (unresolvedConfiguration: CommandLineOptions) => {
156156

157157
collector.addIgnorePatterns(ignore.map(pattern => join(cwd, pattern)));
158158

159-
// Add dependencies from package.json
159+
// Add dependencies from package.json#scripts
160160
const options = { manifestScriptNames, cwd: dir, dependencies };
161161
const dependenciesFromManifest = _getDependenciesFromScripts(manifestScripts, options);
162162
principal.addReferencedDependencies(name, new Set(dependenciesFromManifest.map(id => [manifestPath, id])));
163163

164-
// Add entry paths from package.json
164+
// Add entry paths from package.json#main, #bin, #exports
165165
const entryPathsFromManifest = await getEntryPathFromManifest(manifest, { ...sharedGlobOptions, ignore });
166166
debugLogArray(name, 'Entry paths in package.json', entryPathsFromManifest);
167167
principal.addEntryPaths(entryPathsFromManifest);

‎packages/knip/src/typescript/getImportsAndExports.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { isBuiltin } from 'node:module';
22
import ts from 'typescript';
3-
import { ANONYMOUS, DEFAULT_EXTENSIONS, IMPORT_STAR } from '../constants.js';
3+
import { ALIAS_TAG, ANONYMOUS, DEFAULT_EXTENSIONS, IMPORT_STAR } from '../constants.js';
44
import type { Tags } from '../types/cli.js';
55
import type { Export, ExportMap, ExportMember, ImportMap, UnresolvedImport } from '../types/dependency-graph.js';
66
import type { ExportNode, ExportNodeMember } from '../types/exports.js';
@@ -265,7 +265,7 @@ const getImportsAndExports = (
265265
});
266266
}
267267

268-
if (!jsDocTags.has('@alias')) {
268+
if (!jsDocTags.has(ALIAS_TAG)) {
269269
if (ts.isExportAssignment(node)) maybeAddAliasedExport(node.expression, 'default');
270270
if (ts.isVariableDeclaration(node)) maybeAddAliasedExport(node.initializer, identifier);
271271
}

‎packages/knip/src/util/globby.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { readFileSync } from 'node:fs';
22
import { promisify } from 'node:util';
3-
import { walk as _walk } from '@nodelib/fs.walk';
4-
import type { Entry } from '@nodelib/fs.walk';
5-
import type { Options as FastGlobOptions } from 'fast-glob';
6-
import fg from 'fast-glob';
3+
import { type Entry, walk as _walk } from '@nodelib/fs.walk';
4+
import fg, { type Options as FastGlobOptions } from 'fast-glob';
75
import picomatch from 'picomatch';
86
import { GLOBAL_IGNORE_PATTERNS, ROOT_WORKSPACE_NAME } from '../constants.js';
97
import { timerify } from './Performance.js';
@@ -159,7 +157,7 @@ export async function globby(patterns: string | string[], options: GlobOptions):
159157
return fg.glob(patterns, fastGlobOptions);
160158
}
161159

162-
export async function getGitIgnoredFn(options: Options): Promise<(path: string) => boolean> {
160+
export async function getGitIgnoredHandler(options: Options): Promise<(path: string) => boolean> {
163161
cachedIgnores.clear();
164162

165163
if (options.gitignore === false) return () => false;

‎packages/knip/src/util/modules.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { isBuiltin } from 'node:module';
2+
import { DT_SCOPE } from '../constants.js';
23
import type { PackageJson } from '../types/package-json.js';
34
import { _glob } from './glob.js';
45
import { getStringValues } from './object.js';
@@ -10,26 +11,28 @@ export const getPackageNameFromModuleSpecifier = (moduleSpecifier: string) => {
1011
return moduleSpecifier.startsWith('@') ? parts.join('/') : parts[0];
1112
};
1213

14+
const lastPackageNameMatch = /(?<=node_modules\/)(@[^/]+\/[^/]+|[^/]+)/g;
1315
export const getPackageNameFromFilePath = (value: string) => {
14-
const match = toPosix(value).match(/(?<=node_modules\/)(@[^/]+\/[^/]+|[^/]+)/g);
16+
const match = toPosix(value).match(lastPackageNameMatch);
1517
if (match) return match[match.length - 1];
1618
return value;
1719
};
1820

21+
const packageNameMatch = /.*\/node_modules\/(.+)/;
1922
export const normalizeSpecifierFromFilePath = (value: string) => {
20-
const match = toPosix(value).match(/.*\/node_modules\/(.+)/);
23+
const match = toPosix(value).match(packageNameMatch);
2124
if (match) return match[match.length - 1];
2225
return value;
2326
};
2427

2528
export const isStartsLikePackageName = (specifier: string) => /^@?[a-z0-9]/.test(specifier);
2629

27-
export const isDefinitelyTyped = (packageName: string) => packageName.startsWith('@types/');
30+
export const isDefinitelyTyped = (packageName: string) => packageName.startsWith(`${DT_SCOPE}/`);
2831

2932
export const getDefinitelyTypedFor = (packageName: string) => {
3033
if (isDefinitelyTyped(packageName)) return packageName;
31-
if (packageName.startsWith('@')) return `@types/${packageName.slice(1).replace('/', '__')}`;
32-
return `@types/${packageName}`;
34+
if (packageName.startsWith('@')) return [DT_SCOPE, packageName.slice(1).replace('/', '__')].join('/');
35+
return [DT_SCOPE, packageName].join('/');
3336
};
3437

3538
export const getPackageFromDefinitelyTyped = (typedDependency: string) => {

‎packages/knip/src/util/regex.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
const isRegexLike = /[*+\\(|{^$]/;
12
export const toRegexOrString = (value: string | RegExp) =>
2-
typeof value === 'string' && /[*+\\(|{^$]/.test(value) ? new RegExp(value) : value;
3+
typeof value === 'string' && isRegexLike.test(value) ? new RegExp(value) : value;
34

45
export const findMatch = (haystack: undefined | (string | RegExp)[], needle: string) =>
56
haystack?.find(n => (typeof n === 'string' ? n === needle : n.test(needle)));

‎packages/knip/src/util/tag.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ALIAS_TAG, BETA_TAG, INTERNAL_TAG, PUBLIC_TAG } from '../constants.js';
12
import type { Tags } from '../types/cli.js';
23

34
export const splitTags = (rawTags: string[]) => {
@@ -22,9 +23,9 @@ export const shouldIgnore = (jsDocTags: Set<string>, tags: Tags) => {
2223
};
2324

2425
export const getShouldIgnoreHandler = (isProduction: boolean) => (jsDocTags: Set<string>) =>
25-
jsDocTags.has('@public') ||
26-
jsDocTags.has('@beta') ||
27-
jsDocTags.has('@alias') ||
28-
(isProduction && jsDocTags.has('@internal'));
26+
jsDocTags.has(PUBLIC_TAG) ||
27+
jsDocTags.has(BETA_TAG) ||
28+
jsDocTags.has(ALIAS_TAG) ||
29+
(isProduction && jsDocTags.has(INTERNAL_TAG));
2930

3031
export const getShouldIgnoreTagHandler = (tags: Tags) => (jsDocTags: Set<string>) => shouldIgnore(jsDocTags, tags);

0 commit comments

Comments
 (0)
Please sign in to comment.