ucn 3.0.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.

Potentially problematic release.


This version of ucn might be problematic. Click here for more details.

Files changed (45) hide show
  1. package/.claude/skills/ucn/SKILL.md +77 -0
  2. package/LICENSE +21 -0
  3. package/README.md +135 -0
  4. package/cli/index.js +2437 -0
  5. package/core/discovery.js +513 -0
  6. package/core/imports.js +558 -0
  7. package/core/output.js +1274 -0
  8. package/core/parser.js +279 -0
  9. package/core/project.js +3261 -0
  10. package/index.js +52 -0
  11. package/languages/go.js +653 -0
  12. package/languages/index.js +267 -0
  13. package/languages/java.js +826 -0
  14. package/languages/javascript.js +1346 -0
  15. package/languages/python.js +667 -0
  16. package/languages/rust.js +950 -0
  17. package/languages/utils.js +457 -0
  18. package/package.json +42 -0
  19. package/test/fixtures/go/go.mod +3 -0
  20. package/test/fixtures/go/main.go +257 -0
  21. package/test/fixtures/go/service.go +187 -0
  22. package/test/fixtures/java/DataService.java +279 -0
  23. package/test/fixtures/java/Main.java +287 -0
  24. package/test/fixtures/java/Utils.java +199 -0
  25. package/test/fixtures/java/pom.xml +6 -0
  26. package/test/fixtures/javascript/main.js +109 -0
  27. package/test/fixtures/javascript/package.json +1 -0
  28. package/test/fixtures/javascript/service.js +88 -0
  29. package/test/fixtures/javascript/utils.js +67 -0
  30. package/test/fixtures/python/main.py +198 -0
  31. package/test/fixtures/python/pyproject.toml +3 -0
  32. package/test/fixtures/python/service.py +166 -0
  33. package/test/fixtures/python/utils.py +118 -0
  34. package/test/fixtures/rust/Cargo.toml +3 -0
  35. package/test/fixtures/rust/main.rs +253 -0
  36. package/test/fixtures/rust/service.rs +210 -0
  37. package/test/fixtures/rust/utils.rs +154 -0
  38. package/test/fixtures/typescript/main.ts +154 -0
  39. package/test/fixtures/typescript/package.json +1 -0
  40. package/test/fixtures/typescript/repository.ts +149 -0
  41. package/test/fixtures/typescript/types.ts +114 -0
  42. package/test/parser.test.js +3661 -0
  43. package/test/public-repos-test.js +477 -0
  44. package/test/systematic-test.js +619 -0
  45. package/ucn.js +8 -0
