sonance-brand-mcp 1.3.30 → 1.3.31

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.
@@ -746,17 +746,23 @@ function gatherAllImports(
746
746
  maxDepth: number = 4
747
747
  ): { path: string; content: string }[] {
748
748
  // Prevent infinite loops and limit total files
749
- if (visited.has(filePath) || visited.size > 50) return [];
749
+ if (visited.has(filePath) || visited.size > 50) {
750
+ return [];
751
+ }
750
752
  visited.add(filePath);
751
753
 
752
754
  const results: { path: string; content: string }[] = [];
753
755
  const fullPath = path.join(projectRoot, filePath);
754
756
 
755
- if (!fs.existsSync(fullPath)) return results;
757
+ if (!fs.existsSync(fullPath)) {
758
+ debugLog("[apply] gatherAllImports: file not found", { filePath, fullPath });
759
+ return results;
760
+ }
756
761
 
757
762
  try {
758
763
  const content = fs.readFileSync(fullPath, "utf-8");
759
764
  results.push({ path: filePath, content });
765
+ debugLog("[apply] gatherAllImports: added file", { filePath, contentLength: content.length });
760
766
 
761
767
  // Continue recursing if we haven't hit max depth
762
768
  if (maxDepth > 0) {
@@ -769,8 +775,8 @@ function gatherAllImports(
769
775
  }
770
776
  }
771
777
  }
772
- } catch {
773
- // Skip files that can't be read
778
+ } catch (e) {
779
+ debugLog("[apply] gatherAllImports: error reading file", { filePath, error: String(e) });
774
780
  }
775
781
 
776
782
  return results;
@@ -1081,7 +1087,8 @@ function findDynamicRoute(cleanRoute: string, projectRoot: string): string | nul
1081
1087
  }
1082
1088
 
1083
1089
  function extractImports(content: string): string[] {
1084
- const importRegex = /import\s+.*?\s+from\s+["'](@\/components\/[^"']+|\.\.?\/[^"']+)["']/g;
1090
+ // Match ALL @/ imports (not just @/components) and relative imports
1091
+ const importRegex = /import\s+.*?\s+from\s+["'](@\/[^"']+|\.\.?\/[^"']+)["']/g;
1085
1092
  const imports: string[] = [];
1086
1093
  let match;
1087
1094
 
@@ -1089,32 +1096,134 @@ function extractImports(content: string): string[] {
1089
1096
  imports.push(match[1]);
1090
1097
  }
1091
1098
 
1099
+ debugLog("[apply] Extracted imports", { count: imports.length, imports: imports.slice(0, 10) });
1092
1100
  return imports;
1093
1101
  }
1094
1102
 
