linguist-js 2.3.2 → 2.4.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 CHANGED
@@ -26,6 +26,7 @@ commander_1.program
26
26
  .option('-I|--checkIgnored [bool]', 'Force the checking of gitignore files', true)
27
27
  .option('-H|--checkHeuristics [bool]', 'Apply heuristics to ambiguous languages', true)
28
28
  .option('-S|--checkShebang [bool]', 'Check shebang lines for explicit classification', true)
29
+ .option('-M|--checkModeline [bool]', 'Check modelines for explicit classification', true)
29
30
  .helpOption(`-h|--help`, 'Display this help message')
30
31
  .version(VERSION, '-v|--version', 'Display the installed version of linguist-js');
31
32
  commander_1.program.parse(process.argv);
@@ -5,12 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const fs_1 = __importDefault(require("fs"));
7
7
  const path_1 = __importDefault(require("path"));
8
- const allFiles = new Set();
9
- const allFolders = new Set();
8
+ let allFiles;
9
+ let allFolders;
10
10
  /** Generate list of files in a directory. */
11
- function walk(root, folders, ignored = []) {
12
- // Switch out paths that expect being in root
13
- ignored = ignored.map(match => match.replace(/^\^/, '^\\./'));
11
+ function walk(init, root, folders, gitignores, regexIgnores) {
12
+ // Initialise files and folders lists
13
+ if (init) {
14
+ allFiles = new Set();
15
+ allFolders = new Set();
16
+ }
14
17
  // Walk tree of a folder
15
18
  if (folders.length === 1) {
16
19
  const folder = folders[0];
@@ -27,7 +30,10 @@ function walk(root, folders, ignored = []) {
27
30
  // Create absolute path for disc operations
28
31
  const path = path_1.default.resolve(root, file).replace(/\\/g, '/');
29
32
  // Skip if nonexistant or ignored
30
- if (!fs_1.default.existsSync(path) || ignored.some(pattern => RegExp(pattern).test(file)))
33
+ const nonExistant = !fs_1.default.existsSync(path);
34
+ const isGitIgnored = gitignores.test(file.replace('./', '')).ignored;
35
+ const isRegexIgnored = regexIgnores.find(match => file.replace('./', '').match(match));
36
+ if (nonExistant || isGitIgnored || isRegexIgnored)
31
37
  continue;
32
38
  // Add absolute folder path to list
33
39
  allFolders.add(path_1.default.resolve(folder).replace(/\\/g, '/'));
@@ -35,7 +41,7 @@ function walk(root, folders, ignored = []) {
35
41
  if (file.endsWith('/')) {
36
42
  // Recurse into subfolders
37
43
  allFolders.add(path);
38
- walk(root, [path], ignored);
44
+ walk(false, root, [path], gitignores, regexIgnores);
39
45
  }
40
46
  else {
41
47
  // Add relative file path to list
@@ -46,7 +52,7 @@ function walk(root, folders, ignored = []) {
46
52
  // Recurse into all folders
47
53
  else {
48
54
  for (const path of folders) {
49
- walk(root, [path], ignored);
55
+ walk(false, root, [path], gitignores, regexIgnores);
50
56
  }
51
57
  }
52
58
  // Return absolute files and folders lists
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ 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 glob_to_regexp_1 = __importDefault(require("glob-to-regexp"));
8
+ const ignore_1 = __importDefault(require("ignore"));
9
9
  const common_path_prefix_1 = __importDefault(require("common-path-prefix"));
10
10
  const binary_extensions_1 = __importDefault(require("binary-extensions"));
11
11
  const isbinaryfile_1 = require("isbinaryfile");
@@ -13,10 +13,9 @@ const walk_tree_1 = __importDefault(require("./helpers/walk-tree"));
13
13
  const load_data_1 = __importDefault(require("./helpers/load-data"));
14
14
  const read_file_1 = __importDefault(require("./helpers/read-file"));
15
15
  const convert_pcre_1 = __importDefault(require("./helpers/convert-pcre"));
16
- const convert_glob_1 = __importDefault(require("./helpers/convert-glob"));
17
16
  async function analyse(input, opts = {}) {
18
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
19
- var _r, _s, _t;
17
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
18
+ var _t, _u, _v;
20
19
  const useRawContent = opts.fileContent !== undefined;
21
20
  input = [input !== null && input !== void 0 ? input : []].flat();
22
21
  opts.fileContent = [(_a = opts.fileContent) !== null && _a !== void 0 ? _a : []].flat();
@@ -25,10 +24,11 @@ async function analyse(input, opts = {}) {
25
24
  const vendorData = await (0, load_data_1.default)('vendor.yml').then(js_yaml_1.default.load);
26
25
  const docData = await (0, load_data_1.default)('documentation.yml').then(js_yaml_1.default.load);
27
26
  const heuristicsData = await (0, load_data_1.default)('heuristics.yml').then(js_yaml_1.default.load);
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 : []; });
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 : []; });
29
28
  const vendorPaths = [...vendorData, ...docData, ...generatedData];
30
29
  // Setup main variables
31
30
  const fileAssociations = {};
31
+ const definiteness = {};
32
32
  const extensions = {};
33
33
  const overrides = {};
34
34
  const results = {
@@ -37,13 +37,18 @@ async function analyse(input, opts = {}) {
37
37
  unknown: { count: 0, bytes: 0, extensions: {}, filenames: {} },
38
38
  };
39
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
- }
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, '/');
47
52
  // Load file paths and folders
48
53
  let files, folders;
49
54
  if (useRawContent) {
@@ -53,15 +58,19 @@ async function analyse(input, opts = {}) {
53
58
  }
54
59
  else {
55
60
  // 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);
61
+ const data = (0, walk_tree_1.default)(true, commonRoot, input, gitignores, regexIgnores);
60
62
  files = data.files;
61
63
  folders = data.folders;
62
64
  }
63
65
  // Apply aliases
64
- opts = { checkIgnored: !opts.quick, checkAttributes: !opts.quick, checkHeuristics: !opts.quick, checkShebang: !opts.quick, ...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
+ };
65
74
  // Ignore specific languages
66
75
  for (const lang of (_b = opts.ignoredLanguages) !== null && _b !== void 0 ? _b : []) {
67
76
  for (const key in langData) {
@@ -71,40 +80,36 @@ async function analyse(input, opts = {}) {
71
80
  }
72
81
  }
73
82
  }
74
- // Load gitattributes
75
- const customIgnored = [];
76
- const customBinary = [];
77
- const customText = [];
83
+ // Load gitignores and gitattributes
84
+ const customBinary = (0, ignore_1.default)();
85
+ const customText = (0, ignore_1.default)();
78
86
  if (!useRawContent && opts.checkAttributes) {
79
87
  for (const folder of folders) {
80
88
  // Skip if folder is marked in gitattributes
81
- if (customIgnored.some(path => (0, convert_glob_1.default)(path).test(folder)))
89
+ if (relPath(folder) && gitignores.ignores(relPath(folder)))
82
90
  continue;
83
91
  // Parse gitignores
84
92
  const ignoresFile = path_1.default.join(folder, '.gitignore');
85
93
  if (opts.checkIgnored && fs_1.default.existsSync(ignoresFile)) {
86
94
  const ignoresData = await (0, read_file_1.default)(ignoresFile);
87
- const ignoresList = ignoresData.split(/\r?\n/).filter(line => line.trim() && !line.startsWith('#'));
88
- const ignoredPaths = ignoresList.map(path => (0, convert_glob_1.default)(path).source);
89
- customIgnored.push(...ignoredPaths);
95
+ gitignores.add(ignoresData);
90
96
  }
91
97
  // Parse gitattributes
92
98
  const attributesFile = path_1.default.join(folder, '.gitattributes');
93
99
  if (opts.checkAttributes && fs_1.default.existsSync(attributesFile)) {
94
100
  const attributesData = await (0, read_file_1.default)(attributesFile);
95
- const relPathToRegex = (path) => (0, convert_glob_1.default)(path).source.substr(1).replace(folder, '');
96
101
  // Explicit text/binary associations
97
102
  const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)(?!=auto)/gm);
98
103
  for (const [_line, path, type] of contentTypeMatches) {
99
104
  if (['text', '-binary'].includes(type))
100
- customText.push(relPathToRegex(path));
105
+ customText.add(path);
101
106
  if (['-text', 'binary'].includes(type))
102
- customBinary.push(relPathToRegex(path));
107
+ customBinary.add(path);
103
108
  }
104
109
  // Custom vendor options
105
110
  const vendorMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-(vendored|generated|documentation)(?!=false)/gm);
106
111
  for (const [_line, path] of vendorMatches) {
107
- customIgnored.push(relPathToRegex(path));
112
+ gitignores.add(path);
108
113
  }
109
114
  // Custom file associations
110
115
  const customLangMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-language=(\S+)/gm);
@@ -115,17 +120,22 @@ async function analyse(input, opts = {}) {
115
120
  if (overrideLang)
116
121
  forcedLang = overrideLang[0];
117
122
  }
118
- const fullPath = folder + (0, convert_glob_1.default)(path).source.substr(1);
123
+ const fullPath = relPath(folder) + '/' + path;
119
124
  overrides[fullPath] = forcedLang;
120
125
  }
121
126
  }
122
127
  }
123
128
  }
