linguist-js 2.6.1 → 2.7.0

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
@@ -4,6 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const VERSION = require('../package.json').version;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
7
9
  const commander_1 = require("commander");
8
10
  const index_1 = __importDefault(require("./index"));
9
11
  const colouredMsg = ([r, g, b], msg) => `\u001B[${38};2;${r};${g};${b}m${msg}${'\u001b[0m'}`;
@@ -18,6 +20,7 @@ commander_1.program
18
20
  .option('-C|--childLanguages [bool]', 'Display child languages instead of their parents', false)
19
21
  .option('-j|--json [bool]', 'Display the output as JSON', false)
20
22
  .option('-t|--tree <traversal>', 'Which part of the output JSON to display (dot-delimited)')
23
+ .option('-F|--listFiles [bool]', 'Whether to list every matching file under the language results', false)
21
24
  .option('-q|--quick [bool]', 'Skip complex language analysis (alias for -{A|I|H|S}=false)', false)
22
25
  .option('-o|--offline [bool]', 'Use packaged data files instead of fetching latest from GitHub', false)
23
26
  .option('-V|--keepVendored [bool]', 'Prevent skipping over vendored/generated files', false)
@@ -59,21 +62,52 @@ if (args.analyze)
59
62
  const sortedEntries = Object.entries(languages.results).sort((a, b) => a[1].bytes < b[1].bytes ? +1 : -1);
60
63
  const totalBytes = languages.bytes;
61
64
  console.log(`\n Analysed ${files.bytes.toLocaleString()} B from ${files.count} files with linguist-js`);
62
- console.log(`\n Language analysis results:`);
65
+ console.log(`\n Language analysis results: \n`);
63
66
  let count = 0;
64
67
  if (sortedEntries.length === 0)
65
68
  console.log(` None`);
69
+ // Collate files per language
70
+ const filesPerLanguage = {};
71
+ if (args.listFiles) {
72
+ for (const language of Object.keys(languages.results)) {
73
+ filesPerLanguage[language] = [];
74
+ }
75
+ for (const [file, lang] of Object.entries(files.results)) {
76
+ if (lang)
77
+ filesPerLanguage[lang].push(file);
78
+ }
79
+ }
66
80
  // List parsed results
67
81
  for (const [lang, { bytes, color }] of sortedEntries) {
82
+ const percent = (bytes) => bytes / (totalBytes || 1) * 100;
68
83
  const fmtd = {
69
84
  index: (++count).toString().padStart(2, ' '),
70
85
  lang: lang.padEnd(24, ' '),
71
- percent: (bytes / (totalBytes || 1) * 100).toFixed(2).padStart(5, ' '),
86
+ percent: percent(bytes).toFixed(2).padStart(5, ' '),
72
87
  bytes: bytes.toLocaleString().padStart(10, ' '),
73
88
  icon: colouredMsg(hexToRgb(color !== null && color !== void 0 ? color : '#ededed'), '\u2588'),
74
89
  };
75
90
  console.log(` ${fmtd.index}. ${fmtd.icon} ${fmtd.lang} ${fmtd.percent}% ${fmtd.bytes} B`);
91
+ // If using `listFiles` option, list all files tagged as this language
92
+ if (args.listFiles) {
93
+ console.log(); // padding
94
+ for (const file of filesPerLanguage[lang]) {
95
+ let relFile = path_1.default.relative(path_1.default.resolve('.'), file).replace(/\\/g, '/');
96
+ if (!relFile.startsWith('../'))
97
+ relFile = './' + relFile;
98
+ const bytes = (await fs_1.default.promises.stat(file)).size;
99
+ const fmtd2 = {
100
+ file: relFile.padEnd(42, ' '),
101
+ percent: percent(bytes).toFixed(2).padStart(5, ' '),
102
+ bytes: bytes.toLocaleString().padStart(10, ' '),
103
+ };
104
+ console.log(` ${fmtd.icon} ${fmtd2.file} ${fmtd2.percent}% ${fmtd2.bytes} B`);
105
+ }
106
+ console.log(); // padding
107
+ }
76
108
  }
109
+ if (!args.listFiles)
110
+ console.log(); // padding
77
111
  console.log(` Total: ${totalBytes.toLocaleString()} B`);
78
112
  // List unknown files/extensions