@@ -0,0 +1,267 @@
1
+ /**
2
+ * languages/index.js - Language registry and detection
3
+ *
4
+ * Manages language parsers and provides extension-based detection.
5
+ */
6
+
7
+ const path = require('path');
8
+
9
+ // Lazy-loaded tree-sitter
10
+ let TreeSitter = null;
11
+
12
+ // Cached parser instances
13
+ const parsers = {};
14
+
15
+ // Language configurations
16
+ const LANGUAGES = {
17
+ javascript: {
18
+ name: 'javascript',
19
+ extensions: ['.js', '.jsx', '.mjs', '.cjs'],
20
+ treeSitterLang: 'javascript',
21
+ module: () => require('./javascript')
22
+ },
23
+ typescript: {
24
+ name: 'typescript',
25
+ extensions: ['.ts'],
26
+ treeSitterLang: 'typescript',
27
+ module: () => require('./javascript') // Same module, different parser
28
+ },
29
+ tsx: {
30
+ name: 'tsx',
31
+ extensions: ['.tsx'],
32
+ treeSitterLang: 'tsx',
33
+ module: () => require('./javascript')
34
+ },
35
+ python: {
36
+ name: 'python',
37
+ extensions: ['.py', '.pyi'],
38
+ treeSitterLang: 'python',
39
+ module: () => require('./python')
40
+ },
41
+ go: {
42
+ name: 'go',
43
+ extensions: ['.go'],
44
+ treeSitterLang: 'go',
45
+ module: () => require('./go')
46
+ },
47
+ rust: {
48
+ name: 'rust',
49
+ extensions: ['.rs'],
50
+ treeSitterLang: 'rust',
51
+ module: () => require('./rust')
52
+ },
53
+ java: {
54
+ name: 'java',
55
+ extensions: ['.java'],
56
+ treeSitterLang: 'java',
57
+ module: () => require('./java')
58
+ }
59
+ };
60
+
61
+ // Extension to language mapping
62
+ const EXT_MAP = {};
63
+ for (const [langName, config] of Object.entries(LANGUAGES)) {
64
+ for (const ext of config.extensions) {
65
+ EXT_MAP[ext] = langName;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Load tree-sitter module (lazy)
71
+ * @returns {object} TreeSitter class
72
+ */
73
+ function loadTreeSitter() {
74
+ if (!TreeSitter) {
75
+ try {
76
+ TreeSitter = require('tree-sitter');
77
+ } catch (e) {
78
+ throw new Error(
79
+ 'tree-sitter is required but not installed.\n' +
80
+ 'Install with: npm install'
81
+ );
82
+ }
83
+ }
84
+ return TreeSitter;
85
+ }
86
+
87
+ /**
88
+ * Get or create parser for a language
89
+ * @param {string} language - Language name
90
+ * @returns {object} Tree-sitter parser instance
91
+ */
92
+ function getParser(language) {
93
+ if (parsers[language]) return parsers[language];
94
+
95
+ const TS = loadTreeSitter();
96
+ const parser = new TS();
97
+ const config = LANGUAGES[language];
98
+
99
+ if (!config) {
100
+ throw new Error(`Unsupported language: ${language}`);
101
+ }
102
+
103
+ try {
104
+ let lang;
105
+ switch (language) {
106
+ case 'javascript':
107
+ lang = require('tree-sitter-javascript');
108
+ break;
109
+ case 'typescript':
110
+ lang = require('tree-sitter-typescript').typescript;
111
+ break;
112
+ case 'tsx':
113
+ lang = require('tree-sitter-typescript').tsx;
114
+ break;
115
+ case 'python':
116
+ lang = require('tree-sitter-python');
117
+ break;
118
+ case 'go':
119
+ lang = require('tree-sitter-go');
120
+ break;
121
+ case 'java':
122
+ lang = require('tree-sitter-java');
123
+ break;
124
+ case 'rust':
125
+ lang = require('tree-sitter-rust');
126
+ break;
127
+ default:
128
+ throw new Error(`No tree-sitter grammar for: ${language}`);
129
+ }
130
+ parser.setLanguage(lang);
131
+ } catch (e) {
132
+ throw new Error(
133
+ `Failed to load tree-sitter-${language}.\n` +
134
+ `Install with: npm install tree-sitter-${language}\n` +
135
+ `Original error: ${e.message}`
136
+ );
137
+ }
138
+
139
+ parsers[language] = parser;
140
+ return parser;
141
+ }
142
+
143
+ /**
144
+ * Detect language from file path
145
+ * @param {string} filePath - File path
146
+ * @returns {string|null} Language name or null if unsupported
147
+ */
148
+ function detectLanguage(filePath) {
149
+ const ext = path.extname(filePath).toLowerCase();
150
+ return EXT_MAP[ext] || null;
151
+ }
152
+
153
+ /**
154
+ * Get language module for a language
155
+ * @param {string} language - Language name
156
+ * @returns {object} Language module with parse functions
157
+ */
158
+ function getLanguageModule(language) {
159
+ const config = LANGUAGES[language];
160
+ if (!config) {
161
+ throw new Error(`Unsupported language: ${language}`);
162
+ }
163
+ return config.module();
164
+ }
165
+
166
+ /**
167
+ * Check if a language is supported
168
+ * @param {string} language - Language name
169
+ * @returns {boolean}
170
+ */
171
+ function isSupported(language) {
172
+ return language in LANGUAGES;
173
+ }
174
+
175
+ /**
176
+ * Get all supported extensions
177
+ * @returns {string[]}
178
+ */
179
+ function getSupportedExtensions() {
180
+ return Object.keys(EXT_MAP);
181
+ }
182
+
183
+ /**
184
+ * Get all supported languages
185
+ * @returns {string[]}
186
+ */
187
+ function getSupportedLanguages() {
188
+ return Object.keys(LANGUAGES);
189
+ }
190
+
191
+ // Buffer size for tree-sitter parser (workaround for default 32KB limit)
192
+ // Default 1MB handles most files; can be overridden via UCN_BUFFER_SIZE env var
193
+ const DEFAULT_BUFFER_SIZE = 1024 * 1024; // 1MB
194
+ const MAX_BUFFER_SIZE = 64 * 1024 * 1024; // 64MB cap
195
+
196
+ const PARSE_OPTIONS = {
197
+ bufferSize: parseInt(process.env.UCN_BUFFER_SIZE, 10) || DEFAULT_BUFFER_SIZE
198
+ };
199
+
200
+ /**
201
+ * Get parse options with dynamic buffer sizing based on content size
202
+ * @param {number} contentLength - Length of content to parse
203
+ * @returns {object} Parse options with appropriate buffer size
204
+ */
205
+ function getParseOptions(contentLength = 0) {
206
+ // Start with configured/default size, scale up for large files
207
+ // Buffer needs room for syntax tree which can be 2-3x content size
208
+ const minBuffer = parseInt(process.env.UCN_BUFFER_SIZE, 10) || DEFAULT_BUFFER_SIZE;
209
+ const scaledBuffer = Math.max(minBuffer, contentLength * 3);
210
+ const bufferSize = Math.min(scaledBuffer, MAX_BUFFER_SIZE);
211
+ return { bufferSize };
212
+ }
213
+
214
+ /**
215
+ * Safely parse content with automatic buffer retry on failure
216
+ * @param {object} parser - tree-sitter parser instance
217
+ * @param {string} content - Source code to parse
218
+ * @param {object} oldTree - Previous tree for incremental parsing (optional)
219
+ * @param {object} options - Additional parse options
220
+ * @returns {object} Parsed tree
221
+ */
222
+ function safeParse(parser, content, oldTree = undefined, options = {}) {
223
+ const contentLength = content.length;
224
+
225
+ // Try with escalating buffer sizes
226
+ const bufferSizes = [
227
+ parseInt(process.env.UCN_BUFFER_SIZE, 10) || DEFAULT_BUFFER_SIZE,
228
+ Math.max(DEFAULT_BUFFER_SIZE, contentLength * 2),
229
+ Math.max(4 * 1024 * 1024, contentLength * 3),
230
+ Math.max(16 * 1024 * 1024, contentLength * 4),
231
+ MAX_BUFFER_SIZE
232
+ ].filter((size, i, arr) => i === 0 || size > arr[i - 1]); // Remove duplicates
233
+
234
+ let lastError;
235
+ for (const bufferSize of bufferSizes) {
236
+ try {
237
+ return parser.parse(content, oldTree, { ...options, bufferSize });
238
+ } catch (e) {
239
+ lastError = e;
240
+ // Only retry on buffer-related errors
241
+ if (!e.message?.toLowerCase().includes('buffer') &&
242
+ !e.message?.toLowerCase().includes('memory') &&
243
+ !e.message?.toLowerCase().includes('alloc')) {
244
+ throw e; // Non-buffer error, don't retry
245
+ }
246
+ // Continue to next buffer size
247
+ }
248
+ }
249
+
250
+ // All attempts failed
251
+ throw lastError;
252
+ }
253
+
254
+ module.exports = {
255
+ detectLanguage,
256
+ getParser,
257
+ getLanguageModule,
258
+ isSupported,
259
+ getSupportedExtensions,
260
+ getSupportedLanguages,
261
+ LANGUAGES,
262
+ PARSE_OPTIONS,
263
+ getParseOptions,
264
+ safeParse,
265
+ DEFAULT_BUFFER_SIZE,
266
+ MAX_BUFFER_SIZE
267
+ };