Skip to content

Commit

Permalink
Minor refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
webpro committed Jul 10, 2024
1 parent 3c13118 commit 09fc536
Show file tree
Hide file tree
Showing 9 changed files with 39 additions and 25 deletions.
3 changes: 2 additions & 1 deletion packages/knip/src/DependencyDeputy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isBuiltin } from 'node:module';
import type { Workspace } from './ConfigurationChief.js';
import {
DT_SCOPE,
IGNORED_DEPENDENCIES,
IGNORED_GLOBAL_BINARIES,
IGNORED_RUNTIME_DEPENDENCIES,
Expand Down Expand Up @@ -256,7 +257,7 @@ export class DependencyDeputy {
if (isPeerDep && peerDepRecs[dependency]) return false;

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

Expand Down
9 changes: 6 additions & 3 deletions packages/knip/src/ProjectPrincipal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { PrincipalOptions } from './PrincipalFactory.js';
import type { ReferencedDependencies } from './WorkspaceWorker.js';
import { getCompilerExtensions } from './compilers/index.js';
import type { AsyncCompilers, SyncCompilers } from './compilers/types.js';
import { ANONYMOUS, DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from './constants.js';
import { ANONYMOUS, DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS, PUBLIC_TAG } from './constants.js';
import type { DependencyGraph, Export, ExportMember, FileNode, UnresolvedImport } from './types/dependency-graph.js';
import type { BoundSourceFile } from './typescript/SourceFile.js';
import type { SourceFileManager } from './typescript/SourceFileManager.js';
Expand Down Expand Up @@ -269,6 +269,9 @@ export class ProjectPrincipal {
// Ignore Deno style http import specifiers
if (specifier.startsWith('http')) continue;

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

return members.filter(member => {
if (member.jsDocTags.has('@public')) return false;
if (member.jsDocTags.has(PUBLIC_TAG)) return false;
const referencedSymbols = this.findReferences?.(filePath, member.pos) ?? [];
const refs = referencedSymbols.flatMap(refs => refs.references).filter(ref => !ref.isDefinition);
return refs.length === 0;
});
}

public hasExternalReferences(filePath: string, exportedItem: Export) {
if (exportedItem.jsDocTags.has('@public')) return false;
if (exportedItem.jsDocTags.has(PUBLIC_TAG)) return false;

if (!this.findReferences) {
const languageService = ts.createLanguageService(this.backend.languageServiceHost, ts.createDocumentRegistry());
Expand Down
7 changes: 7 additions & 0 deletions packages/knip/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export const DEFAULT_EXTENSIONS = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx',

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

export const PUBLIC_TAG = '@public';
export const INTERNAL_TAG = '@internal';
export const BETA_TAG = '@beta';
export const ALIAS_TAG = '@alias';

export const DT_SCOPE = '@types';

// Binaries that are expected to be globally installed
// In other words, https://www.npmjs.com/package/[name] is NOT the expected dependency
// Package may exist in npm registry, but last publish is at least 6 years ago
Expand Down
8 changes: 4 additions & 4 deletions packages/knip/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { debugLog, debugLogArray, debugLogObject } from './util/debug.js';
import { addNsValues, addValues, createFileNode } from './util/dependency-graph.js';
import { isFile } from './util/fs.js';
import { _glob, negate } from './util/glob.js';
import { getGitIgnoredFn } from './util/globby.js';
import { getGitIgnoredHandler } from './util/globby.js';
import { getHandler } from './util/handle-dependency.js';
import { getHasStrictlyNsReferences, getType } from './util/has-strictly-ns-references.js';
import { getIsIdentifierReferencedHandler } from './util/is-identifier-referenced.js';
Expand Down Expand Up @@ -66,7 +66,7 @@ export const main = async (unresolvedConfiguration: CommandLineOptions) => {
const factory = new PrincipalFactory();
const streamer = new ConsoleStreamer({ isEnabled: isShowProgress });

const isGitIgnored = await getGitIgnoredFn({ cwd, gitignore });
const isGitIgnored = await getGitIgnoredHandler({ cwd, gitignore });
const toSourceFilePath = getToSourcePathHandler(chief);

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

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

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

// Add entry paths from package.json
// Add entry paths from package.json#main, #bin, #exports
const entryPathsFromManifest = await getEntryPathFromManifest(manifest, { ...sharedGlobOptions, ignore });
debugLogArray(name, 'Entry paths in package.json', entryPathsFromManifest);
principal.addEntryPaths(entryPathsFromManifest);
Expand Down
4 changes: 2 additions & 2 deletions packages/knip/src/typescript/getImportsAndExports.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isBuiltin } from 'node:module';
import ts from 'typescript';
import { ANONYMOUS, DEFAULT_EXTENSIONS, IMPORT_STAR } from '../constants.js';
import { ALIAS_TAG, ANONYMOUS, DEFAULT_EXTENSIONS, IMPORT_STAR } from '../constants.js';
import type { Tags } from '../types/cli.js';
import type { Export, ExportMap, ExportMember, ImportMap, UnresolvedImport } from '../types/dependency-graph.js';
import type { ExportNode, ExportNodeMember } from '../types/exports.js';
Expand Down Expand Up @@ -265,7 +265,7 @@ const getImportsAndExports = (
});
}

if (!jsDocTags.has('@alias')) {
if (!jsDocTags.has(ALIAS_TAG)) {
if (ts.isExportAssignment(node)) maybeAddAliasedExport(node.expression, 'default');
if (ts.isVariableDeclaration(node)) maybeAddAliasedExport(node.initializer, identifier);
}
Expand Down
8 changes: 3 additions & 5 deletions packages/knip/src/util/globby.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { readFileSync } from 'node:fs';
import { promisify } from 'node:util';
import { walk as _walk } from '@nodelib/fs.walk';
import type { Entry } from '@nodelib/fs.walk';
import type { Options as FastGlobOptions } from 'fast-glob';
import fg from 'fast-glob';
import { type Entry, walk as _walk } from '@nodelib/fs.walk';
import fg, { type Options as FastGlobOptions } from 'fast-glob';
import picomatch from 'picomatch';
import { GLOBAL_IGNORE_PATTERNS, ROOT_WORKSPACE_NAME } from '../constants.js';
import { timerify } from './Performance.js';
Expand Down Expand Up @@ -159,7 +157,7 @@ export async function globby(patterns: string | string[], options: GlobOptions):
return fg.glob(patterns, fastGlobOptions);
}

export async function getGitIgnoredFn(options: Options): Promise<(path: string) => boolean> {
export async function getGitIgnoredHandler(options: Options): Promise<(path: string) => boolean> {
cachedIgnores.clear();

if (options.gitignore === false) return () => false;
Expand Down
13 changes: 8 additions & 5 deletions packages/knip/src/util/modules.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isBuiltin } from 'node:module';
import { DT_SCOPE } from '../constants.js';
import type { PackageJson } from '../types/package-json.js';
import { _glob } from './glob.js';
import { getStringValues } from './object.js';
Expand All @@ -10,26 +11,28 @@ export const getPackageNameFromModuleSpecifier = (moduleSpecifier: string) => {
return moduleSpecifier.startsWith('@') ? parts.join('/') : parts[0];
};

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

const packageNameMatch = /.*\/node_modules\/(.+)/;
export const normalizeSpecifierFromFilePath = (value: string) => {
const match = toPosix(value).match(/.*\/node_modules\/(.+)/);
const match = toPosix(value).match(packageNameMatch);
if (match) return match[match.length - 1];
return value;
};

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

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

export const getDefinitelyTypedFor = (packageName: string) => {
if (isDefinitelyTyped(packageName)) return packageName;
if (packageName.startsWith('@')) return `@types/${packageName.slice(1).replace('/', '__')}`;
return `@types/${packageName}`;
if (packageName.startsWith('@')) return [DT_SCOPE, packageName.slice(1).replace('/', '__')].join('/');
return [DT_SCOPE, packageName].join('/');
};

export const getPackageFromDefinitelyTyped = (typedDependency: string) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/knip/src/util/regex.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const isRegexLike = /[*+\\(|{^$]/;
export const toRegexOrString = (value: string | RegExp) =>
typeof value === 'string' && /[*+\\(|{^$]/.test(value) ? new RegExp(value) : value;
typeof value === 'string' && isRegexLike.test(value) ? new RegExp(value) : value;

export const findMatch = (haystack: undefined | (string | RegExp)[], needle: string) =>
haystack?.find(n => (typeof n === 'string' ? n === needle : n.test(needle)));
Expand Down
9 changes: 5 additions & 4 deletions packages/knip/src/util/tag.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ALIAS_TAG, BETA_TAG, INTERNAL_TAG, PUBLIC_TAG } from '../constants.js';
import type { Tags } from '../types/cli.js';

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

export const getShouldIgnoreHandler = (isProduction: boolean) => (jsDocTags: Set<string>) =>
jsDocTags.has('@public') ||
jsDocTags.has('@beta') ||
jsDocTags.has('@alias') ||
(isProduction && jsDocTags.has('@internal'));
jsDocTags.has(PUBLIC_TAG) ||
jsDocTags.has(BETA_TAG) ||
jsDocTags.has(ALIAS_TAG) ||
(isProduction && jsDocTags.has(INTERNAL_TAG));

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

0 comments on commit 09fc536

Please sign in to comment.