@adhisang/minecraft-modding-mcp 3.1.1 → 3.2.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/CHANGELOG.md +24 -0
- package/README.md +20 -8
- package/dist/access-transformer-parser.d.ts +17 -0
- package/dist/access-transformer-parser.js +97 -0
- package/dist/concurrency.d.ts +1 -0
- package/dist/concurrency.js +24 -0
- package/dist/decompiler/vineflower.js +22 -21
- package/dist/entry-tools/analyze-mod-service.d.ts +4 -4
- package/dist/entry-tools/analyze-symbol-service.d.ts +20 -20
- package/dist/entry-tools/inspect-minecraft-service.d.ts +148 -148
- package/dist/entry-tools/validate-project-service.d.ts +153 -16
- package/dist/entry-tools/validate-project-service.js +360 -23
- package/dist/gradle-paths.d.ts +4 -0
- package/dist/gradle-paths.js +57 -0
- package/dist/index.js +65 -13
- package/dist/mapping-pipeline-service.d.ts +3 -1
- package/dist/mapping-pipeline-service.js +16 -1
- package/dist/mapping-service.d.ts +4 -0
- package/dist/mapping-service.js +155 -60
- package/dist/minecraft-explorer-service.d.ts +13 -0
- package/dist/minecraft-explorer-service.js +8 -4
- package/dist/mixin-validator.d.ts +33 -2
- package/dist/mixin-validator.js +197 -11
- package/dist/mod-analyzer.d.ts +1 -0
- package/dist/mod-analyzer.js +17 -1
- package/dist/mod-decompile-service.js +4 -4
- package/dist/mod-remap-service.js +1 -54
- package/dist/mod-search-service.d.ts +1 -0
- package/dist/mod-search-service.js +84 -51
- package/dist/response-utils.d.ts +35 -0
- package/dist/response-utils.js +113 -0
- package/dist/source-jar-reader.d.ts +16 -0
- package/dist/source-jar-reader.js +103 -1
- package/dist/source-resolver.js +9 -10
- package/dist/source-service.d.ts +22 -2
- package/dist/source-service.js +914 -105
- package/dist/tool-contract-manifest.js +8 -6
- package/dist/types.d.ts +17 -0
- package/dist/workspace-mapping-service.d.ts +13 -0
- package/dist/workspace-mapping-service.js +146 -14
- package/package.json +1 -1
|
@@ -2,12 +2,14 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import fastGlob from "fast-glob";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { mapWithConcurrencyLimit } from "../concurrency.js";
|
|
5
6
|
import { createError, ERROR_CODES } from "../errors.js";
|
|
6
7
|
import { buildIncludeSchema, detailSchema } from "./entry-tool-schema.js";
|
|
7
8
|
import { buildEntryToolResult, createSummarySubject } from "./response-contract.js";
|
|
8
9
|
import { resolveDetail, resolveInclude } from "./request-normalizers.js";
|
|
9
10
|
const nonEmptyString = z.string().trim().min(1);
|
|
10
11
|
const INCLUDE_GROUPS = ["warnings", "issues", "workspace", "recovery"];
|
|
12
|
+
const WORKSPACE_TEXT_FILE_READ_CONCURRENCY = 4;
|
|
11
13
|
const mixinInputSchema = z.discriminatedUnion("mode", [
|
|
12
14
|
z.object({ mode: z.literal("inline"), source: nonEmptyString }),
|
|
13
15
|
z.object({ mode: z.literal("path"), path: nonEmptyString }),
|
|
@@ -19,11 +21,15 @@ const accessWidenerInputSchema = z.discriminatedUnion("mode", [
|
|
|
19
21
|
z.object({ mode: z.literal("inline"), content: nonEmptyString }),
|
|
20
22
|
z.object({ mode: z.literal("path"), path: nonEmptyString })
|
|
21
23
|
]);
|
|
24
|
+
const accessTransformerInputSchema = z.discriminatedUnion("mode", [
|
|
25
|
+
z.object({ mode: z.literal("inline"), content: nonEmptyString }),
|
|
26
|
+
z.object({ mode: z.literal("path"), path: nonEmptyString })
|
|
27
|
+
]);
|
|
22
28
|
const subjectSchema = z.discriminatedUnion("kind", [
|
|
23
29
|
z.object({
|
|
24
30
|
kind: z.literal("workspace"),
|
|
25
31
|
projectPath: nonEmptyString,
|
|
26
|
-
discover: z.array(z.enum(["mixins", "access-wideners"])).optional()
|
|
32
|
+
discover: z.array(z.enum(["mixins", "access-wideners", "access-transformers"])).optional()
|
|
27
33
|
}),
|
|
28
34
|
z.object({
|
|
29
35
|
kind: z.literal("mixin"),
|
|
@@ -32,13 +38,18 @@ const subjectSchema = z.discriminatedUnion("kind", [
|
|
|
32
38
|
z.object({
|
|
33
39
|
kind: z.literal("access-widener"),
|
|
34
40
|
input: accessWidenerInputSchema
|
|
41
|
+
}),
|
|
42
|
+
z.object({
|
|
43
|
+
kind: z.literal("access-transformer"),
|
|
44
|
+
input: accessTransformerInputSchema
|
|
35
45
|
})
|
|
36
46
|
]);
|
|
37
47
|
export const validateProjectShape = {
|
|
38
|
-
task: z.enum(["project-summary", "mixin", "access-widener"]),
|
|
48
|
+
task: z.enum(["project-summary", "mixin", "access-widener", "access-transformer"]),
|
|
39
49
|
subject: subjectSchema,
|
|
40
50
|
version: nonEmptyString.optional(),
|
|
41
51
|
mapping: z.enum(["obfuscated", "mojang", "intermediary", "yarn"]).optional(),
|
|
52
|
+
atNamespace: z.enum(["srg", "mojang", "obfuscated"]).optional(),
|
|
42
53
|
sourcePriority: z.enum(["loom-first", "maven-first"]).optional(),
|
|
43
54
|
scope: z.enum(["vanilla", "merged", "loader"]).optional(),
|
|
44
55
|
preferProjectVersion: z.boolean().optional(),
|
|
@@ -77,6 +88,13 @@ export const validateProjectSchema = z.object(validateProjectShape).superRefine(
|
|
|
77
88
|
message: "task=access-widener requires subject.kind=access-widener."
|
|
78
89
|
});
|
|
79
90
|
}
|
|
91
|
+
if (value.task === "access-transformer" && value.subject.kind !== "access-transformer") {
|
|
92
|
+
ctx.addIssue({
|
|
93
|
+
code: z.ZodIssueCode.custom,
|
|
94
|
+
path: ["subject", "kind"],
|
|
95
|
+
message: "task=access-transformer requires subject.kind=access-transformer."
|
|
96
|
+
});
|
|
97
|
+
}
|
|
80
98
|
if (value.configPaths?.length && value.task !== "project-summary") {
|
|
81
99
|
ctx.addIssue({
|
|
82
100
|
code: z.ZodIssueCode.custom,
|
|
@@ -89,33 +107,153 @@ export async function discoverWorkspaceMixins(projectPath, configPaths) {
|
|
|
89
107
|
if (configPaths?.length) {
|
|
90
108
|
return [...configPaths];
|
|
91
109
|
}
|
|
92
|
-
return fastGlob.
|
|
110
|
+
return (await fastGlob.glob(["**/*.mixins.json"], {
|
|
93
111
|
cwd: projectPath,
|
|
94
112
|
absolute: true,
|
|
95
113
|
onlyFiles: true,
|
|
96
114
|
ignore: ["**/.git/**", "**/build/**", "**/out/**", "**/node_modules/**"]
|
|
97
|
-
});
|
|
115
|
+
})).sort((left, right) => left.localeCompare(right));
|
|
98
116
|
}
|
|
99
117
|
export async function discoverWorkspaceAccessWideners(projectPath) {
|
|
100
|
-
const descriptorFiles = fastGlob.
|
|
118
|
+
const descriptorFiles = (await fastGlob.glob(["fabric.mod.json", "quilt.mod.json", "**/fabric.mod.json", "**/quilt.mod.json"], {
|
|
101
119
|
cwd: projectPath,
|
|
102
120
|
absolute: true,
|
|
103
121
|
onlyFiles: true,
|
|
104
122
|
ignore: ["**/.git/**", "**/build/**", "**/out/**", "**/node_modules/**"]
|
|
105
|
-
});
|
|
123
|
+
})).sort((left, right) => left.localeCompare(right));
|
|
106
124
|
const discovered = new Set();
|
|
107
|
-
|
|
125
|
+
const matches = await mapWithConcurrencyLimit(descriptorFiles, WORKSPACE_TEXT_FILE_READ_CONCURRENCY, async (descriptorPath) => {
|
|
108
126
|
try {
|
|
109
127
|
const parsed = JSON.parse(await readFile(descriptorPath, "utf8"));
|
|
110
128
|
const relative = parsed.accessWidener ?? parsed.access_widener;
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
return relative ? [resolve(descriptorPath, "..", relative)] : [];
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
for (const matchList of matches) {
|
|
136
|
+
for (const match of matchList) {
|
|
137
|
+
discovered.add(match);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return [...discovered].sort((left, right) => left.localeCompare(right));
|
|
141
|
+
}
|
|
142
|
+
function addDiscoveredPath(discovered, filePath, relativePath) {
|
|
143
|
+
const trimmed = relativePath?.trim();
|
|
144
|
+
if (!trimmed) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
discovered.add(resolve(filePath, "..", trimmed));
|
|
148
|
+
}
|
|
149
|
+
function collectFileArgumentMatches(content, filePath, discovered, pattern) {
|
|
150
|
+
for (const match of content.matchAll(pattern)) {
|
|
151
|
+
addDiscoveredPath(discovered, filePath, match[2]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function extractNamedDslBlocks(content, blockName) {
|
|
155
|
+
const blocks = [];
|
|
156
|
+
const blockPattern = new RegExp(`${blockName}\\s*\\{`, "g");
|
|
157
|
+
for (const match of content.matchAll(blockPattern)) {
|
|
158
|
+
const blockStart = (match.index ?? -1) + match[0].length;
|
|
159
|
+
if (blockStart < match[0].length) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
let depth = 1;
|
|
163
|
+
for (let index = blockStart; index < content.length; index++) {
|
|
164
|
+
const char = content[index];
|
|
165
|
+
if (char === "{") {
|
|
166
|
+
depth++;
|
|
113
167
|
}
|
|
168
|
+
else if (char === "}") {
|
|
169
|
+
depth--;
|
|
170
|
+
}
|
|
171
|
+
if (depth === 0) {
|
|
172
|
+
blocks.push(content.slice(blockStart, index));
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return blocks;
|
|
178
|
+
}
|
|
179
|
+
function collectTomlAccessTransformerEntries(content, filePath, discovered) {
|
|
180
|
+
const lines = content.split(/\r?\n/);
|
|
181
|
+
let currentBlock = [];
|
|
182
|
+
const flushBlock = () => {
|
|
183
|
+
if (currentBlock.length === 0) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const match of currentBlock.join("\n").matchAll(/^\s*file\s*=\s*(["'])(.+?)\1\s*$/gm)) {
|
|
187
|
+
addDiscoveredPath(discovered, filePath, match[2]);
|
|
188
|
+
}
|
|
189
|
+
currentBlock = [];
|
|
190
|
+
};
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
if (/^\s*\[\[accessTransformers\]\]\s*$/.test(line)) {
|
|
193
|
+
flushBlock();
|
|
194
|
+
currentBlock.push(line);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (currentBlock.length > 0 && /^\s*(?:\[\[.*\]\]|\[[^\[])/.test(line)) {
|
|
198
|
+
flushBlock();
|
|
199
|
+
}
|
|
200
|
+
if (currentBlock.length > 0) {
|
|
201
|
+
currentBlock.push(line);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
flushBlock();
|
|
205
|
+
}
|
|
206
|
+
export async function discoverWorkspaceAccessTransformers(projectPath) {
|
|
207
|
+
const discovered = new Set();
|
|
208
|
+
const textFiles = (await fastGlob.glob([
|
|
209
|
+
"build.gradle",
|
|
210
|
+
"build.gradle.kts",
|
|
211
|
+
"META-INF/mods.toml",
|
|
212
|
+
"META-INF/neoforge.mods.toml",
|
|
213
|
+
"**/build.gradle",
|
|
214
|
+
"**/build.gradle.kts",
|
|
215
|
+
"**/META-INF/mods.toml",
|
|
216
|
+
"**/META-INF/neoforge.mods.toml"
|
|
217
|
+
], {
|
|
218
|
+
cwd: projectPath,
|
|
219
|
+
absolute: true,
|
|
220
|
+
onlyFiles: true,
|
|
221
|
+
ignore: ["**/.git/**", "**/build/**", "**/out/**", "**/node_modules/**"]
|
|
222
|
+
})).sort((left, right) => left.localeCompare(right));
|
|
223
|
+
const discoveredByFile = await mapWithConcurrencyLimit(textFiles, WORKSPACE_TEXT_FILE_READ_CONCURRENCY, async (filePath) => {
|
|
224
|
+
let content;
|
|
225
|
+
try {
|
|
226
|
+
content = await readFile(filePath, "utf8");
|
|
114
227
|
}
|
|
115
228
|
catch {
|
|
116
|
-
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
const perFileDiscovered = new Set();
|
|
232
|
+
collectFileArgumentMatches(content, filePath, perFileDiscovered, /accessTransformer\s*=\s*file\(\s*(["'])(.+?)\1\s*\)/g);
|
|
233
|
+
collectFileArgumentMatches(content, filePath, perFileDiscovered, /accessTransformers\.from\s*\(\s*file\(\s*(["'])(.+?)\1\s*\)\s*\)/g);
|
|
234
|
+
for (const block of extractNamedDslBlocks(content, "accessTransformers")) {
|
|
235
|
+
collectFileArgumentMatches(block, filePath, perFileDiscovered, /file\(\s*(["'])(.+?)\1\s*\)/g);
|
|
236
|
+
}
|
|
237
|
+
collectTomlAccessTransformerEntries(content, filePath, perFileDiscovered);
|
|
238
|
+
return [...perFileDiscovered];
|
|
239
|
+
});
|
|
240
|
+
for (const matchList of discoveredByFile) {
|
|
241
|
+
for (const match of matchList) {
|
|
242
|
+
discovered.add(match);
|
|
117
243
|
}
|
|
118
244
|
}
|
|
245
|
+
for (const fallbackPath of (await fastGlob.glob([
|
|
246
|
+
"**/META-INF/accesstransformer.cfg",
|
|
247
|
+
"**/*_at.cfg",
|
|
248
|
+
"**/accesstransformer*.cfg"
|
|
249
|
+
], {
|
|
250
|
+
cwd: projectPath,
|
|
251
|
+
absolute: true,
|
|
252
|
+
onlyFiles: true,
|
|
253
|
+
ignore: ["**/.git/**", "**/build/**", "**/out/**", "**/node_modules/**"]
|
|
254
|
+
})).sort((left, right) => left.localeCompare(right))) {
|
|
255
|
+
discovered.add(fallbackPath);
|
|
256
|
+
}
|
|
119
257
|
return [...discovered].sort((left, right) => left.localeCompare(right));
|
|
120
258
|
}
|
|
121
259
|
export class ValidateProjectService {
|
|
@@ -202,7 +340,9 @@ export class ValidateProjectService {
|
|
|
202
340
|
content,
|
|
203
341
|
version: input.version,
|
|
204
342
|
mapping: input.mapping,
|
|
205
|
-
sourcePriority: input.sourcePriority
|
|
343
|
+
sourcePriority: input.sourcePriority,
|
|
344
|
+
scope: input.scope,
|
|
345
|
+
preferProjectVersion: input.preferProjectVersion
|
|
206
346
|
});
|
|
207
347
|
return {
|
|
208
348
|
...buildEntryToolResult({
|
|
@@ -242,6 +382,77 @@ export class ValidateProjectService {
|
|
|
242
382
|
warnings: Array.isArray(output.warnings) ? output.warnings : []
|
|
243
383
|
};
|
|
244
384
|
}
|
|
385
|
+
case "access-transformer": {
|
|
386
|
+
if (input.subject.kind !== "access-transformer") {
|
|
387
|
+
throw createError({
|
|
388
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
389
|
+
message: "task=access-transformer requires subject.kind=access-transformer."
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
const content = input.subject.input.mode === "inline"
|
|
393
|
+
? input.subject.input.content
|
|
394
|
+
: await readFile(input.subject.input.path, "utf8");
|
|
395
|
+
if (!this.deps.validateAccessTransformer) {
|
|
396
|
+
throw createError({
|
|
397
|
+
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
398
|
+
message: "Access Transformer validation is not configured."
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
const output = await this.deps.validateAccessTransformer({
|
|
402
|
+
content,
|
|
403
|
+
version: input.version,
|
|
404
|
+
atNamespace: input.atNamespace,
|
|
405
|
+
sourcePriority: input.sourcePriority,
|
|
406
|
+
scope: input.scope,
|
|
407
|
+
preferProjectVersion: input.preferProjectVersion
|
|
408
|
+
});
|
|
409
|
+
const issueEntries = Array.isArray(output.entries)
|
|
410
|
+
? output.entries.filter((entry) => {
|
|
411
|
+
if (!entry || typeof entry !== "object" || !("valid" in entry)) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
return entry.valid !== true;
|
|
415
|
+
})
|
|
416
|
+
: undefined;
|
|
417
|
+
return {
|
|
418
|
+
...buildEntryToolResult({
|
|
419
|
+
task: "access-transformer",
|
|
420
|
+
detail,
|
|
421
|
+
include,
|
|
422
|
+
summary: {
|
|
423
|
+
status: output.valid ? "ok" : "invalid",
|
|
424
|
+
headline: output.valid
|
|
425
|
+
? "Access Transformer is valid."
|
|
426
|
+
: "Access Transformer contains validation issues.",
|
|
427
|
+
subject: createSummarySubject({
|
|
428
|
+
task: "access-transformer",
|
|
429
|
+
kind: input.subject.kind,
|
|
430
|
+
input: input.subject.input,
|
|
431
|
+
version: input.version,
|
|
432
|
+
sourcePriority: input.sourcePriority,
|
|
433
|
+
scope: input.scope,
|
|
434
|
+
atNamespace: input.atNamespace
|
|
435
|
+
}),
|
|
436
|
+
counts: {
|
|
437
|
+
valid: output.valid ? 1 : 0,
|
|
438
|
+
invalid: output.valid ? 0 : 1
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
blocks: {
|
|
442
|
+
project: {
|
|
443
|
+
summary: {
|
|
444
|
+
total: 1,
|
|
445
|
+
valid: output.valid ? 1 : 0,
|
|
446
|
+
invalid: output.valid ? 0 : 1
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
issues: include.includes("issues") || detail !== "summary" ? issueEntries : undefined
|
|
450
|
+
},
|
|
451
|
+
alwaysBlocks: ["project"]
|
|
452
|
+
}),
|
|
453
|
+
warnings: Array.isArray(output.warnings) ? output.warnings : []
|
|
454
|
+
};
|
|
455
|
+
}
|
|
245
456
|
case "project-summary": {
|
|
246
457
|
if (input.subject.kind !== "workspace") {
|
|
247
458
|
throw createError({
|
|
@@ -269,10 +480,12 @@ export class ValidateProjectService {
|
|
|
269
480
|
tool: "validate-project",
|
|
270
481
|
params: {
|
|
271
482
|
task: "project-summary",
|
|
272
|
-
version: "1.21.10",
|
|
273
483
|
subject: input.subject
|
|
274
484
|
}
|
|
275
485
|
}
|
|
486
|
+
],
|
|
487
|
+
notes: [
|
|
488
|
+
"Pass version explicitly, or retry with preferProjectVersion=true when gradle.properties declares the Minecraft version."
|
|
276
489
|
]
|
|
277
490
|
},
|
|
278
491
|
blocks: {
|
|
@@ -285,15 +498,98 @@ export class ValidateProjectService {
|
|
|
285
498
|
};
|
|
286
499
|
}
|
|
287
500
|
const projectPath = input.subject.projectPath;
|
|
501
|
+
const detectedProjectVersion = input.preferProjectVersion
|
|
502
|
+
? await this.deps.detectProjectMinecraftVersion?.(projectPath)
|
|
503
|
+
: undefined;
|
|
504
|
+
const resolvedVersion = detectedProjectVersion ?? input.version;
|
|
288
505
|
const discover = input.subject.discover ?? ["mixins", "access-wideners"];
|
|
289
|
-
const [mixinConfigs, accessWideners] = await Promise.all([
|
|
506
|
+
const [mixinConfigs, accessWideners, accessTransformers] = await Promise.all([
|
|
290
507
|
discover.includes("mixins")
|
|
291
508
|
? this.deps.discoverMixins(projectPath, input.configPaths)
|
|
292
509
|
: Promise.resolve([]),
|
|
293
510
|
discover.includes("access-wideners")
|
|
294
511
|
? this.deps.discoverAccessWideners(projectPath)
|
|
512
|
+
: Promise.resolve([]),
|
|
513
|
+
discover.includes("access-transformers")
|
|
514
|
+
? this.deps.discoverAccessTransformers?.(projectPath) ?? Promise.resolve([])
|
|
295
515
|
: Promise.resolve([])
|
|
296
516
|
]);
|
|
517
|
+
if (!resolvedVersion && (mixinConfigs.length > 0 || accessWideners.length > 0 || accessTransformers.length > 0)) {
|
|
518
|
+
return {
|
|
519
|
+
...buildEntryToolResult({
|
|
520
|
+
task: "project-summary",
|
|
521
|
+
detail,
|
|
522
|
+
include,
|
|
523
|
+
summary: {
|
|
524
|
+
status: "blocked",
|
|
525
|
+
headline: "Could not resolve Minecraft version for discovered workspace validators.",
|
|
526
|
+
subject: createSummarySubject({
|
|
527
|
+
task: "project-summary",
|
|
528
|
+
kind: input.subject.kind,
|
|
529
|
+
projectPath,
|
|
530
|
+
discover: input.subject.discover,
|
|
531
|
+
mapping: input.mapping,
|
|
532
|
+
sourcePriority: input.sourcePriority,
|
|
533
|
+
scope: input.scope
|
|
534
|
+
}),
|
|
535
|
+
nextActions: [
|
|
536
|
+
{
|
|
537
|
+
tool: "validate-project",
|
|
538
|
+
params: {
|
|
539
|
+
task: "project-summary",
|
|
540
|
+
subject: input.subject
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
notes: [
|
|
545
|
+
"Pass version explicitly, or make sure gradle.properties declares the Minecraft version before using preferProjectVersion=true."
|
|
546
|
+
]
|
|
547
|
+
},
|
|
548
|
+
blocks: {
|
|
549
|
+
workspace: {
|
|
550
|
+
projectPath
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}),
|
|
554
|
+
warnings: [
|
|
555
|
+
"Could not resolve Minecraft version from gradle.properties for discovered workspace validators."
|
|
556
|
+
]
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
if (!resolvedVersion) {
|
|
560
|
+
return {
|
|
561
|
+
...buildEntryToolResult({
|
|
562
|
+
task: "project-summary",
|
|
563
|
+
detail,
|
|
564
|
+
include,
|
|
565
|
+
summary: {
|
|
566
|
+
status: "ok",
|
|
567
|
+
headline: `Validated ${mixinConfigs.length} mixin config(s), ${accessWideners.length} access widener(s), and ${accessTransformers.length} access transformer(s).`,
|
|
568
|
+
subject: createSummarySubject({
|
|
569
|
+
task: "project-summary",
|
|
570
|
+
kind: input.subject.kind,
|
|
571
|
+
projectPath,
|
|
572
|
+
discover: input.subject.discover,
|
|
573
|
+
mapping: input.mapping,
|
|
574
|
+
sourcePriority: input.sourcePriority,
|
|
575
|
+
scope: input.scope
|
|
576
|
+
}),
|
|
577
|
+
counts: {
|
|
578
|
+
valid: 0,
|
|
579
|
+
partial: 0,
|
|
580
|
+
invalid: 0
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
blocks: {
|
|
584
|
+
workspace: {
|
|
585
|
+
projectPath
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}),
|
|
589
|
+
warnings: []
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
const validationVersion = resolvedVersion;
|
|
297
593
|
const warnings = [];
|
|
298
594
|
let validMixins = 0;
|
|
299
595
|
let partialMixins = 0;
|
|
@@ -305,11 +601,12 @@ export class ValidateProjectService {
|
|
|
305
601
|
mode: "config",
|
|
306
602
|
configPaths: [configPath]
|
|
307
603
|
},
|
|
308
|
-
version:
|
|
604
|
+
version: validationVersion,
|
|
309
605
|
mapping: input.mapping,
|
|
310
606
|
sourcePriority: input.sourcePriority,
|
|
311
607
|
scope: input.scope,
|
|
312
|
-
|
|
608
|
+
projectPath,
|
|
609
|
+
preferProjectVersion: false,
|
|
313
610
|
preferProjectMapping: input.preferProjectMapping,
|
|
314
611
|
sourceRoots: input.sourceRoots,
|
|
315
612
|
minSeverity: input.minSeverity,
|
|
@@ -341,9 +638,12 @@ export class ValidateProjectService {
|
|
|
341
638
|
try {
|
|
342
639
|
const output = await this.deps.validateAccessWidener({
|
|
343
640
|
content: await readFile(awPath, "utf8"),
|
|
344
|
-
version:
|
|
641
|
+
version: validationVersion,
|
|
345
642
|
mapping: input.mapping,
|
|
346
|
-
sourcePriority: input.sourcePriority
|
|
643
|
+
sourcePriority: input.sourcePriority,
|
|
644
|
+
projectPath,
|
|
645
|
+
scope: input.scope,
|
|
646
|
+
preferProjectVersion: input.preferProjectVersion
|
|
347
647
|
});
|
|
348
648
|
if (output.valid) {
|
|
349
649
|
validAw += 1;
|
|
@@ -362,7 +662,43 @@ export class ValidateProjectService {
|
|
|
362
662
|
}
|
|
363
663
|
}
|
|
364
664
|
}
|
|
365
|
-
|
|
665
|
+
let validAt = 0;
|
|
666
|
+
let invalidAt = 0;
|
|
667
|
+
for (const atPath of accessTransformers) {
|
|
668
|
+
try {
|
|
669
|
+
if (!this.deps.validateAccessTransformer) {
|
|
670
|
+
throw createError({
|
|
671
|
+
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
672
|
+
message: "Access Transformer validation is not configured."
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
const output = await this.deps.validateAccessTransformer({
|
|
676
|
+
content: await readFile(atPath, "utf8"),
|
|
677
|
+
version: validationVersion,
|
|
678
|
+
atNamespace: input.atNamespace,
|
|
679
|
+
sourcePriority: input.sourcePriority,
|
|
680
|
+
projectPath,
|
|
681
|
+
scope: input.scope,
|
|
682
|
+
preferProjectVersion: input.preferProjectVersion
|
|
683
|
+
});
|
|
684
|
+
if (output.valid) {
|
|
685
|
+
validAt += 1;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
invalidAt += 1;
|
|
689
|
+
}
|
|
690
|
+
if (Array.isArray(output.warnings)) {
|
|
691
|
+
warnings.push(...output.warnings);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
invalidAt += 1;
|
|
696
|
+
if (error instanceof Error) {
|
|
697
|
+
warnings.push(error.message);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const invalidCount = invalidMixins + invalidAw + invalidAt;
|
|
366
702
|
const partialCount = partialMixins;
|
|
367
703
|
const status = invalidCount > 0 ? "invalid" : partialCount > 0 ? "partial" : "ok";
|
|
368
704
|
return {
|
|
@@ -372,19 +708,19 @@ export class ValidateProjectService {
|
|
|
372
708
|
include,
|
|
373
709
|
summary: {
|
|
374
710
|
status,
|
|
375
|
-
headline: `Validated ${mixinConfigs.length} mixin config(s)
|
|
711
|
+
headline: `Validated ${mixinConfigs.length} mixin config(s), ${accessWideners.length} access widener(s), and ${accessTransformers.length} access transformer(s).`,
|
|
376
712
|
subject: createSummarySubject({
|
|
377
713
|
task: "project-summary",
|
|
378
714
|
kind: input.subject.kind,
|
|
379
715
|
projectPath,
|
|
380
716
|
discover: input.subject.discover,
|
|
381
|
-
version:
|
|
717
|
+
version: resolvedVersion,
|
|
382
718
|
mapping: input.mapping,
|
|
383
719
|
sourcePriority: input.sourcePriority,
|
|
384
720
|
scope: input.scope
|
|
385
721
|
}),
|
|
386
722
|
counts: {
|
|
387
|
-
valid: validMixins + validAw,
|
|
723
|
+
valid: validMixins + validAw + validAt,
|
|
388
724
|
partial: partialCount,
|
|
389
725
|
invalid: invalidCount
|
|
390
726
|
}
|
|
@@ -392,7 +728,7 @@ export class ValidateProjectService {
|
|
|
392
728
|
blocks: {
|
|
393
729
|
project: {
|
|
394
730
|
summary: {
|
|
395
|
-
valid: validMixins + validAw,
|
|
731
|
+
valid: validMixins + validAw + validAt,
|
|
396
732
|
partial: partialCount,
|
|
397
733
|
invalid: invalidCount
|
|
398
734
|
}
|
|
@@ -400,7 +736,8 @@ export class ValidateProjectService {
|
|
|
400
736
|
workspace: {
|
|
401
737
|
projectPath,
|
|
402
738
|
mixinConfigs,
|
|
403
|
-
accessWideners
|
|
739
|
+
accessWideners,
|
|
740
|
+
accessTransformers
|
|
404
741
|
}
|
|
405
742
|
},
|
|
406
743
|
alwaysBlocks: ["project"]
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function normalizeOptionalProjectPath(projectPath: string | undefined): string | undefined;
|
|
2
|
+
export declare function resolveGradleUserHomePath(): string;
|
|
3
|
+
export declare function buildVersionSourceSearchRoots(projectPath: string | undefined): string[];
|
|
4
|
+
export declare function buildLoaderRuntimeSearchRoots(projectPath: string | undefined): string[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { dirname, isAbsolute, resolve as resolvePath } from "node:path";
|
|
3
|
+
import { normalizePathForHost } from "./path-converter.js";
|
|
4
|
+
export function normalizeOptionalProjectPath(projectPath) {
|
|
5
|
+
if (!projectPath) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
const trimmed = projectPath.trim();
|
|
9
|
+
if (!trimmed) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const normalized = normalizePathForHost(trimmed, undefined, "projectPath");
|
|
13
|
+
return isAbsolute(normalized) ? normalized : resolvePath(process.cwd(), normalized);
|
|
14
|
+
}
|
|
15
|
+
export function resolveGradleUserHomePath() {
|
|
16
|
+
const configured = process.env.GRADLE_USER_HOME?.trim();
|
|
17
|
+
if (!configured) {
|
|
18
|
+
return resolvePath(homedir(), ".gradle");
|
|
19
|
+
}
|
|
20
|
+
const normalized = normalizePathForHost(configured, undefined, "GRADLE_USER_HOME");
|
|
21
|
+
return isAbsolute(normalized) ? normalized : resolvePath(process.cwd(), normalized);
|
|
22
|
+
}
|
|
23
|
+
export function buildVersionSourceSearchRoots(projectPath) {
|
|
24
|
+
const roots = new Set();
|
|
25
|
+
if (projectPath) {
|
|
26
|
+
roots.add(resolvePath(projectPath, ".gradle", "loom-cache"));
|
|
27
|
+
roots.add(resolvePath(projectPath, ".gradle-user", "caches", "fabric-loom"));
|
|
28
|
+
roots.add(resolvePath(projectPath, ".gradle", "caches", "fabric-loom"));
|
|
29
|
+
const projectParent = dirname(projectPath);
|
|
30
|
+
roots.add(resolvePath(projectParent, ".gradle-user-home", "loom-cache"));
|
|
31
|
+
roots.add(resolvePath(projectParent, ".gradle-user-home", "caches", "fabric-loom"));
|
|
32
|
+
}
|
|
33
|
+
const homeGradle = resolveGradleUserHomePath();
|
|
34
|
+
roots.add(resolvePath(homeGradle, "loom-cache"));
|
|
35
|
+
roots.add(resolvePath(homeGradle, "caches", "fabric-loom"));
|
|
36
|
+
return [...roots];
|
|
37
|
+
}
|
|
38
|
+
export function buildLoaderRuntimeSearchRoots(projectPath) {
|
|
39
|
+
const roots = new Set();
|
|
40
|
+
if (projectPath) {
|
|
41
|
+
roots.add(resolvePath(projectPath, "build"));
|
|
42
|
+
roots.add(resolvePath(projectPath, ".gradle"));
|
|
43
|
+
roots.add(resolvePath(projectPath, ".gradle", "forge-userdev"));
|
|
44
|
+
roots.add(resolvePath(projectPath, ".gradle", "neogradle"));
|
|
45
|
+
roots.add(resolvePath(projectPath, ".gradle", "caches", "forge_gradle"));
|
|
46
|
+
roots.add(resolvePath(projectPath, ".gradle", "caches", "neogradle"));
|
|
47
|
+
roots.add(resolvePath(projectPath, ".gradle", "caches", "neoformruntime"));
|
|
48
|
+
roots.add(resolvePath(projectPath, ".gradle", "caches", "moddev"));
|
|
49
|
+
}
|
|
50
|
+
const homeGradle = resolveGradleUserHomePath();
|
|
51
|
+
roots.add(resolvePath(homeGradle, "caches", "forge_gradle"));
|
|
52
|
+
roots.add(resolvePath(homeGradle, "caches", "neogradle"));
|
|
53
|
+
roots.add(resolvePath(homeGradle, "caches", "neoformruntime"));
|
|
54
|
+
roots.add(resolvePath(homeGradle, "caches", "moddev"));
|
|
55
|
+
return [...roots];
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=gradle-paths.js.map
|