linguist-js 2.7.1 → 2.8.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 +5 -2
- package/dist/helpers/convert-pcre.js +1 -1
- package/dist/helpers/load-data.d.ts +1 -1
- package/dist/helpers/load-data.js +4 -5
- package/dist/helpers/parse-gitattributes.d.ts +1 -0
- package/dist/helpers/parse-gitattributes.js +6 -4
- package/dist/helpers/parse-gitignore.js +1 -1
- package/dist/helpers/read-file.d.ts +1 -1
- package/dist/helpers/read-file.js +2 -2
- package/dist/helpers/walk-tree.js +7 -2
- package/dist/index.js +73 -32
- package/dist/package.json +56 -0
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +146 -0
- package/dist/src/helpers/convert-pcre.d.ts +2 -0
- package/dist/src/helpers/convert-pcre.js +38 -0
- package/dist/src/helpers/load-data.d.ts +4 -0
- package/dist/src/helpers/load-data.js +37 -0
- package/dist/src/helpers/norm-path.d.ts +2 -0
- package/dist/src/helpers/norm-path.js +15 -0
- package/dist/src/helpers/parse-gitattributes.d.ts +17 -0
- package/dist/src/helpers/parse-gitattributes.js +37 -0
- package/dist/src/helpers/parse-gitignore.d.ts +1 -0
- package/dist/src/helpers/parse-gitignore.js +12 -0
- package/dist/src/helpers/read-file.d.ts +5 -0
- package/dist/src/helpers/read-file.js +23 -0
- package/dist/src/helpers/walk-tree.d.ts +20 -0
- package/dist/src/helpers/walk-tree.js +85 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +485 -0
- package/dist/src/schema.d.ts +37 -0
- package/dist/src/schema.js +2 -0
- package/dist/src/types.d.ts +74 -0
- package/dist/src/types.js +2 -0
- package/dist/types.d.ts +22 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +4 -0
- package/ext/documentation.yml +0 -3
- package/ext/generated.rb +5 -0
- package/ext/heuristics.yml +91 -16
- package/ext/languages.yml +317 -8
- package/ext/vendor.yml +0 -2
- package/package.json +6 -5
- package/readme.md +29 -5
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
exports.parseGeneratedDataFile = parseGeneratedDataFile;
|
|
7
|
+
exports.default = loadFile;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const cross_fetch_1 = __importDefault(require("cross-fetch"));
|
|
11
|
+
const node_cache_1 = __importDefault(require("node-cache"));
|
|
12
|
+
const cache = new node_cache_1.default({});
|
|
13
|
+
async function loadWebFile(file) {
|
|
14
|
+
// Return cache if it exists
|
|
15
|
+
const cachedContent = cache.get(file);
|
|
16
|
+
if (cachedContent)
|
|
17
|
+
return cachedContent;
|
|
18
|
+
// Otherwise cache the request
|
|
19
|
+
const dataUrl = (file) => `https://raw.githubusercontent.com/github/linguist/HEAD/lib/linguist/${file}`;
|
|
20
|
+
// Load file content, falling back to the local file if the request fails
|
|
21
|
+
const fileContent = await (0, cross_fetch_1.default)(dataUrl(file)).then(data => data.text()).catch(async () => await loadLocalFile(file));
|
|
22
|
+
cache.set(file, fileContent);
|
|
23
|
+
return fileContent;
|
|
24
|
+
}
|
|
25
|
+
async function loadLocalFile(file) {
|
|
26
|
+
const filePath = node_path_1.default.resolve(__dirname, '../../ext', file);
|
|
27
|
+
return node_fs_1.default.promises.readFile(filePath).then(buffer => buffer.toString());
|
|
28
|
+
}
|
|
29
|
+
/** Nukes unused `generated.rb` file content. */
|
|
30
|
+
async function parseGeneratedDataFile(fileContent) {
|
|
31
|
+
var _a;
|
|
32
|
+
return [...(_a = fileContent.match(/(?<=name\.match\(\/).+?(?=(?<!\\)\/)/gm)) !== null && _a !== void 0 ? _a : []];
|
|
33
|
+
}
|
|
34
|
+
/** Load a data file from github-linguist. */
|
|
35
|
+
async function loadFile(file, offline = false) {
|
|
36
|
+
return offline ? loadLocalFile(file) : loadWebFile(file);
|
|
37
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
exports.normAbsPath = exports.normPath = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const normPath = function normalisedPath(...inputPaths) {
|
|
9
|
+
return node_path_1.default.join(...inputPaths).replace(/\\/g, '/');
|
|
10
|
+
};
|
|
11
|
+
exports.normPath = normPath;
|
|
12
|
+
const normAbsPath = function normalisedAbsolutePath(...inputPaths) {
|
|
13
|
+
return node_path_1.default.resolve(...inputPaths).replace(/\\/g, '/');
|
|
14
|
+
};
|
|
15
|
+
exports.normAbsPath = normAbsPath;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as T from '../types';
|
|
2
|
+
export type FlagAttributes = {
|
|
3
|
+
'vendored': boolean | null;
|
|
4
|
+
'generated': boolean | null;
|
|
5
|
+
'documentation': boolean | null;
|
|
6
|
+
'detectable': boolean | null;
|
|
7
|
+
'binary': boolean | null;
|
|
8
|
+
'language': T.LanguageResult;
|
|
9
|
+
};
|
|
10
|
+
export type ParsedGitattributes = Array<{
|
|
11
|
+
glob: T.FileGlob;
|
|
12
|
+
attrs: FlagAttributes;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Parses a gitattributes file.
|
|
16
|
+
*/
|
|
17
|
+
export default function parseAttributes(content: string, folderRoot?: string): ParsedGitattributes;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = parseAttributes;
|
|
4
|
+
const norm_path_1 = require("./norm-path");
|
|
5
|
+
/**
|
|
6
|
+
* Parses a gitattributes file.
|
|
7
|
+
*/
|
|
8
|
+
function parseAttributes(content, folderRoot = '.') {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
const output = [];
|
|
11
|
+
for (const rawLine of content.split('\n')) {
|
|
12
|
+
const line = rawLine.replace(/#.*/, '').trim();
|
|
13
|
+
if (!line)
|
|
14
|
+
continue;
|
|
15
|
+
const parts = line.split(/\s+/g);
|
|
16
|
+
const fileGlob = parts[0];
|
|
17
|
+
const relFileGlob = (0, norm_path_1.normPath)(folderRoot, fileGlob);
|
|
18
|
+
const attrParts = parts.slice(1);
|
|
19
|
+
const isTrue = (str) => !str.startsWith('-') && !str.endsWith('=false');
|
|
20
|
+
const isFalse = (str) => str.startsWith('-') || str.endsWith('=false');
|
|
21
|
+
const trueParts = (str) => attrParts.filter(part => part.includes(str) && isTrue(part));
|
|
22
|
+
const falseParts = (str) => attrParts.filter(part => part.includes(str) && isFalse(part));
|
|
23
|
+
const hasTrueParts = (str) => trueParts(str).length > 0;
|
|
24
|
+
const hasFalseParts = (str) => falseParts(str).length > 0;
|
|
25
|
+
const boolOrNullVal = (str) => hasTrueParts(str) ? true : hasFalseParts(str) ? false : null;
|
|
26
|
+
const attrs = {
|
|
27
|
+
'generated': boolOrNullVal('linguist-generated'),
|
|
28
|
+
'vendored': boolOrNullVal('linguist-vendored'),
|
|
29
|
+
'documentation': boolOrNullVal('linguist-documentation'),
|
|
30
|
+
'detectable': boolOrNullVal('linguist-detectable'),
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function parseGitignore(content: string): string[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = parseGitignore;
|
|
4
|
+
function parseGitignore(content) {
|
|
5
|
+
const readableData = content
|
|
6
|
+
// Remove comments unless escaped
|
|
7
|
+
.replace(/(?<!\\)#.+/g, '')
|
|
8
|
+
// Remove whitespace unless escaped
|
|
9
|
+
.replace(/(?:(?<!\\)\s)+$/g, '');
|
|
10
|
+
const arrayData = readableData.split(/\r?\n/).filter(data => data);
|
|
11
|
+
return arrayData;
|
|
12
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
exports.default = readFileChunk;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
/**
|
|
9
|
+
* Read part of a file on disc.
|
|
10
|
+
* @throws 'EPERM' if the file is not readable.
|
|
11
|
+
*/
|
|
12
|
+
async function readFileChunk(filename, onlyFirstLine = false) {
|
|
13
|
+
const chunkSize = 100;
|
|
14
|
+
const stream = node_fs_1.default.createReadStream(filename, { highWaterMark: chunkSize });
|
|
15
|
+
let content = '';
|
|
16
|
+
for await (const data of stream) { // may throw
|
|
17
|
+
content += data.toString();
|
|
18
|
+
if (onlyFirstLine && content.includes('\n')) {
|
|
19
|
+
return content.split(/\r?\n/)[0];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return content;
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Ignore } from 'ignore';
|
|
2
|
+
interface WalkInput {
|
|
3
|
+
/** Whether this is walking the tree from the root */
|
|
4
|
+
init: boolean;
|
|
5
|
+
/** The common root absolute path of all folders being checked */
|
|
6
|
+
commonRoot: string;
|
|
7
|
+
/** The absolute path that each folder is relative to */
|
|
8
|
+
folderRoots: string[];
|
|
9
|
+
/** The absolute path of folders being checked */
|
|
10
|
+
folders: string[];
|
|
11
|
+
/** An instantiated Ignore object listing ignored files */
|
|
12
|
+
ignored: Ignore;
|
|
13
|
+
}
|
|
14
|
+
interface WalkOutput {
|
|
15
|
+
files: string[];
|
|
16
|
+
folders: string[];
|
|
17
|
+
}
|
|
18
|
+
/** Generate list of files in a directory. */
|
|
19
|
+
export default function walk(data: WalkInput): WalkOutput;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
exports.default = walk;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const parse_gitignore_1 = __importDefault(require("./parse-gitignore"));
|
|
10
|
+
const norm_path_1 = require("./norm-path");
|
|
11
|
+
let allFiles;
|
|
12
|
+
let allFolders;
|
|
13
|
+
;
|
|
14
|
+
;
|
|
15
|
+
/** Generate list of files in a directory. */
|
|
16
|
+
function walk(data) {
|
|
17
|
+
const { init, commonRoot, folderRoots, folders, ignored } = data;
|
|
18
|
+
// Initialise files and folders lists
|
|
19
|
+
if (init) {
|
|
20
|
+
allFiles = new Set();
|
|
21
|
+
allFolders = new Set();
|
|
22
|
+
}
|
|
23
|
+
// Walk tree of a folder
|
|
24
|
+
if (folders.length === 1) {
|
|
25
|
+
const folder = folders[0];
|
|
26
|
+
const localRoot = folderRoots[0].replace(commonRoot, '').replace(/^\//, '');
|
|
27
|
+
// Get list of files and folders inside this folder
|
|
28
|
+
const files = node_fs_1.default.readdirSync(folder).map(file => {
|
|
29
|
+
// Create path relative to root
|
|
30
|
+
const base = (0, norm_path_1.normAbsPath)(folder, file).replace(commonRoot, '.');
|
|
31
|
+
// Add trailing slash to mark directories
|
|
32
|
+
const isDir = node_fs_1.default.lstatSync(node_path_1.default.resolve(commonRoot, base)).isDirectory();
|
|
33
|
+
return isDir ? `${base}/` : base;
|
|
34
|
+
});
|
|
35
|
+
// Read and apply gitignores
|
|
36
|
+
const gitignoreFilename = (0, norm_path_1.normPath)(folder, '.gitignore');
|
|
37
|
+
if (node_fs_1.default.existsSync(gitignoreFilename)) {
|
|
38
|
+
const gitignoreContents = node_fs_1.default.readFileSync(gitignoreFilename, 'utf-8');
|
|
39
|
+
const ignoredPaths = (0, parse_gitignore_1.default)(gitignoreContents);
|
|
40
|
+
ignored.add(ignoredPaths);
|
|
41
|
+
}
|
|
42
|
+
// Add gitattributes if present
|
|
43
|
+
const gitattributesPath = (0, norm_path_1.normPath)(folder, '.gitattributes');
|
|
44
|
+
if (node_fs_1.default.existsSync(gitattributesPath)) {
|
|
45
|
+
allFiles.add(gitattributesPath);
|
|
46
|
+
}
|
|
47
|
+
// Loop through files and folders
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
// Create absolute path for disc operations
|
|
50
|
+
const path = (0, norm_path_1.normAbsPath)(commonRoot, file);
|
|
51
|
+
const localPath = localRoot ? file.replace(`./${localRoot}/`, '') : file.replace('./', '');
|
|
52
|
+
// Skip if nonexistant
|
|
53
|
+
const nonExistant = !node_fs_1.default.existsSync(path);
|
|
54
|
+
if (nonExistant)
|
|
55
|
+
continue;
|
|
56
|
+
// Skip if marked in gitignore
|
|
57
|
+
const isIgnored = ignored.test(localPath).ignored;
|
|
58
|
+
if (isIgnored)
|
|
59
|
+
continue;
|
|
60
|
+
// Add absolute folder path to list
|
|
61
|
+
allFolders.add((0, norm_path_1.normAbsPath)(folder));
|
|
62
|
+
// Check if this is a folder or file
|
|
63
|
+
if (file.endsWith('/')) {
|
|
64
|
+
// Recurse into subfolders
|
|
65
|
+
allFolders.add(path);
|
|
66
|
+
walk({ init: false, commonRoot, folderRoots, folders: [path], ignored });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Add file path to list
|
|
70
|
+
allFiles.add(path);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Recurse into all folders
|
|
75
|
+
else {
|
|
76
|
+
for (const i in folders) {
|
|
77
|
+
walk({ init: false, commonRoot, folderRoots: [folderRoots[i]], folders: [folders[i]], ignored });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Return absolute files and folders lists
|
|
81
|
+
return {
|
|
82
|
+
files: [...allFiles].map(file => file.replace(/^\./, commonRoot)),
|
|
83
|
+
folders: [...allFolders],
|
|
84
|
+
};
|
|
85
|
+
}
|