79
113
  if (unknown.bytes > 0) {
@@ -0,0 +1,16 @@
1
+ import * as T from '../types';
2
+ export type FlagAttributes = {
3
+ 'vendored': boolean | null;
4
+ 'generated': boolean | null;
5
+ 'documentation': boolean | null;
6
+ 'binary': boolean | null;
7
+ 'language': T.LanguageResult;
8
+ };
9
+ export type ParsedGitattributes = Array<{
10
+ glob: T.FileGlob;
11
+ attrs: FlagAttributes;
12
+ }>;
13
+ /**
14
+ * Parses a gitattributes file.
15
+ */
16
+ export default function parseAttributes(content: string, folderRoot?: string): ParsedGitattributes;
@@ -0,0 +1,38 @@
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 path_1 = __importDefault(require("path"));
7
+ /**
8
+ * Parses a gitattributes file.
9
+ */
10
+ function parseAttributes(content, folderRoot = '.') {
11
+ var _a, _b;
12
+ const output = [];
13
+ for (const rawLine of content.split('\n')) {
14
+ const line = rawLine.replace(/#.*/, '').trim();
15
+ if (!line)
16
+ continue;
17
+ const parts = line.split(/\s+/g);
18
+ const fileGlob = parts[0];
19
+ const relFileGlob = path_1.default.join(folderRoot, fileGlob).replace(/\\/g, '/');
20
+ const attrParts = parts.slice(1);
21
+ const isTrue = (str) => !str.startsWith('-') && !str.endsWith('=false');
22
+ const isFalse = (str) => str.startsWith('-') || str.endsWith('=false');
23
+ const trueParts = (str) => attrParts.filter(part => part.includes(str) && isTrue(part));
24
+ const falseParts = (str) => attrParts.filter(part => part.includes(str) && isFalse(part));
25
+ const hasTrueParts = (str) => trueParts(str).length > 0;
26
+ const hasFalseParts = (str) => falseParts(str).length > 0;
27
+ const attrs = {
28
+ 'generated': hasTrueParts('linguist-generated') ? true : hasFalseParts('linguist-generated') ? false : null,
29
+ 'vendored': hasTrueParts('linguist-vendored') ? true : hasFalseParts('linguist-vendored') ? false : null,
30
+ 'documentation': hasTrueParts('linguist-documentation') ? true : hasFalseParts('linguist-documentation') ? false : null,
31
+ 'binary': hasTrueParts('binary') || hasFalseParts('text') ? true : hasFalseParts('binary') || hasTrueParts('text') ? false : null,
32
+ 'language': (_b = (_a = trueParts('linguist-language').at(-1)) === null || _a === void 0 ? void 0 : _a.split('=')[1]) !== null && _b !== void 0 ? _b : null,
33
+ };
34
+ output.push({ glob: relFileGlob, attrs });
35
+ }
36
+ return output;
37
+ }
38
+ exports.default = parseAttributes;
@@ -8,8 +8,8 @@ interface WalkInput {
8
8
  folderRoots: string[];
9
9
  /** The absolute path of folders being checked */
10
10
  folders: string[];
11
- gitignores: Ignore;
12
- regexIgnores: RegExp[];
11
+ /** An instantiated Ignore object listing ignored files */
12
+ ignored: Ignore;
13
13
  }
14
14
  interface WalkOutput {
15
15
  files: string[];
@@ -11,7 +11,7 @@ let allFolders;
11
11
  ;
12
12
  /** Generate list of files in a directory. */
13
13
  function walk(data) {
14
- const { init, commonRoot, folderRoots, folders, gitignores, regexIgnores } = data;
14
+ const { init, commonRoot, folderRoots, folders, ignored } = data;
15
15
  // Initialise files and folders lists
16
16
  if (init) {
17
17
  allFiles = new Set();
@@ -34,11 +34,13 @@ function walk(data) {
34
34
  // Create absolute path for disc operations
35
35
  const path = path_1.default.resolve(commonRoot, file).replace(/\\/g, '/');
36
36
  const localPath = localRoot ? file.replace(`./${localRoot}/`, '') : file.replace('./', '');
37
- // Skip if nonexistant or ignored
37
+ // Skip if nonexistant
38
38
  const nonExistant = !fs_1.default.existsSync(path);
39
- const isGitIgnored = gitignores.test(localPath).ignored;
40
- const isRegexIgnored = regexIgnores.find(match => localPath.match(match));
41
- if (nonExistant || isGitIgnored || isRegexIgnored)
39
+ if (nonExistant)
40
+ continue;
41
+ // Skip if marked as ignored
42
+ const isIgnored = ignored.test(localPath).ignored;
43
+ if (isIgnored)
42
44
  continue;
43
45
  // Add absolute folder path to list
44
46
  allFolders.add(path_1.default.resolve(folder).replace(/\\/g, '/'));
@@ -46,7 +48,7 @@ function walk(data) {
46
48
  if (file.endsWith('/')) {
47
49
  // Recurse into subfolders
48
50
  allFolders.add(path);
49
- walk({ init: false, commonRoot: commonRoot, folderRoots, folders: [path], gitignores, regexIgnores });
51
+ walk({ init: false, commonRoot, folderRoots, folders: [path], ignored });
50
52
  }
51
53
  else {
52
54
  // Add file path to list
@@ -57,7 +59,7 @@ function walk(data) {
57
59
  // Recurse into all folders
58
60
  else {
59
61
  for (const i in folders) {
60
- walk({ init: false, commonRoot: commonRoot, folderRoots: [folderRoots[i]], folders: [folders[i]], gitignores, regexIgnores });
62
+ walk({ init: false, commonRoot, folderRoots: [folderRoots[i]], folders: [folders[i]], ignored });
61
63
  }
62
64
  }
63
65
  // Return absolute files and folders lists
package/dist/index.js CHANGED
@@ -35,13 +35,23 @@ const isbinaryfile_1 = require("isbinaryfile");
35
35
  const walk_tree_1 = __importDefault(require("./helpers/walk-tree"));
36
36
  const load_data_1 = __importStar(require("./helpers/load-data"));
37
37
  const read_file_1 = __importDefault(require("./helpers/read-file"));
38
+ const parse_gitattributes_1 = __importDefault(require("./helpers/parse-gitattributes"));
38
39
  const convert_pcre_1 = __importDefault(require("./helpers/convert-pcre"));
39
- async function analyse(input, opts = {}) {
40
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
41
- var _t, _u;
40
+ async function analyse(rawPaths, opts = {}) {
41
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
42
+ var _r, _s;
42
43
  const useRawContent = opts.fileContent !== undefined;
43
- input = [input !== null && input !== void 0 ? input : []].flat();
44
- opts.fileContent = [(_a = opts.fileContent) !== null && _a !== void 0 ? _a : []].flat();
44
+ const input = [rawPaths !== null && rawPaths !== void 0 ? rawPaths : []].flat();
45
+ const manualFileContent = [(_a = opts.fileContent) !== null && _a !== void 0 ? _a : []].flat();
46
+ // Normalise input option arguments
47
+ opts = {
48
+ checkIgnored: !opts.quick,
49
+ checkAttributes: !opts.quick,
50
+ checkHeuristics: !opts.quick,
51
+ checkShebang: !opts.quick,
52
+ checkModeline: !opts.quick,
53
+ ...opts,
54
+ };
45
55
  // Load data from github-linguist web repo
46
56
  const langData = await (0, load_data_1.default)('languages.yml', opts.offline).then(js_yaml_1.default.load);
47
57
  const vendorData = await (0, load_data_1.default)('vendor.yml', opts.offline).then(js_yaml_1.default.load);
@@ -52,30 +62,33 @@ async function analyse(input, opts = {}) {
52
62
  // Setup main variables
53
63
  const fileAssociations = {};
54
64
  const extensions = {};
55
- const overrides = {};
65
+ const globOverrides = {};
56
66
  const results = {
57
67
  files: { count: 0, bytes: 0, results: {}, alternatives: {} },
58
68
  languages: { count: 0, bytes: 0, results: {} },
59
69
  unknown: { count: 0, bytes: 0, extensions: {}, filenames: {} },
60
70
  };
61
- // Prepare list of ignored files
62
- const gitignores = (0, ignore_1.default)();
63
- const regexIgnores = [];
64
- gitignores.add('.git');
65
- if (!opts.keepVendored)
66
- regexIgnores.push(...vendorPaths.map(path => RegExp(path, 'i')));
67
- if (opts.ignoredFiles)
68
- gitignores.add(opts.ignoredFiles);
69
71
  // Set a common root path so that vendor paths do not incorrectly match parent folders
70
72
  const normPath = (file) => file.replace(/\\/g, '/');
71
73
  const resolvedInput = input.map(path => normPath(path_1.default.resolve(path)));
72
74
  const commonRoot = (input.length > 1 ? (0, common_path_prefix_1.default)(resolvedInput) : resolvedInput[0]).replace(/\/?$/, '');
73
75
  const localRoot = (folder) => folder.replace(commonRoot, '').replace(/^\//, '');
74
- const relPath = (file) => normPath(path_1.default.relative(commonRoot, file));
75
- const unRelPath = (file) => normPath(path_1.default.resolve(commonRoot, file));
76
- const localPath = (file) => localRoot(unRelPath(file));
76
+ const relPath = (file) => useRawContent ? file : normPath(path_1.default.relative(commonRoot, file));
77
+ const unRelPath = (file) => useRawContent ? file : normPath(path_1.default.resolve(commonRoot, file));
78
+ // Other helper functions
79
+ const fileMatchesGlobs = (file, ...globs) => (0, ignore_1.default)().add(globs).ignores(relPath(file));
80
+ const filterOutIgnored = (files, ignored) => ignored.filter(files.map(relPath)).map(unRelPath);
81
+ //*PREPARE FILES AND DATA*//
82
+ // Prepare list of ignored files
83
+ const ignored = (0, ignore_1.default)();
84
+ ignored.add('.git/');
85
+ ignored.add((_b = opts.ignoredFiles) !== null && _b !== void 0 ? _b : []);
86
+ const regexIgnores = [];
87
+ if (!opts.keepVendored)
88
+ regexIgnores.push(...vendorPaths.map(path => RegExp(path, 'i')));
77
89
  // Load file paths and folders
78
- let files, folders;
90
+ let files;
91
+ let folders;
79
92
  if (useRawContent) {
80
93
  // Uses raw file content
81
94
  files = input;
@@ -83,21 +96,76 @@ async function analyse(input, opts = {}) {
83
96
  }
84
97
  else {
85
98
  // Uses directory on disc
86
- const data = (0, walk_tree_1.default)({ init: true, commonRoot, folderRoots: resolvedInput, folders: resolvedInput, gitignores, regexIgnores });
99
+ const data = (0, walk_tree_1.default)({ init: true, commonRoot, folderRoots: resolvedInput, folders: resolvedInput, ignored });
87
100
  files = data.files;
88
101
  folders = data.folders;
89
102
  }
90
- // Apply aliases
91
- opts = {
92
- checkIgnored: !opts.quick,
93
- checkAttributes: !opts.quick,
94
- checkHeuristics: !opts.quick,
95
- checkShebang: !opts.quick,
96
- checkModeline: !opts.quick,
97
- ...opts
103
+ // Load gitignore data and apply ignores rules
104
+ if (!useRawContent && opts.checkIgnored) {
105
+ const nestedIgnoreFiles = files.filter(file => file.endsWith('.gitignore'));
106
+ for (const ignoresFile of nestedIgnoreFiles) {
107
+ const relIgnoresFile = relPath(ignoresFile);
108
+ const relIgnoresFolder = path_1.default.dirname(relIgnoresFile);
109
+ // Parse gitignores
110
+ const ignoresDataRaw = await (0, read_file_1.default)(ignoresFile);
111
+ const ignoresData = ignoresDataRaw.replace(/#.+|\s+$/gm, '');
112
+ const absoluteIgnoresData = ignoresData
113
+ // '.file' -> 'root/*/.file'
114
+ .replace(/^(?=[^\s\/\\])/gm, localRoot(relIgnoresFolder) + '/*/')
115
+ // '/folder' -> 'root/folder'
116
+ .replace(/^[\/\\]/gm, localRoot(relIgnoresFolder) + '/');
117
+ ignored.add(absoluteIgnoresData);
118
+ files = filterOutIgnored(files, ignored);
119
+ }
120
+ }
121
+ // Fetch and normalise gitattributes data of all subfolders and save to metadata
122
+ const manualAttributes = {}; // Maps file globs to gitattribute boolean flags
123
+ const getFlaggedGlobs = (attr, val) => {
124
+ return Object.entries(manualAttributes).filter(([, attrs]) => attrs[attr] === val).map(([glob,]) => glob);
98
125
  };
126
+ if (!useRawContent && opts.checkAttributes) {
127
+ const nestedAttrFiles = files.filter(file => file.endsWith('.gitattributes'));
128
+ for (const attrFile of nestedAttrFiles) {
129
+ const relAttrFile = relPath(attrFile);
130
+ const relAttrFolder = path_1.default.dirname(relAttrFile);
131
+ const contents = await (0, read_file_1.default)(attrFile);
132
+ const parsed = (0, parse_gitattributes_1.default)(contents, relAttrFolder);
133
+ for (const { glob, attrs } of parsed) {
134
+ manualAttributes[glob] = attrs;
135
+ }
136
+ }
137
+ }
138
+ // Apply vendor file path matches and filter out vendored files
139
+ if (!opts.keepVendored) {
140
+ // Get data of files that have been manually marked with metadata
141
+ const vendorTrueGlobs = [...getFlaggedGlobs('vendored', true), ...getFlaggedGlobs('generated', true), ...getFlaggedGlobs('documentation', true)];
142
+ const vendorFalseGlobs = [...getFlaggedGlobs('vendored', false), ...getFlaggedGlobs('generated', false), ...getFlaggedGlobs('documentation', false)];
143
+ // Set up glob ignore object to use for expanding globs to match files
144
+ const vendorTrueIgnore = (0, ignore_1.default)().add(vendorTrueGlobs);
145
+ const vendorFalseIgnore = (0, ignore_1.default)().add(vendorFalseGlobs);
146
+ // Remove all files marked as vendored by default
147
+ const excludedFiles = files.filter(file => vendorPaths.some(pathPtn => RegExp(pathPtn, 'i').test(relPath(file))));
148
+ files = files.filter(file => !excludedFiles.includes(file));
149
+ // Re-add removed files that are overridden manually in gitattributes
150
+ const overriddenExcludedFiles = excludedFiles.filter(file => vendorFalseIgnore.ignores(relPath(file)));
151
+ files.push(...overriddenExcludedFiles);
152
+ // Remove files explicitly marked as vendored in gitattributes
153
+ files = files.filter(file => !vendorTrueIgnore.ignores(relPath(file)));
154
+ }
155
+ // Filter out binary files
156
+ if (!opts.keepBinary) {
157
+ // Filter out files that are binary by default
158
+ files = files.filter(file => !binary_extensions_1.default.some(ext => file.endsWith('.' + ext)));
159
+ // Filter out manually specified binary files
160
+ const binaryIgnored = (0, ignore_1.default)().add(getFlaggedGlobs('binary', true));
161
+ files = filterOutIgnored(files, binaryIgnored);
162
+ // Re-add files manually marked not as binary
163
+ const binaryUnignored = (0, ignore_1.default)().add(getFlaggedGlobs('binary', false));
164
+ const unignoredList = filterOutIgnored(files, binaryUnignored);
165
+ files.push(...unignoredList);
166
+ }
99
167
  // Ignore specific languages
100
- for (const lang of (_b = opts.ignoredLanguages) !== null && _b !== void 0 ? _b : []) {
168
+ for (const lang of (_c = opts.ignoredLanguages) !== null && _c !== void 0 ? _c : []) {
101
169
  for (const key in langData) {
102
170
  if (lang.toLowerCase() === key.toLowerCase()) {
103
171
  delete langData[key];
@@ -105,71 +173,22 @@ async function analyse(input, opts = {}) {
105
173
  }
106
174
  }
107
175
  }
108
- // Load gitignores and gitattributes
109
- const customBinary = (0, ignore_1.default)();
110
- const customText = (0, ignore_1.default)();
111
- if (!useRawContent && opts.checkAttributes) {
112
- for (const folder of folders) {
113
- // TODO FIX: this is absolute when only 1 path given
114
- const localFilePath = (path) => localRoot(folder) ? localRoot(folder) + '/' + localPath(path) : path;
115
- // Skip if folder is marked in gitattributes
116
- if (relPath(folder) && gitignores.ignores(relPath(folder))) {
117
- continue;
118
- }
119
- // Parse gitignores
120
- const ignoresFile = path_1.default.join(folder, '.gitignore');
121
- if (opts.checkIgnored && fs_1.default.existsSync(ignoresFile)) {
122
- const ignoresData = await (0, read_file_1.default)(ignoresFile);
123
- const localIgnoresData = ignoresData.replace(/^[\/\\]/g, localRoot(folder) + '/');
124
- gitignores.add(localIgnoresData);
125
- }
126
- // Parse gitattributes
127
- const attributesFile = path_1.default.join(folder, '.gitattributes');
128
- if (opts.checkAttributes && fs_1.default.existsSync(attributesFile)) {
129
- const attributesData = await (0, read_file_1.default)(attributesFile);
130
- // Explicit text/binary associations
131
- const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)(?!=auto)/gm);
132
- for (const [_line, path, type] of contentTypeMatches) {
133
- if (['text', '-binary'].includes(type)) {
134
- customText.add(localFilePath(path));
135
- }
136
- if (['-text', 'binary'].includes(type)) {
137
- customBinary.add(localFilePath(path));
138
- }
139
- }
140
- // Custom vendor options
141
- const vendorMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-(vendored|generated|documentation)(?!=false)/gm);
142
- for (const [_line, path] of vendorMatches) {
143
- gitignores.add(localFilePath(path));
144
- }
145
- // Custom file associations
146
- const customLangMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-language=(\S+)/gm);
147
- for (let [_line, path, forcedLang] of customLangMatches) {
148
- // If specified language is an alias, associate it with its full name
149
- if (!langData[forcedLang]) {
150
- const overrideLang = Object.entries(langData).find(entry => { var _a; return (_a = entry[1].aliases) === null || _a === void 0 ? void 0 : _a.includes(forcedLang.toLowerCase()); });
151
- if (overrideLang) {
152
- forcedLang = overrideLang[0];
153
- }
154
- }
155
- const fullPath = path_1.default.join(relPath(folder), path);
156
- overrides[fullPath] = forcedLang;
157
- }
176
+ // Establish language overrides taken from gitattributes
177
+ const forcedLangs = Object.entries(manualAttributes).filter(([, attrs]) => attrs.language);
178
+ for (const [globPath, attrs] of forcedLangs) {
179
+ let forcedLang = attrs.language;
180
+ if (!forcedLang)
181
+ continue;
182
+ // If specified language is an alias, associate it with its full name
183
+ if (!langData[forcedLang]) {
184
+ const overrideLang = Object.entries(langData).find(entry => { var _a; return (_a = entry[1].aliases) === null || _a === void 0 ? void 0 : _a.includes(forcedLang.toLowerCase()); });
185
+ if (overrideLang) {
186
+ forcedLang = overrideLang[0];
158
187
  }
159
188
  }
189
+ globOverrides[globPath] = forcedLang;
160
190
  }
161
- // Check vendored files
162
- if (!opts.keepVendored) {
163
- // Filter out any files that match a vendor file path
164
- if (useRawContent) {
165
- files = gitignores.filter(files);
166
- files = files.filter(file => !regexIgnores.find(match => match.test(file)));
167
- }
168
- else {
169
- files = gitignores.filter(files.map(localPath)).map(unRelPath);
170
- }
171
- }
172
- // Load all files and parse languages
191
+ //*PARSE LANGUAGES*//
173
192
  const addResult = (file, result) => {
174
193
  if (!fileAssociations[file]) {
175
194
  fileAssociations[file] = [];
@@ -177,26 +196,36 @@ async function analyse(input, opts = {}) {
177
196
  }
178
197
  // Set parent to result group if it is present
179
198
  // Is nullish if either `opts.childLanguages` is set or if there is no group
180
- const finalResult = !opts.childLanguages && result && langData[result].group || result;
181
- if (!fileAssociations[file].includes(finalResult))
199
+ const finalResult = !opts.childLanguages && result && langData[result] && langData[result].group || result;
200
+ if (!fileAssociations[file].includes(finalResult)) {
182
201
  fileAssociations[file].push(finalResult);
202
+ }
183
203
  extensions[file] = path_1.default.extname(file).toLowerCase();
184
204
  };
185
- const overridesArray = Object.entries(overrides);
186
- // List all languages that could be associated with a given file
187
205
  const definiteness = {};
188
206
  const fromShebang = {};
189
- for (const file of files) {
207
+ fileLoop: for (const file of files) {
208
+ // Check manual override
209
+ for (const globMatch in globOverrides) {
210
+ if (!fileMatchesGlobs(file, globMatch))
211
+ continue;
212
+ // If the given file matches the glob, apply the override to the file
213
+ const forcedLang = globOverrides[globMatch];
214
+ addResult(file, forcedLang);
215
+ definiteness[file] = true;
216
+ continue fileLoop; // no need to check other heuristics, the classified language has been found
217
+ }
218
+ // Check first line for readability
190
219
  let firstLine;
191
220
  if (useRawContent) {
192
- 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;
221
+ firstLine = (_e = (_d = manualFileContent[files.indexOf(file)]) === null || _d === void 0 ? void 0 : _d.split('\n')[0]) !== null && _e !== void 0 ? _e : null;
193
222
  }
194
223
  else if (fs_1.default.existsSync(file) && !fs_1.default.lstatSync(file).isDirectory()) {
195
224
  firstLine = await (0, read_file_1.default)(file, true).catch(() => null);
196
225
  }
197
226
  else
198
227
  continue;
199
- // Skip if file is unreadable
228
+ // Skip if file is unreadable or blank
200
229
  if (firstLine === null)
201
230
  continue;
202
231
  // Check first line for explicit classification
@@ -231,17 +260,6 @@ async function analyse(input, opts = {}) {
231
260
  continue;
232
261
  }
233
262
  }
234
- // Check override for manual language classification
235
- if (!useRawContent && !opts.quick && opts.checkAttributes) {
236
- const isOverridden = (path) => (0, ignore_1.default)().add(path).ignores(relPath(file));
237
- const match = overridesArray.find(item => isOverridden(item[0]));
238
- if (match) {
239
- const forcedLang = match[1];
240
- addResult(file, forcedLang);
241
- definiteness[file] = true;
242
- continue;
243
- }
244
- }
245
263
  // Search each language
246
264
  let skipExts = false;
247
265
  // Check if filename is a match
@@ -286,12 +304,8 @@ async function analyse(input, opts = {}) {
286
304
  }
287
305
  // Skip binary files
288
306
  if (!useRawContent && !opts.keepBinary) {
289
- const isCustomText = customText.ignores(relPath(file));
290
- const isCustomBinary = customBinary.ignores(relPath(file));
291
- const isBinaryExt = binary_extensions_1.default.some(ext => file.endsWith('.' + ext));
292
- if (!isCustomText && (isCustomBinary || isBinaryExt || await (0, isbinaryfile_1.isBinaryFile)(file))) {
307
+ if (await (0, isbinaryfile_1.isBinaryFile)(file))
293
308
  continue;
294
- }
295
309
  }
296
310
  // Parse heuristics if applicable
297
311
  if (opts.checkHeuristics)
@@ -327,7 +341,7 @@ async function analyse(input, opts = {}) {
327
341
  }
328
342
  }
329
343
  // Check file contents and apply heuristic patterns
330
- const fileContent = ((_l = opts.fileContent) === null || _l === void 0 ? void 0 : _l.length) ? opts.fileContent[files.indexOf(file)] : await (0, read_file_1.default)(file).catch(() => null);
344
+ const fileContent = opts.fileContent ? manualFileContent[files.indexOf(file)] : await (0, read_file_1.default)(file).catch(() => null);
331
345
  // Skip if file read errors
332
346
  if (fileContent === null)
333
347
  continue;
@@ -351,7 +365,7 @@ async function analyse(input, opts = {}) {
351
365
  }
352
366
  }
353
367
  // Skip specified categories
354
- if ((_m = opts.categories) === null || _m === void 0 ? void 0 : _m.length) {
368
+ if ((_l = opts.categories) === null || _l === void 0 ? void 0 : _l.length) {
355
369
  const categories = ['data', 'markup', 'programming', 'prose'];
356
370
  const hiddenCategories = categories.filter(cat => !opts.categories.includes(cat));
357
371
  for (const [file, lang] of Object.entries(results.files.results)) {
@@ -387,21 +401,21 @@ async function analyse(input, opts = {}) {
387
401
  for (const [file, lang] of Object.entries(results.files.results)) {
388
402
  if (lang && !langData[lang])
389
403
  continue;
390
- 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;
404
+ const fileSize = (_o = (_m = manualFileContent[files.indexOf(file)]) === null || _m === void 0 ? void 0 : _m.length) !== null && _o !== void 0 ? _o : fs_1.default.statSync(file).size;
391
405
  results.files.bytes += fileSize;
392
406
  // If no language found, add extension in other section
393
407
  if (!lang) {
394
408
  const ext = path_1.default.extname(file);
395
409
  const unknownType = ext === '' ? 'filenames' : 'extensions';
396
410
  const name = ext === '' ? path_1.default.basename(file) : ext;
397
- (_r = (_t = results.unknown[unknownType])[name]) !== null && _r !== void 0 ? _r : (_t[name] = 0);
411
+ (_p = (_r = results.unknown[unknownType])[name]) !== null && _p !== void 0 ? _p : (_r[name] = 0);
398
412
  results.unknown[unknownType][name] += fileSize;
399
413
  results.unknown.bytes += fileSize;
400
414
  continue;
401
415
  }
402
416
  // Add language and bytes data to corresponding section
403
417
  const { type } = langData[lang];
404
- (_s = (_u = results.languages.results)[lang]) !== null && _s !== void 0 ? _s : (_u[lang] = { type, bytes: 0, color: langData[lang].color });
418
+ (_q = (_s = results.languages.results)[lang]) !== null && _q !== void 0 ? _q : (_s[lang] = { type, bytes: 0, color: langData[lang].color });
405
419
  if (opts.childLanguages) {
406
420
  results.languages.results[lang].parent = langData[lang].group;
407
421
  }
package/dist/types.d.ts CHANGED
@@ -4,6 +4,10 @@ export type Category = 'data' | 'markup' | 'programming' | 'prose';
4
4
  export type FilePath = string;
5
5
  export type Bytes = Integer;
6
6
  export type Integer = number;
7
+ export type RelFile = string & {};
8
+ export type AbsFile = string & {};
9
+ export type AbsFolder = string & {};
10
+ export type FileGlob = string & {};
7
11
  export interface Options {
8
12
  fileContent?: string | string[];
9
13
  ignoredFiles?: string[];
package/ext/generated.rb CHANGED
@@ -22,3 +22,4 @@
22
22
  - ppport\.h$
23
23
  - __generated__\/
24
24
  - _tlb\.pas$
25
+ - (?:^|\/)htmlcov\/
@@ -45,7 +45,7 @@ disambiguations:
45
45
  - language: Public Key
46
46
  pattern: '^(----[- ]BEGIN|ssh-(rsa|dss)) '
47
47
  - language: AsciiDoc
48
- pattern: '^[=-]+(\s|\n)|\{\{[A-Za-z]'
48
+ pattern: '^[=-]+\s|\{\{[A-Za-z]'
49
49
  - language: AGS Script
50
50
  pattern: '^(\/\/.+|((import|export)\s+)?(function|int|float|char)\s+((room|repeatedly|on|game)_)?([A-Za-z]+[A-Za-z_0-9]+)\s*[;\(])'
51
51
  - extensions: ['.asm']
@@ -101,6 +101,8 @@ disambiguations:
101
101
  - (?i:^\s*(end\ssub)$)
102
102
  - (?i:^\s*(?=^function\s)(?:function\s*\w+\(.*?\)\s*as\s*\w*)|(?::\s*function\(.*?\)\s*as\s*\w*)$)
103
103
  - (?i:^\s*(end\sfunction)$)
104
+ - language: Bluespec BH
105
+ pattern: '^package\s+[A-Za-z_][A-Za-z0-9_'']*(?:\s*\(|\s+where)'
104
106
  - extensions: ['.builds']
105
107
  rules:
106
108
  - language: XML
@@ -132,7 +134,7 @@ disambiguations:
132
134
  - extensions: ['.cmp']
133
135
  rules:
134
136
  - language: Gerber Image
135
- pattern: '^[DGMT][0-9]{2}\*\r?\n'
137
+ pattern: '^[DGMT][0-9]{2}\*(?:\r?\n|\r)'
136
138
  - extensions: ['.cs']
137
139
  rules:
138
140
  - language: Smalltalk
@@ -242,7 +244,7 @@ disambiguations:
242
244
  - extensions: ['.ftl']
243
245
  rules:
244
246
  - language: FreeMarker
245
- pattern: '^(?:<|[a-zA-Z-][a-zA-Z0-9_-]+[ \t]+\w)|\$\{\w+[^\n]*?\}|^[ \t]*(?:<#--.*?-->|<#([a-z]+)(?=\s|>)[^>]*>.*?</#\1>|\[#--.*?--\]|\[#([a-z]+)(?=\s|\])[^\]]*\].*?\[#\2\])'
247
+ pattern: '^(?:<|[a-zA-Z-][a-zA-Z0-9_-]+[ \t]+\w)|\$\{\w+[^\r\n]*?\}|^[ \t]*(?:<#--.*?-->|<#([a-z]+)(?=\s|>)[^>]*>.*?</#\1>|\[#--.*?--\]|\[#([a-z]+)(?=\s|\])[^\]]*\].*?\[#\2\])'
246
248
  - language: Fluent
247
249
  pattern: '^-?[a-zA-Z][a-zA-Z0-9_-]* *=|\{\$-?[a-zA-Z][-\w]*(?:\.[a-zA-Z][-\w]*)?\}'
248
250
  - extensions: ['.g']
@@ -250,7 +252,7 @@ disambiguations:
250
252
  - language: GAP
251
253
  pattern: '\s*(Declare|BindGlobal|KeyDependentOperation|Install(Method|GlobalFunction)|SetPackageInfo)'
252
254
  - language: G-code
253
- pattern: '^[MG][0-9]+\n'
255
+ pattern: '^[MG][0-9]+(?:\r?\n|\r)'
254
256
  - extensions: ['.gd']
255
257
  rules:
256
258
  - language: GAP
@@ -350,6 +352,12 @@ disambiguations:
350
352
  pattern: '^\.[A-Za-z]{2}(\s|$)'
351
353
  - language: PicoLisp
352
354
  pattern: '^\((de|class|rel|code|data|must)\s'
355
+ - extensions: ['.lean']
356
+ rules:
357
+ - language: Lean
358
+ pattern: '^import [a-z]'
359
+ - language: Lean 4
360
+ pattern: '^import [A-Z]'
353
361
  - extensions: ['.ls']
354
362
  rules:
355
363
  - language: LoomScript
@@ -395,7 +403,7 @@ disambiguations:
395
403
  - language: Win32 Message File
396
404
  pattern: '(?i)^[ \t]*(?>\/\*\s*)?MessageId=|^\.$'
397
405
  - language: M4
398
- pattern: '^dnl|^divert\((?:-?\d+)?\)|^\w+\(`[^\n]*?''[),]'
406
+ pattern: '^dnl|^divert\((?:-?\d+)?\)|^\w+\(`[^\r\n]*?''[),]'
399
407
  - language: Monkey C
400
408
  pattern: '\b(?:using|module|function|class|var)\s+\w'
401
409
  - extensions: ['.md']
@@ -440,7 +448,7 @@ disambiguations:
440
448
  - language: XML
441
449
  pattern: '^\s*<\?xml\s+version'
442
450
  - language: Gerber Image
443
- pattern: '^[DGMT][0-9]{2}\*\r?\n'
451
+ pattern: '^[DGMT][0-9]{2}\*(?:\r?\n|\r)'
444
452
  - language: Text
445
453
  pattern: 'THE_TITLE'
446
454
  - extensions: ['.nl']
@@ -504,7 +512,7 @@ disambiguations:
504
512
  - extensions: ['.pod']
505
513
  rules:
506
514
  - language: Pod 6
507
- pattern: '^[\s&&[^\n]]*=(comment|begin pod|begin para|item\d+)'
515
+ pattern: '^[\s&&[^\r\n]]*=(comment|begin pod|begin para|item\d+)'
508
516
  - language: Pod
509
517
  - extensions: ['.pp']
510
518
  rules:
@@ -543,7 +551,7 @@ disambiguations:
543
551
  - extensions: ['.q']
544
552
  rules:
545
553
  - language: q
546
- pattern: '((?i:[A-Z.][\w.]*:\{)|(^|\n)\\(cd?|d|l|p|ts?) )'
554
+ pattern: '((?i:[A-Z.][\w.]*:\{)|^\\(cd?|d|l|p|ts?) )'
547
555
  - language: HiveQL
548
556
  pattern: '(?i:SELECT\s+[\w*,]+\s+FROM|(CREATE|ALTER|DROP)\s(DATABASE|SCHEMA|TABLE))'
549
557
  - extensions: ['.qs']
@@ -556,6 +564,8 @@ disambiguations:
556
564
  rules:
557
565
  - language: Rebol
558
566
  pattern: '(?i:\bRebol\b)'
567
+ - language: Rez
568
+ pattern: '(#include\s+["<](Types\.r|Carbon\/Carbon\.r)[">])|((resource|data|type)\s+''[A-Za-z0-9]{4}''\s+((\(.*\)\s+){0,1}){)'
559
569
  - language: R
560
570
  pattern: '<-|^\s*#'
561
571
  - extensions: ['.re']
@@ -615,7 +625,7 @@ disambiguations:
615
625
  - language: Solidity
616
626
  pattern: '\bpragma\s+solidity\b|\b(?:abstract\s+)?contract\s+(?!\d)[a-zA-Z0-9$_]+(?:\s+is\s+(?:[a-zA-Z0-9$_][^\{]*?)?)?\s*\{'
617
627
  - language: Gerber Image
618
- pattern: '^[DGMT][0-9]{2}\*\r?\n'
628
+ pattern: '^[DGMT][0-9]{2}\*(?:\r?\n|\r)'
619
629
  - extensions: ['.sql']
620
630
  rules:
621
631
  - language: PLpgSQL
@@ -645,7 +655,7 @@ disambiguations:
645
655
  - extensions: ['.stl']
646
656
  rules:
647
657
  - language: STL
648
- pattern: '\A\s*solid(?=$|\s)(?:.|[\r\n])*?^endsolid(?:$|\s)'
658
+ pattern: '\A\s*solid(?:$|\s)[\s\S]*^endsolid(?:$|\s)'
649
659
  - extensions: ['.sw']
650
660
  rules:
651
661
  - language: Sway
@@ -699,10 +709,15 @@ disambiguations:
699
709
  - language: Adblock Filter List
700
710
  pattern: '\A\[(?<version>(?:[Aa]d[Bb]lock(?:[ \t][Pp]lus)?|u[Bb]lock(?:[ \t][Oo]rigin)?|[Aa]d[Gg]uard)(?:[ \t] \d+(?:\.\d+)*+)?)(?:[ \t]?;[ \t]?\g<version>)*+\]'
701
711
  - language: Text
712
+ - extensions: ['.typ']
713
+ rules:
714
+ - language: Typst
715
+ pattern: '^#(import|show|let|set)'
716
+ - language: XML
702
717
  - extensions: ['.url']
703
718
  rules:
704
719
  - language: INI
705
- pattern: '^\[InternetShortcut\](?:\r?\n|\r)(?>[^\s\[][^\n]*(?:\r?\n|\r))*URL='
720
+ pattern: '^\[InternetShortcut\](?:\r?\n|\r)(?>[^\s\[][^\r\n]*(?:\r?\n|\r))*URL='
706
721
  - extensions: ['.v']
707
722
  rules:
708
723
  - language: Coq
package/ext/languages.yml CHANGED
@@ -606,9 +606,28 @@ Bluespec:
606
606
  color: "#12223c"
607
607
  extensions:
608
608
  - ".bsv"
609
+ aliases:
610
+ - bluespec bsv
611
+ - bsv
609
612
  tm_scope: source.bsv
610
613
  ace_mode: verilog
614
+ codemirror_mode: verilog
615
+ codemirror_mime_type: text/x-systemverilog
611
616
  language_id: 36
617
+ Bluespec BH:
618
+ type: programming
619
+ group: Bluespec
620
+ color: "#12223c"
621
+ extensions:
622
+ - ".bs"
623
+ aliases:
624
+ - bh
625
+ - bluespec classic
626
+ tm_scope: source.haskell
627
+ ace_mode: haskell
628
+ codemirror_mode: haskell
629
+ codemirror_mime_type: text/x-haskell
630
+ language_id: 641580358
612
631
  Boo:
613
632
  type: programming
614
633
  color: "#d4bec1"
@@ -1607,7 +1626,7 @@ ECL:
1607
1626
  ECLiPSe:
1608
1627
  type: programming
1609
1628
  color: "#001d9d"
1610
- group: prolog
1629
+ group: Prolog
1611
1630
  extensions:
1612
1631
  - ".ecl"
1613
1632
  tm_scope: source.prolog.eclipse
@@ -1689,6 +1708,17 @@ Ecmarkup:
1689
1708
  aliases:
1690
1709
  - ecmarkdown
1691
1710
  language_id: 844766630
1711
+ EdgeQL:
1712
+ type: programming
1713
+ color: "#31A7FF"
1714
+ aliases:
1715
+ - esdl
1716
+ extensions:
1717
+ - ".edgeql"
1718
+ - ".esdl"
1719
+ ace_mode: text
1720
+ tm_scope: source.edgeql
1721
+ language_id: 925235833
1692
1722
  EditorConfig:
1693
1723
  type: data
1694
1724
  color: "#fff1f2"
@@ -2144,6 +2174,7 @@ GLSL:
2144
2174
  - ".tese"
2145
2175
  - ".vert"
2146
2176
  - ".vrx"
2177
+ - ".vs"
2147
2178
  - ".vsh"
2148
2179
  - ".vshader"
2149
2180
  tm_scope: source.glsl
@@ -2205,20 +2236,20 @@ Gemini:
2205
2236
  wrap: true
2206
2237
  tm_scope: source.gemini
2207
2238
  language_id: 310828396
2208
- Genero:
2239
+ Genero 4gl:
2209
2240
  type: programming
2210
2241
  color: "#63408e"
2211
2242
  extensions:
2212
2243
  - ".4gl"
2213
- tm_scope: source.genero
2244
+ tm_scope: source.genero-4gl
2214
2245
  ace_mode: text
2215
2246
  language_id: 986054050
2216
- Genero Forms:
2247
+ Genero per:
2217
2248
  type: markup
2218
2249
  color: "#d8df39"
2219
2250
  extensions:
2220
2251
  - ".per"
2221
- tm_scope: source.genero-forms
2252
+ tm_scope: source.genero-per
2222
2253
  ace_mode: text
2223
2254
  language_id: 902995658
2224
2255
  Genie:
@@ -2316,7 +2347,6 @@ Gherkin:
2316
2347
  Git Attributes:
2317
2348
  type: data
2318
2349
  color: "#F44D27"
2319
- group: INI
2320
2350
  aliases:
2321
2351
  - gitattributes
2322
2352
  filenames:
@@ -2361,6 +2391,15 @@ Gleam:
2361
2391
  - ".gleam"
2362
2392
  tm_scope: source.gleam
2363
2393
  language_id: 1054258749
2394
+ Glimmer JS:
2395
+ type: programming
2396
+ extensions:
2397
+ - ".gjs"
2398
+ ace_mode: javascript
2399
+ color: "#F5835F"
2400
+ tm_scope: source.gjs
2401
+ group: JavaScript
2402
+ language_id: 5523150
2364
2403
  Glyph:
2365
2404
  type: programming
2366
2405
  color: "#c1ac7f"
@@ -2489,6 +2528,15 @@ Gradle:
2489
2528
  tm_scope: source.groovy.gradle
2490
2529
  ace_mode: text
2491
2530
  language_id: 136
2531
+ Gradle Kotlin DSL:
2532
+ group: Gradle
2533
+ type: data
2534
+ color: "#02303a"
2535
+ extensions:
2536
+ - ".gradle.kts"
2537
+ ace_mode: text
2538
+ tm_scope: source.kotlin
2539
+ language_id: 432600901
2492
2540
  Grammatical Framework:
2493
2541
  type: programming
2494
2542
  aliases:
@@ -2603,8 +2651,8 @@ HOCON:
2603
2651
  extensions:
2604
2652
  - ".hocon"
2605
2653
  filenames:
2606
- - .scalafix.conf
2607
- - .scalafmt.conf
2654
+ - ".scalafix.conf"
2655
+ - ".scalafmt.conf"
2608
2656
  tm_scope: source.hocon
2609
2657
  ace_mode: text
2610
2658
  language_id: 679725279
@@ -2812,6 +2860,8 @@ Hosts File:
2812
2860
  filenames:
2813
2861
  - HOSTS
2814
2862
  - hosts
2863
+ aliases:
2864
+ - hosts
2815
2865
  tm_scope: source.hosts
2816
2866
  ace_mode: text
2817
2867
  language_id: 231021894
@@ -2910,7 +2960,6 @@ Idris:
2910
2960
  Ignore List:
2911
2961
  type: data
2912
2962
  color: "#000000"
2913
- group: INI
2914
2963
  aliases:
2915
2964
  - ignore
2916
2965
  - gitignore
@@ -3104,6 +3153,7 @@ JSON:
3104
3153
  - ".watchmanconfig"
3105
3154
  - Pipfile.lock
3106
3155
  - composer.lock
3156
+ - deno.lock
3107
3157
  - flake.lock
3108
3158
  - mcmod.info
3109
3159
  language_id: 174
@@ -3120,6 +3170,7 @@ JSON with Comments:
3120
3170
  extensions:
3121
3171
  - ".jsonc"
3122
3172
  - ".code-snippets"
3173
+ - ".code-workspace"
3123
3174
  - ".sublime-build"
3124
3175
  - ".sublime-commands"
3125
3176
  - ".sublime-completions"
@@ -3623,6 +3674,14 @@ Lean:
3623
3674
  tm_scope: source.lean
3624
3675
  ace_mode: text
3625
3676
  language_id: 197
3677
+ Lean 4:
3678
+ type: programming
3679
+ group: Lean
3680
+ extensions:
3681
+ - ".lean"
3682
+ tm_scope: source.lean4
3683
+ ace_mode: text
3684
+ language_id: 455147478
3626
3685
  Less:
3627
3686
  type: markup
3628
3687
  color: "#1d365d"
@@ -4374,7 +4433,7 @@ Nasal:
4374
4433
  extensions:
4375
4434
  - ".nas"
4376
4435
  tm_scope: source.nasal
4377
- ace_mode: text
4436
+ ace_mode: nasal
4378
4437
  language_id: 178322513
4379
4438
  Nearley:
4380
4439
  type: programming
@@ -4823,6 +4882,8 @@ Option List:
4823
4882
  - ackrc
4824
4883
  filenames:
4825
4884
  - ".ackrc"
4885
+ - ".rspec"
4886
+ - ".yardopts"
4826
4887
  - ackrc
4827
4888
  - mocha.opts
4828
4889
  tm_scope: source.opts
@@ -5101,6 +5162,8 @@ Pic:
5101
5162
  extensions:
5102
5163
  - ".pic"
5103
5164
  - ".chem"
5165
+ aliases:
5166
+ - pikchr
5104
5167
  ace_mode: text
5105
5168
  codemirror_mode: troff
5106
5169
  codemirror_mime_type: text/troff
@@ -5257,6 +5320,14 @@ PowerShell:
5257
5320
  interpreters:
5258
5321
  - pwsh
5259
5322
  language_id: 293
5323
+ Praat:
5324
+ type: programming
5325
+ color: "#c8506d"
5326
+ tm_scope: source.praat
5327
+ ace_mode: praat
5328
+ extensions:
5329
+ - ".praat"
5330
+ language_id: 106029007
5260
5331
  Prisma:
5261
5332
  type: data
5262
5333
  color: "#0c344b"
@@ -5851,6 +5922,14 @@ RenderScript:
5851
5922
  tm_scope: none
5852
5923
  ace_mode: text
5853
5924
  language_id: 323
5925
+ Rez:
5926
+ type: programming
5927
+ extensions:
5928
+ - ".r"
5929
+ tm_scope: source.rez
5930
+ ace_mode: text
5931
+ color: "#FFDAB3"
5932
+ language_id: 498022874
5854
5933
  Rich Text Format:
5855
5934
  type: markup
5856
5935
  extensions:
@@ -6740,7 +6819,7 @@ Svelte:
6740
6819
  language_id: 928734530
6741
6820
  Sway:
6742
6821
  type: programming
6743
- color: "#dea584"
6822
+ color: "#00F58C"
6744
6823
  extensions:
6745
6824
  - ".sw"
6746
6825
  tm_scope: source.sway
@@ -6748,6 +6827,14 @@ Sway:
6748
6827
  codemirror_mode: rust
6749
6828
  codemirror_mime_type: text/x-rustsrc
6750
6829
  language_id: 271471144
6830
+ Sweave:
6831
+ type: prose
6832
+ color: "#198ce7"
6833
+ extensions:
6834
+ - ".rnw"
6835
+ tm_scope: text.tex.latex.sweave
6836
+ ace_mode: tex
6837
+ language_id: 558779190
6751
6838
  Swift:
6752
6839
  type: programming
6753
6840
  color: "#F05138"
@@ -6784,7 +6871,7 @@ TI Program:
6784
6871
  TL-Verilog:
6785
6872
  type: programming
6786
6873
  extensions:
6787
- - ".tlv"
6874
+ - ".tlv"
6788
6875
  tm_scope: source.tlverilog
6789
6876
  ace_mode: verilog
6790
6877
  color: "#C40023"
@@ -6939,6 +7026,17 @@ Terra:
6939
7026
  interpreters:
6940
7027
  - lua
6941
7028
  language_id: 371
7029
+ Terraform Template:
7030
+ type: markup
7031
+ extensions:
7032
+ - ".tftpl"
7033
+ color: "#7b42bb"
7034
+ tm_scope: source.hcl.terraform
7035
+ ace_mode: ruby
7036
+ codemirror_mode: ruby
7037
+ codemirror_mime_type: text/x-ruby
7038
+ group: HCL
7039
+ language_id: 856832701
6942
7040
  Texinfo:
6943
7041
  type: prose
6944
7042
  wrap: true
@@ -7023,6 +7121,14 @@ Thrift:
7023
7121
  - ".thrift"
7024
7122
  ace_mode: text
7025
7123
  language_id: 374
7124
+ Toit:
7125
+ type: programming
7126
+ color: "#c2c9fb"
7127
+ extensions:
7128
+ - ".toit"
7129
+ tm_scope: source.toit
7130
+ ace_mode: text
7131
+ language_id: 356554395
7026
7132
  Turing:
7027
7133
  type: programming
7028
7134
  color: "#cf142b"
@@ -7077,6 +7183,16 @@ TypeScript:
7077
7183
  codemirror_mode: javascript
7078
7184
  codemirror_mime_type: application/typescript
7079
7185
  language_id: 378
7186
+ Typst:
7187
+ type: programming
7188
+ color: "#239dad"
7189
+ aliases:
7190
+ - typ
7191
+ extensions:
7192
+ - ".typ"
7193
+ tm_scope: source.typst
7194
+ ace_mode: text
7195
+ language_id: 704730682
7080
7196
  Unified Parallel C:
7081
7197
  type: programming
7082
7198
  color: "#4e3617"
@@ -7687,6 +7803,7 @@ XML:
7687
7803
  - ".tml"
7688
7804
  - ".ts"
7689
7805
  - ".tsx"
7806
+ - ".typ"
7690
7807
  - ".ui"
7691
7808
  - ".urdf"
7692
7809
  - ".ux"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linguist-js",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
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": {
@@ -42,13 +42,13 @@
42
42
  "commander": "^9.5.0 <10",
43
43
  "common-path-prefix": "^3.0.0",
44
44
  "cross-fetch": "^3.1.8 <4",
45
- "ignore": "^5.2.4",
45
+ "ignore": "^5.3.1",
46
46
  "isbinaryfile": "^4.0.10 <5",
47
47
  "js-yaml": "^4.1.0",
48
48
  "node-cache": "^5.1.2"
49
49
  },
50
50
  "devDependencies": {
51
- "@types/js-yaml": "^4.0.5",
51
+ "@types/js-yaml": "^4.0.9",
52
52
  "@types/node": "ts5.0",
53
53
  "deep-object-diff": "^1.1.9",
54
54
  "typescript": "~5.0.4 <5.1"
package/readme.md CHANGED
@@ -95,9 +95,17 @@ Running LinguistJS on this folder will return the following JSON:
95
95
 
96
96
  ```js
97
97
  const linguist = require('linguist-js');
98
- let folder = './src';
99
- let options = { keepVendored: false, quick: false };
100
- const { files, languages, unknown } = linguist(folder, options);
98
+
99
+ // Analyse folder on disc
100
+ const folder = './src';
101
+ const options = { keepVendored: false, quick: false };
102
+ const { files, languages, unknown } = await linguist(folder, options);
103
+
104
+ // Analyse file content from raw input
105
+ const fileNames = ['file1.ts', 'file2.ts', 'ignoreme.js'];
106
+ const fileContent = ['#!/usr/bin/env node', 'console.log("Example");', '"ignored"'];
107
+ const options = { ignoredFiles: ['ignore*'] };
108
+ const { files, languages, unknown } = await linguist(fileNames, { fileContent, ...options });
101
109
  ```
102
110
 
103
111
  - `linguist(entry?, opts?)` (default export):
@@ -122,7 +130,7 @@ const { files, languages, unknown } = linguist(folder, options);
122
130
  Alias for `checkAttributes:false, checkIgnored:false, checkHeuristics:false, checkShebang:false, checkModeline:false`.
123
131
  - `offline` (boolean):
124
132
  Whether to use pre-packaged metadata files instead of fetching them from GitHub at runtime (defaults to `false`).
125
- - `keepVendored` (boolean):
133
+ - `keepVendored` (boolean):
126
134
  Whether to keep vendored files (dependencies, etc) (defaults to `false`).
127
135
  Does nothing when `fileContent` is set.
128
136
  - `keepBinary` (boolean):
@@ -160,14 +168,20 @@ linguist --version
160
168
  A list of languages to exclude from the output.
161
169
  - `--categories <categories...>`:
162
170
  A list of language categories that should be displayed in the output.
163
- Must be one or more of `data`, `prose`, `programming`, `markup`.
171
+ Must be one or more of `data`, `prose`, `programming`, `markup`.
164
172
  - `--childLanguages`:
165
173
  Display sub-languages instead of their parents, when possible.
166
174
  - `--json`:
175
+ Only affects the CLI output.
167
176
  Display the outputted language data as JSON.
168
177
  - `--tree <traversal>`:
178
+ Only affects the CLI output.
169
179
  A dot-delimited traversal to the nested object that should be logged to the console instead of the entire output.
170
180
  Requires `--json` to be specified.
181
+ - `--listFiles`:
182
+ Only affects the visual CLI output.
183
+ List each matching file and its size under each outputted language result.
184
+ Does nothing if `--json` is specified.
171
185
  - `--quick`:
172
186
  Skip the checking of `.gitattributes` and `.gitignore` files for manual language classifications.
173
187
  Alias for `--checkAttributes=false --checkIgnored=false --checkHeuristics=false --checkShebang=false --checkModeline=false`.