linguist-js 2.5.1 → 2.5.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/index.js CHANGED
@@ -28,7 +28,6 @@ async function analyse(input, opts = {}) {
28
28
  const vendorPaths = [...vendorData, ...docData, ...generatedData];
29
29
  // Setup main variables
30
30
  const fileAssociations = {};
31
- const definiteness = {};
32
31
  const extensions = {};
33
32
  const overrides = {};
34
33
  const results = {
@@ -153,6 +152,8 @@ async function analyse(input, opts = {}) {
153
152
  };
154
153
  const overridesArray = Object.entries(overrides);
155
154
  // List all languages that could be associated with a given file
155
+ const definiteness = {};
156
+ const fromShebang = {};
156
157
  for (const file of files) {
157
158
  let firstLine;
158
159
  if (useRawContent) {
@@ -174,22 +175,27 @@ async function analyse(input, opts = {}) {
174
175
  for (const [lang, data] of Object.entries(langData)) {
175
176
  const langMatcher = (lang) => `\\b${lang.toLowerCase().replace(/\W/g, '\\$&')}(?![\\w#+*]|-\*-)`;
176
177
  // Check for interpreter match
177
- const matchesInterpretor = (_f = data.interpreters) === null || _f === void 0 ? void 0 : _f.some(interpreter => firstLine.match(`\\b${interpreter}\\b`));
178
+ if (opts.checkShebang && hasShebang) {
179
+ const matchesInterpretor = (_f = data.interpreters) === null || _f === void 0 ? void 0 : _f.some(interpreter => firstLine.match(`\\b${interpreter}\\b`));
180
+ if (matchesInterpretor)
181
+ matches.push(lang);
182
+ }
178
183
  // Check modeline declaration
179
- const matchesLang = firstLine.toLowerCase().match(langMatcher(lang));
180
- const matchesAlias = (_g = data.aliases) === null || _g === void 0 ? void 0 : _g.some(lang => firstLine.toLowerCase().match(langMatcher(lang)));
181
- // Add language
182
- const interpretorCheck = opts.checkShebang && matchesInterpretor;
183
- const modelineCheck = opts.checkModeline && (matchesLang || matchesAlias);
184
- if (interpretorCheck || modelineCheck) {
185
- matches.push(lang);
184
+ if (opts.checkModeline && hasModeline) {
185
+ const modelineText = firstLine.toLowerCase().replace(/^.*-\*-(.+)-\*-.*$/, '$1');
186
+ const matchesLang = modelineText.match(langMatcher(lang));
187
+ const matchesAlias = (_g = data.aliases) === null || _g === void 0 ? void 0 : _g.some(lang => modelineText.match(langMatcher(lang)));
188
+ if (matchesLang || matchesAlias)
189
+ matches.push(lang);
186
190
  }
187
191
  }
192
+ // Add identified language(s)
188
193
  if (matches.length) {
189
- // Add explicitly-identified language
190
- const forcedLang = matches[0];
191
- addResult(file, forcedLang);
192
- definiteness[file] = true;
194
+ for (const match of matches)
195
+ addResult(file, match);
196
+ if (matches.length === 1)
197
+ definiteness[file] = true;
198
+ fromShebang[file] = true;
193
199
  continue;
194
200
  }
195
201
  }
@@ -206,22 +212,34 @@ async function analyse(input, opts = {}) {
206
212
  }
207
213
  // Search each language
208
214
  let skipExts = false;
215
+ // Check if filename is a match
209
216
  for (const lang in langData) {
210
- // Check if filename is a match
211
217
  const matchesName = (_h = langData[lang].filenames) === null || _h === void 0 ? void 0 : _h.some(name => path_1.default.basename(file.toLowerCase()) === name.toLowerCase());
212
218
  if (matchesName) {
213
219
  addResult(file, lang);
214
220
  skipExts = true;
215
221
  }
216
222
  }
223
+ // Check if extension is a match
224
+ const possibleExts = [];
217
225
  if (!skipExts)
218
226
  for (const lang in langData) {
219
- // Check if extension is a match
220
- const matchesExt = (_j = langData[lang].extensions) === null || _j === void 0 ? void 0 : _j.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
221
- if (matchesExt) {
222
- addResult(file, lang);
227
+ const extMatches = (_j = langData[lang].extensions) === null || _j === void 0 ? void 0 : _j.filter(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
228
+ if (extMatches === null || extMatches === void 0 ? void 0 : extMatches.length) {
229
+ for (const ext of extMatches)
230
+ possibleExts.push({ ext, lang });
223
231
  }
224
232
  }
233
+ // Apply more specific extension if available
234
+ const isComplexExt = (ext) => /\..+\./.test(ext);
235
+ const hasComplexExt = possibleExts.some(data => isComplexExt(data.ext));
236
+ for (const { ext, lang } of possibleExts) {
237
+ if (hasComplexExt && !isComplexExt(ext))
238
+ continue;
239
+ if (!hasComplexExt && isComplexExt(ext))
240
+ continue;
241
+ addResult(file, lang);
242
+ }
225
243
  // Fallback to null if no language matches
226
244
  if (!fileAssociations[file]) {
227
245
  addResult(file, null);
@@ -247,9 +265,8 @@ async function analyse(input, opts = {}) {
247
265
  if (opts.checkHeuristics)
248
266
  for (const heuristics of heuristicsData.disambiguations) {
249
267
  // Make sure the extension matches the current file
250
- if (!heuristics.extensions.includes(extensions[file])) {
268
+ if (!fromShebang[file] && !heuristics.extensions.includes(extensions[file]))
251
269
  continue;
252
- }
253
270
  // Load heuristic rules
254
271
  for (const heuristic of heuristics.rules) {
255
272
  // Make sure the language is not an array
@@ -112,7 +112,7 @@ disambiguations:
112
112
  - language: Smalltalk
113
113
  pattern: '![\w\s]+methodsFor: '
114
114
  - language: 'C#'
115
- pattern: '^(\s*namespace\s*[\w\.]+\s*{|\s*\/\/)'
115
+ pattern: '^(\s*namespace\s*[\w\.]+\s*({|;)|\s*\/\/)'
116
116
  - extensions: ['.csc']
117
117
  rules:
118
118
  - language: GSC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linguist-js",
3
- "version": "2.5.1",
3
+ "version": "2.5.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": {
@@ -11,7 +11,7 @@
11
11
  "node": ">=12"
12
12
  },
13
13
  "scripts": {
14
- "download-files": "npx tsx build/download-files",
14
+ "download-files": "npx tsx@2 build/download-files",
15
15
  "prepare": "npm run download-files && npm test && npm run perf",
16
16
  "perf": "tsc && node test/perf",
17
17
  "test": "tsc && node test/folder && echo --- && node test/unit"
package/readme.md CHANGED
@@ -77,7 +77,7 @@ Running LinguistJS 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
- - Do not rely on any language classification output from LinguistJS being unchanged between runs.
80
+ - Unless running in offline mode, do not rely on any language classification output from LinguistJS being unchanged between runs.
81
81
  Language data is fetched each run from the latest classifications of [`github-linguist`](https://github.com/github/linguist).
82
82
  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.
83
83