dsh-gitnexus-plus 0.0.0-stage → 0.1.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/LICENSE +38 -0
- package/README.md +631 -2
- package/cordis.patch.yml +18 -0
- package/lib/bin/gitnexus-coverage.d.ts +14 -0
- package/lib/bin/gitnexus-coverage.d.ts.map +1 -0
- package/lib/bin/gitnexus-coverage.js +171 -0
- package/lib/bin/gitnexus-coverage.js.map +1 -0
- package/lib/bin/search-helper.d.ts +22 -0
- package/lib/bin/search-helper.d.ts.map +1 -0
- package/lib/bin/search-helper.js +57 -0
- package/lib/bin/search-helper.js.map +1 -0
- package/lib/client.js +2131 -0
- package/lib/gitnexus/analyze.d.ts +173 -0
- package/lib/gitnexus/analyze.d.ts.map +1 -0
- package/lib/gitnexus/analyze.js +308 -0
- package/lib/gitnexus/analyze.js.map +1 -0
- package/lib/gitnexus/commands.d.ts +129 -0
- package/lib/gitnexus/commands.d.ts.map +1 -0
- package/lib/gitnexus/commands.js +185 -0
- package/lib/gitnexus/commands.js.map +1 -0
- package/lib/gitnexus/coverage.d.ts +340 -0
- package/lib/gitnexus/coverage.d.ts.map +1 -0
- package/lib/gitnexus/coverage.js +554 -0
- package/lib/gitnexus/coverage.js.map +1 -0
- package/lib/gitnexus/format.d.ts +16 -0
- package/lib/gitnexus/format.d.ts.map +1 -0
- package/lib/gitnexus/format.js +178 -0
- package/lib/gitnexus/format.js.map +1 -0
- package/lib/gitnexus/git-activity.d.ts +136 -0
- package/lib/gitnexus/git-activity.d.ts.map +1 -0
- package/lib/gitnexus/git-activity.js +345 -0
- package/lib/gitnexus/git-activity.js.map +1 -0
- package/lib/gitnexus/git.d.ts +70 -0
- package/lib/gitnexus/git.d.ts.map +1 -0
- package/lib/gitnexus/git.js +121 -0
- package/lib/gitnexus/git.js.map +1 -0
- package/lib/gitnexus/index-meta.d.ts +160 -0
- package/lib/gitnexus/index-meta.d.ts.map +1 -0
- package/lib/gitnexus/index-meta.js +176 -0
- package/lib/gitnexus/index-meta.js.map +1 -0
- package/lib/gitnexus/install.d.ts +97 -0
- package/lib/gitnexus/install.d.ts.map +1 -0
- package/lib/gitnexus/install.js +228 -0
- package/lib/gitnexus/install.js.map +1 -0
- package/lib/gitnexus/languages.d.ts +80 -0
- package/lib/gitnexus/languages.d.ts.map +1 -0
- package/lib/gitnexus/languages.js +297 -0
- package/lib/gitnexus/languages.js.map +1 -0
- package/lib/gitnexus/root.d.ts +74 -0
- package/lib/gitnexus/root.d.ts.map +1 -0
- package/lib/gitnexus/root.js +168 -0
- package/lib/gitnexus/root.js.map +1 -0
- package/lib/gitnexus/scan.d.ts +68 -0
- package/lib/gitnexus/scan.d.ts.map +1 -0
- package/lib/gitnexus/scan.js +249 -0
- package/lib/gitnexus/scan.js.map +1 -0
- package/lib/gitnexus/search.d.ts +167 -0
- package/lib/gitnexus/search.d.ts.map +1 -0
- package/lib/gitnexus/search.js +517 -0
- package/lib/gitnexus/search.js.map +1 -0
- package/lib/gitnexus/toolchain.d.ts +136 -0
- package/lib/gitnexus/toolchain.d.ts.map +1 -0
- package/lib/gitnexus/toolchain.js +251 -0
- package/lib/gitnexus/toolchain.js.map +1 -0
- package/lib/index.d.ts +98 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +252 -0
- package/lib/index.js.map +1 -0
- package/lib/routes/analyze.d.ts +41 -0
- package/lib/routes/analyze.d.ts.map +1 -0
- package/lib/routes/analyze.js +75 -0
- package/lib/routes/analyze.js.map +1 -0
- package/lib/routes/coverage.d.ts +52 -0
- package/lib/routes/coverage.d.ts.map +1 -0
- package/lib/routes/coverage.js +81 -0
- package/lib/routes/coverage.js.map +1 -0
- package/lib/routes/guard.d.ts +92 -0
- package/lib/routes/guard.d.ts.map +1 -0
- package/lib/routes/guard.js +187 -0
- package/lib/routes/guard.js.map +1 -0
- package/lib/routes/install.d.ts +27 -0
- package/lib/routes/install.d.ts.map +1 -0
- package/lib/routes/install.js +46 -0
- package/lib/routes/install.js.map +1 -0
- package/lib/routes/search.d.ts +27 -0
- package/lib/routes/search.d.ts.map +1 -0
- package/lib/routes/search.js +77 -0
- package/lib/routes/search.js.map +1 -0
- package/package.json +87 -4
- package/scripts/link-runtime.mjs +146 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install GitNexus on this machine, on the operator's explicit instruction.
|
|
3
|
+
*
|
|
4
|
+
* WHY THE PLUGIN DOES THIS ITSELF
|
|
5
|
+
* -------------------------------
|
|
6
|
+
* The plugin reads `.gitnexus/gitnexus.json` — a file that only GitNexus can produce,
|
|
7
|
+
* containing every function definition, symbol relationship and execution flow. Without
|
|
8
|
+
* it there is nothing to display, and no amount of scanning the working tree substitutes
|
|
9
|
+
* for it. So "the user has no GitNexus" is not a degraded state to design around; it is
|
|
10
|
+
* a missing prerequisite, and the most useful thing the plugin can do about it is offer
|
|
11
|
+
* to satisfy it.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IT DOES NOT DO
|
|
14
|
+
* -------------------
|
|
15
|
+
* It does not download anything itself, and it does not pick a registry. It runs `npm
|
|
16
|
+
* install` and lets npm use whatever registry the machine is already configured for —
|
|
17
|
+
* a mirror, a corporate feed, a local Verdaccio. Hardcoding one would break exactly the
|
|
18
|
+
* users whose network is the reason they need this path, and the operator's `.npmrc` is
|
|
19
|
+
* the only authority on where their packages come from.
|
|
20
|
+
*
|
|
21
|
+
* It also does not build anything itself. npm runs the dependency install scripts that
|
|
22
|
+
* GitNexus needs (`@ladybugdb/core`, `tree-sitter`, its own grammar build) — which is
|
|
23
|
+
* why this uses npm rather than pnpm, whose ≥10 line refuses dependency build scripts by
|
|
24
|
+
* default and would fail the install in a way the operator could not act on.
|
|
25
|
+
*
|
|
26
|
+
* INSTALL LOCATION
|
|
27
|
+
* ----------------
|
|
28
|
+
* A directory the plugin owns (`$DSH_HOME/gitnexus-plus/gitnexus`), never the global
|
|
29
|
+
* prefix. A global install needs elevation on Windows and would collide with a GitNexus
|
|
30
|
+
* the operator already has. A plugin-owned copy is removable, self-describing, and does
|
|
31
|
+
* not make the plugin responsible for someone else's global state.
|
|
32
|
+
*
|
|
33
|
+
* The cost is real and the confirmation must state it: roughly 500 MB to 1 GB, several
|
|
34
|
+
* minutes, network access, and the package's build scripts running on this machine.
|
|
35
|
+
*/
|
|
36
|
+
import os from 'node:os';
|
|
37
|
+
import path from 'node:path';
|
|
38
|
+
/** The version range this plugin is known to work against. */
|
|
39
|
+
export const GITNEXUS_RANGE = '^1.6.12';
|
|
40
|
+
const DEFAULT_TIMEOUT_MS = 20 * 60 * 1000;
|
|
41
|
+
const DEFAULT_OUTPUT_LIMIT = 256 * 1024;
|
|
42
|
+
const DEFAULT_TAIL_LIMIT = 6 * 1024;
|
|
43
|
+
/**
|
|
44
|
+
* The default install target.
|
|
45
|
+
*
|
|
46
|
+
* Under `DSH_HOME` rather than under the plugin, because a profile-installed plugin
|
|
47
|
+
* lives inside `node_modules` and writing a 500 MB tree there would be lost on the next
|
|
48
|
+
* install and would confuse the package manager's own bookkeeping.
|
|
49
|
+
*/
|
|
50
|
+
export function defaultInstallPrefix(env = process.env) {
|
|
51
|
+
const home = env.DSH_HOME ?? path.join(os.homedir(), '.dsh');
|
|
52
|
+
return path.join(home, 'gitnexus-plus', 'gitnexus');
|
|
53
|
+
}
|
|
54
|
+
/** Quote one argument for the shell the executor spawns. */
|
|
55
|
+
function quote(value, platform) {
|
|
56
|
+
return platform === 'win32' ? `'${value.replace(/'/g, "''")}'` : `'${value.replace(/'/g, "'\\''")}'`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The `npm install` line, as the shell will read it.
|
|
60
|
+
*
|
|
61
|
+
* `--prefix` rather than `cd`: the working directory stays wherever the executor put it,
|
|
62
|
+
* so a failed install cannot leave a half-written tree in the operator's project. No
|
|
63
|
+
* `--registry` on purpose — see the module comment.
|
|
64
|
+
*/
|
|
65
|
+
export function installCommandLine(prefix, spec, options = {}) {
|
|
66
|
+
const platform = options.platform ?? process.platform;
|
|
67
|
+
const args = ['install', '--prefix', quote(prefix, platform), spec, '--no-audit', '--no-fund'];
|
|
68
|
+
return `npm ${args.join(' ')}`;
|
|
69
|
+
}
|
|
70
|
+
/** Keep the last `limit` characters, on a line boundary when one is available. */
|
|
71
|
+
function tail(text, limit) {
|
|
72
|
+
if (text.length <= limit)
|
|
73
|
+
return text;
|
|
74
|
+
const cut = text.slice(text.length - limit);
|
|
75
|
+
const firstBreak = cut.indexOf('\n');
|
|
76
|
+
return firstBreak === -1 ? cut : cut.slice(firstBreak + 1);
|
|
77
|
+
}
|
|
78
|
+
function looksDenied(text) {
|
|
79
|
+
return /\[sandbox:[^\]]*denied/i.test(text) || /access is denied|EPERM|EACCES/i.test(text);
|
|
80
|
+
}
|
|
81
|
+
export function createInstallRunner(options) {
|
|
82
|
+
const { shell } = options;
|
|
83
|
+
const platform = options.platform ?? process.platform;
|
|
84
|
+
const prefix = options.prefix ?? defaultInstallPrefix();
|
|
85
|
+
const spec = options.spec ?? `gitnexus@${GITNEXUS_RANGE}`;
|
|
86
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
87
|
+
const outputLimit = options.outputLimit ?? DEFAULT_OUTPUT_LIMIT;
|
|
88
|
+
const tailLimit = options.tailLimit ?? DEFAULT_TAIL_LIMIT;
|
|
89
|
+
const now = options.now ?? (() => Date.now());
|
|
90
|
+
let status = null;
|
|
91
|
+
let controller = null;
|
|
92
|
+
const cliPath = () => path.join(prefix, 'node_modules', 'gitnexus', 'dist', 'cli', 'index.js');
|
|
93
|
+
/** The shape a status starts from, before any attempt has touched it. */
|
|
94
|
+
const BLANK = {
|
|
95
|
+
state: 'failed',
|
|
96
|
+
command: '',
|
|
97
|
+
prefix,
|
|
98
|
+
startedAt: 0,
|
|
99
|
+
finishedAt: null,
|
|
100
|
+
exitCode: null,
|
|
101
|
+
message: null,
|
|
102
|
+
output: '',
|
|
103
|
+
};
|
|
104
|
+
const finish = (patch) => {
|
|
105
|
+
status = {
|
|
106
|
+
...BLANK,
|
|
107
|
+
...status,
|
|
108
|
+
...patch,
|
|
109
|
+
// Set last and unconditionally: every path here is finishing, so a caller passing
|
|
110
|
+
// `finishedAt: null` means "not finished", not a silent no-op.
|
|
111
|
+
finishedAt: patch.finishedAt ?? now(),
|
|
112
|
+
};
|
|
113
|
+
controller = null;
|
|
114
|
+
return status;
|
|
115
|
+
};
|
|
116
|
+
const start = () => {
|
|
117
|
+
if (status !== null && status.state === 'running')
|
|
118
|
+
return status;
|
|
119
|
+
if (shell === null) {
|
|
120
|
+
return finish({
|
|
121
|
+
state: 'unavailable',
|
|
122
|
+
command: installCommandLine(prefix, spec, { platform }),
|
|
123
|
+
startedAt: now(),
|
|
124
|
+
message: '当前部署没有提供 shell 执行器,无法安装。请手动执行下面列出的命令。',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const command = installCommandLine(prefix, spec, { platform });
|
|
128
|
+
const abort = new AbortController();
|
|
129
|
+
controller = abort;
|
|
130
|
+
status = {
|
|
131
|
+
state: 'running',
|
|
132
|
+
command,
|
|
133
|
+
prefix,
|
|
134
|
+
startedAt: now(),
|
|
135
|
+
finishedAt: null,
|
|
136
|
+
exitCode: null,
|
|
137
|
+
message: null,
|
|
138
|
+
output: '',
|
|
139
|
+
};
|
|
140
|
+
const spec0 = shell.resolve({
|
|
141
|
+
command,
|
|
142
|
+
timeoutMs,
|
|
143
|
+
onExpiry: 'kill',
|
|
144
|
+
stdoutMaxBytes: outputLimit,
|
|
145
|
+
signal: abort.signal,
|
|
146
|
+
// No sandboxPolicy: the deployment decides. See analyze.ts for why.
|
|
147
|
+
});
|
|
148
|
+
void (async () => {
|
|
149
|
+
let execution;
|
|
150
|
+
try {
|
|
151
|
+
execution = await shell.execute(spec0);
|
|
152
|
+
}
|
|
153
|
+
catch (cause) {
|
|
154
|
+
const aborted = abort.signal.aborted;
|
|
155
|
+
finish({
|
|
156
|
+
state: aborted ? 'cancelled' : 'failed',
|
|
157
|
+
message: aborted ? '已取消。' : `无法启动安装:${String(cause?.message ?? cause)}`,
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
let result;
|
|
162
|
+
try {
|
|
163
|
+
result = await execution.result();
|
|
164
|
+
}
|
|
165
|
+
catch (cause) {
|
|
166
|
+
finish({ state: 'failed', message: `安装失败:${String(cause?.message ?? cause)}` });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const combined = `${result.stdout?.text ?? ''}${result.stderr?.text ?? ''}`;
|
|
170
|
+
const body = tail(combined, tailLimit);
|
|
171
|
+
const exitCode = typeof result.exitCode === 'number' ? result.exitCode : null;
|
|
172
|
+
if (abort.signal.aborted) {
|
|
173
|
+
finish({ state: 'cancelled', exitCode, output: body, message: '已取消。' });
|
|
174
|
+
}
|
|
175
|
+
else if (result.timedOut === true) {
|
|
176
|
+
finish({
|
|
177
|
+
state: 'timed-out',
|
|
178
|
+
exitCode,
|
|
179
|
+
output: body,
|
|
180
|
+
message: `超过 ${Math.round(timeoutMs / 60000)} 分钟上限已终止。网络慢或需要本地编译时,可以延长 analyzeTimeoutMs 后重试。`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else if (looksDenied(combined)) {
|
|
184
|
+
finish({
|
|
185
|
+
state: 'denied',
|
|
186
|
+
exitCode,
|
|
187
|
+
output: body,
|
|
188
|
+
message: '沙箱拒绝了写入。本插件不会申请扩权 —— 请在终端里手动执行这条命令。',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else if (exitCode === 0) {
|
|
192
|
+
finish({
|
|
193
|
+
state: 'done',
|
|
194
|
+
exitCode,
|
|
195
|
+
output: body,
|
|
196
|
+
message: `已安装到 ${prefix}。`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
finish({
|
|
201
|
+
state: 'failed',
|
|
202
|
+
exitCode,
|
|
203
|
+
output: body,
|
|
204
|
+
message: `npm 以退出码 ${exitCode ?? '未知'} 结束。常见原因:` +
|
|
205
|
+
'访问不到你的 npm registry、或者缺少 C++ 编译工具链(这些包需要本地编译)。',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
})();
|
|
209
|
+
return status;
|
|
210
|
+
};
|
|
211
|
+
return {
|
|
212
|
+
start,
|
|
213
|
+
status: () => status,
|
|
214
|
+
cancel: () => {
|
|
215
|
+
if (controller === null)
|
|
216
|
+
return false;
|
|
217
|
+
controller.abort();
|
|
218
|
+
return true;
|
|
219
|
+
},
|
|
220
|
+
cliPath,
|
|
221
|
+
dispose: () => {
|
|
222
|
+
if (controller !== null)
|
|
223
|
+
controller.abort();
|
|
224
|
+
controller = null;
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/gitnexus/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language classification for a file path, reusing GitNexus's own detector.
|
|
3
|
+
*
|
|
4
|
+
* `gitnexus-shared` is not published separately, but `analyze` bundles it into
|
|
5
|
+
* `gitnexus/dist/_shared/`. Importing that copy is deliberate: the detector that
|
|
6
|
+
* classified the files *in an existing index* is the one that must classify them
|
|
7
|
+
* in the report. Copying the map from source would be wrong — the installed
|
|
8
|
+
* 1.6.12 differs from the checkout (e.g. `.ipynb` resolves to `null` there, not
|
|
9
|
+
* to `python`), and such gaps are invisible until a percentage looks odd.
|
|
10
|
+
*
|
|
11
|
+
* Three classes, because "is this file in the graph as code?" has three answers:
|
|
12
|
+
*
|
|
13
|
+
* parsed — GitNexus has a LanguageProvider; symbols inside become graph nodes.
|
|
14
|
+
* recognized — known format, no parser; the file exists as a `File` node only.
|
|
15
|
+
* other — not recognised at all.
|
|
16
|
+
*
|
|
17
|
+
* This is the distinction GitHub's language bar cannot express, and it is what
|
|
18
|
+
* makes the widget answer "how much of my repository does GitNexus actually
|
|
19
|
+
* understand?" rather than merely "what is this repo written in?".
|
|
20
|
+
*/
|
|
21
|
+
import type { ScannedFile } from './scan.js';
|
|
22
|
+
/** Which of the three classes a path falls into. */
|
|
23
|
+
export type LanguageClass = 'parsed' | 'recognized' | 'other';
|
|
24
|
+
/**
|
|
25
|
+
* GitNexus's language detector, as this plugin uses it: the handle passed around
|
|
26
|
+
* by {@link classifyFile} and {@link aggregateLanguages}.
|
|
27
|
+
*/
|
|
28
|
+
export interface LanguageDetector {
|
|
29
|
+
getLanguageFromFilename: (filePath: string) => string | null;
|
|
30
|
+
getSyntaxLanguageFromFilename: (filePath: string) => string;
|
|
31
|
+
}
|
|
32
|
+
/** What {@link classifyFile} decides about one path. */
|
|
33
|
+
export interface ClassifiedLanguage {
|
|
34
|
+
key: string;
|
|
35
|
+
label: string;
|
|
36
|
+
color: string;
|
|
37
|
+
klass: LanguageClass;
|
|
38
|
+
}
|
|
39
|
+
/** One row of the language breakdown: a {@link ClassifiedLanguage} with totals. */
|
|
40
|
+
export interface LanguageRow extends ClassifiedLanguage {
|
|
41
|
+
bytes: number;
|
|
42
|
+
files: number;
|
|
43
|
+
}
|
|
44
|
+
/** The language breakdown itself, as returned by {@link aggregateLanguages}. */
|
|
45
|
+
export interface LanguageAggregate {
|
|
46
|
+
rows: LanguageRow[];
|
|
47
|
+
totalFiles: number;
|
|
48
|
+
totalBytes: number;
|
|
49
|
+
/**
|
|
50
|
+
* Which classifier produced the rows. `builtin` means GitNexus was unavailable
|
|
51
|
+
* and {@link FALLBACK_EXTENSIONS} filled in; the rows are still useful, but no
|
|
52
|
+
* row may be presented as "GitNexus parses this".
|
|
53
|
+
*/
|
|
54
|
+
detector: 'gitnexus' | 'builtin';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Load GitNexus's bundled detector once per process.
|
|
58
|
+
* Returns `null` when the bundled copy is unavailable, in which case every file
|
|
59
|
+
* is classified `other` — a degraded but honest answer, never a fabricated one.
|
|
60
|
+
*/
|
|
61
|
+
export declare function loadLanguageDetector(options?: {
|
|
62
|
+
env?: NodeJS.ProcessEnv;
|
|
63
|
+
managedPrefix?: string;
|
|
64
|
+
}): Promise<LanguageDetector | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Classify one repo-relative path.
|
|
67
|
+
*
|
|
68
|
+
* @param filePath repo-relative, forward slashes
|
|
69
|
+
*/
|
|
70
|
+
export declare function classifyFile(filePath: string, det: LanguageDetector | null): ClassifiedLanguage;
|
|
71
|
+
/**
|
|
72
|
+
* Aggregate files into a language breakdown.
|
|
73
|
+
*
|
|
74
|
+
* `klass` rank decides ordering ties so the three visual bands stay contiguous
|
|
75
|
+
* and the bar does not interleave "understood" and "not understood" segments.
|
|
76
|
+
*
|
|
77
|
+
* @param include extra filter, e.g. "is indexed"
|
|
78
|
+
*/
|
|
79
|
+
export declare function aggregateLanguages(files: ScannedFile[], det: LanguageDetector | null, include?: (filePath: string) => boolean): LanguageAggregate;
|
|
80
|
+
//# sourceMappingURL=languages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"languages.d.ts","sourceRoot":"","sources":["../../src/gitnexus/languages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAC;AAkL9D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC7D,6BAA6B,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7D;AAED,wDAAwD;AACxD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,mFAAmF;AACnF,MAAM,WAAW,WAAY,SAAQ,kBAAkB;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;CAClC;AAID;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAChE,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAqBlC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,GAAG,IAAI,GAAG,kBAAkB,CA6C/F;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAAE,EACpB,GAAG,EAAE,gBAAgB,GAAG,IAAI,EAC5B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GACtC,iBAAiB,CA4BnB"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language classification for a file path, reusing GitNexus's own detector.
|
|
3
|
+
*
|
|
4
|
+
* `gitnexus-shared` is not published separately, but `analyze` bundles it into
|
|
5
|
+
* `gitnexus/dist/_shared/`. Importing that copy is deliberate: the detector that
|
|
6
|
+
* classified the files *in an existing index* is the one that must classify them
|
|
7
|
+
* in the report. Copying the map from source would be wrong — the installed
|
|
8
|
+
* 1.6.12 differs from the checkout (e.g. `.ipynb` resolves to `null` there, not
|
|
9
|
+
* to `python`), and such gaps are invisible until a percentage looks odd.
|
|
10
|
+
*
|
|
11
|
+
* Three classes, because "is this file in the graph as code?" has three answers:
|
|
12
|
+
*
|
|
13
|
+
* parsed — GitNexus has a LanguageProvider; symbols inside become graph nodes.
|
|
14
|
+
* recognized — known format, no parser; the file exists as a `File` node only.
|
|
15
|
+
* other — not recognised at all.
|
|
16
|
+
*
|
|
17
|
+
* This is the distinction GitHub's language bar cannot express, and it is what
|
|
18
|
+
* makes the widget answer "how much of my repository does GitNexus actually
|
|
19
|
+
* understand?" rather than merely "what is this repo written in?".
|
|
20
|
+
*/
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
23
|
+
import { findGitnexusRoot } from './root.js';
|
|
24
|
+
/**
|
|
25
|
+
* Display names and stable colours. Colours follow GitHub linguist's palette for
|
|
26
|
+
* the languages it defines, so the bar reads the same as a repository page; the
|
|
27
|
+
* remainder are assigned from a fixed table rather than a hash so a colour never
|
|
28
|
+
* moves between renders.
|
|
29
|
+
*/
|
|
30
|
+
const DISPLAY = Object.freeze({
|
|
31
|
+
javascript: ['JavaScript', '#f1e05a'],
|
|
32
|
+
typescript: ['TypeScript', '#3178c6'],
|
|
33
|
+
python: ['Python', '#3572A5'],
|
|
34
|
+
java: ['Java', '#b07219'],
|
|
35
|
+
c: ['C', '#555555'],
|
|
36
|
+
cpp: ['C++', '#f34b7d'],
|
|
37
|
+
'objective-c': ['Objective-C', '#438eff'],
|
|
38
|
+
csharp: ['C#', '#178600'],
|
|
39
|
+
go: ['Go', '#00ADD8'],
|
|
40
|
+
ruby: ['Ruby', '#701516'],
|
|
41
|
+
rust: ['Rust', '#dea584'],
|
|
42
|
+
php: ['PHP', '#4F5D95'],
|
|
43
|
+
kotlin: ['Kotlin', '#A97BFF'],
|
|
44
|
+
swift: ['Swift', '#F05138'],
|
|
45
|
+
dart: ['Dart', '#00B4AB'],
|
|
46
|
+
vue: ['Vue', '#41b883'],
|
|
47
|
+
cobol: ['COBOL', '#6b6b6b'],
|
|
48
|
+
zig: ['Zig', '#ec915c'],
|
|
49
|
+
// Recognised formats: neutral colours, intentionally outside the code palette.
|
|
50
|
+
markdown: ['Markdown', '#8b949e'],
|
|
51
|
+
json: ['JSON', '#a8a8a8'],
|
|
52
|
+
yaml: ['YAML', '#7d9199'],
|
|
53
|
+
markup: ['HTML', '#e34c26'],
|
|
54
|
+
css: ['CSS', '#563d7c'],
|
|
55
|
+
bash: ['Shell', '#89e051'],
|
|
56
|
+
sql: ['SQL', '#9c9c9c'],
|
|
57
|
+
toml: ['TOML', '#9c4221'],
|
|
58
|
+
ini: ['INI', '#8a8a8a'],
|
|
59
|
+
docker: ['Dockerfile', '#384d54'],
|
|
60
|
+
makefile: ['Makefile', '#427819'],
|
|
61
|
+
// GitNexus's detector does not know these. They are added here so the bar reads
|
|
62
|
+
// like a repository page instead of dumping them into "other" — measured on the
|
|
63
|
+
// GitNexus repository itself, where 6 `.jinja` and 3 `.ps1` files were the only
|
|
64
|
+
// thing separating this breakdown from GitHub's.
|
|
65
|
+
jinja: ['Jinja', '#a52a22'],
|
|
66
|
+
powershell: ['PowerShell', '#012456'],
|
|
67
|
+
batch: ['Batchfile', '#C1F12E'],
|
|
68
|
+
diff: ['Diff', '#8b949e'],
|
|
69
|
+
plaintext: ['Text', '#6e7681'],
|
|
70
|
+
xml: ['XML', '#0060ac'],
|
|
71
|
+
text: ['其他', '#484f58'],
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Extensions GitNexus's detector has no entry for, mapped to the display keys
|
|
75
|
+
* above. Consulted only after that detector has declined the file, so it can
|
|
76
|
+
* never override a language GitNexus genuinely parses.
|
|
77
|
+
*/
|
|
78
|
+
const SUPPLEMENTARY_EXTENSIONS = Object.freeze({
|
|
79
|
+
'.jinja': 'jinja',
|
|
80
|
+
'.jinja2': 'jinja',
|
|
81
|
+
'.j2': 'jinja',
|
|
82
|
+
'.njk': 'jinja',
|
|
83
|
+
'.ps1': 'powershell',
|
|
84
|
+
'.psm1': 'powershell',
|
|
85
|
+
'.psd1': 'powershell',
|
|
86
|
+
'.ps1xml': 'powershell',
|
|
87
|
+
'.bat': 'batch',
|
|
88
|
+
'.cmd': 'batch',
|
|
89
|
+
'.patch': 'diff',
|
|
90
|
+
'.diff': 'diff',
|
|
91
|
+
'.txt': 'plaintext',
|
|
92
|
+
'.log': 'plaintext',
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Built-in extension map used only when GitNexus's detector is unavailable.
|
|
96
|
+
*
|
|
97
|
+
* This is the fallback that was missing: with no GitNexus package, `detector` is
|
|
98
|
+
* `null` and every ordinary source file used to fall through to `text`, so a
|
|
99
|
+
* TypeScript repository reported a one-row "其他" breakdown — indistinguishable
|
|
100
|
+
* from "this repository has no code". The map covers the languages this plugin
|
|
101
|
+
* already knows how to display; it is not a reimplementation of GitNexus's parser
|
|
102
|
+
* table, and it never claims a file is parsed. Every hit is classed `recognized`
|
|
103
|
+
* because, without GitNexus, no parser support can be verified.
|
|
104
|
+
*
|
|
105
|
+
* Deliberately absent: extensions where the format is genuinely ambiguous
|
|
106
|
+
* (`.h` is C here, matching GitHub linguist's default) or where upstream makes a
|
|
107
|
+
* decision this plugin should not second-guess (`.ipynb`).
|
|
108
|
+
*/
|
|
109
|
+
const FALLBACK_EXTENSIONS = Object.freeze({
|
|
110
|
+
'.ts': 'typescript',
|
|
111
|
+
'.tsx': 'typescript',
|
|
112
|
+
'.mts': 'typescript',
|
|
113
|
+
'.cts': 'typescript',
|
|
114
|
+
'.js': 'javascript',
|
|
115
|
+
'.jsx': 'javascript',
|
|
116
|
+
'.mjs': 'javascript',
|
|
117
|
+
'.cjs': 'javascript',
|
|
118
|
+
'.py': 'python',
|
|
119
|
+
'.pyw': 'python',
|
|
120
|
+
'.java': 'java',
|
|
121
|
+
'.c': 'c',
|
|
122
|
+
'.h': 'c',
|
|
123
|
+
'.cpp': 'cpp',
|
|
124
|
+
'.cc': 'cpp',
|
|
125
|
+
'.cxx': 'cpp',
|
|
126
|
+
'.hpp': 'cpp',
|
|
127
|
+
'.hh': 'cpp',
|
|
128
|
+
'.hxx': 'cpp',
|
|
129
|
+
'.m': 'objective-c',
|
|
130
|
+
'.mm': 'objective-c',
|
|
131
|
+
'.cs': 'csharp',
|
|
132
|
+
'.go': 'go',
|
|
133
|
+
'.rb': 'ruby',
|
|
134
|
+
'.rake': 'ruby',
|
|
135
|
+
'.rs': 'rust',
|
|
136
|
+
'.php': 'php',
|
|
137
|
+
'.kt': 'kotlin',
|
|
138
|
+
'.kts': 'kotlin',
|
|
139
|
+
'.swift': 'swift',
|
|
140
|
+
'.dart': 'dart',
|
|
141
|
+
'.vue': 'vue',
|
|
142
|
+
'.cob': 'cobol',
|
|
143
|
+
'.cbl': 'cobol',
|
|
144
|
+
'.zig': 'zig',
|
|
145
|
+
'.md': 'markdown',
|
|
146
|
+
'.markdown': 'markdown',
|
|
147
|
+
'.mdx': 'markdown',
|
|
148
|
+
'.json': 'json',
|
|
149
|
+
'.jsonc': 'json',
|
|
150
|
+
'.json5': 'json',
|
|
151
|
+
'.yaml': 'yaml',
|
|
152
|
+
'.yml': 'yaml',
|
|
153
|
+
'.html': 'markup',
|
|
154
|
+
'.htm': 'markup',
|
|
155
|
+
'.xhtml': 'markup',
|
|
156
|
+
'.css': 'css',
|
|
157
|
+
'.scss': 'css',
|
|
158
|
+
'.sass': 'css',
|
|
159
|
+
'.less': 'css',
|
|
160
|
+
'.sh': 'bash',
|
|
161
|
+
'.bash': 'bash',
|
|
162
|
+
'.zsh': 'bash',
|
|
163
|
+
'.fish': 'bash',
|
|
164
|
+
'.sql': 'sql',
|
|
165
|
+
'.toml': 'toml',
|
|
166
|
+
'.ini': 'ini',
|
|
167
|
+
'.cfg': 'ini',
|
|
168
|
+
'.conf': 'ini',
|
|
169
|
+
'.xml': 'xml',
|
|
170
|
+
'.xsd': 'xml',
|
|
171
|
+
'.svg': 'xml',
|
|
172
|
+
});
|
|
173
|
+
/**
|
|
174
|
+
* Cases no extension map can decide, resolved by filename.
|
|
175
|
+
* Kept explicit so an unlisted name falls to `other` rather than a guess.
|
|
176
|
+
*/
|
|
177
|
+
const BASENAME_LANGUAGES = Object.freeze({
|
|
178
|
+
dockerfile: 'docker',
|
|
179
|
+
makefile: 'makefile',
|
|
180
|
+
'cmakelists.txt': 'text',
|
|
181
|
+
'.gitignore': 'text',
|
|
182
|
+
'.gitattributes': 'text',
|
|
183
|
+
'.editorconfig': 'text',
|
|
184
|
+
'.npmrc': 'text',
|
|
185
|
+
'.nvmrc': 'text',
|
|
186
|
+
});
|
|
187
|
+
let detector = null;
|
|
188
|
+
/**
|
|
189
|
+
* Load GitNexus's bundled detector once per process.
|
|
190
|
+
* Returns `null` when the bundled copy is unavailable, in which case every file
|
|
191
|
+
* is classified `other` — a degraded but honest answer, never a fabricated one.
|
|
192
|
+
*/
|
|
193
|
+
export async function loadLanguageDetector(options = {}) {
|
|
194
|
+
if (detector !== null)
|
|
195
|
+
return detector;
|
|
196
|
+
const root = findGitnexusRoot(options);
|
|
197
|
+
if (root === null)
|
|
198
|
+
return null;
|
|
199
|
+
try {
|
|
200
|
+
const mod = (await import(pathToFileURL(path.join(root, 'dist', '_shared', 'language-detection.js')).href));
|
|
201
|
+
if (typeof mod.getLanguageFromFilename === 'function') {
|
|
202
|
+
detector = {
|
|
203
|
+
getLanguageFromFilename: mod.getLanguageFromFilename,
|
|
204
|
+
getSyntaxLanguageFromFilename: typeof mod.getSyntaxLanguageFromFilename === 'function'
|
|
205
|
+
? mod.getSyntaxLanguageFromFilename
|
|
206
|
+
: () => 'text',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
/* fall through to the degraded path */
|
|
212
|
+
}
|
|
213
|
+
return detector;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Classify one repo-relative path.
|
|
217
|
+
*
|
|
218
|
+
* @param filePath repo-relative, forward slashes
|
|
219
|
+
*/
|
|
220
|
+
export function classifyFile(filePath, det) {
|
|
221
|
+
let key = null;
|
|
222
|
+
let klass = 'other';
|
|
223
|
+
if (det !== null) {
|
|
224
|
+
const parsed = det.getLanguageFromFilename(filePath);
|
|
225
|
+
if (parsed) {
|
|
226
|
+
key = parsed;
|
|
227
|
+
klass = 'parsed';
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
const syntax = det.getSyntaxLanguageFromFilename(filePath);
|
|
231
|
+
if (syntax && syntax !== 'text') {
|
|
232
|
+
key = syntax;
|
|
233
|
+
klass = 'recognized';
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (key === null) {
|
|
238
|
+
const lastDot = filePath.lastIndexOf('.');
|
|
239
|
+
if (lastDot >= 0) {
|
|
240
|
+
const ext = filePath.slice(lastDot).toLowerCase();
|
|
241
|
+
const supplement = SUPPLEMENTARY_EXTENSIONS[ext] ?? FALLBACK_EXTENSIONS[ext];
|
|
242
|
+
if (supplement !== undefined)
|
|
243
|
+
key = supplement;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (key === null) {
|
|
247
|
+
const base = filePath.split('/').pop()?.toLowerCase() ?? '';
|
|
248
|
+
const byName = BASENAME_LANGUAGES[base];
|
|
249
|
+
if (byName !== undefined) {
|
|
250
|
+
key = byName;
|
|
251
|
+
klass = byName === 'text' ? 'other' : 'recognized';
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const entry = key !== null ? DISPLAY[key] : undefined;
|
|
255
|
+
// `key === null` already implies `entry === undefined`; stating both is what lets
|
|
256
|
+
// the checker narrow `key` to a string for the return below.
|
|
257
|
+
if (entry === undefined || key === null) {
|
|
258
|
+
return { key: 'text', label: '其他', color: DISPLAY.text[1], klass: 'other' };
|
|
259
|
+
}
|
|
260
|
+
// A supplementary hit is a known format GitNexus cannot parse — never `parsed`.
|
|
261
|
+
if (klass === 'other' && key !== 'text')
|
|
262
|
+
klass = 'recognized';
|
|
263
|
+
return { key, label: entry[0], color: entry[1], klass };
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Aggregate files into a language breakdown.
|
|
267
|
+
*
|
|
268
|
+
* `klass` rank decides ordering ties so the three visual bands stay contiguous
|
|
269
|
+
* and the bar does not interleave "understood" and "not understood" segments.
|
|
270
|
+
*
|
|
271
|
+
* @param include extra filter, e.g. "is indexed"
|
|
272
|
+
*/
|
|
273
|
+
export function aggregateLanguages(files, det, include) {
|
|
274
|
+
const map = new Map();
|
|
275
|
+
let totalFiles = 0;
|
|
276
|
+
let totalBytes = 0;
|
|
277
|
+
for (const file of files) {
|
|
278
|
+
if (include && !include(file.path))
|
|
279
|
+
continue;
|
|
280
|
+
const { key, label, color, klass } = classifyFile(file.path, det);
|
|
281
|
+
let row = map.get(key);
|
|
282
|
+
if (row === undefined) {
|
|
283
|
+
row = { key, label, color, klass, files: 0, bytes: 0 };
|
|
284
|
+
map.set(key, row);
|
|
285
|
+
}
|
|
286
|
+
row.files += 1;
|
|
287
|
+
row.bytes += file.size;
|
|
288
|
+
totalFiles += 1;
|
|
289
|
+
totalBytes += file.size;
|
|
290
|
+
}
|
|
291
|
+
const klassRank = { parsed: 0, recognized: 1, other: 2 };
|
|
292
|
+
const rows = [...map.values()].sort((a, b) => klassRank[a.klass] - klassRank[b.klass] ||
|
|
293
|
+
b.bytes - a.bytes ||
|
|
294
|
+
(a.key < b.key ? -1 : 1));
|
|
295
|
+
return { rows, totalFiles, totalBytes, detector: det === null ? 'builtin' : 'gitnexus' };
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=languages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"languages.js","sourceRoot":"","sources":["../../src/gitnexus/languages.ts"],"names":[],"mappings":"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"}
|