linguist-js 2.2.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +4 -5
- package/dist/helpers/convert-pcre.js +7 -4
- package/dist/helpers/read-file.js +5 -2
- package/dist/helpers/walk-tree.js +37 -15
- package/dist/index.js +73 -37
- package/dist/types.d.ts +1 -0
- package/package.json +9 -8
- package/readme.md +3 -0
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const VERSION = require('../package.json').version;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const index_1 = __importDefault(require("./index"));
|
|
9
|
-
const walk_tree_1 = __importDefault(require("./helpers/walk-tree"));
|
|
10
9
|
const colouredMsg = ([r, g, b], msg) => `\u001B[${38};2;${r};${g};${b}m${msg}${'\u001b[0m'}`;
|
|
11
10
|
const hexToRgb = (hex) => [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];
|
|
12
11
|
commander_1.program
|
|
@@ -53,17 +52,16 @@ if (args.analyze)
|
|
|
53
52
|
const root = args.analyze === true ? '.' : args.analyze;
|
|
54
53
|
const data = await (0, index_1.default)(root, args);
|
|
55
54
|
const { files, languages, unknown } = data;
|
|
56
|
-
// Get file count
|
|
57
|
-
let totalFiles = (0, walk_tree_1.default)(root).files.length;
|
|
58
55
|
// Print output
|
|
59
56
|
if (!args.json) {
|
|
60
57
|
const sortedEntries = Object.entries(languages.results).sort((a, b) => a[1].bytes < b[1].bytes ? +1 : -1);
|
|
61
58
|
const totalBytes = languages.bytes;
|
|
62
|
-
console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${
|
|
59
|
+
console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${files.count} files with linguist-js`);
|
|
63
60
|
console.log(`\n Language analysis results:`);
|
|
64
61
|
let count = 0;
|
|
65
62
|
if (sortedEntries.length === 0)
|
|
66
63
|
console.log(` None`);
|
|
64
|
+
// List parsed results
|
|
67
65
|
for (const [lang, { bytes, color }] of sortedEntries) {
|
|
68
66
|
const fmtd = {
|
|
69
67
|
index: (++count).toString().padStart(2, ' '),
|
|
@@ -75,6 +73,7 @@ if (args.analyze)
|
|
|
75
73
|
console.log(` ${fmtd.index}. ${fmtd.icon} ${fmtd.lang} ${fmtd.percent}% ${fmtd.bytes} B`);
|
|
76
74
|
}
|
|
77
75
|
console.log(` Total: ${totalBytes.toLocaleString()} B`);
|
|
76
|
+
// List unknown files/extensions
|
|
78
77
|
if (unknown.bytes > 0) {
|
|
79
78
|
console.log(`\n Unknown files and extensions:`);
|
|
80
79
|
for (const [name, bytes] of Object.entries(unknown.filenames)) {
|
|
@@ -101,6 +100,6 @@ if (args.analyze)
|
|
|
101
100
|
}
|
|
102
101
|
})();
|
|
103
102
|
else {
|
|
104
|
-
console.log(`Welcome to linguist-js,
|
|
103
|
+
console.log(`Welcome to linguist-js, a JavaScript port of GitHub's language analyzer.`);
|
|
105
104
|
console.log(`Type 'linguist --help' for a list of commands.`);
|
|
106
105
|
}
|
|
@@ -3,23 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
/** Convert a PCRE regex into JS. */
|
|
4
4
|
function pcre(regex) {
|
|
5
5
|
let finalRegex = regex;
|
|
6
|
+
const replace = (search, replace) => finalRegex = finalRegex.replace(search, replace);
|
|
6
7
|
const finalFlags = new Set();
|
|
7
8
|
// Convert inline flag declarations
|
|
8
9
|
const inlineMatches = regex.matchAll(/\?([a-z]):/g);
|
|
9
10
|
const startMatches = regex.matchAll(/\(\?([a-z]+)\)/g);
|
|
10
11
|
for (const [match, flags] of [...inlineMatches, ...startMatches]) {
|
|
11
|
-
|
|
12
|
+
replace(match, '');
|
|
12
13
|
[...flags].forEach(flag => finalFlags.add(flag));
|
|
13
14
|
}
|
|
14
|
-
// Remove invalid
|
|
15
|
-
|
|
15
|
+
// Remove invalid syntax
|
|
16
|
+
replace(/([*+]){2}/g, '$1');
|
|
17
|
+
replace(/\(\?>/g, '(?:');
|
|
16
18
|
// Remove start/end-of-file markers
|
|
17
19
|
if (/\\[AZ]/.test(finalRegex)) {
|
|
18
|
-
|
|
20
|
+
replace(/\\A/g, '^').replace(/\\Z/g, '$');
|
|
19
21
|
finalFlags.delete('m');
|
|
20
22
|
}
|
|
21
23
|
else
|
|
22
24
|
finalFlags.add('m');
|
|
25
|
+
// Return final regex
|
|
23
26
|
return RegExp(finalRegex, [...finalFlags].join(''));
|
|
24
27
|
}
|
|
25
28
|
exports.default = pcre;
|
|
@@ -4,12 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const fs_1 = __importDefault(require("fs"));
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Read part of a file on disc.
|
|
9
|
+
* @throws 'EPERM' if the file is not readable.
|
|
10
|
+
*/
|
|
8
11
|
async function readFile(filename, onlyFirstLine = false) {
|
|
9
12
|
const chunkSize = 100;
|
|
10
13
|
const stream = fs_1.default.createReadStream(filename, { highWaterMark: chunkSize });
|
|
11
14
|
let content = '';
|
|
12
|
-
for await (const data of stream) {
|
|
15
|
+
for await (const data of stream) { // may throw
|
|
13
16
|
content += data.toString();
|
|
14
17
|
if (onlyFirstLine && content.includes('\n')) {
|
|
15
18
|
return content.split(/\r?\n/)[0];
|
|
@@ -8,28 +8,50 @@ const path_1 = __importDefault(require("path"));
|
|
|
8
8
|
const allFiles = new Set();
|
|
9
9
|
const allFolders = new Set();
|
|
10
10
|
/** Generate list of files in a directory. */
|
|
11
|
-
function walk(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
function walk(root, folders, ignored = []) {
|
|
12
|
+
// Switch out paths that expect being in root
|
|
13
|
+
ignored = ignored.map(match => match.replace(/^\^/, '^\\./'));
|
|
14
|
+
// Walk tree of a folder
|
|
15
|
+
if (folders.length === 1) {
|
|
16
|
+
const folder = folders[0];
|
|
17
|
+
// Get list of files and folders inside this folder
|
|
18
|
+
const files = fs_1.default.readdirSync(folder).map(file => {
|
|
19
|
+
// Create path relative to root
|
|
20
|
+
const base = path_1.default.resolve(folder, file).replace(/\\/g, '/').replace(root, '.');
|
|
21
|
+
// Add trailing slash to mark directories
|
|
22
|
+
const isDir = fs_1.default.lstatSync(path_1.default.resolve(root, base)).isDirectory();
|
|
23
|
+
return isDir ? `${base}/` : base;
|
|
24
|
+
});
|
|
25
|
+
// Loop through files and folders
|
|
18
26
|
for (const file of files) {
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
// Create absolute path for disc operations
|
|
28
|
+
const path = path_1.default.resolve(root, file).replace(/\\/g, '/');
|
|
29
|
+
// Skip if nonexistant or ignored
|
|
30
|
+
if (!fs_1.default.existsSync(path) || ignored.some(pattern => RegExp(pattern).test(file)))
|
|
21
31
|
continue;
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
// Add absolute folder path to list
|
|
33
|
+
allFolders.add(path_1.default.resolve(folder).replace(/\\/g, '/'));
|
|
34
|
+
// Check if this is a folder or file
|
|
35
|
+
if (file.endsWith('/')) {
|
|
36
|
+
// Recurse into subfolders
|
|
24
37
|
allFolders.add(path);
|
|
25
|
-
walk(path, ignored);
|
|
26
|
-
|
|
38
|
+
walk(root, [path], ignored);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Add relative file path to list
|
|
42
|
+
allFiles.add(path);
|
|
27
43
|
}
|
|
28
|
-
allFiles.add(path);
|
|
29
44
|
}
|
|
30
45
|
}
|
|
46
|
+
// Recurse into all folders
|
|
47
|
+
else {
|
|
48
|
+
for (const path of folders) {
|
|
49
|
+
walk(root, [path], ignored);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Return absolute files and folders lists
|
|
31
53
|
return {
|
|
32
|
-
files: [...allFiles],
|
|
54
|
+
files: [...allFiles].map(file => file.replace(/^\./, root)),
|
|
33
55
|
folders: [...allFolders],
|
|
34
56
|
};
|
|
35
57
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
6
6
|
const path_1 = __importDefault(require("path"));
|
|
7
7
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
8
8
|
const glob_to_regexp_1 = __importDefault(require("glob-to-regexp"));
|
|
9
|
+
const common_path_prefix_1 = __importDefault(require("common-path-prefix"));
|
|
9
10
|
const binary_extensions_1 = __importDefault(require("binary-extensions"));
|
|
10
11
|
const isbinaryfile_1 = require("isbinaryfile");
|
|
11
12
|
const walk_tree_1 = __importDefault(require("./helpers/walk-tree"));
|
|
@@ -14,13 +15,19 @@ const read_file_1 = __importDefault(require("./helpers/read-file"));
|
|
|
14
15
|
const convert_pcre_1 = __importDefault(require("./helpers/convert-pcre"));
|
|
15
16
|
const convert_glob_1 = __importDefault(require("./helpers/convert-glob"));
|
|
16
17
|
async function analyse(input, opts = {}) {
|
|
17
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
18
|
-
var
|
|
18
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
19
|
+
var _r, _s, _t;
|
|
20
|
+
const useRawContent = opts.fileContent !== undefined;
|
|
21
|
+
input = [input !== null && input !== void 0 ? input : []].flat();
|
|
22
|
+
opts.fileContent = [(_a = opts.fileContent) !== null && _a !== void 0 ? _a : []].flat();
|
|
23
|
+
// Load data from github-linguist web repo
|
|
19
24
|
const langData = await (0, load_data_1.default)('languages.yml').then(js_yaml_1.default.load);
|
|
20
25
|
const vendorData = await (0, load_data_1.default)('vendor.yml').then(js_yaml_1.default.load);
|
|
26
|
+
const docData = await (0, load_data_1.default)('documentation.yml').then(js_yaml_1.default.load);
|
|
21
27
|
const heuristicsData = await (0, load_data_1.default)('heuristics.yml').then(js_yaml_1.default.load);
|
|
22
28
|
const generatedData = await (0, load_data_1.default)('generated.rb').then(text => { var _a; return (_a = text.match(/(?<=name\.match\(\/).+?(?=(?<!\\)\/\))/gm)) !== null && _a !== void 0 ? _a : []; });
|
|
23
|
-
vendorData
|
|
29
|
+
const vendorPaths = [...vendorData, ...docData, ...generatedData];
|
|
30
|
+
// Setup main variables
|
|
24
31
|
const fileAssociations = {};
|
|
25
32
|
const extensions = {};
|
|
26
33
|
const overrides = {};
|
|
@@ -29,16 +36,34 @@ async function analyse(input, opts = {}) {
|
|
|
29
36
|
languages: { count: 0, bytes: 0, results: {} },
|
|
30
37
|
unknown: { count: 0, bytes: 0, extensions: {}, filenames: {} },
|
|
31
38
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
// Prepare list of ignored files
|
|
40
|
+
const ignoredFiles = ['(^|\\/)\\.git\\/'];
|
|
41
|
+
if (!opts.keepVendored) {
|
|
42
|
+
ignoredFiles.push(...vendorPaths);
|
|
43
|
+
}
|
|
44
|
+
if (opts.ignoredFiles) {
|
|
45
|
+
ignoredFiles.push(...opts.ignoredFiles.map(path => (0, glob_to_regexp_1.default)('*' + path + '*', { extended: true }).source));
|
|
46
|
+
}
|
|
47
|
+
// Load file paths and folders
|
|
48
|
+
let files, folders;
|
|
49
|
+
if (useRawContent) {
|
|
50
|
+
// Uses raw file content
|
|
51
|
+
files = input;
|
|
52
|
+
folders = [''];
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Uses directory on disc
|
|
56
|
+
// Set a common root path so that vendor paths do not incorrectly match parent folders
|
|
57
|
+
const resolvedInput = input.map(path => path_1.default.resolve(path).replace(/\\/g, '/'));
|
|
58
|
+
const commonRoot = (input.length > 1 ? (0, common_path_prefix_1.default)(resolvedInput) : resolvedInput[0]).replace(/\/?$/, '');
|
|
59
|
+
const data = (0, walk_tree_1.default)(commonRoot, input, ignoredFiles);
|
|
60
|
+
files = data.files;
|
|
61
|
+
folders = data.folders;
|
|
62
|
+
}
|
|
38
63
|
// Apply aliases
|
|
39
64
|
opts = { checkIgnored: !opts.quick, checkAttributes: !opts.quick, checkHeuristics: !opts.quick, checkShebang: !opts.quick, ...opts };
|
|
40
65
|
// Ignore specific languages
|
|
41
|
-
for (const lang of (
|
|
66
|
+
for (const lang of (_b = opts.ignoredLanguages) !== null && _b !== void 0 ? _b : []) {
|
|
42
67
|
for (const key in langData) {
|
|
43
68
|
if (lang.toLowerCase() === key.toLowerCase()) {
|
|
44
69
|
delete langData[key];
|
|
@@ -50,7 +75,7 @@ async function analyse(input, opts = {}) {
|
|
|
50
75
|
const customIgnored = [];
|
|
51
76
|
const customBinary = [];
|
|
52
77
|
const customText = [];
|
|
53
|
-
if (!opts.
|
|
78
|
+
if (!useRawContent && opts.checkAttributes) {
|
|
54
79
|
for (const folder of folders) {
|
|
55
80
|
// Skip if folder is marked in gitattributes
|
|
56
81
|
if (customIgnored.some(path => (0, convert_glob_1.default)(path).test(folder)))
|
|
@@ -69,7 +94,7 @@ async function analyse(input, opts = {}) {
|
|
|
69
94
|
const attributesData = await (0, read_file_1.default)(attributesFile);
|
|
70
95
|
const relPathToRegex = (path) => (0, convert_glob_1.default)(path).source.substr(1).replace(folder, '');
|
|
71
96
|
// Explicit text/binary associations
|
|
72
|
-
const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)/gm);
|
|
97
|
+
const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)(?!=auto)/gm);
|
|
73
98
|
for (const [_line, path, type] of contentTypeMatches) {
|
|
74
99
|
if (['text', '-binary'].includes(type))
|
|
75
100
|
customText.push(relPathToRegex(path));
|
|
@@ -97,7 +122,7 @@ async function analyse(input, opts = {}) {
|
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
124
|
// Check vendored files
|
|
100
|
-
if (!opts.keepVendored) {
|
|
125
|
+
if (!useRawContent && !opts.keepVendored) {
|
|
101
126
|
// Filter out any files that match a vendor file path
|
|
102
127
|
const matcher = (match) => RegExp(match.replace(/\/$/, '/.+$').replace(/^\.\//, ''));
|
|
103
128
|
files = files.filter(file => !customIgnored.some(pattern => matcher(pattern).test(file)));
|
|
@@ -110,27 +135,35 @@ async function analyse(input, opts = {}) {
|
|
|
110
135
|
}
|
|
111
136
|
const parent = !opts.childLanguages && result && langData[result].group || false;
|
|
112
137
|
fileAssociations[file].push(parent || result);
|
|
113
|
-
extensions[file] = path_1.default.extname(file);
|
|
138
|
+
extensions[file] = path_1.default.extname(file).toLowerCase();
|
|
114
139
|
};
|
|
115
140
|
const overridesArray = Object.entries(overrides);
|
|
116
141
|
// List all languages that could be associated with a given file
|
|
117
142
|
for (const file of files) {
|
|
118
|
-
|
|
143
|
+
let firstLine;
|
|
144
|
+
if (useRawContent) {
|
|
145
|
+
firstLine = (_e = (_d = (_c = opts.fileContent) === null || _c === void 0 ? void 0 : _c[files.indexOf(file)]) === null || _d === void 0 ? void 0 : _d.split('\n')[0]) !== null && _e !== void 0 ? _e : null;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
if (!fs_1.default.existsSync(file) || fs_1.default.lstatSync(file).isDirectory())
|
|
149
|
+
continue;
|
|
150
|
+
firstLine = await (0, read_file_1.default)(file, true).catch(() => null);
|
|
151
|
+
}
|
|
152
|
+
// Skip if file is unreadable
|
|
153
|
+
if (firstLine === null)
|
|
119
154
|
continue;
|
|
120
155
|
// Check shebang line for explicit classification
|
|
121
|
-
if (!opts.quick && opts.checkShebang) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
156
|
+
if (!opts.quick && opts.checkShebang && firstLine.startsWith('#!')) {
|
|
157
|
+
// Find matching interpreters
|
|
158
|
+
const matches = Object.entries(langData).filter(([, data]) => { var _a; return (_a = data.interpreters) === null || _a === void 0 ? void 0 : _a.some(interpreter => firstLine.match('\\b' + interpreter + '\\b')); });
|
|
159
|
+
if (matches.length) {
|
|
160
|
+
// Add explicitly-identified language
|
|
161
|
+
const forcedLang = matches[0][0];
|
|
162
|
+
addResult(file, forcedLang);
|
|
130
163
|
}
|
|
131
164
|
}
|
|
132
165
|
// Check override for manual language classification
|
|
133
|
-
if (!opts.quick && opts.checkAttributes) {
|
|
166
|
+
if (!useRawContent && !opts.quick && opts.checkAttributes) {
|
|
134
167
|
const match = overridesArray.find(item => RegExp(item[0]).test(file));
|
|
135
168
|
if (match) {
|
|
136
169
|
const forcedLang = match[1];
|
|
@@ -142,7 +175,7 @@ async function analyse(input, opts = {}) {
|
|
|
142
175
|
let skipExts = false;
|
|
143
176
|
for (const lang in langData) {
|
|
144
177
|
// Check if filename is a match
|
|
145
|
-
const matchesName = (
|
|
178
|
+
const matchesName = (_f = langData[lang].filenames) === null || _f === void 0 ? void 0 : _f.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
|
|
146
179
|
if (matchesName) {
|
|
147
180
|
addResult(file, lang);
|
|
148
181
|
skipExts = true;
|
|
@@ -151,7 +184,7 @@ async function analyse(input, opts = {}) {
|
|
|
151
184
|
if (!skipExts)
|
|
152
185
|
for (const lang in langData) {
|
|
153
186
|
// Check if extension is a match
|
|
154
|
-
const matchesExt = (
|
|
187
|
+
const matchesExt = (_g = langData[lang].extensions) === null || _g === void 0 ? void 0 : _g.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
|
|
155
188
|
if (matchesExt)
|
|
156
189
|
addResult(file, lang);
|
|
157
190
|
}
|
|
@@ -162,7 +195,7 @@ async function analyse(input, opts = {}) {
|
|
|
162
195
|
// Narrow down file associations to the best fit
|
|
163
196
|
for (const file in fileAssociations) {
|
|
164
197
|
// Skip binary files
|
|
165
|
-
if (!opts.keepBinary) {
|
|
198
|
+
if (!useRawContent && !opts.keepBinary) {
|
|
166
199
|
const isCustomText = customText.some(path => RegExp(path).test(file));
|
|
167
200
|
const isCustomBinary = customBinary.some(path => RegExp(path).test(file));
|
|
168
201
|
const isBinaryExt = binary_extensions_1.default.some(ext => file.endsWith('.' + ext));
|
|
@@ -184,19 +217,22 @@ async function analyse(input, opts = {}) {
|
|
|
184
217
|
heuristic.language = heuristic.language[0];
|
|
185
218
|
}
|
|
186
219
|
// Make sure the results includes this language
|
|
220
|
+
const languageGroup = (_h = langData[heuristic.language]) === null || _h === void 0 ? void 0 : _h.group;
|
|
187
221
|
const matchesLang = fileAssociations[file].includes(heuristic.language);
|
|
188
|
-
const matchesParent =
|
|
222
|
+
const matchesParent = languageGroup && fileAssociations[file].includes(languageGroup);
|
|
189
223
|
if (!matchesLang && !matchesParent)
|
|
190
224
|
continue;
|
|
191
225
|
// Normalise heuristic data
|
|
192
226
|
const patterns = [];
|
|
193
|
-
const normalise = (contents) => patterns.push(...
|
|
227
|
+
const normalise = (contents) => patterns.push(...[contents].flat());
|
|
194
228
|
if (heuristic.pattern)
|
|
195
229
|
normalise(heuristic.pattern);
|
|
196
230
|
if (heuristic.named_pattern)
|
|
197
231
|
normalise(heuristicsData.named_patterns[heuristic.named_pattern]);
|
|
198
232
|
// Check file contents and apply heuristic patterns
|
|
199
|
-
const fileContent = await (0, read_file_1.default)(file);
|
|
233
|
+
const fileContent = await (0, read_file_1.default)(file).catch(() => null);
|
|
234
|
+
if (fileContent === null)
|
|
235
|
+
continue;
|
|
200
236
|
if (!patterns.length || patterns.some(pattern => (0, convert_pcre_1.default)(pattern).test(fileContent))) {
|
|
201
237
|
results.files.results[file] = heuristic.language;
|
|
202
238
|
break;
|
|
@@ -204,10 +240,10 @@ async function analyse(input, opts = {}) {
|
|
|
204
240
|
}
|
|
205
241
|
}
|
|
206
242
|
// If no heuristics, assign a language
|
|
207
|
-
(
|
|
243
|
+
(_j = (_r = results.files.results)[file]) !== null && _j !== void 0 ? _j : (_r[file] = fileAssociations[file][0]);
|
|
208
244
|
}
|
|
209
245
|
// Skip specified categories
|
|
210
|
-
if ((
|
|
246
|
+
if ((_k = opts.categories) === null || _k === void 0 ? void 0 : _k.length) {
|
|
211
247
|
const categories = ['data', 'markup', 'programming', 'prose'];
|
|
212
248
|
const hiddenCategories = categories.filter(cat => !opts.categories.includes(cat));
|
|
213
249
|
for (const [file, lang] of Object.entries(results.files.results)) {
|
|
@@ -225,7 +261,7 @@ async function analyse(input, opts = {}) {
|
|
|
225
261
|
}
|
|
226
262
|
}
|
|
227
263
|
// Convert paths to relative
|
|
228
|
-
if (opts.relativePaths) {
|
|
264
|
+
if (!useRawContent && opts.relativePaths) {
|
|
229
265
|
const newMap = {};
|
|
230
266
|
for (const [file, lang] of Object.entries(results.files.results)) {
|
|
231
267
|
let relPath = path_1.default.relative(process.cwd(), file).replace(/\\/g, '/');
|
|
@@ -239,21 +275,21 @@ async function analyse(input, opts = {}) {
|
|
|
239
275
|
for (const [file, lang] of Object.entries(results.files.results)) {
|
|
240
276
|
if (lang && !langData[lang])
|
|
241
277
|
continue;
|
|
242
|
-
const fileSize = fs_1.default.statSync(file).size;
|
|
278
|
+
const fileSize = (_o = (_m = (_l = opts.fileContent) === null || _l === void 0 ? void 0 : _l[files.indexOf(file)]) === null || _m === void 0 ? void 0 : _m.length) !== null && _o !== void 0 ? _o : fs_1.default.statSync(file).size;
|
|
243
279
|
results.files.bytes += fileSize;
|
|
244
280
|
// If no language found, add extension in other section
|
|
245
281
|
if (!lang) {
|
|
246
282
|
const ext = path_1.default.extname(file);
|
|
247
283
|
const unknownType = ext === '' ? 'filenames' : 'extensions';
|
|
248
284
|
const name = ext === '' ? path_1.default.basename(file) : ext;
|
|
249
|
-
(
|
|
285
|
+
(_p = (_s = results.unknown[unknownType])[name]) !== null && _p !== void 0 ? _p : (_s[name] = 0);
|
|
250
286
|
results.unknown[unknownType][name] += fileSize;
|
|
251
287
|
results.unknown.bytes += fileSize;
|
|
252
288
|
continue;
|
|
253
289
|
}
|
|
254
290
|
// Add language and bytes data to corresponding section
|
|
255
291
|
const { type } = langData[lang];
|
|
256
|
-
(
|
|
292
|
+
(_q = (_t = results.languages.results)[lang]) !== null && _q !== void 0 ? _q : (_t[lang] = { type, bytes: 0, color: langData[lang].color });
|
|
257
293
|
if (opts.childLanguages)
|
|
258
294
|
results.languages.results[lang].parent = langData[lang].group;
|
|
259
295
|
results.languages.results[lang].bytes += fileSize;
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare type FilePath = string;
|
|
|
5
5
|
export declare type Bytes = Integer;
|
|
6
6
|
export declare type Integer = number;
|
|
7
7
|
export interface Options {
|
|
8
|
+
fileContent?: string | string[];
|
|
8
9
|
ignoredFiles?: string[];
|
|
9
10
|
ignoredLanguages?: Language[];
|
|
10
11
|
categories?: Category[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linguist-js",
|
|
3
|
-
"version": "2.2
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Analyse languages used in a folder. Powered by GitHub Linguist, although it doesn't need to be installed.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -37,18 +37,19 @@
|
|
|
37
37
|
"homepage": "https://github.com/Nixinova/Linguist#readme",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"binary-extensions": "^2.2.0",
|
|
40
|
-
"commander": "^
|
|
41
|
-
"
|
|
40
|
+
"commander": "^9.0.0",
|
|
41
|
+
"common-path-prefix": "^3.0.0",
|
|
42
|
+
"cross-fetch": "^3.1.5",
|
|
42
43
|
"glob-to-regexp": "~0.4.1",
|
|
43
44
|
"isbinaryfile": "^4.0.8",
|
|
44
45
|
"js-yaml": "^4.1.0",
|
|
45
46
|
"node-cache": "^5.1.2"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
|
-
"@types/glob-to-regexp": "ts4.
|
|
49
|
-
"@types/js-yaml": "ts4.
|
|
50
|
-
"@types/node": "ts4.
|
|
51
|
-
"deep-object-diff": "^1.1.
|
|
52
|
-
"typescript": "~4.
|
|
49
|
+
"@types/glob-to-regexp": "ts4.6",
|
|
50
|
+
"@types/js-yaml": "ts4.6",
|
|
51
|
+
"@types/node": "ts4.6",
|
|
52
|
+
"deep-object-diff": "^1.1.7",
|
|
53
|
+
"typescript": "~4.6.1-rc"
|
|
53
54
|
}
|
|
54
55
|
}
|
package/readme.md
CHANGED
|
@@ -77,6 +77,7 @@ Running Linguist on this folder will return the following JSON:
|
|
|
77
77
|
### Notes
|
|
78
78
|
|
|
79
79
|
- File paths in the output use only forward slashes as delimiters, even on Windows.
|
|
80
|
+
- This tool does not work when offline.
|
|
80
81
|
- Do not rely on any language classification output from Linguist being unchanged between runs.
|
|
81
82
|
Language data is fetched each run from the latest classifications of [`github-linguist`](https://github.com/github/linguist).
|
|
82
83
|
This data is subject to change at any time and may change the results of a run even when using the same version of Linguist.
|
|
@@ -99,6 +100,8 @@ const { files, languages, unknown } = linguist(folder, options);
|
|
|
99
100
|
Analyse multiple folders using the syntax `"{folder1,folder2,...}"`.
|
|
100
101
|
- `opts` (optional; object):
|
|
101
102
|
An object containing analyser options.
|
|
103
|
+
- `fileContent` (string or string array):
|
|
104
|
+
Provides the file content associated with the file name(s) given as `entry` to analyse instead of reading from a folder on disk.
|
|
102
105
|
- `ignoredFiles` (string array):
|
|
103
106
|
A list of file path globs to explicitly ignore.
|
|
104
107
|
- `ignoredLanguages` (string array):
|