linguist-js 2.3.1 → 2.4.1
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 +2 -4
- package/dist/helpers/walk-tree.js +38 -15
- package/dist/index.js +102 -54
- package/dist/types.d.ts +1 -0
- package/license.md +1 -1
- package/package.json +3 -2
- package/readme.md +9 -3
- package/dist/helpers/convert-glob.js +0 -11
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
|
|
@@ -27,6 +26,7 @@ commander_1.program
|
|
|
27
26
|
.option('-I|--checkIgnored [bool]', 'Force the checking of gitignore files', true)
|
|
28
27
|
.option('-H|--checkHeuristics [bool]', 'Apply heuristics to ambiguous languages', true)
|
|
29
28
|
.option('-S|--checkShebang [bool]', 'Check shebang lines for explicit classification', true)
|
|
29
|
+
.option('-M|--checkModeline [bool]', 'Check modelines for explicit classification', true)
|
|
30
30
|
.helpOption(`-h|--help`, 'Display this help message')
|
|
31
31
|
.version(VERSION, '-v|--version', 'Display the installed version of linguist-js');
|
|
32
32
|
commander_1.program.parse(process.argv);
|
|
@@ -53,13 +53,11 @@ if (args.analyze)
|
|
|
53
53
|
const root = args.analyze === true ? '.' : args.analyze;
|
|
54
54
|
const data = await (0, index_1.default)(root, args);
|
|
55
55
|
const { files, languages, unknown } = data;
|
|
56
|
-
// Get file count
|
|
57
|
-
const totalFiles = (0, walk_tree_1.default)(root).files.length;
|
|
58
56
|
// Print output
|
|
59
57
|
if (!args.json) {
|
|
60
58
|
const sortedEntries = Object.entries(languages.results).sort((a, b) => a[1].bytes < b[1].bytes ? +1 : -1);
|
|
61
59
|
const totalBytes = languages.bytes;
|
|
62
|
-
console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${
|
|
60
|
+
console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${files.count} files with linguist-js`);
|
|
63
61
|
console.log(`\n Language analysis results:`);
|
|
64
62
|
let count = 0;
|
|
65
63
|
if (sortedEntries.length === 0)
|
|
@@ -8,28 +8,51 @@ 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, gitignores, regexIgnores) {
|
|
12
|
+
// Walk tree of a folder
|
|
13
|
+
if (folders.length === 1) {
|
|
14
|
+
const folder = folders[0];
|
|
15
|
+
// Get list of files and folders inside this folder
|
|
16
|
+
const files = fs_1.default.readdirSync(folder).map(file => {
|
|
17
|
+
// Create path relative to root
|
|
18
|
+
const base = path_1.default.resolve(folder, file).replace(/\\/g, '/').replace(root, '.');
|
|
19
|
+
// Add trailing slash to mark directories
|
|
20
|
+
const isDir = fs_1.default.lstatSync(path_1.default.resolve(root, base)).isDirectory();
|
|
21
|
+
return isDir ? `${base}/` : base;
|
|
22
|
+
});
|
|
23
|
+
// Loop through files and folders
|
|
18
24
|
for (const file of files) {
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
// Create absolute path for disc operations
|
|
26
|
+
const path = path_1.default.resolve(root, file).replace(/\\/g, '/');
|
|
27
|
+
// Skip if nonexistant or ignored
|
|
28
|
+
const nonExistant = !fs_1.default.existsSync(path);
|
|
29
|
+
const isGitIgnored = gitignores.test(file.replace('./', '')).ignored;
|
|
30
|
+
const isRegexIgnored = regexIgnores.find(match => file.replace('./', '').match(match));
|
|
31
|
+
if (nonExistant || isGitIgnored || isRegexIgnored)
|
|
21
32
|
continue;
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
// Add absolute folder path to list
|
|
34
|
+
allFolders.add(path_1.default.resolve(folder).replace(/\\/g, '/'));
|
|
35
|
+
// Check if this is a folder or file
|
|
36
|
+
if (file.endsWith('/')) {
|
|
37
|
+
// Recurse into subfolders
|
|
24
38
|
allFolders.add(path);
|
|
25
|
-
walk(path,
|
|
26
|
-
|
|
39
|
+
walk(root, [path], gitignores, regexIgnores);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Add relative file path to list
|
|
43
|
+
allFiles.add(path);
|
|
27
44
|
}
|
|
28
|
-
allFiles.add(path);
|
|
29
45
|
}
|
|
30
46
|
}
|
|
47
|
+
// Recurse into all folders
|
|
48
|
+
else {
|
|
49
|
+
for (const path of folders) {
|
|
50
|
+
walk(root, [path], gitignores, regexIgnores);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Return absolute files and folders lists
|
|
31
54
|
return {
|
|
32
|
-
files: [...allFiles],
|
|
55
|
+
files: [...allFiles].map(file => file.replace(/^\./, root)),
|
|
33
56
|
folders: [...allFolders],
|
|
34
57
|
};
|
|
35
58
|
}
|
package/dist/index.js
CHANGED
|
@@ -5,24 +5,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
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
|
-
const
|
|
8
|
+
const ignore_1 = __importDefault(require("ignore"));
|
|
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"));
|
|
12
13
|
const load_data_1 = __importDefault(require("./helpers/load-data"));
|
|
13
14
|
const read_file_1 = __importDefault(require("./helpers/read-file"));
|
|
14
15
|
const convert_pcre_1 = __importDefault(require("./helpers/convert-pcre"));
|
|
15
|
-
const convert_glob_1 = __importDefault(require("./helpers/convert-glob"));
|
|
16
16
|
async function analyse(input, opts = {}) {
|
|
17
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
18
|
-
var
|
|
17
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
18
|
+
var _t, _u, _v;
|
|
19
19
|
const useRawContent = opts.fileContent !== undefined;
|
|
20
|
+
input = [input !== null && input !== void 0 ? input : []].flat();
|
|
21
|
+
opts.fileContent = [(_a = opts.fileContent) !== null && _a !== void 0 ? _a : []].flat();
|
|
22
|
+
// Load data from github-linguist web repo
|
|
20
23
|
const langData = await (0, load_data_1.default)('languages.yml').then(js_yaml_1.default.load);
|
|
21
24
|
const vendorData = await (0, load_data_1.default)('vendor.yml').then(js_yaml_1.default.load);
|
|
25
|
+
const docData = await (0, load_data_1.default)('documentation.yml').then(js_yaml_1.default.load);
|
|
22
26
|
const heuristicsData = await (0, load_data_1.default)('heuristics.yml').then(js_yaml_1.default.load);
|
|
23
|
-
const generatedData = await (0, load_data_1.default)('generated.rb').then(text => { var _a; return (_a = text.match(/(?<=name\.match\(\/).+?(?=(?<!\\)
|
|
24
|
-
vendorData
|
|
27
|
+
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 : []; });
|
|
28
|
+
const vendorPaths = [...vendorData, ...docData, ...generatedData];
|
|
29
|
+
// Setup main variables
|
|
25
30
|
const fileAssociations = {};
|
|
31
|
+
const definiteness = {};
|
|
26
32
|
const extensions = {};
|
|
27
33
|
const overrides = {};
|
|
28
34
|
const results = {
|
|
@@ -30,25 +36,43 @@ async function analyse(input, opts = {}) {
|
|
|
30
36
|
languages: { count: 0, bytes: 0, results: {} },
|
|
31
37
|
unknown: { count: 0, bytes: 0, extensions: {}, filenames: {} },
|
|
32
38
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
// Prepare list of ignored files
|
|
40
|
+
const gitignores = (0, ignore_1.default)();
|
|
41
|
+
const regexIgnores = [];
|
|
42
|
+
gitignores.add('/.git');
|
|
43
|
+
if (!opts.keepVendored)
|
|
44
|
+
regexIgnores.push(...vendorPaths.map(path => RegExp(path, 'i')));
|
|
45
|
+
if (opts.ignoredFiles)
|
|
46
|
+
gitignores.add(opts.ignoredFiles);
|
|
47
|
+
// Set a common root path so that vendor paths do not incorrectly match parent folders
|
|
48
|
+
const resolvedInput = input.map(path => path_1.default.resolve(path).replace(/\\/g, '/'));
|
|
49
|
+
const commonRoot = (input.length > 1 ? (0, common_path_prefix_1.default)(resolvedInput) : resolvedInput[0]).replace(/\/?$/, '');
|
|
50
|
+
const relPath = (file) => path_1.default.relative(commonRoot, file).replace(/\\/g, '/');
|
|
51
|
+
const unRelPath = (file) => path_1.default.resolve(commonRoot, file).replace(/\\/g, '/');
|
|
52
|
+
// Load file paths and folders
|
|
38
53
|
let files, folders;
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
files =
|
|
54
|
+
if (useRawContent) {
|
|
55
|
+
// Uses raw file content
|
|
56
|
+
files = input;
|
|
42
57
|
folders = [''];
|
|
43
58
|
}
|
|
44
59
|
else {
|
|
45
|
-
|
|
46
|
-
(
|
|
60
|
+
// Uses directory on disc
|
|
61
|
+
const data = (0, walk_tree_1.default)(commonRoot, input, gitignores, regexIgnores);
|
|
62
|
+
files = data.files;
|
|
63
|
+
folders = data.folders;
|
|
47
64
|
}
|
|
48
65
|
// Apply aliases
|
|
49
|
-
opts = {
|
|
66
|
+
opts = {
|
|
67
|
+
checkIgnored: !opts.quick,
|
|
68
|
+
checkAttributes: !opts.quick,
|
|
69
|
+
checkHeuristics: !opts.quick,
|
|
70
|
+
checkShebang: !opts.quick,
|
|
71
|
+
checkModeline: !opts.quick,
|
|
72
|
+
...opts
|
|
73
|
+
};
|
|
50
74
|
// Ignore specific languages
|
|
51
|
-
for (const lang of (
|
|
75
|
+
for (const lang of (_b = opts.ignoredLanguages) !== null && _b !== void 0 ? _b : []) {
|
|
52
76
|
for (const key in langData) {
|
|
53
77
|
if (lang.toLowerCase() === key.toLowerCase()) {
|
|
54
78
|
delete langData[key];
|
|
@@ -56,40 +80,36 @@ async function analyse(input, opts = {}) {
|
|
|
56
80
|
}
|
|
57
81
|
}
|
|
58
82
|
}
|
|
59
|
-
// Load gitattributes
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
if (!useRawContent && !opts.quick) {
|
|
83
|
+
// Load gitignores and gitattributes
|
|
84
|
+
const customBinary = (0, ignore_1.default)();
|
|
85
|
+
const customText = (0, ignore_1.default)();
|
|
86
|
+
if (!useRawContent && opts.checkAttributes) {
|
|
64
87
|
for (const folder of folders) {
|
|
65
88
|
// Skip if folder is marked in gitattributes
|
|
66
|
-
if (
|
|
89
|
+
if (relPath(folder) && gitignores.test(relPath(folder)).ignored)
|
|
67
90
|
continue;
|
|
68
91
|
// Parse gitignores
|
|
69
92
|
const ignoresFile = path_1.default.join(folder, '.gitignore');
|
|
70
93
|
if (opts.checkIgnored && fs_1.default.existsSync(ignoresFile)) {
|
|
71
94
|
const ignoresData = await (0, read_file_1.default)(ignoresFile);
|
|
72
|
-
|
|
73
|
-
const ignoredPaths = ignoresList.map(path => (0, convert_glob_1.default)(path).source);
|
|
74
|
-
customIgnored.push(...ignoredPaths);
|
|
95
|
+
gitignores.add(ignoresData);
|
|
75
96
|
}
|
|
76
97
|
// Parse gitattributes
|
|
77
98
|
const attributesFile = path_1.default.join(folder, '.gitattributes');
|
|
78
99
|
if (opts.checkAttributes && fs_1.default.existsSync(attributesFile)) {
|
|
79
100
|
const attributesData = await (0, read_file_1.default)(attributesFile);
|
|
80
|
-
const relPathToRegex = (path) => (0, convert_glob_1.default)(path).source.substr(1).replace(folder, '');
|
|
81
101
|
// Explicit text/binary associations
|
|
82
102
|
const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)(?!=auto)/gm);
|
|
83
103
|
for (const [_line, path, type] of contentTypeMatches) {
|
|
84
104
|
if (['text', '-binary'].includes(type))
|
|
85
|
-
customText.
|
|
105
|
+
customText.add(path);
|
|
86
106
|
if (['-text', 'binary'].includes(type))
|
|
87
|
-
customBinary.
|
|
107
|
+
customBinary.add(path);
|
|
88
108
|
}
|
|
89
109
|
// Custom vendor options
|
|
90
110
|
const vendorMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-(vendored|generated|documentation)(?!=false)/gm);
|
|
91
111
|
for (const [_line, path] of vendorMatches) {
|
|
92
|
-
|
|
112
|
+
gitignores.add(path);
|
|
93
113
|
}
|
|
94
114
|
// Custom file associations
|
|
95
115
|
const customLangMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-language=(\S+)/gm);
|
|
@@ -100,17 +120,22 @@ async function analyse(input, opts = {}) {
|
|
|
100
120
|
if (overrideLang)
|
|
101
121
|
forcedLang = overrideLang[0];
|
|
102
122
|
}
|
|
103
|
-
const fullPath = folder +
|
|
123
|
+
const fullPath = relPath(folder) + '/' + path;
|
|
104
124
|
overrides[fullPath] = forcedLang;
|
|
105
125
|
}
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
}
|
|
109
129
|
// Check vendored files
|
|
110
|
-
if (!
|
|
130
|
+
if (!opts.keepVendored) {
|
|
111
131
|
// Filter out any files that match a vendor file path
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
if (useRawContent) {
|
|
133
|
+
files = gitignores.filter(files);
|
|
134
|
+
files = files.filter(file => !regexIgnores.find(match => match.test(file)));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
files = gitignores.filter(files.map(relPath)).map(unRelPath);
|
|
138
|
+
}
|
|
114
139
|
}
|
|
115
140
|
// Load all files and parse languages
|
|
116
141
|
const addResult = (file, result) => {
|
|
@@ -126,8 +151,8 @@ async function analyse(input, opts = {}) {
|
|
|
126
151
|
// List all languages that could be associated with a given file
|
|
127
152
|
for (const file of files) {
|
|
128
153
|
let firstLine;
|
|
129
|
-
if (
|
|
130
|
-
firstLine = (
|
|
154
|
+
if (useRawContent) {
|
|
155
|
+
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;
|
|
131
156
|
}
|
|
132
157
|
else {
|
|
133
158
|
if (!fs_1.default.existsSync(file) || fs_1.default.lstatSync(file).isDirectory())
|
|
@@ -137,22 +162,40 @@ async function analyse(input, opts = {}) {
|
|
|
137
162
|
// Skip if file is unreadable
|
|
138
163
|
if (firstLine === null)
|
|
139
164
|
continue;
|
|
140
|
-
// Check
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
// Check first line for explicit classification
|
|
166
|
+
const hasShebang = opts.checkShebang && /^#!/.test(firstLine);
|
|
167
|
+
const hasModeline = opts.checkModeline && /-\*-|(syntax|filetype|ft)\s*=/.test(firstLine);
|
|
168
|
+
if (!opts.quick && (hasShebang || hasModeline)) {
|
|
169
|
+
const matches = [];
|
|
170
|
+
for (const [lang, data] of Object.entries(langData)) {
|
|
171
|
+
const langMatcher = (lang) => `\\b${lang.toLowerCase().replace(/\W/g, '\\$&')}(?![\\w#+*]|-\*-)`;
|
|
172
|
+
// Check for interpreter match
|
|
173
|
+
const matchesInterpretor = (_f = data.interpreters) === null || _f === void 0 ? void 0 : _f.some(interpreter => firstLine.match(`\\b${interpreter}\\b`));
|
|
174
|
+
// Check modeline declaration
|
|
175
|
+
const matchesLang = firstLine.toLowerCase().match(langMatcher(lang));
|
|
176
|
+
const matchesAlias = (_g = data.aliases) === null || _g === void 0 ? void 0 : _g.some(lang => firstLine.toLowerCase().match(langMatcher(lang)));
|
|
177
|
+
// Add language
|
|
178
|
+
if (opts.checkShebang && matchesInterpretor)
|
|
179
|
+
matches.push(lang);
|
|
180
|
+
if (opts.checkModeline && (matchesLang || matchesAlias))
|
|
181
|
+
matches.push(lang);
|
|
182
|
+
}
|
|
144
183
|
if (matches.length) {
|
|
145
184
|
// Add explicitly-identified language
|
|
146
|
-
const forcedLang = matches[0]
|
|
185
|
+
const forcedLang = matches[0];
|
|
147
186
|
addResult(file, forcedLang);
|
|
187
|
+
definiteness[file] = true;
|
|
188
|
+
continue;
|
|
148
189
|
}
|
|
149
190
|
}
|
|
150
191
|
// Check override for manual language classification
|
|
151
192
|
if (!useRawContent && !opts.quick && opts.checkAttributes) {
|
|
152
|
-
const
|
|
193
|
+
const isOverridden = (path) => (0, ignore_1.default)().add(path).test(relPath(file)).ignored;
|
|
194
|
+
const match = overridesArray.find(item => isOverridden(item[0]));
|
|
153
195
|
if (match) {
|
|
154
196
|
const forcedLang = match[1];
|
|
155
197
|
addResult(file, forcedLang);
|
|
198
|
+
definiteness[file] = true;
|
|
156
199
|
continue;
|
|
157
200
|
}
|
|
158
201
|
}
|
|
@@ -160,7 +203,7 @@ async function analyse(input, opts = {}) {
|
|
|
160
203
|
let skipExts = false;
|
|
161
204
|
for (const lang in langData) {
|
|
162
205
|
// Check if filename is a match
|
|
163
|
-
const matchesName = (
|
|
206
|
+
const matchesName = (_h = langData[lang].filenames) === null || _h === void 0 ? void 0 : _h.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
|
|
164
207
|
if (matchesName) {
|
|
165
208
|
addResult(file, lang);
|
|
166
209
|
skipExts = true;
|
|
@@ -169,7 +212,7 @@ async function analyse(input, opts = {}) {
|
|
|
169
212
|
if (!skipExts)
|
|
170
213
|
for (const lang in langData) {
|
|
171
214
|
// Check if extension is a match
|
|
172
|
-
const matchesExt = (
|
|
215
|
+
const matchesExt = (_j = langData[lang].extensions) === null || _j === void 0 ? void 0 : _j.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
|
|
173
216
|
if (matchesExt)
|
|
174
217
|
addResult(file, lang);
|
|
175
218
|
}
|
|
@@ -179,10 +222,15 @@ async function analyse(input, opts = {}) {
|
|
|
179
222
|
}
|
|
180
223
|
// Narrow down file associations to the best fit
|
|
181
224
|
for (const file in fileAssociations) {
|
|
225
|
+
// Skip if file has explicit association
|
|
226
|
+
if (definiteness[file]) {
|
|
227
|
+
results.files.results[file] = fileAssociations[file][0];
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
182
230
|
// Skip binary files
|
|
183
231
|
if (!useRawContent && !opts.keepBinary) {
|
|
184
|
-
const isCustomText = customText.
|
|
185
|
-
const isCustomBinary = customBinary.
|
|
232
|
+
const isCustomText = customText.test(relPath(file)).ignored;
|
|
233
|
+
const isCustomBinary = customBinary.test(relPath(file)).ignored;
|
|
186
234
|
const isBinaryExt = binary_extensions_1.default.some(ext => file.endsWith('.' + ext));
|
|
187
235
|
if (!isCustomText && (isCustomBinary || isBinaryExt || await (0, isbinaryfile_1.isBinaryFile)(file))) {
|
|
188
236
|
continue;
|
|
@@ -202,14 +250,14 @@ async function analyse(input, opts = {}) {
|
|
|
202
250
|
heuristic.language = heuristic.language[0];
|
|
203
251
|
}
|
|
204
252
|
// Make sure the results includes this language
|
|
205
|
-
const languageGroup = (
|
|
253
|
+
const languageGroup = (_k = langData[heuristic.language]) === null || _k === void 0 ? void 0 : _k.group;
|
|
206
254
|
const matchesLang = fileAssociations[file].includes(heuristic.language);
|
|
207
255
|
const matchesParent = languageGroup && fileAssociations[file].includes(languageGroup);
|
|
208
256
|
if (!matchesLang && !matchesParent)
|
|
209
257
|
continue;
|
|
210
258
|
// Normalise heuristic data
|
|
211
259
|
const patterns = [];
|
|
212
|
-
const normalise = (contents) => patterns.push(...
|
|
260
|
+
const normalise = (contents) => patterns.push(...[contents].flat());
|
|
213
261
|
if (heuristic.pattern)
|
|
214
262
|
normalise(heuristic.pattern);
|
|
215
263
|
if (heuristic.named_pattern)
|
|
@@ -225,10 +273,10 @@ async function analyse(input, opts = {}) {
|
|
|
225
273
|
}
|
|
226
274
|
}
|
|
227
275
|
// If no heuristics, assign a language
|
|
228
|
-
(
|
|
276
|
+
(_l = (_t = results.files.results)[file]) !== null && _l !== void 0 ? _l : (_t[file] = fileAssociations[file][0]);
|
|
229
277
|
}
|
|
230
278
|
// Skip specified categories
|
|
231
|
-
if ((
|
|
279
|
+
if ((_m = opts.categories) === null || _m === void 0 ? void 0 : _m.length) {
|
|
232
280
|
const categories = ['data', 'markup', 'programming', 'prose'];
|
|
233
281
|
const hiddenCategories = categories.filter(cat => !opts.categories.includes(cat));
|
|
234
282
|
for (const [file, lang] of Object.entries(results.files.results)) {
|
|
@@ -260,21 +308,21 @@ async function analyse(input, opts = {}) {
|
|
|
260
308
|
for (const [file, lang] of Object.entries(results.files.results)) {
|
|
261
309
|
if (lang && !langData[lang])
|
|
262
310
|
continue;
|
|
263
|
-
const fileSize = (
|
|
311
|
+
const fileSize = (_q = (_p = (_o = opts.fileContent) === null || _o === void 0 ? void 0 : _o[files.indexOf(file)]) === null || _p === void 0 ? void 0 : _p.length) !== null && _q !== void 0 ? _q : fs_1.default.statSync(file).size;
|
|
264
312
|
results.files.bytes += fileSize;
|
|
265
313
|
// If no language found, add extension in other section
|
|
266
314
|
if (!lang) {
|
|
267
315
|
const ext = path_1.default.extname(file);
|
|
268
316
|
const unknownType = ext === '' ? 'filenames' : 'extensions';
|
|
269
317
|
const name = ext === '' ? path_1.default.basename(file) : ext;
|
|
270
|
-
(
|
|
318
|
+
(_r = (_u = results.unknown[unknownType])[name]) !== null && _r !== void 0 ? _r : (_u[name] = 0);
|
|
271
319
|
results.unknown[unknownType][name] += fileSize;
|
|
272
320
|
results.unknown.bytes += fileSize;
|
|
273
321
|
continue;
|
|
274
322
|
}
|
|
275
323
|
// Add language and bytes data to corresponding section
|
|
276
324
|
const { type } = langData[lang];
|
|
277
|
-
(
|
|
325
|
+
(_s = (_v = results.languages.results)[lang]) !== null && _s !== void 0 ? _s : (_v[lang] = { type, bytes: 0, color: langData[lang].color });
|
|
278
326
|
if (opts.childLanguages)
|
|
279
327
|
results.languages.results[lang].parent = langData[lang].group;
|
|
280
328
|
results.languages.results[lang].bytes += fileSize;
|
package/dist/types.d.ts
CHANGED
package/license.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linguist-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
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": {
|
|
@@ -38,8 +38,9 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"binary-extensions": "^2.2.0",
|
|
40
40
|
"commander": "^9.0.0",
|
|
41
|
+
"common-path-prefix": "^3.0.0",
|
|
41
42
|
"cross-fetch": "^3.1.5",
|
|
42
|
-
"
|
|
43
|
+
"ignore": "^5.2.0",
|
|
43
44
|
"isbinaryfile": "^4.0.8",
|
|
44
45
|
"js-yaml": "^4.1.0",
|
|
45
46
|
"node-cache": "^5.1.2"
|
package/readme.md
CHANGED
|
@@ -97,7 +97,6 @@ const { files, languages, unknown } = linguist(folder, options);
|
|
|
97
97
|
Analyse the language of all files found in a folder.
|
|
98
98
|
- `entry` (optional; string or string array):
|
|
99
99
|
The folder(s) to analyse (defaults to `./`).
|
|
100
|
-
Analyse multiple folders using the syntax `"{folder1,folder2,...}"`.
|
|
101
100
|
- `opts` (optional; object):
|
|
102
101
|
An object containing analyser options.
|
|
103
102
|
- `fileContent` (string or string array):
|
|
@@ -113,21 +112,26 @@ const { files, languages, unknown } = linguist(folder, options);
|
|
|
113
112
|
Whether to display sub-languages instead of their parents when possible (defaults to `false`).
|
|
114
113
|
- `quick` (boolean):
|
|
115
114
|
Whether to skip complex language analysis such as the checking of heuristics and gitattributes statements (defaults to `false`).
|
|
116
|
-
Alias for `checkAttributes:false, checkIgnored:false, checkHeuristics:false, checkShebang:false`.
|
|
115
|
+
Alias for `checkAttributes:false, checkIgnored:false, checkHeuristics:false, checkShebang:false, checkModeline:false`.
|
|
117
116
|
- `keepVendored` (boolean):
|
|
118
117
|
Whether to keep vendored files (dependencies, etc) (defaults to `false`).
|
|
118
|
+
Does nothing when `fileContent` is set.
|
|
119
119
|
- `keepBinary` (boolean):
|
|
120
120
|
Whether binary files should be included in the output (defaults to `false`).
|
|
121
121
|
- `relativePaths` (boolean):
|
|
122
122
|
Change the absolute file paths in the output to be relative to the current working directory (defaults to `false`).
|
|
123
123
|
- `checkAttributes` (boolean):
|
|
124
124
|
Force the checking of `.gitattributes` files (defaults to `true` unless `quick` is set).
|
|
125
|
+
Does nothing when `fileContent` is set.
|
|
125
126
|
- `checkIgnored` (boolean):
|
|
126
127
|
Force the checking of `.gitignore` files (defaults to `true` unless `quick` is set).
|
|
128
|
+
Does nothing when `fileContent` is set.
|
|
127
129
|
- `checkHeuristics` (boolean):
|
|
128
130
|
Apply heuristics to ambiguous languages (defaults to `true` unless `quick` is set).
|
|
129
131
|
- `checkShebang` (boolean):
|
|
130
132
|
Check shebang (`#!`) lines for explicit language classification (defaults to `true` unless `quick` is set).
|
|
133
|
+
- `checkModeline` (boolean):
|
|
134
|
+
Check modelines for explicit language classification (defaults to `true` unless `quick` is set).
|
|
131
135
|
|
|
132
136
|
### Command-line
|
|
133
137
|
|
|
@@ -155,7 +159,7 @@ linguist --help
|
|
|
155
159
|
Requires `--json` to be specified.
|
|
156
160
|
- `--quick`:
|
|
157
161
|
Whether to skip the checking of `.gitattributes` and `.gitignore` files for manual language classifications.
|
|
158
|
-
Alias for `--checkAttributes=false --checkIgnored=false --checkHeuristics=false --checkShebang=false`.
|
|
162
|
+
Alias for `--checkAttributes=false --checkIgnored=false --checkHeuristics=false --checkShebang=false --checkModeline=false`.
|
|
159
163
|
- `--keepVendored`:
|
|
160
164
|
Whether to include vendored files (auto-generated files, dependencies folder, etc).
|
|
161
165
|
- `--keepBinary`:
|
|
@@ -170,6 +174,8 @@ linguist --help
|
|
|
170
174
|
Apply heuristics to ambiguous languages (use alongside `--quick` to overwrite).
|
|
171
175
|
- `--checkShebang`:
|
|
172
176
|
Check shebang (`#!`) lines for explicit classification (use alongside `--quick` to overwrite).
|
|
177
|
+
- `--checkModeline`:
|
|
178
|
+
Check modelines for explicit classification (use alongside `--quick` to overwrite).
|
|
173
179
|
- `--help`:
|
|
174
180
|
Display a help message.
|
|
175
181
|
- `--version`:
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const glob_to_regexp_1 = __importDefault(require("glob-to-regexp"));
|
|
7
|
-
function parseGitGlob(path) {
|
|
8
|
-
const globPath = `**/${path}`.replace(/\\/g, '/').replace(/\[:(space|digit):\]/g, (_, val) => `\\${val[0]}`);
|
|
9
|
-
return (0, glob_to_regexp_1.default)(globPath, { globstar: true, extended: true });
|
|
10
|
-
}
|
|
11
|
-
exports.default = parseGitGlob;
|