linguist-js 2.1.4 → 2.3.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 CHANGED
@@ -6,6 +6,7 @@ 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"));
9
10
  const colouredMsg = ([r, g, b], msg) => `\u001B[${38};2;${r};${g};${b}m${msg}${'\u001b[0m'}`;
10
11
  const hexToRgb = (hex) => [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];
11
12
  commander_1.program
@@ -21,6 +22,7 @@ commander_1.program
21
22
  .option('-q|--quick [bool]', 'Skip complex language analysis (alias for -{A|I|H|S}=false)', false)
22
23
  .option('-V|--keepVendored [bool]', 'Prevent skipping over vendored/generated files', false)
23
24
  .option('-B|--keepBinary [bool]', 'Prevent skipping over binary files', false)
25
+ .option('-r|--relativePaths [bool]', 'Convert absolute file paths to relative', false)
24
26
  .option('-A|--checkAttributes [bool]', 'Force the checking of gitattributes files', true)
25
27
  .option('-I|--checkIgnored [bool]', 'Force the checking of gitignore files', true)
26
28
  .option('-H|--checkHeuristics [bool]', 'Apply heuristics to ambiguous languages', true)
@@ -51,16 +53,21 @@ if (args.analyze)
51
53
  const root = args.analyze === true ? '.' : args.analyze;
52
54
  const data = await (0, index_1.default)(root, args);
53
55
  const { files, languages, unknown } = data;
56
+ // Get file count
57
+ const totalFiles = (0, walk_tree_1.default)(root).files.length;
54
58
  // Print output
55
59
  if (!args.json) {
56
60
  const sortedEntries = Object.entries(languages.results).sort((a, b) => a[1].bytes < b[1].bytes ? +1 : -1);
57
61
  const totalBytes = languages.bytes;
58
- console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${files.count} files with linguist-js`);
62
+ console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${totalFiles} files (${totalFiles - files.count} ignored) with linguist-js`);
59
63
  console.log(`\n Language analysis results:`);
60
- let i = 0;
64
+ let count = 0;
65
+ if (sortedEntries.length === 0)
66
+ console.log(` None`);
67
+ // List parsed results
61
68
  for (const [lang, { bytes, color }] of sortedEntries) {
62
69
  const fmtd = {
63
- index: (++i).toString().padStart(2, ' '),
70
+ index: (++count).toString().padStart(2, ' '),
64
71
  lang: lang.padEnd(24, ' '),
65
72
  percent: (bytes / (totalBytes || 1) * 100).toFixed(2).padStart(5, ' '),
66
73
  bytes: bytes.toLocaleString().padStart(10, ' '),
@@ -69,13 +76,14 @@ if (args.analyze)
69
76
  console.log(` ${fmtd.index}. ${fmtd.icon} ${fmtd.lang} ${fmtd.percent}% ${fmtd.bytes} B`);
70
77
  }
71
78
  console.log(` Total: ${totalBytes.toLocaleString()} B`);
79
+ // List unknown files/extensions
72
80
  if (unknown.bytes > 0) {
73
81
  console.log(`\n Unknown files and extensions:`);
74
82
  for (const [name, bytes] of Object.entries(unknown.filenames)) {
75
83
  console.log(` '${name}': ${bytes.toLocaleString()} B`);
76
84
  }
77
85
  for (const [ext, bytes] of Object.entries(unknown.extensions)) {
78
- console.log(` '${ext}': ${bytes.toLocaleString()} B`);
86
+ console.log(` '*${ext}': ${bytes.toLocaleString()} B`);
79
87
  }
80
88
  console.log(` Total: ${unknown.bytes.toLocaleString()} B`);
81
89
  }
@@ -95,6 +103,6 @@ if (args.analyze)
95
103
  }
96
104
  })();
97
105
  else {
98
- console.log(`Welcome to linguist-js, the JavaScript port of GitHub's language analyzer.`);
106
+ console.log(`Welcome to linguist-js, a JavaScript port of GitHub's language analyzer.`);
99
107
  console.log(`Type 'linguist --help' for a list of commands.`);
100
108
  }
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const glob_to_regexp_1 = __importDefault(require("glob-to-regexp"));
7
7
  function parseGitGlob(path) {
8
- const globPath = `**/${path}`.replace(/\[:(space|digit):\]/, (_, val) => `\\${val[0]}`);
8
+ const globPath = `**/${path}`.replace(/\\/g, '/').replace(/\[:(space|digit):\]/g, (_, val) => `\\${val[0]}`);
9
9
  return (0, glob_to_regexp_1.default)(globPath, { globstar: true, extended: true });
10
10
  }
11
11
  exports.default = parseGitGlob;
