zemdomu 1.0.0 → 1.0.1

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.
@@ -0,0 +1,454 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ComponentAnalyzer = void 0;
40
+ const fs = __importStar(require("fs/promises"));
41
+ const path = __importStar(require("path"));
42
+ const parser_1 = require("@babel/parser");
43
+ const traverse_1 = __importDefault(require("@babel/traverse"));
44
+ const t = __importStar(require("@babel/types"));
45
+ const component_path_resolver_1 = require("./component-path-resolver");
46
+ class ComponentAnalyzer {
47
+ constructor(options, perf) {
48
+ this.componentRegistry = new Map();
49
+ this.importToComponentMap = new Map();
50
+ this.processingComponentStack = new Set(); // To prevent circular references
51
+ this.resolver = new component_path_resolver_1.ComponentPathResolver();
52
+ this.options = options;
53
+ this.perf = perf;
54
+ }
55
+ async analyzeFile(filePath) {
56
+ var _a;
57
+ const start = Date.now();
58
+ try {
59
+ const content = await fs.readFile(filePath, 'utf8');
60
+ if (!/\.(jsx|tsx)$/.test(filePath))
61
+ return null;
62
+ const { component, timings } = await this.extractComponentInfo(content, filePath);
63
+ timings.total = Date.now() - start;
64
+ (_a = this.perf) === null || _a === void 0 ? void 0 : _a.record(filePath, timings);
65
+ return component;
66
+ }
67
+ catch (e) {
68
+ console.error(`[ZemDomu] Error analyzing file ${filePath}:`, e);
69
+ return null;
70
+ }
71
+ }
72
+ async extractComponentInfo(content, filePath) {
73
+ var _a, _b;
74
+ const timings = {};
75
+ let t0 = Date.now();
76
+ const ast = (0, parser_1.parse)(content, { sourceType: 'module', plugins: ['typescript', 'jsx'] });
77
+ timings.parse = Date.now() - t0;
78
+ const componentName = path.basename(filePath, path.extname(filePath));
79
+ const componentDef = {
80
+ name: componentName,
81
+ filePath,
82
+ issues: new Map(),
83
+ usesComponents: [],
84
+ headings: []
85
+ };
86
+ // Track imported components
87
+ const importedComponents = new Map();
88
+ // Collect imports
89
+ t0 = Date.now();
90
+ (0, traverse_1.default)(ast, {
91
+ ImportDeclaration(path) {
92
+ const source = path.node.source.value;
93
+ path.node.specifiers.forEach(spec => {
94
+ if (t.isImportSpecifier(spec) || t.isImportDefaultSpecifier(spec)) {
95
+ const name = spec.local.name;
96
+ if (/^[A-Z]/.test(name)) {
97
+ importedComponents.set(name, source);
98
+ }
99
+ }
100
+ });
101
+ }
102
+ });
103
+ timings.collectImports = Date.now() - t0;
104
+ // Collect JSX usages and headings
105
+ t0 = Date.now();
106
+ (0, traverse_1.default)(ast, {
107
+ JSXElement(path) {
108
+ var _a, _b;
109
+ const elt = path.node.openingElement.name;
110
+ if (t.isJSXIdentifier(elt)) {
111
+ const name = elt.name;
112
+ const tag = name.toLowerCase();
113
+ // Record headings
114
+ if (/^h[1-6]$/.test(tag)) {
115
+ const level = parseInt(tag.charAt(1), 10);
116
+ const loc = (_a = elt.loc) === null || _a === void 0 ? void 0 : _a.start;
117
+ if (loc) {
118
+ componentDef.headings.push({
119
+ level,
120
+ line: loc.line - 1,
121
+ column: loc.column,
122
+ filePath
123
+ });
124
+ }
125
+ }
126
+ // Record component usage (only for capitalized components)
127
+ if (/^[A-Z]/.test(name)) {
128
+ const existingRef = componentDef.usesComponents.find(c => c.name === name);
129
+ const loc = (_b = elt.loc) === null || _b === void 0 ? void 0 : _b.start;
130
+ const location = loc ? { line: loc.line - 1, column: loc.column } : { line: 0, column: 0 };
131
+ if (existingRef) {
132
+ // Add usage location to existing reference
133
+ existingRef.usageLocations.push(location);
134
+ }
135
+ else {
136
+ // Create new component reference
137
+ const rawImportPath = importedComponents.get(name) || null;
138
+ componentDef.usesComponents.push({
139
+ name,
140
+ path: null, // Will be resolved later
141
+ rawImportPath,
142
+ sourceLocation: location,
143
+ usageLocations: [location]
144
+ });
145
+ }
146
+ }
147
+ }
148
+ }
149
+ });
150
+ timings.jsxCollect = Date.now() - t0;
151
+ // Store import mappings for this file
152
+ this.importToComponentMap.set(filePath, importedComponents);
153
+ // Resolve import paths
154
+ t0 = Date.now();
155
+ for (const ref of componentDef.usesComponents) {
156
+ if (ref.rawImportPath) {
157
+ const t1 = Date.now();
158
+ ref.path = await this.resolveComponentPath(ref.rawImportPath, filePath);
159
+ timings[`resolve:${ref.rawImportPath}`] = Date.now() - t1;
160
+ }
161
+ }
162
+ timings.resolvePaths = Date.now() - t0;
163
+ // Check for heading order issues within this component
164
+ t0 = Date.now();
165
+ if ((_a = this.options.rules) === null || _a === void 0 ? void 0 : _a.enforceHeadingOrder) {
166
+ let lastHeadingLevel = 0;
167
+ const sortedHeadings = [...componentDef.headings].sort((a, b) => {
168
+ if (a.line !== b.line)
169
+ return a.line - b.line;
170
+ return a.column - b.column;
171
+ });
172
+ for (const heading of sortedHeadings) {
173
+ if (lastHeadingLevel && heading.level > lastHeadingLevel + 1) {
174
+ componentDef.issues.set('enforceHeadingOrder', [
175
+ ...(componentDef.issues.get('enforceHeadingOrder') || []),
176
+ {
177
+ line: heading.line,
178
+ column: heading.column,
179
+ message: `Heading level skipped: <h${heading.level}> after <h${lastHeadingLevel}>`,
180
+ rule: 'enforceHeadingOrder'
181
+ }
182
+ ]);
183
+ }
184
+ lastHeadingLevel = heading.level;
185
+ }
186
+ }
187
+ // Synthetic single-H1 issues
188
+ if ((_b = this.options.rules) === null || _b === void 0 ? void 0 : _b.singleH1) {
189
+ const h1Results = componentDef.headings
190
+ .filter(h => h.level === 1)
191
+ .map(h => ({ line: h.line, column: h.column, message: '<h1>', rule: 'singleH1' }));
192
+ if (h1Results.length > 0) {
193
+ componentDef.issues.set('singleH1', h1Results);
194
+ }
195
+ }
196
+ timings.headingAnalysis = Date.now() - t0;
197
+ // Register component
198
+ this.componentRegistry.set(filePath, componentDef);
199
+ return { component: componentDef, timings };
200
+ }
201
+ async resolveComponentPath(importPath, currentPath) {
202
+ return this.resolver.resolve(importPath, currentPath);
203
+ }
204
+ registerComponent(component, issues) {
205
+ for (const issue of issues) {
206
+ const rule = issue.rule || this.getRuleType(issue.message);
207
+ if (!component.issues.has(rule))
208
+ component.issues.set(rule, []);
209
+ component.issues.get(rule).push(issue);
210
+ }
211
+ this.componentRegistry.set(component.filePath, component);
212
+ }
213
+ getRuleType(msg) {
214
+ if (msg.includes('<h1>'))
215
+ return 'singleH1';
216
+ if (msg.includes('Heading level'))
217
+ return 'enforceHeadingOrder';
218
+ if (msg.includes('<section>'))
219
+ return 'requireSectionHeading';
220
+ if (msg.includes('<img>'))
221
+ return 'requireAltText';
222
+ if (msg.includes('missing title attribute'))
223
+ return 'requireIframeTitle';
224
+ if (msg.includes('missing alt attribute') && msg.includes('input type="image"'))
225
+ return 'requireImageInputAlt';
226
+ if (msg.includes('<html>'))
227
+ return 'requireHtmlLang';
228
+ if (msg.includes('<button>'))
229
+ return 'requireButtonText';
230
+ if (msg.includes('Form control'))
231
+ return 'requireLabelForFormControls';
232
+ if (msg.includes('<li>'))
233
+ return 'enforceListNesting';
234
+ if (msg.includes('<a>'))
235
+ return msg.includes('href') ? 'requireHrefOnAnchors' : 'requireLinkText';
236
+ if (msg.includes('<table>'))
237
+ return 'requireTableCaption';
238
+ if (msg.includes('should not be empty'))
239
+ return 'preventEmptyInlineTags';
240
+ return 'other';
241
+ }
242
+ analyzeComponentTree() {
243
+ var _a;
244
+ const results = [];
245
+ const cross = (_a = this.options.crossComponentAnalysis) !== null && _a !== void 0 ? _a : true;
246
+ const rules = this.options.rules || {};
247
+ if (!cross)
248
+ return results;
249
+ if (rules.singleH1)
250
+ this.findCrossComponentH1Issues(results);
251
+ if (rules.enforceHeadingOrder)
252
+ this.findCrossComponentHeadingOrderIssues(results);
253
+ return results;
254
+ }
255
+ findCrossComponentH1Issues(results) {
256
+ const entryPoints = this.findEntryPoints();
257
+ for (const entry of entryPoints) {
258
+ const comps = this.findComponentsWithRule(entry, 'singleH1');
259
+ if (comps.length > 1) {
260
+ for (let i = 1; i < comps.length; i++) {
261
+ const comp = comps[i];
262
+ const ref = this.findReferenceForComp(entry, comp.filePath);
263
+ if (ref) {
264
+ // Use first JSX usage location instead of import location
265
+ const location = ref.usageLocations[0] || ref.sourceLocation;
266
+ results.push({
267
+ filePath: entry.filePath,
268
+ line: location.line,
269
+ column: location.column,
270
+ message: `Multiple <h1> tags: component '${comp.name}' brings an extra <h1>. Use a lower-level heading.`,
271
+ rule: 'singleH1'
272
+ });
273
+ }
274
+ else {
275
+ const issue = comp.issues.get('singleH1')[0];
276
+ results.push({
277
+ filePath: comp.filePath,
278
+ line: issue.line,
279
+ column: issue.column,
280
+ message: `Multiple <h1> across components - consider using lower-level headings.`,
281
+ rule: 'singleH1'
282
+ });
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+ findReferenceForComp(root, targetPath) {
289
+ for (const ref of root.usesComponents) {
290
+ if (ref.path === targetPath)
291
+ return ref;
292
+ }
293
+ for (const ref of root.usesComponents) {
294
+ if (ref.path && this.componentRegistry.has(ref.path)) {
295
+ const nested = this.findReferenceForComp(this.componentRegistry.get(ref.path), targetPath);
296
+ if (nested)
297
+ return ref;
298
+ }
299
+ }
300
+ return null;
301
+ }
302
+ /**
303
+ * Improved implementation to find heading order issues across components
304
+ */
305
+ findCrossComponentHeadingOrderIssues(results) {
306
+ const entryPoints = this.findEntryPoints();
307
+ for (const entry of entryPoints) {
308
+ // Process each entry point as a document root
309
+ this.processingComponentStack.clear();
310
+ this.analyzeHeadingHierarchy(entry, results);
311
+ }
312
+ }
313
+ /**
314
+ * Collects all headings from a component and its children in document order
315
+ * and checks for heading level issues
316
+ */
317
+ analyzeHeadingHierarchy(component, results) {
318
+ var _a, _b, _c;
319
+ if (this.processingComponentStack.has(component.filePath)) {
320
+ // Avoid circular references
321
+ return;
322
+ }
323
+ this.processingComponentStack.add(component.filePath);
324
+ // Build a flattened view of all headings in document order
325
+ const allHeadings = this.collectHeadingsInDocumentOrder(component);
326
+ // Check for heading level issues
327
+ let lastLevel = 0;
328
+ for (const heading of allHeadings) {
329
+ if (lastLevel > 0 && heading.heading.level > lastLevel + 1) {
330
+ // We found a heading level skip
331
+ results.push({
332
+ filePath: ((_a = heading.usageLocation) === null || _a === void 0 ? void 0 : _a.filePath) || heading.heading.filePath,
333
+ line: ((_b = heading.usageLocation) === null || _b === void 0 ? void 0 : _b.line) || heading.heading.line,
334
+ column: ((_c = heading.usageLocation) === null || _c === void 0 ? void 0 : _c.column) || heading.heading.column,
335
+ message: `Cross-component heading level skipped: <h${heading.heading.level}> after <h${lastLevel}>`,
336
+ rule: 'enforceHeadingOrder'
337
+ });
338
+ }
339
+ lastLevel = heading.heading.level;
340
+ }
341
+ this.processingComponentStack.delete(component.filePath);
342
+ }
343
+ /**
344
+ * Collects all headings from a component and its children in document order
345
+ */
346
+ collectHeadingsInDocumentOrder(component) {
347
+ // Sort headings within this component by line/column
348
+ const localHeadings = [...component.headings].sort((a, b) => {
349
+ if (a.line !== b.line)
350
+ return a.line - b.line;
351
+ return a.column - b.column;
352
+ }).map(h => ({
353
+ heading: h,
354
+ usageLocation: null
355
+ }));
356
+ // Sort child components by their usage location
357
+ const childComponents = component.usesComponents
358
+ .filter(ref => ref.path && this.componentRegistry.has(ref.path))
359
+ .sort((a, b) => {
360
+ const aLoc = a.usageLocations[0] || a.sourceLocation;
361
+ const bLoc = b.usageLocations[0] || b.sourceLocation;
362
+ if (aLoc.line !== bLoc.line)
363
+ return aLoc.line - bLoc.line;
364
+ return aLoc.column - bLoc.column;
365
+ });
366
+ // Merge headings and child component headings in document order
367
+ const allHeadings = [];
368
+ let headingIndex = 0;
369
+ let childIndex = 0;
370
+ // This merges the local headings with child component headings
371
+ // based on their position in the document
372
+ while (headingIndex < localHeadings.length || childIndex < childComponents.length) {
373
+ if (headingIndex >= localHeadings.length) {
374
+ // No more local headings, process remaining children
375
+ const childRef = childComponents[childIndex++];
376
+ if (childRef.path && this.componentRegistry.has(childRef.path) && !this.processingComponentStack.has(childRef.path)) {
377
+ const childComponent = this.componentRegistry.get(childRef.path);
378
+ const usageLoc = childRef.usageLocations[0] || childRef.sourceLocation;
379
+ const usageLocation = {
380
+ filePath: component.filePath,
381
+ line: usageLoc.line,
382
+ column: usageLoc.column
383
+ };
384
+ this.processingComponentStack.add(childRef.path);
385
+ const childHeadings = this.collectHeadingsInDocumentOrder(childComponent)
386
+ .map(h => ({
387
+ heading: h.heading,
388
+ usageLocation: h.usageLocation || usageLocation
389
+ }));
390
+ this.processingComponentStack.delete(childRef.path);
391
+ allHeadings.push(...childHeadings);
392
+ }
393
+ }
394
+ else if (childIndex >= childComponents.length) {
395
+ // No more children, add remaining local headings
396
+ allHeadings.push(localHeadings[headingIndex++]);
397
+ }
398
+ else {
399
+ // Compare positions to decide whether to add a local heading or process a child
400
+ const nextHeading = localHeadings[headingIndex];
401
+ const nextChild = childComponents[childIndex];
402
+ const childLoc = nextChild.usageLocations[0] || nextChild.sourceLocation;
403
+ if (nextHeading.heading.line < childLoc.line ||
404
+ (nextHeading.heading.line === childLoc.line && nextHeading.heading.column < childLoc.column)) {
405
+ // Local heading comes first
406
+ allHeadings.push(nextHeading);
407
+ headingIndex++;
408
+ }
409
+ else {
410
+ // Child component comes first
411
+ childIndex++;
412
+ if (nextChild.path && this.componentRegistry.has(nextChild.path) && !this.processingComponentStack.has(nextChild.path)) {
413
+ const childComponent = this.componentRegistry.get(nextChild.path);
414
+ const usageLocation = {
415
+ filePath: component.filePath,
416
+ line: childLoc.line,
417
+ column: childLoc.column
418
+ };
419
+ this.processingComponentStack.add(nextChild.path);
420
+ const childHeadings = this.collectHeadingsInDocumentOrder(childComponent)
421
+ .map(h => ({
422
+ heading: h.heading,
423
+ usageLocation: h.usageLocation || usageLocation
424
+ }));
425
+ this.processingComponentStack.delete(nextChild.path);
426
+ allHeadings.push(...childHeadings);
427
+ }
428
+ }
429
+ }
430
+ }
431
+ return allHeadings;
432
+ }
433
+ findEntryPoints() {
434
+ const all = Array.from(this.componentRegistry.values());
435
+ const imported = new Set();
436
+ all.forEach(c => c.usesComponents.forEach(r => r.path && imported.add(r.path)));
437
+ return all.filter(c => !imported.has(c.filePath));
438
+ }
439
+ findComponentsWithRule(root, rule) {
440
+ const res = [];
441
+ const visited = new Set();
442
+ const dfs = (c) => {
443
+ if (visited.has(c.filePath))
444
+ return;
445
+ visited.add(c.filePath);
446
+ if (c.issues.has(rule))
447
+ res.push(c);
448
+ c.usesComponents.forEach(r => r.path && this.componentRegistry.has(r.path) && dfs(this.componentRegistry.get(r.path)));
449
+ };
450
+ dfs(root);
451
+ return res;
452
+ }
453
+ }
454
+ exports.ComponentAnalyzer = ComponentAnalyzer;
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ComponentPathResolver = void 0;
37
+ const fs = __importStar(require("fs/promises"));
38
+ const path = __importStar(require("path"));
39
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
40
+ const glob = require('glob');
41
+ let vscodeApi;
42
+ try {
43
+ vscodeApi = require('vscode');
44
+ }
45
+ catch {
46
+ vscodeApi = undefined;
47
+ }
48
+ class ComponentPathResolver {
49
+ static setRootDir(dir) {
50
+ this.rootDir = dir;
51
+ }
52
+ static updateDevMode(dev) {
53
+ this.devMode = dev;
54
+ }
55
+ static async loadTsconfig() {
56
+ var _a;
57
+ if (this.tsconfigLoaded)
58
+ return;
59
+ this.tsconfigLoaded = true;
60
+ const folder = (_a = vscodeApi === null || vscodeApi === void 0 ? void 0 : vscodeApi.workspace.workspaceFolders) === null || _a === void 0 ? void 0 : _a[0];
61
+ const root = folder ? folder.uri.fsPath : this.rootDir;
62
+ const tsconfigPath = path.join(root, 'tsconfig.json');
63
+ try {
64
+ const buf = await fs.readFile(tsconfigPath, 'utf8');
65
+ const json = JSON.parse(buf);
66
+ const opts = json.compilerOptions || {};
67
+ const baseUrl = opts.baseUrl ? path.resolve(root, opts.baseUrl) : root;
68
+ const paths = opts.paths || {};
69
+ for (const [alias, targets] of Object.entries(paths)) {
70
+ const prefix = alias.replace(/\*$/, '').replace(/\/$/, '');
71
+ const wildcard = alias.includes('*');
72
+ const mapped = [];
73
+ for (const t of targets) {
74
+ const cleaned = t.replace(/\*$/, '').replace(/\/$/, '');
75
+ mapped.push(path.resolve(baseUrl, cleaned));
76
+ }
77
+ this.tsAliases.push({ prefix, wildcard, targets: mapped });
78
+ }
79
+ }
80
+ catch {
81
+ // ignore
82
+ }
83
+ }
84
+ async tryExtensions(base) {
85
+ if (path.extname(base)) {
86
+ if (await this.fileExists(base))
87
+ return base;
88
+ }
89
+ else {
90
+ const exts = ['.tsx', '.jsx', '.ts', '.js'];
91
+ for (const ext of exts) {
92
+ const candidate = `${base}${ext}`;
93
+ if (await this.fileExists(candidate))
94
+ return candidate;
95
+ }
96
+ for (const ext of exts) {
97
+ const candidate = path.join(base, `index${ext}`);
98
+ if (await this.fileExists(candidate))
99
+ return candidate;
100
+ }
101
+ if (await this.fileExists(base))
102
+ return base;
103
+ }
104
+ return null;
105
+ }
106
+ async resolveWithTsconfig(importPath) {
107
+ await ComponentPathResolver.loadTsconfig();
108
+ for (const entry of ComponentPathResolver.tsAliases) {
109
+ if (entry.wildcard) {
110
+ if (!importPath.startsWith(entry.prefix))
111
+ continue;
112
+ const rest = importPath.substring(entry.prefix.length);
113
+ for (const tgt of entry.targets) {
114
+ const base = path.join(tgt, rest);
115
+ const r = await this.tryExtensions(base);
116
+ if (r)
117
+ return r;
118
+ }
119
+ }
120
+ else {
121
+ if (importPath === entry.prefix || importPath.startsWith(entry.prefix + '/')) {
122
+ let rest = '';
123
+ if (importPath.length > entry.prefix.length) {
124
+ rest = importPath.substring(entry.prefix.length);
125
+ if (rest.startsWith('/'))
126
+ rest = rest.substring(1);
127
+ }
128
+ for (const tgt of entry.targets) {
129
+ const base = rest ? path.join(tgt, rest) : tgt;
130
+ const r = await this.tryExtensions(base);
131
+ if (r)
132
+ return r;
133
+ }
134
+ }
135
+ }
136
+ }
137
+ return null;
138
+ }
139
+ static normalizeKey(p) {
140
+ return p
141
+ .replace(/\\/g, '/')
142
+ .replace(/\/+$/, '')
143
+ .replace(/\.(tsx|ts|jsx|js)$/, '')
144
+ .replace(/\/index$/, '')
145
+ .toLowerCase();
146
+ }
147
+ async fileExists(p) {
148
+ if (ComponentPathResolver.statCache.has(p)) {
149
+ return ComponentPathResolver.statCache.get(p);
150
+ }
151
+ try {
152
+ await fs.stat(p);
153
+ ComponentPathResolver.statCache.set(p, true);
154
+ return true;
155
+ }
156
+ catch {
157
+ ComponentPathResolver.statCache.set(p, false);
158
+ return false;
159
+ }
160
+ }
161
+ async resolve(importPath, currentPath) {
162
+ const tStart = Date.now();
163
+ const rawKey = importPath.startsWith('.')
164
+ ? path.resolve(path.dirname(currentPath), importPath)
165
+ : importPath;
166
+ const key = ComponentPathResolver.normalizeKey(rawKey);
167
+ if (ComponentPathResolver.unresolved.has(key))
168
+ return null;
169
+ if (ComponentPathResolver.resolveCache.has(key)) {
170
+ return ComponentPathResolver.resolveCache.get(key);
171
+ }
172
+ let result = null;
173
+ try {
174
+ if (importPath.startsWith('.')) {
175
+ const base = path.resolve(path.dirname(currentPath), importPath);
176
+ result = await this.tryExtensions(base);
177
+ }
178
+ else {
179
+ result = await this.resolveWithTsconfig(importPath);
180
+ if (!result) {
181
+ const prefix = importPath.split('/')[0];
182
+ let alias = ComponentPathResolver.aliasCache.get(prefix);
183
+ if (!alias) {
184
+ const pattern = `**/${prefix}/**/*.{tsx,jsx,ts,js}`;
185
+ const files = await new Promise((resolve, reject) => {
186
+ glob(pattern, { cwd: ComponentPathResolver.rootDir, ignore: '**/node_modules/**', nodir: true }, (err, matches) => (err ? reject(err) : resolve(matches)));
187
+ });
188
+ alias = new Map();
189
+ for (const relPath of files.slice(0, ComponentPathResolver.aliasFileLimit)) {
190
+ const rel = path.resolve(ComponentPathResolver.rootDir, relPath).replace(/\\/g, '/');
191
+ const idx = rel.lastIndexOf(`/${prefix}/`);
192
+ if (idx === -1)
193
+ continue;
194
+ const after = rel.substring(idx + prefix.length + 2).replace(/\.(tsx|ts|jsx|js)$/, '');
195
+ const key1 = ComponentPathResolver.normalizeKey(`${prefix}/${after}`);
196
+ alias.set(key1, rel);
197
+ if (after.endsWith('/index')) {
198
+ const trimmed = after.replace(/\/index$/, '');
199
+ alias.set(ComponentPathResolver.normalizeKey(`${prefix}/${trimmed}`), rel);
200
+ }
201
+ }
202
+ ComponentPathResolver.aliasCache.set(prefix, alias);
203
+ }
204
+ const normImport = ComponentPathResolver.normalizeKey(importPath);
205
+ result = alias.get(normImport) || null;
206
+ if (!result) {
207
+ const patterns = [
208
+ `**/${importPath}.{tsx,jsx,ts,js}`,
209
+ `**/${importPath}/index.{tsx,jsx,ts,js}`
210
+ ];
211
+ for (const ptn of patterns) {
212
+ const pKey = `glob:${ptn}`;
213
+ if (ComponentPathResolver.resolveCache.has(pKey)) {
214
+ const cached = ComponentPathResolver.resolveCache.get(pKey);
215
+ if (cached) {
216
+ result = cached;
217
+ break;
218
+ }
219
+ continue;
220
+ }
221
+ const matches = await new Promise((resolve, reject) => {
222
+ glob(ptn, { cwd: ComponentPathResolver.rootDir, ignore: '**/node_modules/**', nodir: true }, (err, files) => (err ? reject(err) : resolve(files)));
223
+ });
224
+ if (matches.length) {
225
+ result = path.resolve(ComponentPathResolver.rootDir, matches[0]);
226
+ ComponentPathResolver.resolveCache.set(pKey, result);
227
+ break;
228
+ }
229
+ else {
230
+ ComponentPathResolver.resolveCache.set(pKey, null);
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ catch {
238
+ result = null;
239
+ }
240
+ ComponentPathResolver.resolveCache.set(key, result);
241
+ if (result === null)
242
+ ComponentPathResolver.unresolved.add(key);
243
+ const tTotal = Date.now() - tStart;
244
+ if (ComponentPathResolver.devMode) {
245
+ console.debug(`[ZemDomu] resolved ${importPath} -> ${result} (${tTotal}ms)`);
246
+ }
247
+ return result;
248
+ }
249
+ }
250
+ exports.ComponentPathResolver = ComponentPathResolver;
251
+ ComponentPathResolver.resolveCache = new Map();
252
+ ComponentPathResolver.statCache = new Map();
253
+ ComponentPathResolver.aliasCache = new Map();
254
+ ComponentPathResolver.unresolved = new Set();
255
+ ComponentPathResolver.devMode = false;
256
+ ComponentPathResolver.tsconfigLoaded = false;
257
+ ComponentPathResolver.tsAliases = [];
258
+ ComponentPathResolver.aliasFileLimit = 100;
259
+ ComponentPathResolver.rootDir = process.cwd();
package/out/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  // Exposes the core lint function and types
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.lint = void 0;
4
+ exports.ComponentPathResolver = exports.ComponentAnalyzer = exports.lint = void 0;
5
5
  var linter_1 = require("./linter");
6
6
  Object.defineProperty(exports, "lint", { enumerable: true, get: function () { return linter_1.lint; } });
7
+ var component_analyzer_1 = require("./component-analyzer");
8
+ Object.defineProperty(exports, "ComponentAnalyzer", { enumerable: true, get: function () { return component_analyzer_1.ComponentAnalyzer; } });
9
+ var component_path_resolver_1 = require("./component-path-resolver");
10
+ Object.defineProperty(exports, "ComponentPathResolver", { enumerable: true, get: function () { return component_path_resolver_1.ComponentPathResolver; } });
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "zemdomu",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Hello",
5
5
  "main": "./out/index.js",
6
- "files": ["out"],
6
+ "files": [
7
+ "out"
8
+ ],
7
9
  "private": false,
8
10
  "scripts": {
9
- "build": "tsc -p tsconfig.json",
11
+ "build": "tsc -p tsconfig.json",
12
+ "compile": "tsc -p tsconfig.json",
10
13
  "test": "npm run compile && node tests/linter-labels.test.js && node tests/unique-ids.test.js"
11
14
  },
12
15
  "keywords": [],
@@ -25,6 +28,7 @@
25
28
  "dependencies": {
26
29
  "@babel/parser": "^7.27.0",
27
30
  "@babel/traverse": "^7.27.0",
28
- "@babel/types": "^7.27.0"
31
+ "@babel/types": "^7.27.0",
32
+ "glob": "^7.2.3"
29
33
  }
30
34
  }