227 lines
4.7 KiB
TypeScript
227 lines
4.7 KiB
TypeScript
import globby from 'globby';
|
|
import fs from 'fs-extra';
|
|
import path from 'path';
|
|
import slash from 'slash2';
|
|
|
|
import { LineInfo, LocFile } from './file';
|
|
import { Languages } from './languages';
|
|
|
|
const defaultInfo: LineInfo = {
|
|
total: 0,
|
|
code: 0,
|
|
comment: 0,
|
|
};
|
|
|
|
export interface LocDirOptions {
|
|
cwd?: string;
|
|
include?: string[] | string;
|
|
exclude?: string[] | string;
|
|
analysisLanguages?: string[];
|
|
}
|
|
|
|
export interface LocResult {
|
|
files: string[];
|
|
info: LineInfo;
|
|
languages: {
|
|
[key: string]: LineInfo & {
|
|
sum: number;
|
|
};
|
|
};
|
|
}
|
|
|
|
const defaultExclude = [
|
|
// javascript
|
|
'**/*.map',
|
|
'**/yarn**',
|
|
'**/.github',
|
|
'**/node_modules/**',
|
|
'**/dist/**',
|
|
'**/*.snap',
|
|
|
|
// java
|
|
'**/target',
|
|
'**/*.class',
|
|
'**/*.o',
|
|
'**/bin',
|
|
'**/*.map',
|
|
|
|
// python
|
|
'**/*.pyc',
|
|
'**/*.pyo',
|
|
|
|
// other
|
|
'**/*.dil',
|
|
'**/*.ra',
|
|
|
|
// images
|
|
'**/*.png',
|
|
'**/*.jpg',
|
|
'**/*.jpeg',
|
|
'**/*.gif',
|
|
'**/*.ico',
|
|
'**/*.bmp',
|
|
'**/*.webp',
|
|
'**/*.tiff',
|
|
'**/*.psd',
|
|
'**/*.ai',
|
|
'**/*.ps',
|
|
'**/*.eps',
|
|
|
|
// fonts
|
|
'**/*.ttf',
|
|
'**/*.otf',
|
|
'**/*.woff',
|
|
'**/*.woff2',
|
|
'**/*.eot',
|
|
'**/*.ttc',
|
|
|
|
// audio
|
|
'**/*.mp3',
|
|
'**/*.wav',
|
|
'**/*.ogg',
|
|
'**/*.flac',
|
|
'**/*.aac',
|
|
'**/*.m4a',
|
|
'**/*.aif*',
|
|
|
|
// video
|
|
'**/*.mp4',
|
|
'**/*.mkv',
|
|
'**/*.avi',
|
|
'**/*.mov',
|
|
'**/*.wmv',
|
|
'**/*.mpg',
|
|
'**/*.mpeg',
|
|
'**/*.m2v',
|
|
'**/*.m4v',
|
|
|
|
// office
|
|
'**/*.doc',
|
|
'**/*.docx',
|
|
'**/*.docm',
|
|
'**/*.dot',
|
|
'**/*.dotx',
|
|
'**/*.xls',
|
|
'**/*.xlsx',
|
|
|
|
// documents
|
|
'**/*.pdf',
|
|
'**/*.epub',
|
|
'**/*.mobi',
|
|
|
|
// archives
|
|
'**/*.rar',
|
|
'**/*.zip',
|
|
'**/*.7z',
|
|
'**/*.tar',
|
|
'**/*.gz',
|
|
'**/*.bz2',
|
|
'**/*.bz',
|
|
'**/*.tbz',
|
|
'**/*.tgz',
|
|
];
|
|
|
|
function ensureArray(arr?: string[] | string, dfault?: string) {
|
|
if (!arr) {
|
|
return dfault ? [dfault] : [];
|
|
}
|
|
return Array.isArray(arr) ? arr : [arr];
|
|
}
|
|
|
|
/**
|
|
* Collect the info of a directory.
|
|
*/
|
|
export class LocDir {
|
|
private cwd: string;
|
|
|
|
private include: string[];
|
|
|
|
private exclude: string[];
|
|
|
|
private analysisLanguages?: string[];
|
|
|
|
private allLanguages = new Languages();
|
|
|
|
constructor(options: LocDirOptions) {
|
|
// ensure all excludes are globstar. Note that '**/*.ts/**' matches files
|
|
// that end in .ts because the globstar indicates 0 or more directory paths.
|
|
this.exclude = ensureArray(options.exclude)
|
|
.concat(defaultExclude)
|
|
.map((item) => (item.endsWith('**') ? item : `${item}/**`));
|
|
|
|
// remove all leading './' since this messes up globstar matches in the
|
|
// excludes.
|
|
this.include = ensureArray(options.include, '**')
|
|
.map((item) => (item.startsWith('./') ? item.substring(2) : item))
|
|
.map((item) => (item.endsWith('**') ? item : `${item}/**`));
|
|
this.cwd = options.cwd || process.cwd();
|
|
this.analysisLanguages = options.analysisLanguages;
|
|
}
|
|
|
|
/**
|
|
* Calculate directory info.
|
|
*/
|
|
async loadInfo(): Promise<LocResult> {
|
|
const paths = await globby(this.include, {
|
|
cwd: this.cwd,
|
|
ignore: this.exclude,
|
|
nodir: true,
|
|
});
|
|
const files: string[] = [];
|
|
const info: LineInfo = { ...defaultInfo };
|
|
let languages: {
|
|
[key: string]: LineInfo & {
|
|
sum: number;
|
|
};
|
|
} = {};
|
|
|
|
// We _could_ use Promise.all to count the files in parallel, but that
|
|
// would lead to out of memory errors when there are many files.
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
for (const pathItem of paths) {
|
|
const fullPath = slash(path.join(this.cwd, pathItem));
|
|
if (
|
|
!pathItem ||
|
|
this.ignoreLanguage(pathItem) ||
|
|
!(await fs.pathExists(fullPath)) ||
|
|
(await fs.stat(fullPath)).isDirectory()
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
const file = new LocFile(fullPath);
|
|
const fileLineInfo = await file.getFileInfo();
|
|
const { lines } = fileLineInfo;
|
|
info.total += lines.total;
|
|
info.code += lines.code;
|
|
info.comment += lines.comment;
|
|
const language = { ...languages[fileLineInfo.languages] };
|
|
language.code = lines.code + (language.code || 0);
|
|
language.sum = (language.sum || 0) + 1;
|
|
language.comment = lines.comment + (language.comment || 0);
|
|
language.total = lines.total + (language.total || 0);
|
|
languages = {
|
|
...languages,
|
|
[fileLineInfo.languages]: language,
|
|
};
|
|
files.push(fullPath);
|
|
}
|
|
|
|
return {
|
|
files,
|
|
info,
|
|
languages,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Ignore analyzing this file if analysis languages are specified
|
|
* and this language is not one of them.
|
|
*/
|
|
private ignoreLanguage(pathItem: string): boolean {
|
|
return (
|
|
this.analysisLanguages &&
|
|
!this.analysisLanguages.includes(this.allLanguages.getType(pathItem))
|
|
);
|
|
}
|
|
}
|