linguist-js
Advanced tools
Comparing version 1.4.4 to 1.4.5
@@ -8,61 +8,26 @@ "use strict"; | ||
const path_1 = __importDefault(require("path")); | ||
const yargs_parser_1 = __importDefault(require("yargs-parser")); | ||
const commander_1 = require("commander"); | ||
const index_1 = __importDefault(require("./index")); | ||
const indent = (n) => ' '.repeat(n * 4); | ||
const usage = (cmd, desc) => console.log('\n' + indent(2) + cmd + '\n' + indent(3) + desc.replace(/\n/g, '\n' + indent(4))); | ||
const argOpts = { | ||
alias: { | ||
help: ['h'], | ||
version: ['v'], | ||
analyse: ['a', 'analyze'], | ||
ignore: ['i'], | ||
files: ['f', 'full'], | ||
summary: ['s'], | ||
quick: ['q'], | ||
keepVendored: ['V', 'vendor', 'vendored'], | ||
checkAttributes: ['A'], | ||
checkIgnored: ['I'], | ||
checkHeuristics: ['H'], | ||
}, | ||
boolean: ['help', 'version', 'analyse', 'files', 'summary', 'quick', 'keepVendored', 'checkAttributes', 'checkIgnored', 'checkHeuristics'], | ||
}; | ||
const args = yargs_parser_1.default(process.argv.slice(2), argOpts); | ||
if (args.help) { | ||
console.log(`\n${indent(1)}linguist usage:`); | ||
usage(`linguist --analyse [<folder>] [<...options>]`, [ | ||
`Analyse the language of all files found in a folder.`, | ||
`<folder>`, `\tThe folder to analyse (optional; default './')`, | ||
`-i|--ignore`, `\tA list of file path globs (delimited with ':', ';' or '|') to ignore (optional).`, | ||
`-f|--files`, `\tList every file parsed (optional)`, | ||
`-s|--summary`, `\tShow output in a human-readable format (optional)`, | ||
`-q|--quick`, `\tSkip checking of gitattributes/gitignore files (optional)`, `\tAlias for -AIH=false`, | ||
`-V|--keepVendored`, `\tPrevent skipping over vendored/generated files (optional)`, | ||
`-A|--checkAttributes`, `\tForce the checking of gitattributes files (optional; use alongside --quick to overwrite)`, | ||
`-I|--checkIgnored`, `\tForce the checking of gitignore files (optional; use alongside --quick to overwrite)`, | ||
`-H|--checkHeuristics`, `\tApply heuristics to ambiguous languages (optional; use alongside --quick to overwrite)`, | ||
].join('\n')); | ||
usage(`linguist --version`, 'Display the installed version of linguist-js'); | ||
usage(`linguist --help`, 'Display this help message'); | ||
} | ||
else if (args.version) { | ||
console.log(`The latest version of linguist-js is ${VERSION}.`); | ||
} | ||
else if (args.analyse) { | ||
commander_1.program | ||
.name('linguist --analyze') | ||
.usage('[<folder>] [<options...>]') | ||
.requiredOption('-a|--analyze|--analyse [folder]', 'Analyse the language of all files found in a folder') | ||
.option('-i|--ignore <files...>', `A list of file path globs to ignore`) | ||
.option('-f|--files|--full', 'List every file parsed', false) | ||
.option('-s|--summary', 'Show output in a human-readable format', false) | ||
.option('-q|--quick', 'Skip checking of gitattributes/gitignore files (alias for -AIH=false)', false) | ||
.option('-V|--keepVendored', 'Prevent skipping over vendored/generated files', false) | ||
.option('-A|--checkAttributes', 'Force the checking of gitattributes files (default: true unless --quick is set)') | ||
.option('-I|--checkIgnored', 'Force the checking of gitignore files (default: true unless --quick is set)') | ||
.option('-H|--checkHeuristics', 'Apply heuristics to ambiguous languages (default: true unless --quick is set)') | ||
.helpOption(`-h|--help`, 'Display this help message') | ||
.version(VERSION, '-v|--version', 'Display the installed version of linguist-js'); | ||
commander_1.program.parse(process.argv); | ||
const args = commander_1.program.opts(); | ||
if (args.analyze) { | ||
(async () => { | ||
var _a, _b; | ||
const opts = {}; | ||
if (args.ignore) | ||
opts.ignore = (_a = args.ignore) === null || _a === void 0 ? void 0 : _a.split(/(?<!\\)[:;|]/); | ||
if (args.q) | ||
opts.quick = args.q; | ||
if (args.v) | ||
opts.keepVendored = args.v; | ||
if (args.A) | ||
opts.checkAttributes = args.A; | ||
if (args.I) | ||
opts.checkIgnored = args.I; | ||
if (args.H) | ||
opts.checkHeuristics = args.H; | ||
const root = (_b = args._[0]) !== null && _b !== void 0 ? _b : '.'; | ||
const { count, languages, results } = await index_1.default(root, opts); | ||
const root = args.analyze === true ? '.' : args.analyze; | ||
if (typeof args.ignore === 'string') | ||
args.ignore = args.ignore.split(/(?<!\\)[:;|]/); | ||
const { count, languages, results } = await index_1.default(root, args); | ||
if (args.summary) { | ||
@@ -86,3 +51,5 @@ const { data, markup, programming, prose, total: { bytes: totalBytes } } = languages; | ||
} | ||
console.log(args.files ? { results: relResults, count, languages } : { count, languages }); | ||
const languageData = languages; | ||
delete languageData.all; | ||
console.log(args.files ? { results: relResults, count, languages: languageData } : { count, languages: languageData }); | ||
} | ||
@@ -89,0 +56,0 @@ })(); |
import * as T from './types'; | ||
declare const _default: (root?: string, opts?: T.Options) => Promise<{ | ||
count: number; | ||
results: Record<string, T.Language>; | ||
languages: T.LanguagesData; | ||
}>; | ||
declare const _default: (root?: string, opts?: T.Options) => Promise<T.Results>; | ||
export = _default; |
@@ -6,6 +6,5 @@ "use strict"; | ||
const fs_1 = __importDefault(require("fs")); | ||
const path_1 = __importDefault(require("path")); | ||
const node_fetch_1 = __importDefault(require("node-fetch")); | ||
const cross_fetch_1 = __importDefault(require("cross-fetch")); | ||
const js_yaml_1 = __importDefault(require("js-yaml")); | ||
const glob_1 = __importDefault(require("glob")); | ||
const fast_glob_1 = __importDefault(require("fast-glob")); | ||
const glob_to_regexp_1 = __importDefault(require("glob-to-regexp")); | ||
@@ -16,6 +15,6 @@ const convertToRegex = (path) => glob_to_regexp_1.default('**/' + path, { globstar: true, extended: true }); | ||
const dataUrl = (file) => `https://raw.githubusercontent.com/github/linguist/HEAD/lib/linguist/${file}`; | ||
const loadFile = async (file) => await node_fetch_1.default(dataUrl(file)).then(data => data.text()); | ||
const loadFile = async (file) => await cross_fetch_1.default(dataUrl(file)).then(data => data.text()); | ||
module.exports = async function analyse(root = '.', opts = {}) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
var _j, _k, _l; | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
var _h, _j, _k; | ||
const langData = await loadFile('languages.yml').then(js_yaml_1.default.load); | ||
@@ -36,4 +35,5 @@ const vendorData = await loadFile('vendor.yml').then(js_yaml_1.default.load); | ||
}; | ||
const sourceFiles = glob_1.default.sync(path_1.default.resolve(root) + '/**/*', {}); | ||
const folders = new Set(); | ||
let files = await fast_glob_1.default(root + '/**/*', { absolute: true, onlyFiles: true, dot: true }); | ||
files = files.filter(file => !file.includes('/.git/')); | ||
const folders = new Set(files.map(file => file.replace(/[^/]+$/, ''))); | ||
// Apply aliases | ||
@@ -48,5 +48,2 @@ opts = { checkIgnored: !opts.quick, checkAttributes: !opts.quick, checkHeuristics: !opts.quick, ...opts }; | ||
if (!opts.quick) { | ||
for (const file of sourceFiles) { | ||
folders.add(file.replace(/[^\\/]+$/, '')); | ||
} | ||
for (const folder of folders) { | ||
@@ -56,59 +53,35 @@ // Skip checks if folder is already ignored | ||
continue; | ||
// Attempt to read gitignores | ||
if (opts.checkIgnored) | ||
try { | ||
let ignoresData = ''; | ||
try { | ||
ignoresData = fs_1.default.readFileSync(folder + '.gitignore', { encoding: 'utf8' }); | ||
} | ||
catch { | ||
throw 404; | ||
} | ||
const ignoresList = ignoresData.split(/\r?\n/).filter(line => line.trim() && !line.startsWith('#')); | ||
const ignoredPaths = ignoresList.map(path => glob_to_regexp_1.default('*' + path + '*', { extended: true }).source); | ||
vendorData.push(...ignoredPaths); | ||
const attributesFile = folder + '.gitattributes'; | ||
const ignoresFile = folder + '.gitignore'; | ||
// Parse gitignores | ||
if (opts.checkIgnored && fs_1.default.existsSync(ignoresFile)) { | ||
const ignoresData = fs_1.default.readFileSync(ignoresFile, { encoding: 'utf8' }); | ||
const ignoresList = ignoresData.split(/\r?\n/).filter(line => line.trim() && !line.startsWith('#')); | ||
const ignoredPaths = ignoresList.map(path => glob_to_regexp_1.default('*' + path + '*', { extended: true }).source); | ||
vendorData.push(...ignoredPaths); | ||
} | ||
// Parse gitattributes | ||
if (opts.checkAttributes && fs_1.default.existsSync(attributesFile)) { | ||
const attributesData = fs_1.default.readFileSync(attributesFile, { encoding: 'utf8' }); | ||
// Custom vendor options | ||
const vendorMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-(vendored|generated|documentation)(?!=false)/gm); | ||
for (const [_line, path] of vendorMatches) { | ||
vendorData.push(folder + convertToRegex(path).source.substr(1)); | ||
} | ||
catch (err) { | ||
if (err !== 404) | ||
throw err; | ||
} | ||
// Attempt to read gitattributes | ||
if (opts.checkAttributes) | ||
try { | ||
let attributesData = ''; | ||
try { | ||
attributesData = fs_1.default.readFileSync(folder + '.gitattributes', { encoding: 'utf8' }); | ||
// Custom file associations | ||
const customLangMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-language=(\S+)/gm); | ||
for (let [_line, path, forcedLang] of customLangMatches) { | ||
// If specified language is an alias, associate it with its full name | ||
if (!langData[forcedLang]) { | ||
const overrideLang = Object.entries(langData).find(entry => { var _a; return (_a = entry[1].aliases) === null || _a === void 0 ? void 0 : _a.includes(forcedLang.toLowerCase()); }); | ||
if (overrideLang) | ||
forcedLang = overrideLang[0]; | ||
} | ||
catch { | ||
throw 404; | ||
} | ||
// Custom vendor options | ||
const vendorMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-(vendored|generated|documentation)(?!=false)/gm); | ||
for (const [_line, path] of vendorMatches) { | ||
vendorData.push(folder + convertToRegex(path).source.substr(1)); | ||
} | ||
// Custom file associations | ||
const customLangMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-language=(\S+)/gm); | ||
for (let [_line, path, forcedLang] of customLangMatches) { | ||
// If specified language is an alias, associate it with its full name | ||
if (!langData[forcedLang]) { | ||
for (const lang in langData) { | ||
if (!((_a = langData[lang].aliases) === null || _a === void 0 ? void 0 : _a.includes(forcedLang.toLowerCase()))) | ||
continue; | ||
forcedLang = lang; | ||
break; | ||
} | ||
} | ||
const fullPath = folder + convertToRegex(path).source.substr(1); | ||
overrides[fullPath] = forcedLang; | ||
} | ||
const fullPath = folder + convertToRegex(path).source.substr(1); | ||
overrides[fullPath] = forcedLang; | ||
} | ||
catch (err) { | ||
if (err !== 404) | ||
throw err; | ||
} | ||
} | ||
} | ||
} | ||
// Check vendored files | ||
let files = [...sourceFiles]; | ||
if (!opts.keepVendored) { | ||
@@ -144,4 +117,4 @@ // Filter out any files that match a vendor file path | ||
// Check if filename is a match | ||
const matchesName = (_b = langData[lang].filenames) === null || _b === void 0 ? void 0 : _b.some(presetName => file.toLowerCase().endsWith(presetName.toLowerCase())); | ||
const matchesExt = (_c = langData[lang].extensions) === null || _c === void 0 ? void 0 : _c.some(ext => file.toLowerCase().endsWith(ext.toLowerCase())); | ||
const matchesName = (_a = langData[lang].filenames) === null || _a === void 0 ? void 0 : _a.some(presetName => file.toLowerCase().endsWith(presetName.toLowerCase())); | ||
const matchesExt = (_b = langData[lang].extensions) === null || _b === void 0 ? void 0 : _b.some(ext => file.toLowerCase().endsWith(ext.toLowerCase())); | ||
if (matchesName || matchesExt) { | ||
@@ -187,3 +160,3 @@ addResult(file, lang); | ||
// Default to final language | ||
(_d = finalResults[file]) !== null && _d !== void 0 ? _d : (finalResults[file] = last(heuristics.rules).language); | ||
(_c = finalResults[file]) !== null && _c !== void 0 ? _c : (finalResults[file] = last(heuristics.rules).language); | ||
} | ||
@@ -194,3 +167,3 @@ } | ||
for (const file in results) { | ||
(_e = finalResults[file]) !== null && _e !== void 0 ? _e : (finalResults[file] = results[file][0]); | ||
(_d = finalResults[file]) !== null && _d !== void 0 ? _d : (finalResults[file] = results[file][0]); | ||
} | ||
@@ -204,4 +177,4 @@ // Load language bytes size | ||
if (!lang) { | ||
let ext = find(file, /(\.[^./]+)?$/); | ||
(_f = (_j = languages.unknown)[ext]) !== null && _f !== void 0 ? _f : (_j[ext] = 0); | ||
const ext = find(file, /(\.[^./]+)?$/); | ||
(_e = (_h = languages.unknown)[ext]) !== null && _e !== void 0 ? _e : (_h[ext] = 0); | ||
languages.unknown[ext] += fileSize; | ||
@@ -213,5 +186,5 @@ languages.total.unknownBytes += fileSize; | ||
const { type } = langData[lang]; | ||
(_g = (_k = languages.all)[lang]) !== null && _g !== void 0 ? _g : (_k[lang] = { type, bytes: 0, color: langData[lang].color }); | ||
(_f = (_j = languages.all)[lang]) !== null && _f !== void 0 ? _f : (_j[lang] = { type, bytes: 0, color: langData[lang].color }); | ||
languages.all[lang].bytes += fileSize; | ||
(_h = (_l = languages[type])[lang]) !== null && _h !== void 0 ? _h : (_l[lang] = 0); | ||
(_g = (_k = languages[type])[lang]) !== null && _g !== void 0 ? _g : (_k[lang] = 0); | ||
languages[type][lang] += fileSize; | ||
@@ -218,0 +191,0 @@ languages.total.bytes += fileSize; |
{ | ||
"name": "linguist-js", | ||
"version": "1.4.4", | ||
"version": "1.4.5", | ||
"description": "Analyse languages used in a folder. Powered by GitHub Linguist, although it doesn't need to be installed.", | ||
@@ -35,17 +35,14 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"glob": "^7.1.7", | ||
"commander": "^8.1.0", | ||
"cross-fetch": "^3.1.4", | ||
"fast-glob": "^3.2.7", | ||
"glob-to-regexp": "^0.4.1", | ||
"js-yaml": "^4.1.0", | ||
"node-fetch": "^2.6.1", | ||
"yargs-parser": "^20.2.9" | ||
"js-yaml": "^4.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/glob": "^7.1.4", | ||
"@types/glob-to-regexp": "^0.4.1", | ||
"@types/js-yaml": "^4.0.2", | ||
"@types/node": "ts4.3", | ||
"@types/node-fetch": "^2.5.12", | ||
"@types/yargs": "^17.0.2", | ||
"typescript": "~4.3.5" | ||
} | ||
} |
@@ -111,3 +111,3 @@ [![Latest version](https://img.shields.io/github/v/release/Nixinova/Linguist?label=latest%20version&style=flat-square)](https://github.com/Nixinova/Linguist/releases) | ||
- `--ignore` (optional): | ||
A list of file path globs (delimited with `:`, `;` or `|`) to ignore. | ||
A list of file path globs to ignore. | ||
- `--files` (optional): | ||
@@ -114,0 +114,0 @@ Whether to print a full list of all files analysed. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4
22379
328
+ Addedcommander@^8.1.0
+ Addedcross-fetch@^3.1.4
+ Addedfast-glob@^3.2.7
+ Added@nodelib/fs.scandir@2.1.5(transitive)
+ Added@nodelib/fs.stat@2.0.5(transitive)
+ Added@nodelib/fs.walk@1.2.8(transitive)
+ Addedbraces@3.0.3(transitive)
+ Addedcommander@8.3.0(transitive)
+ Addedcross-fetch@3.1.8(transitive)
+ Addedfast-glob@3.3.2(transitive)
+ Addedfastq@1.17.1(transitive)
+ Addedfill-range@7.1.1(transitive)
+ Addedglob-parent@5.1.2(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedis-number@7.0.0(transitive)
+ Addedmerge2@1.4.1(transitive)
+ Addedmicromatch@4.0.8(transitive)
+ Addedpicomatch@2.3.1(transitive)
+ Addedqueue-microtask@1.2.3(transitive)
+ Addedreusify@1.0.4(transitive)
+ Addedrun-parallel@1.2.0(transitive)
+ Addedto-regex-range@5.0.1(transitive)
- Removedglob@^7.1.7
- Removednode-fetch@^2.6.1
- Removedyargs-parser@^20.2.9
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@7.2.3(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedyargs-parser@20.2.9(transitive)