knip
Version:
Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects
84 lines (83 loc) • 3.43 kB
JavaScript
import { isBuiltin } from 'node:module';
import { DT_SCOPE, PROTOCOL_VIRTUAL } from '../constants.js';
import { isAbsolute, isInNodeModules, toPosix } from './path.js';
export const getPackageNameFromModuleSpecifier = (moduleSpecifier) => {
if (!isStartsLikePackageName(moduleSpecifier))
return;
const parts = moduleSpecifier.split('/').slice(0, 2);
return moduleSpecifier.startsWith('@') ? parts.join('/') : parts[0];
};
const lastPackageNameMatch = /(?<=node_modules\/)(@[^/]+\/[^/]+|[^/]+)/g;
export const getPackageNameFromFilePath = (value) => {
if (value.includes('node_modules/.bin/'))
return extractBinary(value);
const match = toPosix(value).match(lastPackageNameMatch);
if (match)
return match[match.length - 1];
return value;
};
export const getPackageNameFromSpecifier = (specifier) => isInNodeModules(specifier) ? getPackageNameFromFilePath(specifier) : getPackageNameFromModuleSpecifier(specifier);
const matchPackageNameStart = /^(@[a-z0-9._]|[a-z0-9])/i;
export const isStartsLikePackageName = (specifier) => {
const ch = specifier.charCodeAt(0);
if (ch === 46 || ch === 47 || ch === 35 || ch === 126 || ch === 36)
return false;
return matchPackageNameStart.test(specifier);
};
export const stripVersionFromSpecifier = (specifier) => specifier.replace(/(\S+)@.*/, '$1');
const stripNodeModulesFromPath = (command) => command.replace(/(?:\.{0,2}\/)*node_modules\//, '');
export const extractBinary = (command) => stripVersionFromSpecifier(stripNodeModulesFromPath(command)
.replace(/^(\.bin\/)/, '')
.replace(/\$\(npm bin\)\/(\w+)/, '$1'));
export const isDefinitelyTyped = (packageName) => packageName.startsWith(`${DT_SCOPE}/`);
export const getDefinitelyTypedFor = (packageName) => {
if (isDefinitelyTyped(packageName))
return packageName;
if (packageName.startsWith('@'))
return [DT_SCOPE, packageName.slice(1).replace('/', '__')].join('/');
return [DT_SCOPE, packageName].join('/');
};
export const getPackageFromDefinitelyTyped = (typedDependency) => {
if (typedDependency.includes('__')) {
const [scope, packageName] = typedDependency.split('__');
return `@${scope}/${packageName}`;
}
return typedDependency;
};
const CHAR_EXCLAMATION = 33;
const CHAR_DASH = 45;
const CHAR_SLASH = 47;
const CHAR_COLON = 58;
const CHAR_HASH = 35;
const CHAR_QUESTION = 63;
export const sanitizeSpecifier = (specifier) => {
if (isBuiltin(specifier) ||
isAbsolute(specifier) ||
specifier.charCodeAt(0) === CHAR_COLON ||
specifier.startsWith(PROTOCOL_VIRTUAL)) {
return specifier;
}
const len = specifier.length;
let start = 0;
let end = len;
let colon = -1;
let hasSlash = false;
for (let i = 0; i < len; i++) {
const ch = specifier.charCodeAt(i);
if (i === start && (ch === CHAR_EXCLAMATION || ch === CHAR_DASH)) {
start++;
continue;
}
if (ch === CHAR_SLASH && colon === -1) {
hasSlash = true;
}
if (colon === -1 && ch === CHAR_COLON && !hasSlash) {
colon = i;
}
if (ch === CHAR_EXCLAMATION || ch === CHAR_QUESTION || (ch === CHAR_HASH && i > start)) {
end = i;
break;
}
}
return colon !== -1 && colon < end ? specifier.slice(start, colon) : specifier.slice(start, end);
};