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 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 ${totalFiles} files (${totalFiles - files.count} ignored) with linguist-js`);
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, the JavaScript port of GitHub's language analyzer.`);
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
- finalRegex = finalRegex.replace(match, '');
12
+ replace(match, '');
12
13
  [...flags].forEach(flag => finalFlags.add(flag));
13
14
  }
14
- // Remove invalid modifiers
15
- finalRegex = finalRegex.replace(/([*+]){2}/g, '$1');
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
- finalRegex = finalRegex.replace(/\\A/g, '^').replace(/\\Z/g, '$');
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
- /** Read part of a file on disc. */
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(folder, ignored = []) {
12
- if (Array.isArray(folder)) {
13
- for (const path of folder)
14
- walk(path, ignored);
15
- }
16
- else {
17
- const files = fs_1.default.readdirSync(folder);
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
- const path = path_1.default.resolve(folder, file).replace(/\\/g, '/');
20
- if (!fs_1.default.existsSync(path) || ignored.some(pattern => pattern.test(path)))
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
- allFolders.add(folder.replace(/\\/g, '/'));
23
- if (fs_1.default.lstatSync(path).isDirectory()) {
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
- continue;
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 _k, _l, _m;
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.push(...generatedData);
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
- const ignoredFiles = [
33
- /\/\.git\//,
34
- opts.keepVendored ? [] : vendorData.map(path => RegExp(path)),
35
- (_b = (_a = opts.ignoredFiles) === null || _a === void 0 ? void 0 : _a.map(path => (0, glob_to_regexp_1.default)('*' + path + '*', { extended: true }))) !== null && _b !== void 0 ? _b : [],
36
- ].flat();
37
- let { files, folders } = (0, walk_tree_1.default)(input !== null && input !== void 0 ? input : '.', ignoredFiles);
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 (_c = opts.ignoredLanguages) !== null && _c !== void 0 ? _c : []) {
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.quick) {
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
- if (!fs_1.default.existsSync(file) || fs_1.default.lstatSync(file).isDirectory())
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
- const firstLine = await (0, read_file_1.default)(file, true);
123
- if (firstLine.startsWith('#!')) {
124
- 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')); });
125
- if (matches.length) {
126
- const forcedLang = matches[0][0];
127
- addResult(file, forcedLang);
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 = (_d = langData[lang].filenames) === null || _d === void 0 ? void 0 : _d.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
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 = (_e = langData[lang].extensions) === null || _e === void 0 ? void 0 : _e.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
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 = langData[heuristic.language].group && fileAssociations[file].includes(langData[heuristic.language].group);
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(...(Array.isArray(contents) ? contents : [contents]));
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
- (_f = (_k = results.files.results)[file]) !== null && _f !== void 0 ? _f : (_k[file] = fileAssociations[file][0]);
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 ((_g = opts.categories) === null || _g === void 0 ? void 0 : _g.length) {
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
- (_h = (_l = results.unknown[unknownType])[name]) !== null && _h !== void 0 ? _h : (_l[name] = 0);
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
- (_j = (_m = results.languages.results)[lang]) !== null && _j !== void 0 ? _j : (_m[lang] = { type, bytes: 0, color: langData[lang].color });
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.0",
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": "^8.3.0",
41
- "cross-fetch": "^3.1.4",
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.4",
49
- "@types/js-yaml": "ts4.4",
50
- "@types/node": "ts4.4",
51
- "deep-object-diff": "^1.1.0",
52
- "typescript": "~4.5.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):