@@ -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];
package/dist/index.js CHANGED
@@ -14,8 +14,9 @@ const read_file_1 = __importDefault(require("./helpers/read-file"));
14
14
  const convert_pcre_1 = __importDefault(require("./helpers/convert-pcre"));
15
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;
18
- var _k, _l, _m;
17
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
18
+ var _s, _t, _u;
19
+ const useRawContent = opts.fileContent !== undefined;
19
20
  const langData = await (0, load_data_1.default)('languages.yml').then(js_yaml_1.default.load);
20
21
  const vendorData = await (0, load_data_1.default)('vendor.yml').then(js_yaml_1.default.load);
21
22
  const heuristicsData = await (0, load_data_1.default)('heuristics.yml').then(js_yaml_1.default.load);
@@ -34,7 +35,16 @@ async function analyse(input, opts = {}) {
34
35
  opts.keepVendored ? [] : vendorData.map(path => RegExp(path)),
35
36
  (_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
37
  ].flat();
37
- let { files, folders } = (0, walk_tree_1.default)(input !== null && input !== void 0 ? input : '.', ignoredFiles);
38
+ let files, folders;
39
+ if (opts.fileContent) {
40
+ opts.fileContent = Array.isArray(opts.fileContent) ? opts.fileContent : [opts.fileContent];
41
+ files = [`${input}`];
42
+ folders = [''];
43
+ }
44
+ else {
45
+ const data = (0, walk_tree_1.default)(input !== null && input !== void 0 ? input : '.', ignoredFiles);
46
+ ({ files, folders } = data);
47
+ }
38
48
  // Apply aliases
39
49
  opts = { checkIgnored: !opts.quick, checkAttributes: !opts.quick, checkHeuristics: !opts.quick, checkShebang: !opts.quick, ...opts };
40
50
  // Ignore specific languages
@@ -50,7 +60,7 @@ async function analyse(input, opts = {}) {
50
60
  const customIgnored = [];
51
61
  const customBinary = [];
52
62
  const customText = [];
53
- if (!opts.quick) {
63
+ if (!useRawContent && !opts.quick) {
54
64
  for (const folder of folders) {
55
65
  // Skip if folder is marked in gitattributes
56
66
  if (customIgnored.some(path => (0, convert_glob_1.default)(path).test(folder)))
@@ -69,7 +79,7 @@ async function analyse(input, opts = {}) {
69
79
  const attributesData = await (0, read_file_1.default)(attributesFile);
70
80
  const relPathToRegex = (path) => (0, convert_glob_1.default)(path).source.substr(1).replace(folder, '');
71
81
  // Explicit text/binary associations
72
- const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)/gm);
82
+ const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)(?!=auto)/gm);
73
83
  for (const [_line, path, type] of contentTypeMatches) {
74
84
  if (['text', '-binary'].includes(type))
75
85
  customText.push(relPathToRegex(path));
@@ -97,7 +107,7 @@ async function analyse(input, opts = {}) {
97
107
  }
98
108
  }
99
109
  // Check vendored files
100
- if (!opts.keepVendored) {
110
+ if (!useRawContent && !opts.keepVendored) {
101
111
  // Filter out any files that match a vendor file path
102
112
  const matcher = (match) => RegExp(match.replace(/\/$/, '/.+$').replace(/^\.\//, ''));
103
113
  files = files.filter(file => !customIgnored.some(pattern => matcher(pattern).test(file)));
@@ -110,27 +120,35 @@ async function analyse(input, opts = {}) {
110
120
  }
111
121
  const parent = !opts.childLanguages && result && langData[result].group || false;
112
122
  fileAssociations[file].push(parent || result);
113
- extensions[file] = path_1.default.extname(file);
123
+ extensions[file] = path_1.default.extname(file).toLowerCase();
114
124
  };
115
125
  const overridesArray = Object.entries(overrides);
116
126
  // List all languages that could be associated with a given file
117
127
  for (const file of files) {
118
- if (!fs_1.default.existsSync(file) || fs_1.default.lstatSync(file).isDirectory())
128
+ let firstLine;
129
+ if (opts.fileContent) {
130
+ firstLine = (_f = (_e = (_d = opts.fileContent) === null || _d === void 0 ? void 0 : _d[files.indexOf(file)]) === null || _e === void 0 ? void 0 : _e.split('\n')[0]) !== null && _f !== void 0 ? _f : null;
131
+ }
132
+ else {
133
+ if (!fs_1.default.existsSync(file) || fs_1.default.lstatSync(file).isDirectory())
134
+ continue;
135
+ firstLine = await (0, read_file_1.default)(file, true).catch(() => null);
136
+ }
137
+ // Skip if file is unreadable
138
+ if (firstLine === null)
119
139
  continue;
120
140
  // 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
- }
141
+ if (!opts.quick && opts.checkShebang && firstLine.startsWith('#!')) {
142
+ // Find matching interpreters
143
+ 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')); });
144
+ if (matches.length) {
145
+ // Add explicitly-identified language
146
+ const forcedLang = matches[0][0];
147
+ addResult(file, forcedLang);
130
148
  }
131
149
  }
132
150
  // Check override for manual language classification
133
- if (!opts.quick && opts.checkAttributes) {
151
+ if (!useRawContent && !opts.quick && opts.checkAttributes) {
134
152
  const match = overridesArray.find(item => RegExp(item[0]).test(file));
135
153
  if (match) {
136
154
  const forcedLang = match[1];
@@ -142,7 +160,7 @@ async function analyse(input, opts = {}) {
142
160
  let skipExts = false;
143
161
  for (const lang in langData) {
144
162
  // 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());
163
+ const matchesName = (_g = langData[lang].filenames) === null || _g === void 0 ? void 0 : _g.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
146
164
  if (matchesName) {
147
165
  addResult(file, lang);
148
166
  skipExts = true;
@@ -151,7 +169,7 @@ async function analyse(input, opts = {}) {
151
169
  if (!skipExts)
152
170
  for (const lang in langData) {
153
171
  // 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()));
172
+ const matchesExt = (_h = langData[lang].extensions) === null || _h === void 0 ? void 0 : _h.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
155
173
  if (matchesExt)
156
174
  addResult(file, lang);
157
175
  }
@@ -162,7 +180,7 @@ async function analyse(input, opts = {}) {
162
180
  // Narrow down file associations to the best fit
163
181
  for (const file in fileAssociations) {
164
182
  // Skip binary files
165
- if (!opts.keepBinary) {
183
+ if (!useRawContent && !opts.keepBinary) {
166
184
  const isCustomText = customText.some(path => RegExp(path).test(file));
167
185
  const isCustomBinary = customBinary.some(path => RegExp(path).test(file));
168
186
  const isBinaryExt = binary_extensions_1.default.some(ext => file.endsWith('.' + ext));
@@ -184,8 +202,9 @@ async function analyse(input, opts = {}) {
184
202
  heuristic.language = heuristic.language[0];
185
203
  }
186
204
  // Make sure the results includes this language
205
+ const languageGroup = (_j = langData[heuristic.language]) === null || _j === void 0 ? void 0 : _j.group;
187
206
  const matchesLang = fileAssociations[file].includes(heuristic.language);
188
- const matchesParent = langData[heuristic.language].group && fileAssociations[file].includes(langData[heuristic.language].group);
207
+ const matchesParent = languageGroup && fileAssociations[file].includes(languageGroup);
189
208
  if (!matchesLang && !matchesParent)
190
209
  continue;
191
210
  // Normalise heuristic data
@@ -196,7 +215,9 @@ async function analyse(input, opts = {}) {
196
215
  if (heuristic.named_pattern)
197
216
  normalise(heuristicsData.named_patterns[heuristic.named_pattern]);
198
217
  // Check file contents and apply heuristic patterns
199
- const fileContent = await (0, read_file_1.default)(file);
218
+ const fileContent = await (0, read_file_1.default)(file).catch(() => null);
219
+ if (fileContent === null)
220
+ continue;
200
221
  if (!patterns.length || patterns.some(pattern => (0, convert_pcre_1.default)(pattern).test(fileContent))) {
201
222
  results.files.results[file] = heuristic.language;
202
223
  break;
@@ -204,10 +225,10 @@ async function analyse(input, opts = {}) {
204
225
  }
205
226
  }
206
227
  // If no heuristics, assign a language
207
- (_f = (_k = results.files.results)[file]) !== null && _f !== void 0 ? _f : (_k[file] = fileAssociations[file][0]);
228
+ (_k = (_s = results.files.results)[file]) !== null && _k !== void 0 ? _k : (_s[file] = fileAssociations[file][0]);
208
229
  }
209
230
  // Skip specified categories
210
- if ((_g = opts.categories) === null || _g === void 0 ? void 0 : _g.length) {
231
+ if ((_l = opts.categories) === null || _l === void 0 ? void 0 : _l.length) {
211
232
  const categories = ['data', 'markup', 'programming', 'prose'];
212
233
  const hiddenCategories = categories.filter(cat => !opts.categories.includes(cat));
213
234
  for (const [file, lang] of Object.entries(results.files.results)) {
@@ -224,25 +245,36 @@ async function analyse(input, opts = {}) {
224
245
  }
225
246
  }
226
247
  }
248
+ // Convert paths to relative
249
+ if (!useRawContent && opts.relativePaths) {
250
+ const newMap = {};
251
+ for (const [file, lang] of Object.entries(results.files.results)) {
252
+ let relPath = path_1.default.relative(process.cwd(), file).replace(/\\/g, '/');
253
+ if (!relPath.startsWith('../'))
254
+ relPath = './' + relPath;
255
+ newMap[relPath] = lang;
256
+ }
257
+ results.files.results = newMap;
258
+ }
227
259
  // Load language bytes size
228
260
  for (const [file, lang] of Object.entries(results.files.results)) {
229
261
  if (lang && !langData[lang])
230
262
  continue;
231
- const fileSize = fs_1.default.statSync(file).size;
263
+ const fileSize = (_p = (_o = (_m = opts.fileContent) === null || _m === void 0 ? void 0 : _m[files.indexOf(file)]) === null || _o === void 0 ? void 0 : _o.length) !== null && _p !== void 0 ? _p : fs_1.default.statSync(file).size;
232
264
  results.files.bytes += fileSize;
233
265
  // If no language found, add extension in other section
234
266
  if (!lang) {
235
267
  const ext = path_1.default.extname(file);
236
268
  const unknownType = ext === '' ? 'filenames' : 'extensions';
237
269
  const name = ext === '' ? path_1.default.basename(file) : ext;
238
- (_h = (_l = results.unknown[unknownType])[name]) !== null && _h !== void 0 ? _h : (_l[name] = 0);
270
+ (_q = (_t = results.unknown[unknownType])[name]) !== null && _q !== void 0 ? _q : (_t[name] = 0);
239
271
  results.unknown[unknownType][name] += fileSize;
240
272
  results.unknown.bytes += fileSize;
241
273
  continue;
242
274
  }
243
275
  // Add language and bytes data to corresponding section
244
276
  const { type } = langData[lang];
245
- (_j = (_m = results.languages.results)[lang]) !== null && _j !== void 0 ? _j : (_m[lang] = { type, bytes: 0, color: langData[lang].color });
277
+ (_r = (_u = results.languages.results)[lang]) !== null && _r !== void 0 ? _r : (_u[lang] = { type, bytes: 0, color: langData[lang].color });
246
278
  if (opts.childLanguages)
247
279
  results.languages.results[lang].parent = langData[lang].group;
248
280
  results.languages.results[lang].bytes += fileSize;
package/dist/types.d.ts CHANGED
@@ -5,11 +5,13 @@ 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[];
11
12
  keepVendored?: boolean;
12
13
  keepBinary?: boolean;
14
+ relativePaths?: boolean;
13
15
  childLanguages?: boolean;
14
16
  quick?: boolean;
15
17
  checkIgnored?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linguist-js",
3
- "version": "2.1.4",
3
+ "version": "2.3.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": {
@@ -37,18 +37,18 @@
37
37
  "homepage": "https://github.com/Nixinova/Linguist#readme",
38
38
  "dependencies": {
39
39
  "binary-extensions": "^2.2.0",
40
- "commander": "^8.2.0",
41
- "cross-fetch": "^3.1.4",
40
+ "commander": "^9.0.0",
41
+ "cross-fetch": "^3.1.5",
42
42
  "glob-to-regexp": "~0.4.1",
43
43
  "isbinaryfile": "^4.0.8",
44
44
  "js-yaml": "^4.1.0",
45
45
  "node-cache": "^5.1.2"
46
46
  },
47
47
  "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.4.3"
48
+ "@types/glob-to-regexp": "ts4.6",
49
+ "@types/js-yaml": "ts4.6",
50
+ "@types/node": "ts4.6",
51
+ "deep-object-diff": "^1.1.7",
52
+ "typescript": "~4.6.1-rc"
53
53
  }
54
54
  }
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):
@@ -115,6 +118,8 @@ const { files, languages, unknown } = linguist(folder, options);
115
118
  Whether to keep vendored files (dependencies, etc) (defaults to `false`).
116
119
  - `keepBinary` (boolean):
117
120
  Whether binary files should be included in the output (defaults to `false`).
121
+ - `relativePaths` (boolean):
122
+ Change the absolute file paths in the output to be relative to the current working directory (defaults to `false`).
118
123
  - `checkAttributes` (boolean):
119
124
  Force the checking of `.gitattributes` files (defaults to `true` unless `quick` is set).
120
125
  - `checkIgnored` (boolean):
@@ -155,6 +160,8 @@ linguist --help
155
160
  Whether to include vendored files (auto-generated files, dependencies folder, etc).
156
161
  - `--keepBinary`:
157
162
  Whether binary files should be excluded from the output.
163
+ - `--relativePaths`:
164
+ Change the absolute file paths in the output to be relative to the current working directory.
158
165
  - `--checkAttributes`:
159
166
  Force the checking of `.gitatributes` files (use alongside `--quick` to overwrite).
160
167
  - `--checkIgnored`: