Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

linguist-js

Package Overview
Dependencies
Maintainers
1
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

linguist-js - npm Package Compare versions

Comparing version 1.4.4 to 1.4.5

83

dist/cli.js

@@ -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.

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc