tina4-nodejs 3.10.64 → 3.10.66
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/package.json +1 -1
- package/packages/core/src/metrics.ts +81 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tina4-nodejs",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.66",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Tina4 for Node.js/TypeScript — 54 built-in features, zero dependencies",
|
|
6
6
|
"keywords": ["tina4", "framework", "web", "api", "orm", "graphql", "websocket", "typescript"],
|
|
@@ -50,22 +50,74 @@ function readFileSafe(filePath: string): string | null {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function relativePath(filePath: string): string {
|
|
54
|
-
return path.relative(
|
|
53
|
+
function relativePath(filePath: string, root: string = "."): string {
|
|
54
|
+
return path.relative(root, filePath);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
// Stores the resolved scan root so fileDetail() can locate framework files.
|
|
58
|
+
let _lastScanRoot = "";
|
|
59
|
+
|
|
57
60
|
// ── Test file detection ─────────────────────────────────────
|
|
58
61
|
|
|
59
62
|
function hasMatchingTest(relPath: string): boolean {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
];
|
|
68
|
-
|
|
63
|
+
const parts = relPath.split('/');
|
|
64
|
+
const basename = parts[parts.length - 1] || '';
|
|
65
|
+
const name = basename.replace(/\.(ts|js)$/, '');
|
|
66
|
+
// Parent directory name (e.g. "adapters" from "adapters/sqlite.ts")
|
|
67
|
+
const parentModule = parts.length > 1 ? parts[parts.length - 2] : '';
|
|
68
|
+
|
|
69
|
+
// Stage 1: Filename matching — name.test, name.spec, test_name patterns
|
|
70
|
+
const testDirs = ['test', 'tests', 'spec'];
|
|
71
|
+
for (const td of testDirs) {
|
|
72
|
+
const patterns = [
|
|
73
|
+
`${td}/${name}.test.ts`,
|
|
74
|
+
`${td}/${name}.test.js`,
|
|
75
|
+
`${td}/${name}.spec.ts`,
|
|
76
|
+
`${td}/${name}.spec.js`,
|
|
77
|
+
// Also check parent-named tests (test/database.test.ts covers adapters/sqlite.ts)
|
|
78
|
+
...(parentModule && parentModule !== name ? [
|
|
79
|
+
`${td}/${parentModule}.test.ts`,
|
|
80
|
+
`${td}/${parentModule}.test.js`,
|
|
81
|
+
`${td}/${parentModule}.spec.ts`,
|
|
82
|
+
] : []),
|
|
83
|
+
];
|
|
84
|
+
if (patterns.some(p => fs.existsSync(p))) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Build the module import path for content matching
|
|
90
|
+
// e.g. "packages/core/src/metrics.ts" → "metrics" or "src/metrics"
|
|
91
|
+
const pathWithoutExt = relPath.replace(/\.(ts|js)$/, '');
|
|
92
|
+
// Build CamelCase class name from camelCase/kebab module name
|
|
93
|
+
// e.g. "sqlTranslation" → "SqlTranslation", "sql-translation" → "SqlTranslation"
|
|
94
|
+
const className = name
|
|
95
|
+
.replace(/[-_](.)/g, (_, c: string) => c.toUpperCase())
|
|
96
|
+
.replace(/^(.)/, (_, c: string) => c.toUpperCase());
|
|
97
|
+
|
|
98
|
+
// Stage 2+3: Content scan — check if any test file imports or references this module
|
|
99
|
+
for (const td of testDirs) {
|
|
100
|
+
if (!fs.existsSync(td)) continue;
|
|
101
|
+
const testFiles = walkFiles(td, ['.ts', '.js']);
|
|
102
|
+
for (const testFile of testFiles) {
|
|
103
|
+
const content = readFileSafe(testFile);
|
|
104
|
+
if (content === null) continue;
|
|
105
|
+
// Stage 2: import path matching
|
|
106
|
+
if (content.includes(name) && (
|
|
107
|
+
content.includes(`"${name}"`) || content.includes(`'${name}'`) ||
|
|
108
|
+
content.includes(`/${name}"`) || content.includes(`/${name}'`) ||
|
|
109
|
+
content.includes(pathWithoutExt)
|
|
110
|
+
)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
// Stage 3: class name mention
|
|
114
|
+
if (className !== name && new RegExp(`\\b${className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(content)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return false;
|
|
69
121
|
}
|
|
70
122
|
|
|
71
123
|
// ── Line counting ────────────────────────────────────────────
|
|
@@ -192,7 +244,7 @@ interface FunctionInfo {
|
|
|
192
244
|
file?: string;
|
|
193
245
|
}
|
|
194
246
|
|
|
195
|
-
function extractFunctions(source: string, filePath: string): FunctionInfo[] {
|
|
247
|
+
function extractFunctions(source: string, filePath: string, root: string = "."): FunctionInfo[] {
|
|
196
248
|
const functions: FunctionInfo[] = [];
|
|
197
249
|
const lines = source.split("\n");
|
|
198
250
|
|
|
@@ -268,7 +320,7 @@ function extractFunctions(source: string, filePath: string): FunctionInfo[] {
|
|
|
268
320
|
complexity,
|
|
269
321
|
loc: funcLoc,
|
|
270
322
|
args,
|
|
271
|
-
file: relativePath(filePath),
|
|
323
|
+
file: relativePath(filePath, root),
|
|
272
324
|
});
|
|
273
325
|
|
|
274
326
|
break; // Only match first pattern per line
|
|
@@ -542,10 +594,13 @@ function detectViolations(
|
|
|
542
594
|
function resolveRoot(root: string = "src"): string {
|
|
543
595
|
const rootPath = path.resolve(root);
|
|
544
596
|
if (fs.existsSync(rootPath) && walkFiles(rootPath, [".ts", ".js"]).length > 0) {
|
|
597
|
+
_lastScanRoot = rootPath;
|
|
545
598
|
return root;
|
|
546
599
|
}
|
|
547
600
|
// Fallback: scan the framework package itself
|
|
548
|
-
|
|
601
|
+
const fwDir = path.resolve(path.dirname(new URL(import.meta.url).pathname));
|
|
602
|
+
_lastScanRoot = fwDir;
|
|
603
|
+
return fwDir;
|
|
549
604
|
}
|
|
550
605
|
|
|
551
606
|
// ── Quick Metrics ────────────────────────────────────────────
|
|
@@ -590,7 +645,7 @@ export function quickMetrics(root: string = "src"): Record<string, any> {
|
|
|
590
645
|
totalFunctions += functions;
|
|
591
646
|
|
|
592
647
|
fileDetails.push({
|
|
593
|
-
path: relativePath(f),
|
|
648
|
+
path: relativePath(f, rootPath),
|
|
594
649
|
loc: counts.loc,
|
|
595
650
|
blank: counts.blank,
|
|
596
651
|
comment: counts.comment,
|
|
@@ -704,7 +759,7 @@ export function fullAnalysis(root: string = "src"): Record<string, any> {
|
|
|
704
759
|
const source = readFileSafe(f);
|
|
705
760
|
if (source === null) continue;
|
|
706
761
|
|
|
707
|
-
const relPath = relativePath(f);
|
|
762
|
+
const relPath = relativePath(f, rootPath);
|
|
708
763
|
const lines = source.split("\n");
|
|
709
764
|
const loc = lines.filter(
|
|
710
765
|
(l) => l.trim() && !l.trim().startsWith("//")
|
|
@@ -722,7 +777,7 @@ export function fullAnalysis(root: string = "src"): Record<string, any> {
|
|
|
722
777
|
}
|
|
723
778
|
|
|
724
779
|
// Analyze functions/methods
|
|
725
|
-
const fileFunctions = extractFunctions(source, f);
|
|
780
|
+
const fileFunctions = extractFunctions(source, f, rootPath);
|
|
726
781
|
let fileComplexity = 0;
|
|
727
782
|
|
|
728
783
|
for (const func of fileFunctions) {
|
|
@@ -805,7 +860,15 @@ export function fullAnalysis(root: string = "src"): Record<string, any> {
|
|
|
805
860
|
// ── File Detail ──────────────────────────────────────────────
|
|
806
861
|
|
|
807
862
|
export function fileDetail(filePath: string): Record<string, any> {
|
|
808
|
-
|
|
863
|
+
let resolved = path.resolve(filePath);
|
|
864
|
+
if (!fs.existsSync(resolved) && _lastScanRoot) {
|
|
865
|
+
// Try resolving relative to the last scan root (framework mode)
|
|
866
|
+
const candidate = path.resolve(_lastScanRoot, filePath);
|
|
867
|
+
if (fs.existsSync(candidate)) {
|
|
868
|
+
resolved = candidate;
|
|
869
|
+
filePath = candidate;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
809
872
|
if (!fs.existsSync(resolved)) {
|
|
810
873
|
return { error: `File not found: ${filePath}` };
|
|
811
874
|
}
|