124
129
  // Check vendored files
125
- if (!useRawContent && !opts.keepVendored) {
130
+ if (!opts.keepVendored) {
126
131
  // Filter out any files that match a vendor file path
127
- const matcher = (match) => RegExp(match.replace(/\/$/, '/.+$').replace(/^\.\//, ''));
128
- files = files.filter(file => !customIgnored.some(pattern => matcher(pattern).test(file)));
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
+ }
129
139
  }
130
140
  // Load all files and parse languages
131
141
  const addResult = (file, result) => {
@@ -152,22 +162,40 @@ async function analyse(input, opts = {}) {
152
162
  // Skip if file is unreadable
153
163
  if (firstLine === null)
154
164
  continue;
155
- // Check shebang line for explicit classification
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')); });
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
+ }
159
183
  if (matches.length) {
160
184
  // Add explicitly-identified language
161
- const forcedLang = matches[0][0];
185
+ const forcedLang = matches[0];
162
186
  addResult(file, forcedLang);
187
+ definiteness[file] = true;
188
+ continue;
163
189
  }
164
190
  }
165
191
  // Check override for manual language classification
166
192
  if (!useRawContent && !opts.quick && opts.checkAttributes) {
167
- const match = overridesArray.find(item => RegExp(item[0]).test(file));
193
+ const isOverridden = (path) => (0, ignore_1.default)().add(path).ignores(relPath(file));
194
+ const match = overridesArray.find(item => isOverridden(item[0]));
168
195
  if (match) {
169
196
  const forcedLang = match[1];
170
197
  addResult(file, forcedLang);
198
+ definiteness[file] = true;
171
199
  continue;
172
200
  }
173
201
  }
@@ -175,7 +203,7 @@ async function analyse(input, opts = {}) {
175
203
  let skipExts = false;
176
204
  for (const lang in langData) {
177
205
  // Check if filename is a match
178
- const matchesName = (_f = langData[lang].filenames) === null || _f === void 0 ? void 0 : _f.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
206
+ const matchesName = (_h = langData[lang].filenames) === null || _h === void 0 ? void 0 : _h.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
179
207
  if (matchesName) {
180
208
  addResult(file, lang);
181
209
  skipExts = true;
@@ -184,7 +212,7 @@ async function analyse(input, opts = {}) {
184
212
  if (!skipExts)
185
213
  for (const lang in langData) {
186
214
  // Check if extension is a match
187
- const matchesExt = (_g = langData[lang].extensions) === null || _g === void 0 ? void 0 : _g.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
215
+ const matchesExt = (_j = langData[lang].extensions) === null || _j === void 0 ? void 0 : _j.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
188
216
  if (matchesExt)
189
217
  addResult(file, lang);
190
218
  }
@@ -194,10 +222,15 @@ async function analyse(input, opts = {}) {
194
222
  }
195
223
  // Narrow down file associations to the best fit
196
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
+ }
197
230
  // Skip binary files
198
231
  if (!useRawContent && !opts.keepBinary) {
199
- const isCustomText = customText.some(path => RegExp(path).test(file));
200
- const isCustomBinary = customBinary.some(path => RegExp(path).test(file));
232
+ const isCustomText = customText.ignores(relPath(file));
233
+ const isCustomBinary = customBinary.ignores(relPath(file));
201
234
  const isBinaryExt = binary_extensions_1.default.some(ext => file.endsWith('.' + ext));
202
235
  if (!isCustomText && (isCustomBinary || isBinaryExt || await (0, isbinaryfile_1.isBinaryFile)(file))) {
203
236
  continue;
@@ -217,7 +250,7 @@ async function analyse(input, opts = {}) {
217
250
  heuristic.language = heuristic.language[0];
218
251
  }
219
252
  // Make sure the results includes this language
220
- const languageGroup = (_h = langData[heuristic.language]) === null || _h === void 0 ? void 0 : _h.group;
253
+ const languageGroup = (_k = langData[heuristic.language]) === null || _k === void 0 ? void 0 : _k.group;
221
254
  const matchesLang = fileAssociations[file].includes(heuristic.language);
222
255
  const matchesParent = languageGroup && fileAssociations[file].includes(languageGroup);
223
256
  if (!matchesLang && !matchesParent)
@@ -230,7 +263,7 @@ async function analyse(input, opts = {}) {
230
263
  if (heuristic.named_pattern)
231
264
  normalise(heuristicsData.named_patterns[heuristic.named_pattern]);
232
265
  // Check file contents and apply heuristic patterns
233
- const fileContent = await (0, read_file_1.default)(file).catch(() => null);
266
+ const fileContent = opts.fileContent ? opts.fileContent[files.indexOf(file)] : await (0, read_file_1.default)(file).catch(() => null);
234
267
  if (fileContent === null)
235
268
  continue;
236
269
  if (!patterns.length || patterns.some(pattern => (0, convert_pcre_1.default)(pattern).test(fileContent))) {
@@ -240,10 +273,10 @@ async function analyse(input, opts = {}) {
240
273
  }
241
274
  }
242
275
  // If no heuristics, assign a language
243
- (_j = (_r = results.files.results)[file]) !== null && _j !== void 0 ? _j : (_r[file] = fileAssociations[file][0]);
276
+ (_l = (_t = results.files.results)[file]) !== null && _l !== void 0 ? _l : (_t[file] = fileAssociations[file][0]);
244
277
  }
245
278
  // Skip specified categories
246
- if ((_k = opts.categories) === null || _k === void 0 ? void 0 : _k.length) {
279
+ if ((_m = opts.categories) === null || _m === void 0 ? void 0 : _m.length) {
247
280
  const categories = ['data', 'markup', 'programming', 'prose'];
248
281
  const hiddenCategories = categories.filter(cat => !opts.categories.includes(cat));
249
282
  for (const [file, lang] of Object.entries(results.files.results)) {
@@ -275,21 +308,21 @@ async function analyse(input, opts = {}) {
275
308
  for (const [file, lang] of Object.entries(results.files.results)) {
276
309
  if (lang && !langData[lang])
277
310
  continue;
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;
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;
279
312
  results.files.bytes += fileSize;
280
313
  // If no language found, add extension in other section
281
314
  if (!lang) {
282
315
  const ext = path_1.default.extname(file);
283
316
  const unknownType = ext === '' ? 'filenames' : 'extensions';
284
317
  const name = ext === '' ? path_1.default.basename(file) : ext;
285
- (_p = (_s = results.unknown[unknownType])[name]) !== null && _p !== void 0 ? _p : (_s[name] = 0);
318
+ (_r = (_u = results.unknown[unknownType])[name]) !== null && _r !== void 0 ? _r : (_u[name] = 0);
286
319
  results.unknown[unknownType][name] += fileSize;
287
320
  results.unknown.bytes += fileSize;
288
321
  continue;
289
322
  }
290
323
  // Add language and bytes data to corresponding section
291
324
  const { type } = langData[lang];
292
- (_q = (_t = results.languages.results)[lang]) !== null && _q !== void 0 ? _q : (_t[lang] = { type, bytes: 0, color: langData[lang].color });
325
+ (_s = (_v = results.languages.results)[lang]) !== null && _s !== void 0 ? _s : (_v[lang] = { type, bytes: 0, color: langData[lang].color });
293
326
  if (opts.childLanguages)
294
327
  results.languages.results[lang].parent = langData[lang].group;
295
328
  results.languages.results[lang].bytes += fileSize;
package/dist/types.d.ts CHANGED
@@ -18,6 +18,7 @@ export interface Options {
18
18
  checkAttributes?: boolean;
19
19
  checkHeuristics?: boolean;
20
20
  checkShebang?: boolean;
21
+ checkModeline?: boolean;
21
22
  }
22
23
  export interface Results {
23
24
  files: {
package/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ISC License
2
2
 
3
- Copyright &copy; 2021 Nixinova
3
+ Copyright &copy; Nixinova 2021&ndash;2022
4
4
 
5
5
  Permission to use, copy, modify, and/or distribute this software for any
6
6
  purpose with or without fee is hereby granted, provided that the above
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linguist-js",
3
- "version": "2.3.2",
3
+ "version": "2.4.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": {
@@ -12,7 +12,7 @@
12
12
  "scripts": {
13
13
  "prepare": "npm test && npm run perf",
14
14
  "perf": "tsc && node test/perf",
15
- "test": "tsc && node test/test"
15
+ "test": "tsc && node test/folder && echo --- && node test/unit"
16
16
  },
17
17
  "files": [
18
18
  "bin/",
@@ -37,10 +37,10 @@
37
37
  "homepage": "https://github.com/Nixinova/Linguist#readme",
38
38
  "dependencies": {
39
39
  "binary-extensions": "^2.2.0",
40
- "commander": "^9.0.0",
40
+ "commander": "^9.1.0",
41
41
  "common-path-prefix": "^3.0.0",
42
42
  "cross-fetch": "^3.1.5",
43
- "glob-to-regexp": "~0.4.1",
43
+ "ignore": "^5.2.0",
44
44
  "isbinaryfile": "^4.0.8",
45
45
  "js-yaml": "^4.1.0",
46
46
  "node-cache": "^5.1.2"
@@ -50,6 +50,6 @@
50
50
  "@types/js-yaml": "ts4.6",
51
51
  "@types/node": "ts4.6",
52
52
  "deep-object-diff": "^1.1.7",
53
- "typescript": "~4.6.1-rc"
53
+ "typescript": "~4.6.2"
54
54
  }
55
55
  }
package/readme.md CHANGED
@@ -2,7 +2,7 @@
2
2
  [![Last updated](https://img.shields.io/github/release-date/Nixinova/Linguist?label=updated&style=flat-square)](https://github.com/Nixinova/Linguist/releases)
3
3
  [![npm downloads](https://img.shields.io/npm/dt/linguist-js?logo=npm)](https://www.npmjs.com/package/linguist-js)
4
4
 
5
- # Linguist
5
+ # LinguistJS
6
6
 
7
7
  Analyses the languages of all files in a given folder and collates the results.
8
8
 
@@ -11,7 +11,7 @@ Powered by [github-linguist](https://github.com/github/linguist), although it do
11
11
  ## Install
12
12
 
13
13
  [Node.js](https://nodejs.org) must be installed to be able to use this.
14
- Linguist is available [on npm](https://npmjs.com/package/linguist-js) as `linguist-js`.
14
+ LinguistJS is available [on npm](https://npmjs.com/package/linguist-js) as `linguist-js`.
15
15
 
16
16
  Install locally using `npm install linguist-js` and import it into your code like so:
17
17
 
@@ -27,7 +27,7 @@ linguist --help
27
27
 
28
28
  ## Usage
29
29
 
30
- Linguist contains one function which analyses a given folder.
30
+ LinguistJS contains one function which analyses a given folder.
31
31
 
32
32
  As an example, take the following file structure:
33
33
 
@@ -40,7 +40,7 @@ As an example, take the following file structure:
40
40
  | no-lang 10B
41
41
  ```
42
42
 
43
- Running Linguist on this folder will return the following JSON:
43
+ Running LinguistJS on this folder will return the following JSON:
44
44
 
45
45
  ```json
46
46
  {
@@ -78,7 +78,7 @@ Running Linguist on this folder will return the following JSON:
78
78
 
79
79
  - File paths in the output use only forward slashes as delimiters, even on Windows.
80
80
  - This tool does not work when offline.
81
- - Do not rely on any language classification output from Linguist being unchanged between runs.
81
+ - Do not rely on any language classification output from LinguistJS being unchanged between runs.
82
82
  Language data is fetched each run from the latest classifications of [`github-linguist`](https://github.com/github/linguist).
83
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.
84
84
 
@@ -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;