1095
- function resolveImportPath(
1096
- importPath: string,
1097
- fromFile: string,
1098
- projectRoot: string
1099
- ): string | null {
1100
- if (importPath.startsWith("@/")) {
1101
- const resolved = importPath.replace("@/", "src/");
1102
- const withExt = resolved.endsWith(".tsx") ? resolved : `${resolved}.tsx`;
1103
+ // Cache for tsconfig path aliases
1104
+ let cachedPathAliases: Map<string, string> | null = null;
1105
+ let cachedProjectRoot: string | null = null;
1106
+
1107
+ /**
1108
+ * Read and parse tsconfig.json to get path aliases
1109
+ */
1110
+ function getPathAliases(projectRoot: string): Map<string, string> {
1111
+ // Return cached if same project
1112
+ if (cachedPathAliases && cachedProjectRoot === projectRoot) {
1113
+ return cachedPathAliases;
1114
+ }
1115
+
1116
+ const aliases = new Map<string, string>();
1117
+
1118
+ // Try to read tsconfig.json
1119
+ const tsconfigPath = path.join(projectRoot, "tsconfig.json");
1120
+ if (fs.existsSync(tsconfigPath)) {
1121
+ try {
1122
+ const content = fs.readFileSync(tsconfigPath, "utf-8");
1123
+ // Remove comments (tsconfig allows them)
1124
+ const cleanContent = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
1125
+ const tsconfig = JSON.parse(cleanContent);
1126
+
1127
+ const paths = tsconfig.compilerOptions?.paths || {};
1128
+ const baseUrl = tsconfig.compilerOptions?.baseUrl || ".";
1129
+
1130
+ // Parse path mappings
1131
+ for (const [alias, targets] of Object.entries(paths)) {
1132
+ if (Array.isArray(targets) && targets.length > 0) {
1133
+ // Convert @/* to @/ and src/* to src/
1134
+ const cleanAlias = alias.replace("/*", "/");
1135
+ const cleanTarget = (targets[0] as string).replace("/*", "/").replace("./", "");
1136
+ aliases.set(cleanAlias, cleanTarget);
1137
+ }
1138
+ }
1139
+
1140
+ debugLog("[apply] Loaded tsconfig path aliases", { aliases: Object.fromEntries(aliases) });
1141
+ } catch (e) {
1142
+ debugLog("[apply] Failed to parse tsconfig.json", { error: String(e) });
1143
+ }
1144
+ }
1145
+
1146
+ // If no @/ alias found, try common defaults
1147
+ if (!aliases.has("@/")) {
1148
+ // Check if src/ exists
1149
+ if (fs.existsSync(path.join(projectRoot, "src"))) {
1150
+ aliases.set("@/", "src/");
1151
+ } else if (fs.existsSync(path.join(projectRoot, "app"))) {
1152
+ // Next.js App Router without src/
1153
+ aliases.set("@/", "");
1154
+ } else {
1155
+ aliases.set("@/", "");
1156
+ }
1157
+ debugLog("[apply] Using default @/ alias", { alias: aliases.get("@/") });
1158
+ }
1159
+
1160
+ cachedPathAliases = aliases;
1161
+ cachedProjectRoot = projectRoot;
1162
+ return aliases;
1163
+ }
1164
+
1165
+ /**
1166
+ * Try multiple file extensions for an import
1167
+ */
1168
+ function tryFileExtensions(basePath: string, projectRoot: string): string | null {
1169
+ const extensions = [".tsx", ".ts", ".jsx", ".js"];
1170
+
1171
+ // First try exact path (if already has extension)
1172
+ if (extensions.some(ext => basePath.endsWith(ext))) {
1173
+ if (fs.existsSync(path.join(projectRoot, basePath))) {
1174
+ return basePath;
1175
+ }
1176
+ }
1177
+
1178
+ // Try each extension
1179
+ for (const ext of extensions) {
1180
+ const withExt = `${basePath}${ext}`;
1103
1181
  if (fs.existsSync(path.join(projectRoot, withExt))) {
1104
1182
  return withExt;
1105
1183
  }
1106
- const indexPath = `${resolved}/index.tsx`;
1184
+ }
1185
+
1186
+ // Try as directory with index file
1187
+ for (const ext of extensions) {
1188
+ const indexPath = `${basePath}/index${ext}`;
1107
1189
  if (fs.existsSync(path.join(projectRoot, indexPath))) {
1108
1190
  return indexPath;
1109
1191
  }
1110
- return withExt;
1192
+ }
1193
+
1194
+ return null;
1195
+ }
1196
+
1197
+ function resolveImportPath(
1198
+ importPath: string,
1199
+ fromFile: string,
1200
+ projectRoot: string
1201
+ ): string | null {
1202
+ const aliases = getPathAliases(projectRoot);
1203
+
1204
+ // Handle aliased imports (e.g., @/, ~/, etc.)
1205
+ for (const [alias, target] of aliases) {
1206
+ if (importPath.startsWith(alias)) {
1207
+ const resolved = importPath.replace(alias, target);
1208
+ const result = tryFileExtensions(resolved, projectRoot);
1209
+ if (result) {
1210
+ debugLog("[apply] Resolved alias import", { importPath, resolved: result });
1211
+ return result;
1212
+ }
1213
+ // Return best guess even if file not found
1214
+ return `${resolved}.tsx`;
1215
+ }
1111
1216
  }
1112
1217
 
1218
+ // Handle relative imports
1113
1219
  if (importPath.startsWith(".")) {
1114
1220
  const dir = path.dirname(fromFile);
1115
1221
  const resolved = path.join(dir, importPath);
1116
- const withExt = resolved.endsWith(".tsx") ? resolved : `${resolved}.tsx`;
1117
- return withExt;
1222
+ const result = tryFileExtensions(resolved, projectRoot);
1223
+ if (result) {
1224
+ return result;
1225
+ }
1226
+ return `${resolved}.tsx`;
1118
1227
  }
1119
1228
 
1120
1229
  return null;
@@ -614,17 +614,23 @@ function gatherAllImports(
614
614
  maxDepth: number = 4
615
615
  ): { path: string; content: string }[] {
616
616
  // Prevent infinite loops and limit total files
617
- if (visited.has(filePath) || visited.size > 50) return [];
617
+ if (visited.has(filePath) || visited.size > 50) {
618
+ return [];
619
+ }
618
620
  visited.add(filePath);
619
621
 
620
622
  const results: { path: string; content: string }[] = [];
621
623
  const fullPath = path.join(projectRoot, filePath);
622
624
 
623
- if (!fs.existsSync(fullPath)) return results;
625
+ if (!fs.existsSync(fullPath)) {
626
+ debugLog("[edit] gatherAllImports: file not found", { filePath, fullPath });
627
+ return results;
628
+ }
624
629
 
625
630
  try {
626
631
  const content = fs.readFileSync(fullPath, "utf-8");
627
632
  results.push({ path: filePath, content });
633
+ debugLog("[edit] gatherAllImports: added file", { filePath, contentLength: content.length });
628
634
 
629
635
  // Continue recursing if we haven't hit max depth
630
636
  if (maxDepth > 0) {
@@ -637,8 +643,8 @@ function gatherAllImports(
637
643
  }
638
644
  }
639
645
  }
640
- } catch {
641
- // Skip files that can't be read
646
+ } catch (e) {
647
+ debugLog("[edit] gatherAllImports: error reading file", { filePath, error: String(e) });
642
648
  }
643
649
 
644
650
  return results;
@@ -972,7 +978,8 @@ function findDynamicRoute(cleanRoute: string, projectRoot: string): string | nul
972
978
  * Extract import paths from file content
973
979
  */
974
980
  function extractImports(content: string): string[] {
975
- const importRegex = /import\s+.*?\s+from\s+["'](@\/components\/[^"']+|\.\.?\/[^"']+)["']/g;
981
+ // Match ALL @/ imports (not just @/components) and relative imports
982
+ const importRegex = /import\s+.*?\s+from\s+["'](@\/[^"']+|\.\.?\/[^"']+)["']/g;
976
983
  const imports: string[] = [];
977
984
  let match;
978
985
 
@@ -980,9 +987,104 @@ function extractImports(content: string): string[] {
980
987
  imports.push(match[1]);
981
988
  }
982
989
 
990
+ debugLog("[edit] Extracted imports", { count: imports.length, imports: imports.slice(0, 10) });
983
991
  return imports;
984
992
  }
985
993
 
994
+ // Cache for tsconfig path aliases
995
+ let cachedPathAliases: Map<string, string> | null = null;
996
+ let cachedProjectRoot: string | null = null;
997
+
998
+ /**
999
+ * Read and parse tsconfig.json to get path aliases
1000
+ */
1001
+ function getPathAliases(projectRoot: string): Map<string, string> {
1002
+ // Return cached if same project
1003
+ if (cachedPathAliases && cachedProjectRoot === projectRoot) {
1004
+ return cachedPathAliases;
1005
+ }
1006
+
1007
+ const aliases = new Map<string, string>();
1008
+
1009
+ // Try to read tsconfig.json
1010
+ const tsconfigPath = path.join(projectRoot, "tsconfig.json");
1011
+ if (fs.existsSync(tsconfigPath)) {
1012
+ try {
1013
+ const content = fs.readFileSync(tsconfigPath, "utf-8");
1014
+ // Remove comments (tsconfig allows them)
1015
+ const cleanContent = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
1016
+ const tsconfig = JSON.parse(cleanContent);
1017
+
1018
+ const paths = tsconfig.compilerOptions?.paths || {};
1019
+ const baseUrl = tsconfig.compilerOptions?.baseUrl || ".";
1020
+
1021
+ // Parse path mappings
1022
+ for (const [alias, targets] of Object.entries(paths)) {
1023
+ if (Array.isArray(targets) && targets.length > 0) {
1024
+ // Convert @/* to @/ and src/* to src/
1025
+ const cleanAlias = alias.replace("/*", "/");
1026
+ const cleanTarget = (targets[0] as string).replace("/*", "/").replace("./", "");
1027
+ aliases.set(cleanAlias, cleanTarget);
1028
+ }
1029
+ }
1030
+
1031
+ debugLog("[edit] Loaded tsconfig path aliases", { aliases: Object.fromEntries(aliases) });
1032
+ } catch (e) {
1033
+ debugLog("[edit] Failed to parse tsconfig.json", { error: String(e) });
1034
+ }
1035
+ }
1036
+
1037
+ // If no @/ alias found, try common defaults
1038
+ if (!aliases.has("@/")) {
1039
+ // Check if src/ exists
1040
+ if (fs.existsSync(path.join(projectRoot, "src"))) {
1041
+ aliases.set("@/", "src/");
1042
+ } else if (fs.existsSync(path.join(projectRoot, "app"))) {
1043
+ // Next.js App Router without src/
1044
+ aliases.set("@/", "");
1045
+ } else {
1046
+ aliases.set("@/", "");
1047
+ }
1048
+ debugLog("[edit] Using default @/ alias", { alias: aliases.get("@/") });
1049
+ }
1050
+
1051
+ cachedPathAliases = aliases;
1052
+ cachedProjectRoot = projectRoot;
1053
+ return aliases;
1054
+ }
1055
+
1056
+ /**
1057
+ * Try multiple file extensions for an import
1058
+ */
1059
+ function tryFileExtensions(basePath: string, projectRoot: string): string | null {
1060
+ const extensions = [".tsx", ".ts", ".jsx", ".js"];
1061
+
1062
+ // First try exact path (if already has extension)
1063
+ if (extensions.some(ext => basePath.endsWith(ext))) {
1064
+ if (fs.existsSync(path.join(projectRoot, basePath))) {
1065
+ return basePath;
1066
+ }
1067
+ }
1068
+
1069
+ // Try each extension
1070
+ for (const ext of extensions) {
1071
+ const withExt = `${basePath}${ext}`;
1072
+ if (fs.existsSync(path.join(projectRoot, withExt))) {
1073
+ return withExt;
1074
+ }
1075
+ }
1076
+
1077
+ // Try as directory with index file
1078
+ for (const ext of extensions) {
1079
+ const indexPath = `${basePath}/index${ext}`;
1080
+ if (fs.existsSync(path.join(projectRoot, indexPath))) {
1081
+ return indexPath;
1082
+ }
1083
+ }
1084
+
1085
+ return null;
1086
+ }
1087
+
986
1088
  /**
987
1089
  * Resolve an import path to a file system path
988
1090
  */
@@ -991,28 +1093,31 @@ function resolveImportPath(
991
1093
  fromFile: string,
992
1094
  projectRoot: string
993
1095
  ): string | null {
994
- // Handle @ alias (maps to src/)
995
- if (importPath.startsWith("@/")) {
996
- const resolved = importPath.replace("@/", "src/");
997
- // Try with .tsx extension
998
- const withExt = resolved.endsWith(".tsx") ? resolved : `${resolved}.tsx`;
999
- if (fs.existsSync(path.join(projectRoot, withExt))) {
1000
- return withExt;
1001
- }
1002
- // Try as directory with index
1003
- const indexPath = `${resolved}/index.tsx`;
1004
- if (fs.existsSync(path.join(projectRoot, indexPath))) {
1005
- return indexPath;
1096
+ const aliases = getPathAliases(projectRoot);
1097
+
1098
+ // Handle aliased imports (e.g., @/, ~/, etc.)
1099
+ for (const [alias, target] of aliases) {
1100
+ if (importPath.startsWith(alias)) {
1101
+ const resolved = importPath.replace(alias, target);
1102
+ const result = tryFileExtensions(resolved, projectRoot);
1103
+ if (result) {
1104
+ debugLog("[edit] Resolved alias import", { importPath, resolved: result });
1105
+ return result;
1106
+ }
1107
+ // Return best guess even if file not found
1108
+ return `${resolved}.tsx`;
1006
1109
  }
1007
- return withExt; // Return even if not found for context
1008
1110
  }
1009
1111
 
1010
1112
  // Handle relative imports
1011
1113
  if (importPath.startsWith(".")) {
1012
1114
  const dir = path.dirname(fromFile);
1013
1115
  const resolved = path.join(dir, importPath);
1014
- const withExt = resolved.endsWith(".tsx") ? resolved : `${resolved}.tsx`;
1015
- return withExt;
1116
+ const result = tryFileExtensions(resolved, projectRoot);
1117
+ if (result) {
1118
+ return result;
1119
+ }
1120
+ return `${resolved}.tsx`;
1016
1121
  }
1017
1122
 
1018
1123
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonance-brand-mcp",
3
- "version": "1.3.30",
3
+ "version": "1.3.31",
4
4
  "description": "MCP Server for Sonance Brand Guidelines and Component Library - gives Claude instant access to brand colors, typography, and UI components.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",