From df0d46e63d8e79a970252492eb81ca753467d3dc Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Mon, 26 Jan 2026 03:48:18 +0200 Subject: [PATCH] Properly parsed enclosed namespace types like INpcEvent.UpdateEvent and added them to JSTypeRegistry.types --- .../interpreter/js_parser/JSTypeRegistry.java | 9 +++ .../js_parser/TypeScriptDefinitionParser.java | 60 +++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java index ab6b8f45b..0d2f8e596 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java @@ -333,6 +333,15 @@ public class JSTypeRegistry { if (existingType != null) { mergeType(existingType, type, getCurrentSource()); + + // Preserve an addressable key for the incoming declared name. + // Some .d.ts parsing paths can encounter the same Java type via multiple textual names + // (e.g., due to nested namespaces). Even if we merge by @javaFqn, callers still expect + // lookups like "INpcEvent.DamagedEvent" to work. + if (!types.containsKey(fullName)) { + types.put(fullName, existingType); + typeOrigins.put(fullName, getCurrentSource()); + } return; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java index 70f1e2d7b..9d8bc367c 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java @@ -211,8 +211,20 @@ public class TypeScriptDefinitionParser { * Parse interface and class definitions from content. */ private void parseInterfaceFile(String content, String parentNamespace, String packageName) { + // IMPORTANT: + // Many generated .d.ts files declare nested exported interfaces inside `namespace X { ... }` blocks. + // If we scan `content` directly for `export interface` / `export class`, we'll accidentally treat + // those nested exports as top-level for the current `parentNamespace`. + // That pollutes the registry with incorrectly-scoped type names (e.g. "DamagedEvent" instead of + // "INpcEvent.DamagedEvent"), and later registry merges (by @javaFqn) can prevent the correctly + // namespaced key from ever being registered. + // + // To avoid this, we strip namespace blocks out of the scan input and then parse namespaces + // explicitly via the NAMESPACE_PATTERN recursion further below. + String scanContent = stripNamespaceBlocks(content); + // Find exported interfaces - Matcher interfaceMatcher = INTERFACE_PATTERN.matcher(content); + Matcher interfaceMatcher = INTERFACE_PATTERN.matcher(scanContent); while (interfaceMatcher.find()) { String interfaceName = interfaceMatcher.group(1); String typeParamsStr = interfaceMatcher.group(2); // e.g., "T extends EntityPlayerMP /* net.minecraft.entity.player.EntityPlayerMP */" @@ -267,7 +279,7 @@ public class TypeScriptDefinitionParser { } // Find nested interfaces (without export keyword) - Matcher nestedInterfaceMatcher = NESTED_INTERFACE_PATTERN.matcher(content); + Matcher nestedInterfaceMatcher = NESTED_INTERFACE_PATTERN.matcher(scanContent); while (nestedInterfaceMatcher.find()) { String interfaceName = nestedInterfaceMatcher.group(1); String typeParamsStr = nestedInterfaceMatcher.group(2); @@ -307,7 +319,7 @@ public class TypeScriptDefinitionParser { } // Find classes (same logic as interfaces) - Matcher classMatcher = CLASS_PATTERN.matcher(content); + Matcher classMatcher = CLASS_PATTERN.matcher(scanContent); while (classMatcher.find()) { String className = classMatcher.group(1); String typeParamsStr = classMatcher.group(2); @@ -361,7 +373,7 @@ public class TypeScriptDefinitionParser { // Namespaces contain exported types, so use parseInterfaceFile String fullNamespace = parentNamespace != null ? parentNamespace + "." + namespaceName : namespaceName; - parseInterfaceFile(body, fullNamespace, packageName); + parseInterfaceFile(body, fullNamespace, packageName); // Don't call parseNestedTypes here - namespace members are all exported // and will be caught by parseInterfaceFile's INTERFACE_PATTERN } @@ -372,6 +384,46 @@ public class TypeScriptDefinitionParser { parseTypeAliases(content, null); } } + + /** + * Strip namespace blocks from a file-level scan to avoid incorrectly capturing nested exports. + * + * This replaces the entire `namespace X { ... }` span (including braces) with whitespace so that + * match indices remain stable when we later slice `content` for bodies. + */ + private String stripNamespaceBlocks(String content) { + if (content == null || content.isEmpty()) { + return content; + } + + Matcher m = NAMESPACE_PATTERN.matcher(content); + if (!m.find()) { + return content; + } + + char[] chars = content.toCharArray(); + + int searchFrom = 0; + m.reset(); + while (m.find(searchFrom)) { + int start = m.start(); + int bodyStart = m.end(); + int bodyEnd = findMatchingBrace(content, bodyStart - 1); + if (bodyEnd <= start) { + searchFrom = Math.max(m.end(), searchFrom + 1); + continue; + } + + int endExclusive = Math.min(chars.length, bodyEnd + 1); + for (int i = start; i < endExclusive; i++) { + chars[i] = ' '; + } + + searchFrom = endExclusive; + } + + return new String(chars); + } /** * Parse type parameters from a string like "T extends EntityPlayerMP /* net.minecraft.entity.player.EntityPlayerMP *`/".