code-gauge 3.1.0 → 4.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.
- package/README.md +16 -15
- package/dist/crossFileDuplication.cjs +1 -1
- package/dist/crossFileDuplication.cjs.map +1 -1
- package/dist/crossFileDuplication.js +1 -1
- package/dist/crossFileDuplication.js.map +1 -1
- package/dist/diffCommand.cjs +1 -1
- package/dist/diffCommand.cjs.map +1 -1
- package/dist/diffCommand.js +1 -1
- package/dist/diffCommand.js.map +1 -1
- package/dist/duplication.cjs +1 -1
- package/dist/duplication.cjs.map +1 -1
- package/dist/duplication.d.ts +14 -28
- package/dist/duplication.js +1 -1
- package/dist/duplication.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/languages.cjs +1 -1
- package/dist/languages.cjs.map +1 -1
- package/dist/languages.d.ts +5 -0
- package/dist/languages.js +1 -1
- package/dist/languages.js.map +1 -1
- package/dist/metrics.cjs +1 -1
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.ts +14 -12
- package/dist/metrics.js +1 -1
- package/dist/metrics.js.map +1 -1
- package/dist/nativeMetrics.cjs +3 -1
- package/dist/nativeMetrics.cjs.map +1 -1
- package/dist/nativeMetrics.d.ts +24 -9
- package/dist/nativeMetrics.js +3 -1
- package/dist/nativeMetrics.js.map +1 -1
- package/dist/scan.cjs +1 -1
- package/dist/scan.cjs.map +1 -1
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/types.d.ts +5 -13
- package/native/Cargo.lock +523 -0
- package/native/Cargo.toml +45 -0
- package/native/build.rs +3 -0
- package/native/src/complexity.rs +627 -0
- package/native/src/dep_degree.rs +253 -0
- package/native/src/duplication.rs +2007 -0
- package/native/src/functions.rs +345 -0
- package/native/src/languages.rs +647 -0
- package/native/src/lib.rs +101 -0
- package/native/src/measure.rs +590 -0
- package/native/src/ncss.rs +263 -0
- package/native/src/types.rs +135 -0
- package/native/src/util.rs +139 -0
- package/package.json +16 -19
- package/scripts/buildNative.mjs +25 -0
- package/scripts/installNative.mjs +96 -0
- package/dist/depDegree.cjs +0 -2
- package/dist/depDegree.cjs.map +0 -1
- package/dist/depDegree.d.ts +0 -12
- package/dist/depDegree.js +0 -2
- package/dist/depDegree.js.map +0 -1
- package/dist/ncss.cjs +0 -2
- package/dist/ncss.cjs.map +0 -1
- package/dist/ncss.d.ts +0 -17
- package/dist/ncss.js +0 -2
- package/dist/ncss.js.map +0 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Postinstall hook ensuring the native Rust addon is available: a prebuilt platform package or an
|
|
3
|
+
// already-built native/code-gauge.node is kept when it serves the payload version this checkout
|
|
4
|
+
// expects (a stale addon surviving a `git pull` must not be kept — the runtime loader would
|
|
5
|
+
// reject it), otherwise the addon is built from the bundled sources when a Rust toolchain is
|
|
6
|
+
// available. Never fails the install — the whole body is guarded and the process always exits 0
|
|
7
|
+
// (npm runs lifecycle scripts through cmd.exe on Windows, where a `|| true` suffix would not
|
|
8
|
+
// work) — and the runtime loader raises a descriptive error on first use if no addon could be
|
|
9
|
+
// provided.
|
|
10
|
+
|
|
11
|
+
import { execFileSync } from 'node:child_process';
|
|
12
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const packageRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
|
|
18
|
+
// No explicit process.exit: nothing here keeps the event loop alive, so the process ends with
|
|
19
|
+
// code 0 on its own once the synchronous work returns — and exit() could truncate the warning's
|
|
20
|
+
// asynchronous stderr write (stderr is a pipe under npm, which is asynchronous on Windows).
|
|
21
|
+
try {
|
|
22
|
+
installNativeAddon();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
warnBuildFailure(error);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function installNativeAddon() {
|
|
28
|
+
// The authoritative payload version lives in the bundled Rust source; a parse failure yields
|
|
29
|
+
// undefined, which no addon reports, so validation then always falls through to a fresh build.
|
|
30
|
+
const expectedPayloadVersion = /pub fn payload_version\(\) -> u32 \{\s*(\d+)/.exec(
|
|
31
|
+
readFileSync(path.join(packageRoot, 'native', 'src', 'lib.rs'), 'utf8')
|
|
32
|
+
)?.[1];
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
servesExpectedPayload(`code-gauge-${platformTriplet()}`, expectedPayloadVersion) ||
|
|
36
|
+
servesExpectedPayload(path.join(packageRoot, 'native', 'code-gauge.node'), expectedPayloadVersion)
|
|
37
|
+
) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
execFileSync(process.execPath, [path.join(packageRoot, 'scripts', 'buildNative.mjs')], { stdio: 'inherit' });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
warnBuildFailure(error);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// In an installed package the cargo build tree (hundreds of MB) buys nothing once the addon is
|
|
48
|
+
// copied out; a repository checkout (recognized by its sources) keeps it as the build cache.
|
|
49
|
+
// Guarded separately: a cleanup failure (e.g. a held Windows file handle) leaves a usable addon
|
|
50
|
+
// behind and must not be reported as a build failure.
|
|
51
|
+
if (!existsSync(path.join(packageRoot, 'src'))) {
|
|
52
|
+
try {
|
|
53
|
+
rmSync(path.join(packageRoot, 'native', 'target'), { recursive: true, force: true });
|
|
54
|
+
} catch {
|
|
55
|
+
// The leftover build tree only costs disk space.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The platform-package suffix in the napi-rs naming convention: Linux is qualified by libc ABI
|
|
61
|
+
// (a glibc-linked addon cannot load on Alpine/musl) and Windows by toolchain ABI. Must match
|
|
62
|
+
// platformTriplet in src/nativeMetrics.ts.
|
|
63
|
+
function platformTriplet() {
|
|
64
|
+
const base = `${process.platform}-${process.arch}`;
|
|
65
|
+
if (process.platform === 'win32') {
|
|
66
|
+
return `${base}-msvc`;
|
|
67
|
+
}
|
|
68
|
+
if (process.platform !== 'linux') {
|
|
69
|
+
return base;
|
|
70
|
+
}
|
|
71
|
+
return process.report?.getReport()?.header?.glibcVersionRuntime ? `${base}-gnu` : `${base}-musl`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Probed in a child process: loading the addon here would keep its library mapped for this
|
|
75
|
+
// process's lifetime, and overwriting a mapped DLL is refused on Windows — exactly what the
|
|
76
|
+
// stale-addon rebuild below must be able to do.
|
|
77
|
+
function servesExpectedPayload(specifier, expectedPayloadVersion) {
|
|
78
|
+
try {
|
|
79
|
+
const reported = execFileSync(
|
|
80
|
+
process.execPath,
|
|
81
|
+
['-p', `String(require(${JSON.stringify(specifier)}).payloadVersion?.())`],
|
|
82
|
+
// stderr is discarded: a missing platform package is the normal case, not a diagnostic.
|
|
83
|
+
{ cwd: packageRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
|
|
84
|
+
).trim();
|
|
85
|
+
return expectedPayloadVersion !== undefined && reported === expectedPayloadVersion;
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function warnBuildFailure(error) {
|
|
92
|
+
console.warn(
|
|
93
|
+
`code-gauge: could not build the native addon (${error instanceof Error ? error.message : String(error)}). ` +
|
|
94
|
+
'Install a Rust toolchain and run `node scripts/buildNative.mjs` in the package directory to enable it.'
|
|
95
|
+
);
|
|
96
|
+
}
|
package/dist/depDegree.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const e=new Set([`identifier`,`instance_variable`,`class_variable`,`global_variable`]),t=new Set([`=`,`:=`]),n=new Set([`+=`,`-=`,`*=`,`/=`,`%=`,`**=`,`//=`,`<<=`,`>>=`,`>>>=`,`&=`,`|=`,`^=`,`&&=`,`||=`,`??=`,`@=`,`&^=`]),r=new Map([[`variable_declarator`,`name`],[`let_declaration`,`pattern`],[`assignment`,`left`],[`var_spec`,`name`],[`init_declarator`,`declarator`],[`enhanced_for_statement`,`name`],[`for_statement`,`left`],[`for_in_statement`,`left`],[`for_in_clause`,`left`],[`for_range_loop`,`declarator`],[`for_expression`,`pattern`]]),i=new Set([`expression_list`,`pattern_list`,`tuple_pattern`]),a=new Set([`assignment`,`assignment_statement`,`short_var_declaration`,`for_statement`,`for_in_clause`,`range_clause`]),o=new Set([`type`,`value`]);function s(r,i){let a=[];c(r,void 0,``,{nextScopeId:0},i,a,!0);let o=new Map,s=0;for(let[r,i]of a.entries()){if(!e.has(i.node.type))continue;let c=i.node.text,p=a[r+1]?.node.text;if(p!==void 0&&n.has(p)){u(o.get(c),i.scope)&&(s+=1),l(o,c,i.scope);continue}if(p!==void 0&&t.has(p)||d(i)||f(i)){l(o,c,i.scope);continue}u(o.get(c),i.scope)&&(s+=1)}return s}function c(e,t,n,r,i,a,o){if(e.type===`comment`||e.type===`line_comment`||e.type===`block_comment`)return;if(e.childCount===0){a.push({node:e,fieldName:t,scope:n});return}let s=!o&&i(e)?`${n}/${r.nextScopeId++}`:n,l=e.walk();if(l.gotoFirstChild())do c(l.currentNode,l.currentFieldName??void 0,s,r,i,a,!1);while(l.gotoNextSibling())}function l(e,t,n){let r=e.get(t)??[];r.includes(n)||(r.push(n),e.set(t,r))}function u(e,t){return e!==void 0&&e.some(e=>t===e||t.startsWith(`${e}/`))}function d(e){let t=e.node.parent;if(!t)return!1;let n=r.get(t.type);if(n!==void 0&&n===e.fieldName)return!0;if(!i.has(t.type))return!1;let o=t.parent;return o!==null&&a.has(o.type)&&p(t,o)===`left`}function f(e){let t=e.node;for(let n=0;;n+=1){let r=t.parent;if(!r)return!1;let i=r.type.includes(`parameter`);if(n>=1&&!i&&!r.type.includes(`declarator`))return!1;let a=n===0?e.fieldName:p(t,r);if(a!==void 0&&o.has(a))return!1;if(i||n===0&&(a?.includes(`parameter`)??!1))return!0;t=r}}function p(e,t){for(let n=0;n<t.childCount;n+=1)if(t.child(n)?.id===e.id)return t.fieldNameForChild(n)??void 0}exports.measureDepDegree=s;
|
|
2
|
-
//# sourceMappingURL=depDegree.cjs.map
|
package/dist/depDegree.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"depDegree.cjs","names":[],"sources":["../src/depDegree.ts"],"sourcesContent":["import type Parser from 'tree-sitter';\n\n/** Leaf node types treated as variable references by the def-use approximation. */\nconst variableNodeTypes = new Set(['identifier', 'instance_variable', 'class_variable', 'global_variable']);\n\n/** Tokens directly after a variable that write it without reading it (`x = 1`, `x := 1`). */\nconst pureAssignmentOperators = new Set(['=', ':=']);\n\n/** Tokens directly after a variable that read then write it (`x += 1` depends on x's definition). */\nconst compoundAssignmentOperators = new Set([\n '+=',\n '-=',\n '*=',\n '/=',\n '%=',\n '**=',\n '//=',\n '<<=',\n '>>=',\n '>>>=',\n '&=',\n '|=',\n '^=',\n '&&=',\n '||=',\n '??=',\n '@=',\n '&^=',\n]);\n\n/**\n * Parent type -> field under which an identifier is a definition target even when a type\n * annotation separates it from the `=` token (`const x: T = ...`, `let x: T = ...`,\n * `x: int = ...`, `var x T = ...`), plus loop bindings that carry no assignment token at all.\n */\nconst definitionFieldByParentType = new Map([\n ['variable_declarator', 'name'],\n ['let_declaration', 'pattern'],\n ['assignment', 'left'],\n ['var_spec', 'name'],\n ['init_declarator', 'declarator'],\n ['enhanced_for_statement', 'name'],\n ['for_statement', 'left'],\n ['for_in_statement', 'left'],\n ['for_in_clause', 'left'],\n ['for_range_loop', 'declarator'],\n ['for_expression', 'pattern'],\n]);\n\n/** Multi-target lists (`a, b = ...`, `a, b := ...`) whose holder's `left` field marks definitions. */\nconst definitionListNodeTypes = new Set(['expression_list', 'pattern_list', 'tuple_pattern']);\nconst definitionListHolderTypes = new Set([\n 'assignment',\n 'assignment_statement',\n 'short_var_declaration',\n 'for_statement',\n 'for_in_clause',\n 'range_clause',\n]);\n\n/** Parameter-position fields that annotate or initialize rather than bind (`x: T`, `x = default`). */\nconst nonBindingParameterFields = new Set(['type', 'value']);\n\n/** One leaf of the def-use walk: its field in the parent and its function-scope chain. */\ninterface DepDegreeLeaf {\n node: Parser.SyntaxNode;\n fieldName: string | undefined;\n /** Chain of nested-function scope ids below the measured function: '' for its own body, then '/0', '/0/1', ... */\n scope: string;\n}\n\n/**\n * Approximate def-use pairs of the function's subtree (see FunctionMetrics.depDegree): the number\n * of variable reads with a preceding same-name definition visible at the read. Definitions are\n * recognized token-wise (a variable directly followed by an assignment operator), structurally\n * (declarator/assignment/loop-binding fields, so annotated declarations count), and positionally\n * (parameters). Nested function subtrees are included, but their definitions are scoped: an inner\n * function's parameters and locals cannot reach reads outside it, while inner reads still see\n * outer definitions (closure captures). Reads through destructuring patterns and member accesses\n * are not modeled; the approximation only needs to be stable, since the gate compares deltas.\n */\nexport function measureDepDegree(\n functionNode: Parser.SyntaxNode,\n isNestedFunctionBoundary: (node: Parser.SyntaxNode) => boolean\n): number {\n const leaves: DepDegreeLeaf[] = [];\n collectDepDegreeLeaves(functionNode, undefined, '', { nextScopeId: 0 }, isNestedFunctionBoundary, leaves, true);\n const definitionScopesByName = new Map<string, string[]>();\n let pairs = 0;\n for (const [index, leaf] of leaves.entries()) {\n if (!variableNodeTypes.has(leaf.node.type)) {\n continue;\n }\n const name = leaf.node.text;\n const nextText = leaves[index + 1]?.node.text;\n if (nextText !== undefined && compoundAssignmentOperators.has(nextText)) {\n if (isDefinitionVisible(definitionScopesByName.get(name), leaf.scope)) {\n pairs += 1;\n }\n addDefinition(definitionScopesByName, name, leaf.scope);\n continue;\n }\n if (\n (nextText !== undefined && pureAssignmentOperators.has(nextText)) ||\n isStructuralDefinition(leaf) ||\n isParameterDefinition(leaf)\n ) {\n addDefinition(definitionScopesByName, name, leaf.scope);\n continue;\n }\n if (isDefinitionVisible(definitionScopesByName.get(name), leaf.scope)) {\n pairs += 1;\n }\n }\n return pairs;\n}\n\n/**\n * Collects non-comment leaves with their parent field — taken from one cursor pass, so a child of\n * a high-arity node (a long array literal) costs O(1) instead of an O(children) scan — and their\n * function-scope chain (each nested function boundary below the measured function opens a child\n * scope).\n */\nfunction collectDepDegreeLeaves(\n node: Parser.SyntaxNode,\n fieldName: string | undefined,\n scope: string,\n state: { nextScopeId: number },\n isNestedFunctionBoundary: (node: Parser.SyntaxNode) => boolean,\n leaves: DepDegreeLeaf[],\n isMeasuredRoot: boolean\n): void {\n if (node.type === 'comment' || node.type === 'line_comment' || node.type === 'block_comment') {\n return;\n }\n if (node.childCount === 0) {\n leaves.push({ node, fieldName, scope });\n return;\n }\n const childScope = !isMeasuredRoot && isNestedFunctionBoundary(node) ? `${scope}/${state.nextScopeId++}` : scope;\n const cursor = node.walk();\n if (cursor.gotoFirstChild()) {\n do {\n collectDepDegreeLeaves(\n cursor.currentNode,\n cursor.currentFieldName ?? undefined,\n childScope,\n state,\n isNestedFunctionBoundary,\n leaves,\n false\n );\n } while (cursor.gotoNextSibling());\n }\n}\n\nfunction addDefinition(definitionScopesByName: Map<string, string[]>, name: string, scope: string): void {\n const scopes = definitionScopesByName.get(name) ?? [];\n if (!scopes.includes(scope)) {\n scopes.push(scope);\n definitionScopesByName.set(name, scopes);\n }\n}\n\n/** A definition reaches a read only from the read's own or an enclosing function scope. */\nfunction isDefinitionVisible(definitionScopes: string[] | undefined, scope: string): boolean {\n return (\n definitionScopes !== undefined &&\n definitionScopes.some((definitionScope) => scope === definitionScope || scope.startsWith(`${definitionScope}/`))\n );\n}\n\nfunction isStructuralDefinition(leaf: DepDegreeLeaf): boolean {\n const parent = leaf.node.parent;\n if (!parent) {\n return false;\n }\n const definitionField = definitionFieldByParentType.get(parent.type);\n if (definitionField !== undefined && definitionField === leaf.fieldName) {\n return true;\n }\n if (!definitionListNodeTypes.has(parent.type)) {\n return false;\n }\n const holder = parent.parent;\n return holder !== null && definitionListHolderTypes.has(holder.type) && fieldNameInParent(parent, holder) === 'left';\n}\n\n/**\n * Whether the identifier binds a parameter: an ancestor reached through declarator wrappers (C/C++\n * function-pointer or array parameters) is a parameter-ish node, or it directly occupies a\n * parameter field (bare arrow-function/lambda parameters, catch-clause bindings). Type annotations\n * and default values inside parameter nodes bind nothing.\n */\nfunction isParameterDefinition(leaf: DepDegreeLeaf): boolean {\n let current = leaf.node;\n for (let depth = 0; ; depth += 1) {\n const parent = current.parent;\n if (!parent) {\n return false;\n }\n const parentIsParameterish = parent.type.includes('parameter');\n // Beyond the grandparent, only declarator wrappers keep climbing; checking this before the\n // field lookup also keeps reads inside high-arity nodes (long array literals) O(1).\n if (depth >= 1 && !parentIsParameterish && !parent.type.includes('declarator')) {\n return false;\n }\n const field = depth === 0 ? leaf.fieldName : fieldNameInParent(current, parent);\n if (field !== undefined && nonBindingParameterFields.has(field)) {\n return false;\n }\n if (parentIsParameterish || (depth === 0 && (field?.includes('parameter') ?? false))) {\n return true;\n }\n current = parent;\n }\n}\n\n/** Only called with small-arity parents (declarator wrappers, definition-list holders). */\nfunction fieldNameInParent(node: Parser.SyntaxNode, parent: Parser.SyntaxNode): string | undefined {\n for (let index = 0; index < parent.childCount; index += 1) {\n if (parent.child(index)?.id === node.id) {\n return parent.fieldNameForChild(index) ?? undefined;\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
|
package/dist/depDegree.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type Parser from 'tree-sitter';
|
|
2
|
-
/**
|
|
3
|
-
* Approximate def-use pairs of the function's subtree (see FunctionMetrics.depDegree): the number
|
|
4
|
-
* of variable reads with a preceding same-name definition visible at the read. Definitions are
|
|
5
|
-
* recognized token-wise (a variable directly followed by an assignment operator), structurally
|
|
6
|
-
* (declarator/assignment/loop-binding fields, so annotated declarations count), and positionally
|
|
7
|
-
* (parameters). Nested function subtrees are included, but their definitions are scoped: an inner
|
|
8
|
-
* function's parameters and locals cannot reach reads outside it, while inner reads still see
|
|
9
|
-
* outer definitions (closure captures). Reads through destructuring patterns and member accesses
|
|
10
|
-
* are not modeled; the approximation only needs to be stable, since the gate compares deltas.
|
|
11
|
-
*/
|
|
12
|
-
export declare function measureDepDegree(functionNode: Parser.SyntaxNode, isNestedFunctionBoundary: (node: Parser.SyntaxNode) => boolean): number;
|
package/dist/depDegree.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=new Set([`identifier`,`instance_variable`,`class_variable`,`global_variable`]),t=new Set([`=`,`:=`]),n=new Set([`+=`,`-=`,`*=`,`/=`,`%=`,`**=`,`//=`,`<<=`,`>>=`,`>>>=`,`&=`,`|=`,`^=`,`&&=`,`||=`,`??=`,`@=`,`&^=`]),r=new Map([[`variable_declarator`,`name`],[`let_declaration`,`pattern`],[`assignment`,`left`],[`var_spec`,`name`],[`init_declarator`,`declarator`],[`enhanced_for_statement`,`name`],[`for_statement`,`left`],[`for_in_statement`,`left`],[`for_in_clause`,`left`],[`for_range_loop`,`declarator`],[`for_expression`,`pattern`]]),i=new Set([`expression_list`,`pattern_list`,`tuple_pattern`]),a=new Set([`assignment`,`assignment_statement`,`short_var_declaration`,`for_statement`,`for_in_clause`,`range_clause`]),o=new Set([`type`,`value`]);function s(r,i){let a=[];c(r,void 0,``,{nextScopeId:0},i,a,!0);let o=new Map,s=0;for(let[r,i]of a.entries()){if(!e.has(i.node.type))continue;let c=i.node.text,p=a[r+1]?.node.text;if(p!==void 0&&n.has(p)){u(o.get(c),i.scope)&&(s+=1),l(o,c,i.scope);continue}if(p!==void 0&&t.has(p)||d(i)||f(i)){l(o,c,i.scope);continue}u(o.get(c),i.scope)&&(s+=1)}return s}function c(e,t,n,r,i,a,o){if(e.type===`comment`||e.type===`line_comment`||e.type===`block_comment`)return;if(e.childCount===0){a.push({node:e,fieldName:t,scope:n});return}let s=!o&&i(e)?`${n}/${r.nextScopeId++}`:n,l=e.walk();if(l.gotoFirstChild())do c(l.currentNode,l.currentFieldName??void 0,s,r,i,a,!1);while(l.gotoNextSibling())}function l(e,t,n){let r=e.get(t)??[];r.includes(n)||(r.push(n),e.set(t,r))}function u(e,t){return e!==void 0&&e.some(e=>t===e||t.startsWith(`${e}/`))}function d(e){let t=e.node.parent;if(!t)return!1;let n=r.get(t.type);if(n!==void 0&&n===e.fieldName)return!0;if(!i.has(t.type))return!1;let o=t.parent;return o!==null&&a.has(o.type)&&p(t,o)===`left`}function f(e){let t=e.node;for(let n=0;;n+=1){let r=t.parent;if(!r)return!1;let i=r.type.includes(`parameter`);if(n>=1&&!i&&!r.type.includes(`declarator`))return!1;let a=n===0?e.fieldName:p(t,r);if(a!==void 0&&o.has(a))return!1;if(i||n===0&&(a?.includes(`parameter`)??!1))return!0;t=r}}function p(e,t){for(let n=0;n<t.childCount;n+=1)if(t.child(n)?.id===e.id)return t.fieldNameForChild(n)??void 0}export{s as measureDepDegree};
|
|
2
|
-
//# sourceMappingURL=depDegree.js.map
|
package/dist/depDegree.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"depDegree.js","names":[],"sources":["../src/depDegree.ts"],"sourcesContent":["import type Parser from 'tree-sitter';\n\n/** Leaf node types treated as variable references by the def-use approximation. */\nconst variableNodeTypes = new Set(['identifier', 'instance_variable', 'class_variable', 'global_variable']);\n\n/** Tokens directly after a variable that write it without reading it (`x = 1`, `x := 1`). */\nconst pureAssignmentOperators = new Set(['=', ':=']);\n\n/** Tokens directly after a variable that read then write it (`x += 1` depends on x's definition). */\nconst compoundAssignmentOperators = new Set([\n '+=',\n '-=',\n '*=',\n '/=',\n '%=',\n '**=',\n '//=',\n '<<=',\n '>>=',\n '>>>=',\n '&=',\n '|=',\n '^=',\n '&&=',\n '||=',\n '??=',\n '@=',\n '&^=',\n]);\n\n/**\n * Parent type -> field under which an identifier is a definition target even when a type\n * annotation separates it from the `=` token (`const x: T = ...`, `let x: T = ...`,\n * `x: int = ...`, `var x T = ...`), plus loop bindings that carry no assignment token at all.\n */\nconst definitionFieldByParentType = new Map([\n ['variable_declarator', 'name'],\n ['let_declaration', 'pattern'],\n ['assignment', 'left'],\n ['var_spec', 'name'],\n ['init_declarator', 'declarator'],\n ['enhanced_for_statement', 'name'],\n ['for_statement', 'left'],\n ['for_in_statement', 'left'],\n ['for_in_clause', 'left'],\n ['for_range_loop', 'declarator'],\n ['for_expression', 'pattern'],\n]);\n\n/** Multi-target lists (`a, b = ...`, `a, b := ...`) whose holder's `left` field marks definitions. */\nconst definitionListNodeTypes = new Set(['expression_list', 'pattern_list', 'tuple_pattern']);\nconst definitionListHolderTypes = new Set([\n 'assignment',\n 'assignment_statement',\n 'short_var_declaration',\n 'for_statement',\n 'for_in_clause',\n 'range_clause',\n]);\n\n/** Parameter-position fields that annotate or initialize rather than bind (`x: T`, `x = default`). */\nconst nonBindingParameterFields = new Set(['type', 'value']);\n\n/** One leaf of the def-use walk: its field in the parent and its function-scope chain. */\ninterface DepDegreeLeaf {\n node: Parser.SyntaxNode;\n fieldName: string | undefined;\n /** Chain of nested-function scope ids below the measured function: '' for its own body, then '/0', '/0/1', ... */\n scope: string;\n}\n\n/**\n * Approximate def-use pairs of the function's subtree (see FunctionMetrics.depDegree): the number\n * of variable reads with a preceding same-name definition visible at the read. Definitions are\n * recognized token-wise (a variable directly followed by an assignment operator), structurally\n * (declarator/assignment/loop-binding fields, so annotated declarations count), and positionally\n * (parameters). Nested function subtrees are included, but their definitions are scoped: an inner\n * function's parameters and locals cannot reach reads outside it, while inner reads still see\n * outer definitions (closure captures). Reads through destructuring patterns and member accesses\n * are not modeled; the approximation only needs to be stable, since the gate compares deltas.\n */\nexport function measureDepDegree(\n functionNode: Parser.SyntaxNode,\n isNestedFunctionBoundary: (node: Parser.SyntaxNode) => boolean\n): number {\n const leaves: DepDegreeLeaf[] = [];\n collectDepDegreeLeaves(functionNode, undefined, '', { nextScopeId: 0 }, isNestedFunctionBoundary, leaves, true);\n const definitionScopesByName = new Map<string, string[]>();\n let pairs = 0;\n for (const [index, leaf] of leaves.entries()) {\n if (!variableNodeTypes.has(leaf.node.type)) {\n continue;\n }\n const name = leaf.node.text;\n const nextText = leaves[index + 1]?.node.text;\n if (nextText !== undefined && compoundAssignmentOperators.has(nextText)) {\n if (isDefinitionVisible(definitionScopesByName.get(name), leaf.scope)) {\n pairs += 1;\n }\n addDefinition(definitionScopesByName, name, leaf.scope);\n continue;\n }\n if (\n (nextText !== undefined && pureAssignmentOperators.has(nextText)) ||\n isStructuralDefinition(leaf) ||\n isParameterDefinition(leaf)\n ) {\n addDefinition(definitionScopesByName, name, leaf.scope);\n continue;\n }\n if (isDefinitionVisible(definitionScopesByName.get(name), leaf.scope)) {\n pairs += 1;\n }\n }\n return pairs;\n}\n\n/**\n * Collects non-comment leaves with their parent field — taken from one cursor pass, so a child of\n * a high-arity node (a long array literal) costs O(1) instead of an O(children) scan — and their\n * function-scope chain (each nested function boundary below the measured function opens a child\n * scope).\n */\nfunction collectDepDegreeLeaves(\n node: Parser.SyntaxNode,\n fieldName: string | undefined,\n scope: string,\n state: { nextScopeId: number },\n isNestedFunctionBoundary: (node: Parser.SyntaxNode) => boolean,\n leaves: DepDegreeLeaf[],\n isMeasuredRoot: boolean\n): void {\n if (node.type === 'comment' || node.type === 'line_comment' || node.type === 'block_comment') {\n return;\n }\n if (node.childCount === 0) {\n leaves.push({ node, fieldName, scope });\n return;\n }\n const childScope = !isMeasuredRoot && isNestedFunctionBoundary(node) ? `${scope}/${state.nextScopeId++}` : scope;\n const cursor = node.walk();\n if (cursor.gotoFirstChild()) {\n do {\n collectDepDegreeLeaves(\n cursor.currentNode,\n cursor.currentFieldName ?? undefined,\n childScope,\n state,\n isNestedFunctionBoundary,\n leaves,\n false\n );\n } while (cursor.gotoNextSibling());\n }\n}\n\nfunction addDefinition(definitionScopesByName: Map<string, string[]>, name: string, scope: string): void {\n const scopes = definitionScopesByName.get(name) ?? [];\n if (!scopes.includes(scope)) {\n scopes.push(scope);\n definitionScopesByName.set(name, scopes);\n }\n}\n\n/** A definition reaches a read only from the read's own or an enclosing function scope. */\nfunction isDefinitionVisible(definitionScopes: string[] | undefined, scope: string): boolean {\n return (\n definitionScopes !== undefined &&\n definitionScopes.some((definitionScope) => scope === definitionScope || scope.startsWith(`${definitionScope}/`))\n );\n}\n\nfunction isStructuralDefinition(leaf: DepDegreeLeaf): boolean {\n const parent = leaf.node.parent;\n if (!parent) {\n return false;\n }\n const definitionField = definitionFieldByParentType.get(parent.type);\n if (definitionField !== undefined && definitionField === leaf.fieldName) {\n return true;\n }\n if (!definitionListNodeTypes.has(parent.type)) {\n return false;\n }\n const holder = parent.parent;\n return holder !== null && definitionListHolderTypes.has(holder.type) && fieldNameInParent(parent, holder) === 'left';\n}\n\n/**\n * Whether the identifier binds a parameter: an ancestor reached through declarator wrappers (C/C++\n * function-pointer or array parameters) is a parameter-ish node, or it directly occupies a\n * parameter field (bare arrow-function/lambda parameters, catch-clause bindings). Type annotations\n * and default values inside parameter nodes bind nothing.\n */\nfunction isParameterDefinition(leaf: DepDegreeLeaf): boolean {\n let current = leaf.node;\n for (let depth = 0; ; depth += 1) {\n const parent = current.parent;\n if (!parent) {\n return false;\n }\n const parentIsParameterish = parent.type.includes('parameter');\n // Beyond the grandparent, only declarator wrappers keep climbing; checking this before the\n // field lookup also keeps reads inside high-arity nodes (long array literals) O(1).\n if (depth >= 1 && !parentIsParameterish && !parent.type.includes('declarator')) {\n return false;\n }\n const field = depth === 0 ? leaf.fieldName : fieldNameInParent(current, parent);\n if (field !== undefined && nonBindingParameterFields.has(field)) {\n return false;\n }\n if (parentIsParameterish || (depth === 0 && (field?.includes('parameter') ?? false))) {\n return true;\n }\n current = parent;\n }\n}\n\n/** Only called with small-arity parents (declarator wrappers, definition-list holders). */\nfunction fieldNameInParent(node: Parser.SyntaxNode, parent: Parser.SyntaxNode): string | undefined {\n for (let index = 0; index < parent.childCount; index += 1) {\n if (parent.child(index)?.id === node.id) {\n return parent.fieldNameForChild(index) ?? undefined;\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
|
package/dist/ncss.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";const e=new Set([`comment`,`line_comment`,`block_comment`]),t=new Set([`attribute_item`,`inner_attribute_item`,`empty_statement`,`heredoc_body`,`parenthesized_statements`]),n=new Set([`property_signature`,`method_signature`,`index_signature`,`construct_signature`,`call_signature`]),r=new Set([`else_clause`,`elif_clause`,`else`,`elsif`]),i=new Set([`if_statement`,`if_expression`]),a=new Set([`struct_specifier`,`enum_specifier`,`union_specifier`,`class_specifier`]),o=new WeakMap;function s(e){o.delete(e)}function c(e){let t=o.get(e);return t||(t={countable:new Set(e.ncssNodeTypes),containers:new Set(e.ncssContainerNodeTypes)},o.set(e,t)),t}function l(e,t){let{countable:n,containers:r}=c(t),i=0;function a(e){i+=u(e,n,r);for(let t of e.children)a(t)}return a(e),i}function u(n,r,a){if(!n.isNamed||e.has(n.type)||h(n))return 0;let o=0,s=f(n,a)&&!a.has(n.type)&&!t.has(n.type);return(d(n,r)||s||g(n))&&!p(n,r)&&(o+=1),i.has(n.type)&&(o+=y(n).length),o}function d(e,t){return t.has(e.type)?a.has(e.type)?e.childForFieldName(`body`)!==null:e.type!==`resource`||e.childForFieldName(`name`)!==null:!1}function f(e,t){let n=e.parent;for(;n&&n.type===`parenthesized_statements`;)n=n.parent;return n!==null&&t.has(n.type)}function p(e,t){if(e.type!==`export_statement`)return!1;let n=e.childForFieldName(`declaration`);return n!==null&&(t.has(n.type)||n.type===`internal_module`||n.type===`ambient_declaration`)}const m=new Set([`init`,`initializer`,`condition`,`update`,`increment`]);function h(e){let t=e.parent;if(!t)return!1;if(t.type===`init_statement`&&t.parent?.type===`for_range_loop`)return!0;if(t.type!==`for_statement`&&t.type!==`for_clause`&&t.type!==`for_range_loop`)return!1;for(let n=0;n<t.childCount;n+=1)if(t.child(n)?.id===e.id)return m.has(t.fieldNameForChild(n)??``);return!1}function g(e){let t=e.parent?.type;return e.type===`block`&&t===`class_body`||n.has(e.type)&&t===`interface_body`||t===`match_arm`&&e.type!==`block`&&v(e,`value`)||e.type===`method_signature`&&t===`class_body`||e.type===`internal_module`&&t!==`expression_statement`||(t===`method`||t===`singleton_method`)&&e.type!==`body_statement`&&v(e,`body`)?!0:e.type===`friend_declaration`?!e.namedChildren.some(e=>e.type===`declaration`||e.type===`function_definition`):e.type===`macro_invocation`&&(t===`source_file`||t===`declaration_list`)?!0:e.type===`field_declaration`&&t===`field_declaration_list`?_(e.parent?.parent,`struct_type`):e.type===`method_elem`||e.type===`method_spec`||e.type===`type_elem`?_(e.parent,`interface_type`):!1}function _(e,t){return e?.type===t?e.parent?.type===`type_spec`||e.parent?.type===`type_alias`:!1}function v(e,t){let n=e.parent;if(!n)return!1;for(let r=0;r<n.childCount;r+=1)if(n.child(r)?.id===e.id)return n.fieldNameForChild(r)===t;return!1}function y(e){let t=[];for(let n=0;n<e.childCount;n+=1){let i=e.child(n);i&&e.fieldNameForChild(n)===`alternative`&&!r.has(i.type)&&t.push(i)}return t}exports.commentNodeTypes=e,exports.countNcss=l,exports.getNcssSets=c,exports.invalidateNcssSetsCache=s,exports.ncssContribution=u;
|
|
2
|
-
//# sourceMappingURL=ncss.cjs.map
|
package/dist/ncss.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ncss.cjs","names":[],"sources":["../src/ncss.ts"],"sourcesContent":["import type Parser from 'tree-sitter';\nimport type { LanguageDefinition } from './types.js';\n\nexport const commentNodeTypes = new Set(['comment', 'line_comment', 'block_comment']);\n\n// Nodes never counted positionally inside NCSS containers: metadata, empty statements, Ruby\n// heredoc bodies (tree-sitter emits them as siblings of the statement that opened the heredoc),\n// and Ruby statement parentheses (transparent wrappers whose children count instead).\nconst positionalExclusionTypes = new Set([\n 'attribute_item',\n 'inner_attribute_item',\n 'empty_statement',\n 'heredoc_body',\n 'parenthesized_statements',\n]);\n\n// TypeScript interface members count like Java interface members, but the same node types appear\n// inside object-type annotations (`let x: { a: number }`), which are part of one declaration, so\n// they only count directly under an interface body.\nconst interfaceMemberNodeTypes = new Set([\n 'property_signature',\n 'method_signature',\n 'index_signature',\n 'construct_signature',\n 'call_signature',\n]);\n\n// An if-branch wrapped in one of these already counts through ncssNodeTypes; a bare `alternative`\n// (Java/Go put the else branch directly in the field) needs the extra `else` count instead.\nconst elseClauseNodeTypes = new Set(['else_clause', 'elif_clause', 'else', 'elsif']);\n\nconst ifNodeTypes = new Set(['if_statement', 'if_expression']);\n\n// C/C++ type specifiers only declare something when they carry a body (`struct S { ... }`);\n// without one they are mere type references inside other declarations.\nconst bodylessNcssSpecifierTypes = new Set([\n 'struct_specifier',\n 'enum_specifier',\n 'union_specifier',\n 'class_specifier',\n]);\n\n/**\n * Counts non-commenting source statements (NCSS) in the subtree, PMD-style: one per declaration,\n * statement, and clause (`else`, `case`/`default` label, `catch`, `finally`, try-with-resources\n * resource); `try` itself, braces, blank lines, and comments count 0.\n */\nexport interface NcssSets {\n countable: Set<string>;\n containers: Set<string>;\n}\n\n// Cached per language: countNcss runs once per function plus once per file, so per-call Set\n// construction would add a measurable constant factor on large files.\nconst ncssSetsCache = new WeakMap<LanguageDefinition, NcssSets>();\n\n/** Drops the cached sets so a re-registered (possibly mutated) definition rebuilds them. */\nexport function invalidateNcssSetsCache(language: LanguageDefinition): void {\n ncssSetsCache.delete(language);\n}\n\nexport function getNcssSets(language: LanguageDefinition): NcssSets {\n let sets = ncssSetsCache.get(language);\n if (!sets) {\n sets = { countable: new Set(language.ncssNodeTypes), containers: new Set(language.ncssContainerNodeTypes) };\n ncssSetsCache.set(language, sets);\n }\n return sets;\n}\n\nexport function countNcss(node: Parser.SyntaxNode, language: LanguageDefinition): number {\n const { countable, containers } = getNcssSets(language);\n let count = 0;\n\n function visit(current: Parser.SyntaxNode): void {\n count += ncssContribution(current, countable, containers);\n for (const child of current.children) {\n visit(child);\n }\n }\n\n visit(node);\n return count;\n}\n\nexport function ncssContribution(node: Parser.SyntaxNode, countable: Set<string>, containers: Set<string>): number {\n if (!node.isNamed || commentNodeTypes.has(node.type) || isForHeaderNode(node)) {\n return 0;\n }\n\n let contribution = 0;\n const positional =\n isInContainerPosition(node, containers) && !containers.has(node.type) && !positionalExclusionTypes.has(node.type);\n if (\n (countsThroughNodeType(node, countable) || positional || countsContextually(node)) &&\n !isDeclarationWrapper(node, countable)\n ) {\n contribution += 1;\n }\n\n // A bare else branch (Java/Go `alternative:` without an else-clause wrapper) counts 1 like the\n // `else` keyword does in PMD; an `else if` chain charges the nested if separately on top.\n if (ifNodeTypes.has(node.type)) {\n contribution += findBareAlternatives(node).length;\n }\n\n return contribution;\n}\n\nfunction countsThroughNodeType(node: Parser.SyntaxNode, countable: Set<string>): boolean {\n if (!countable.has(node.type)) {\n return false;\n }\n if (bodylessNcssSpecifierTypes.has(node.type)) {\n return node.childForFieldName('body') !== null;\n }\n // A try-with-resources `resource` counts only when it declares a variable; `try (r)` reuses an\n // existing one and adds no statement (matching PMD).\n if (node.type === 'resource') {\n return node.childForFieldName('name') !== null;\n }\n return true;\n}\n\n/**\n * Direct container children count positionally; Ruby's `(foo; bar)` statement parentheses are\n * transparent (through arbitrary nesting), so their children count when the parentheses\n * themselves sit in a container.\n */\nfunction isInContainerPosition(node: Parser.SyntaxNode, containers: Set<string>): boolean {\n let ancestor = node.parent;\n while (ancestor && ancestor.type === 'parenthesized_statements') {\n ancestor = ancestor.parent;\n }\n return ancestor !== null && containers.has(ancestor.type);\n}\n\n/**\n * `export const x = 1` nests a countable declaration inside `export_statement`; only the inner\n * declaration counts, mirroring how PMD counts one statement per declared entity. The nested\n * declaration may also count contextually (`export namespace N {}` → internal_module) or sit one\n * level deeper (`export declare function f(): void;` → ambient_declaration > function_signature).\n */\nfunction isDeclarationWrapper(node: Parser.SyntaxNode, countable: Set<string>): boolean {\n if (node.type !== 'export_statement') {\n return false;\n }\n const declaration = node.childForFieldName('declaration');\n return (\n declaration !== null &&\n (countable.has(declaration.type) ||\n declaration.type === 'internal_module' ||\n declaration.type === 'ambient_declaration')\n );\n}\n\nconst forHeaderFieldNames = new Set(['init', 'initializer', 'condition', 'update', 'increment']);\n\n/**\n * Statement-shaped nodes in a `for` header (`for (int i = 0; i < n; i++)`) are part of the loop\n * statement, which already counts; PMD does not count them separately. JavaScript parses the\n * condition as an `expression_statement` and Go parses the update as an `inc_statement`, so all\n * header fields must be excluded, not just the initializer.\n */\nfunction isForHeaderNode(node: Parser.SyntaxNode): boolean {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n // C++20 range-for initializers nest one level deeper: for_range_loop > init_statement > node.\n if (parent.type === 'init_statement' && parent.parent?.type === 'for_range_loop') {\n return true;\n }\n if (parent.type !== 'for_statement' && parent.type !== 'for_clause' && parent.type !== 'for_range_loop') {\n return false;\n }\n for (let index = 0; index < parent.childCount; index += 1) {\n if (parent.child(index)?.id === node.id) {\n return forHeaderFieldNames.has(parent.fieldNameForChild(index) ?? '');\n }\n }\n return false;\n}\n\n/** Statements only countable by their position: constructs without a dedicated statement node. */\nfunction countsContextually(node: Parser.SyntaxNode): boolean {\n const parentType = node.parent?.type;\n // A Java instance initializer is a bare `block` in the class body; PMD counts it like the\n // `static_initializer` declaration it parallels.\n if (node.type === 'block' && parentType === 'class_body') {\n return true;\n }\n // TypeScript interface members (see interfaceMemberNodeTypes).\n if (interfaceMemberNodeTypes.has(node.type) && parentType === 'interface_body') {\n return true;\n }\n // A braceless Rust match-arm body (`1 => foo()`) has no expression_statement wrapper; count the\n // value expression so braced and unbraced arms measure alike.\n if (parentType === 'match_arm' && node.type !== 'block' && isFieldOfParent(node, 'value')) {\n return true;\n }\n // A TypeScript class-body method overload signature declares a member like its interface twin.\n if (node.type === 'method_signature' && parentType === 'class_body') {\n return true;\n }\n // An ambient `declare namespace M { ... }` is a bare `internal_module`; the non-ambient\n // `namespace N { ... }` is wrapped in an `expression_statement`, which already counts.\n if (node.type === 'internal_module' && parentType !== 'expression_statement') {\n return true;\n }\n // A Ruby endless method (`def f(x) = expr`) stores its single-statement body directly in the\n // `body` field instead of a positional `body_statement` container.\n if (\n (parentType === 'method' || parentType === 'singleton_method') &&\n node.type !== 'body_statement' &&\n isFieldOfParent(node, 'body')\n ) {\n return true;\n }\n // C++ `friend class X;` declares on its own; `friend void g() { ... }` merely wraps a counted\n // definition.\n if (node.type === 'friend_declaration') {\n return !node.namedChildren.some((child) => child.type === 'declaration' || child.type === 'function_definition');\n }\n // A Rust item-position macro invocation (`foo! {}` at module level) has no expression_statement\n // wrapper; the semicolon form does and already counts through it.\n if (node.type === 'macro_invocation' && (parentType === 'source_file' || parentType === 'declaration_list')) {\n return true;\n }\n // Go struct fields and interface members count like other languages' member declarations, but\n // only inside a named type declaration; inline anonymous types (`var x struct{ ... }`,\n // `func f(h interface{ ... })`) are part of one declaration.\n if (node.type === 'field_declaration' && parentType === 'field_declaration_list') {\n return isGoDeclaredTypeBody(node.parent?.parent, 'struct_type');\n }\n if (node.type === 'method_elem' || node.type === 'method_spec' || node.type === 'type_elem') {\n return isGoDeclaredTypeBody(node.parent, 'interface_type');\n }\n return false;\n}\n\nfunction isGoDeclaredTypeBody(typeNode: Parser.SyntaxNode | null | undefined, expectedType: string): boolean {\n if (typeNode?.type !== expectedType) {\n return false;\n }\n return typeNode.parent?.type === 'type_spec' || typeNode.parent?.type === 'type_alias';\n}\n\nfunction isFieldOfParent(node: Parser.SyntaxNode, fieldName: string): boolean {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n for (let index = 0; index < parent.childCount; index += 1) {\n if (parent.child(index)?.id === node.id) {\n return parent.fieldNameForChild(index) === fieldName;\n }\n }\n return false;\n}\n\nfunction findBareAlternatives(node: Parser.SyntaxNode): Parser.SyntaxNode[] {\n const alternatives: Parser.SyntaxNode[] = [];\n for (let index = 0; index < node.childCount; index += 1) {\n const child = node.child(index);\n if (child && node.fieldNameForChild(index) === 'alternative' && !elseClauseNodeTypes.has(child.type)) {\n alternatives.push(child);\n }\n }\n return alternatives;\n}\n"],"mappings":"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"}
|
package/dist/ncss.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type Parser from 'tree-sitter';
|
|
2
|
-
import type { LanguageDefinition } from './types.js';
|
|
3
|
-
export declare const commentNodeTypes: Set<string>;
|
|
4
|
-
/**
|
|
5
|
-
* Counts non-commenting source statements (NCSS) in the subtree, PMD-style: one per declaration,
|
|
6
|
-
* statement, and clause (`else`, `case`/`default` label, `catch`, `finally`, try-with-resources
|
|
7
|
-
* resource); `try` itself, braces, blank lines, and comments count 0.
|
|
8
|
-
*/
|
|
9
|
-
export interface NcssSets {
|
|
10
|
-
countable: Set<string>;
|
|
11
|
-
containers: Set<string>;
|
|
12
|
-
}
|
|
13
|
-
/** Drops the cached sets so a re-registered (possibly mutated) definition rebuilds them. */
|
|
14
|
-
export declare function invalidateNcssSetsCache(language: LanguageDefinition): void;
|
|
15
|
-
export declare function getNcssSets(language: LanguageDefinition): NcssSets;
|
|
16
|
-
export declare function countNcss(node: Parser.SyntaxNode, language: LanguageDefinition): number;
|
|
17
|
-
export declare function ncssContribution(node: Parser.SyntaxNode, countable: Set<string>, containers: Set<string>): number;
|
package/dist/ncss.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=new Set([`comment`,`line_comment`,`block_comment`]),t=new Set([`attribute_item`,`inner_attribute_item`,`empty_statement`,`heredoc_body`,`parenthesized_statements`]),n=new Set([`property_signature`,`method_signature`,`index_signature`,`construct_signature`,`call_signature`]),r=new Set([`else_clause`,`elif_clause`,`else`,`elsif`]),i=new Set([`if_statement`,`if_expression`]),a=new Set([`struct_specifier`,`enum_specifier`,`union_specifier`,`class_specifier`]),o=new WeakMap;function s(e){o.delete(e)}function c(e){let t=o.get(e);return t||(t={countable:new Set(e.ncssNodeTypes),containers:new Set(e.ncssContainerNodeTypes)},o.set(e,t)),t}function l(e,t){let{countable:n,containers:r}=c(t),i=0;function a(e){i+=u(e,n,r);for(let t of e.children)a(t)}return a(e),i}function u(n,r,a){if(!n.isNamed||e.has(n.type)||h(n))return 0;let o=0,s=f(n,a)&&!a.has(n.type)&&!t.has(n.type);return(d(n,r)||s||g(n))&&!p(n,r)&&(o+=1),i.has(n.type)&&(o+=y(n).length),o}function d(e,t){return t.has(e.type)?a.has(e.type)?e.childForFieldName(`body`)!==null:e.type!==`resource`||e.childForFieldName(`name`)!==null:!1}function f(e,t){let n=e.parent;for(;n&&n.type===`parenthesized_statements`;)n=n.parent;return n!==null&&t.has(n.type)}function p(e,t){if(e.type!==`export_statement`)return!1;let n=e.childForFieldName(`declaration`);return n!==null&&(t.has(n.type)||n.type===`internal_module`||n.type===`ambient_declaration`)}const m=new Set([`init`,`initializer`,`condition`,`update`,`increment`]);function h(e){let t=e.parent;if(!t)return!1;if(t.type===`init_statement`&&t.parent?.type===`for_range_loop`)return!0;if(t.type!==`for_statement`&&t.type!==`for_clause`&&t.type!==`for_range_loop`)return!1;for(let n=0;n<t.childCount;n+=1)if(t.child(n)?.id===e.id)return m.has(t.fieldNameForChild(n)??``);return!1}function g(e){let t=e.parent?.type;return e.type===`block`&&t===`class_body`||n.has(e.type)&&t===`interface_body`||t===`match_arm`&&e.type!==`block`&&v(e,`value`)||e.type===`method_signature`&&t===`class_body`||e.type===`internal_module`&&t!==`expression_statement`||(t===`method`||t===`singleton_method`)&&e.type!==`body_statement`&&v(e,`body`)?!0:e.type===`friend_declaration`?!e.namedChildren.some(e=>e.type===`declaration`||e.type===`function_definition`):e.type===`macro_invocation`&&(t===`source_file`||t===`declaration_list`)?!0:e.type===`field_declaration`&&t===`field_declaration_list`?_(e.parent?.parent,`struct_type`):e.type===`method_elem`||e.type===`method_spec`||e.type===`type_elem`?_(e.parent,`interface_type`):!1}function _(e,t){return e?.type===t?e.parent?.type===`type_spec`||e.parent?.type===`type_alias`:!1}function v(e,t){let n=e.parent;if(!n)return!1;for(let r=0;r<n.childCount;r+=1)if(n.child(r)?.id===e.id)return n.fieldNameForChild(r)===t;return!1}function y(e){let t=[];for(let n=0;n<e.childCount;n+=1){let i=e.child(n);i&&e.fieldNameForChild(n)===`alternative`&&!r.has(i.type)&&t.push(i)}return t}export{e as commentNodeTypes,l as countNcss,c as getNcssSets,s as invalidateNcssSetsCache,u as ncssContribution};
|
|
2
|
-
//# sourceMappingURL=ncss.js.map
|
package/dist/ncss.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ncss.js","names":[],"sources":["../src/ncss.ts"],"sourcesContent":["import type Parser from 'tree-sitter';\nimport type { LanguageDefinition } from './types.js';\n\nexport const commentNodeTypes = new Set(['comment', 'line_comment', 'block_comment']);\n\n// Nodes never counted positionally inside NCSS containers: metadata, empty statements, Ruby\n// heredoc bodies (tree-sitter emits them as siblings of the statement that opened the heredoc),\n// and Ruby statement parentheses (transparent wrappers whose children count instead).\nconst positionalExclusionTypes = new Set([\n 'attribute_item',\n 'inner_attribute_item',\n 'empty_statement',\n 'heredoc_body',\n 'parenthesized_statements',\n]);\n\n// TypeScript interface members count like Java interface members, but the same node types appear\n// inside object-type annotations (`let x: { a: number }`), which are part of one declaration, so\n// they only count directly under an interface body.\nconst interfaceMemberNodeTypes = new Set([\n 'property_signature',\n 'method_signature',\n 'index_signature',\n 'construct_signature',\n 'call_signature',\n]);\n\n// An if-branch wrapped in one of these already counts through ncssNodeTypes; a bare `alternative`\n// (Java/Go put the else branch directly in the field) needs the extra `else` count instead.\nconst elseClauseNodeTypes = new Set(['else_clause', 'elif_clause', 'else', 'elsif']);\n\nconst ifNodeTypes = new Set(['if_statement', 'if_expression']);\n\n// C/C++ type specifiers only declare something when they carry a body (`struct S { ... }`);\n// without one they are mere type references inside other declarations.\nconst bodylessNcssSpecifierTypes = new Set([\n 'struct_specifier',\n 'enum_specifier',\n 'union_specifier',\n 'class_specifier',\n]);\n\n/**\n * Counts non-commenting source statements (NCSS) in the subtree, PMD-style: one per declaration,\n * statement, and clause (`else`, `case`/`default` label, `catch`, `finally`, try-with-resources\n * resource); `try` itself, braces, blank lines, and comments count 0.\n */\nexport interface NcssSets {\n countable: Set<string>;\n containers: Set<string>;\n}\n\n// Cached per language: countNcss runs once per function plus once per file, so per-call Set\n// construction would add a measurable constant factor on large files.\nconst ncssSetsCache = new WeakMap<LanguageDefinition, NcssSets>();\n\n/** Drops the cached sets so a re-registered (possibly mutated) definition rebuilds them. */\nexport function invalidateNcssSetsCache(language: LanguageDefinition): void {\n ncssSetsCache.delete(language);\n}\n\nexport function getNcssSets(language: LanguageDefinition): NcssSets {\n let sets = ncssSetsCache.get(language);\n if (!sets) {\n sets = { countable: new Set(language.ncssNodeTypes), containers: new Set(language.ncssContainerNodeTypes) };\n ncssSetsCache.set(language, sets);\n }\n return sets;\n}\n\nexport function countNcss(node: Parser.SyntaxNode, language: LanguageDefinition): number {\n const { countable, containers } = getNcssSets(language);\n let count = 0;\n\n function visit(current: Parser.SyntaxNode): void {\n count += ncssContribution(current, countable, containers);\n for (const child of current.children) {\n visit(child);\n }\n }\n\n visit(node);\n return count;\n}\n\nexport function ncssContribution(node: Parser.SyntaxNode, countable: Set<string>, containers: Set<string>): number {\n if (!node.isNamed || commentNodeTypes.has(node.type) || isForHeaderNode(node)) {\n return 0;\n }\n\n let contribution = 0;\n const positional =\n isInContainerPosition(node, containers) && !containers.has(node.type) && !positionalExclusionTypes.has(node.type);\n if (\n (countsThroughNodeType(node, countable) || positional || countsContextually(node)) &&\n !isDeclarationWrapper(node, countable)\n ) {\n contribution += 1;\n }\n\n // A bare else branch (Java/Go `alternative:` without an else-clause wrapper) counts 1 like the\n // `else` keyword does in PMD; an `else if` chain charges the nested if separately on top.\n if (ifNodeTypes.has(node.type)) {\n contribution += findBareAlternatives(node).length;\n }\n\n return contribution;\n}\n\nfunction countsThroughNodeType(node: Parser.SyntaxNode, countable: Set<string>): boolean {\n if (!countable.has(node.type)) {\n return false;\n }\n if (bodylessNcssSpecifierTypes.has(node.type)) {\n return node.childForFieldName('body') !== null;\n }\n // A try-with-resources `resource` counts only when it declares a variable; `try (r)` reuses an\n // existing one and adds no statement (matching PMD).\n if (node.type === 'resource') {\n return node.childForFieldName('name') !== null;\n }\n return true;\n}\n\n/**\n * Direct container children count positionally; Ruby's `(foo; bar)` statement parentheses are\n * transparent (through arbitrary nesting), so their children count when the parentheses\n * themselves sit in a container.\n */\nfunction isInContainerPosition(node: Parser.SyntaxNode, containers: Set<string>): boolean {\n let ancestor = node.parent;\n while (ancestor && ancestor.type === 'parenthesized_statements') {\n ancestor = ancestor.parent;\n }\n return ancestor !== null && containers.has(ancestor.type);\n}\n\n/**\n * `export const x = 1` nests a countable declaration inside `export_statement`; only the inner\n * declaration counts, mirroring how PMD counts one statement per declared entity. The nested\n * declaration may also count contextually (`export namespace N {}` → internal_module) or sit one\n * level deeper (`export declare function f(): void;` → ambient_declaration > function_signature).\n */\nfunction isDeclarationWrapper(node: Parser.SyntaxNode, countable: Set<string>): boolean {\n if (node.type !== 'export_statement') {\n return false;\n }\n const declaration = node.childForFieldName('declaration');\n return (\n declaration !== null &&\n (countable.has(declaration.type) ||\n declaration.type === 'internal_module' ||\n declaration.type === 'ambient_declaration')\n );\n}\n\nconst forHeaderFieldNames = new Set(['init', 'initializer', 'condition', 'update', 'increment']);\n\n/**\n * Statement-shaped nodes in a `for` header (`for (int i = 0; i < n; i++)`) are part of the loop\n * statement, which already counts; PMD does not count them separately. JavaScript parses the\n * condition as an `expression_statement` and Go parses the update as an `inc_statement`, so all\n * header fields must be excluded, not just the initializer.\n */\nfunction isForHeaderNode(node: Parser.SyntaxNode): boolean {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n // C++20 range-for initializers nest one level deeper: for_range_loop > init_statement > node.\n if (parent.type === 'init_statement' && parent.parent?.type === 'for_range_loop') {\n return true;\n }\n if (parent.type !== 'for_statement' && parent.type !== 'for_clause' && parent.type !== 'for_range_loop') {\n return false;\n }\n for (let index = 0; index < parent.childCount; index += 1) {\n if (parent.child(index)?.id === node.id) {\n return forHeaderFieldNames.has(parent.fieldNameForChild(index) ?? '');\n }\n }\n return false;\n}\n\n/** Statements only countable by their position: constructs without a dedicated statement node. */\nfunction countsContextually(node: Parser.SyntaxNode): boolean {\n const parentType = node.parent?.type;\n // A Java instance initializer is a bare `block` in the class body; PMD counts it like the\n // `static_initializer` declaration it parallels.\n if (node.type === 'block' && parentType === 'class_body') {\n return true;\n }\n // TypeScript interface members (see interfaceMemberNodeTypes).\n if (interfaceMemberNodeTypes.has(node.type) && parentType === 'interface_body') {\n return true;\n }\n // A braceless Rust match-arm body (`1 => foo()`) has no expression_statement wrapper; count the\n // value expression so braced and unbraced arms measure alike.\n if (parentType === 'match_arm' && node.type !== 'block' && isFieldOfParent(node, 'value')) {\n return true;\n }\n // A TypeScript class-body method overload signature declares a member like its interface twin.\n if (node.type === 'method_signature' && parentType === 'class_body') {\n return true;\n }\n // An ambient `declare namespace M { ... }` is a bare `internal_module`; the non-ambient\n // `namespace N { ... }` is wrapped in an `expression_statement`, which already counts.\n if (node.type === 'internal_module' && parentType !== 'expression_statement') {\n return true;\n }\n // A Ruby endless method (`def f(x) = expr`) stores its single-statement body directly in the\n // `body` field instead of a positional `body_statement` container.\n if (\n (parentType === 'method' || parentType === 'singleton_method') &&\n node.type !== 'body_statement' &&\n isFieldOfParent(node, 'body')\n ) {\n return true;\n }\n // C++ `friend class X;` declares on its own; `friend void g() { ... }` merely wraps a counted\n // definition.\n if (node.type === 'friend_declaration') {\n return !node.namedChildren.some((child) => child.type === 'declaration' || child.type === 'function_definition');\n }\n // A Rust item-position macro invocation (`foo! {}` at module level) has no expression_statement\n // wrapper; the semicolon form does and already counts through it.\n if (node.type === 'macro_invocation' && (parentType === 'source_file' || parentType === 'declaration_list')) {\n return true;\n }\n // Go struct fields and interface members count like other languages' member declarations, but\n // only inside a named type declaration; inline anonymous types (`var x struct{ ... }`,\n // `func f(h interface{ ... })`) are part of one declaration.\n if (node.type === 'field_declaration' && parentType === 'field_declaration_list') {\n return isGoDeclaredTypeBody(node.parent?.parent, 'struct_type');\n }\n if (node.type === 'method_elem' || node.type === 'method_spec' || node.type === 'type_elem') {\n return isGoDeclaredTypeBody(node.parent, 'interface_type');\n }\n return false;\n}\n\nfunction isGoDeclaredTypeBody(typeNode: Parser.SyntaxNode | null | undefined, expectedType: string): boolean {\n if (typeNode?.type !== expectedType) {\n return false;\n }\n return typeNode.parent?.type === 'type_spec' || typeNode.parent?.type === 'type_alias';\n}\n\nfunction isFieldOfParent(node: Parser.SyntaxNode, fieldName: string): boolean {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n for (let index = 0; index < parent.childCount; index += 1) {\n if (parent.child(index)?.id === node.id) {\n return parent.fieldNameForChild(index) === fieldName;\n }\n }\n return false;\n}\n\nfunction findBareAlternatives(node: Parser.SyntaxNode): Parser.SyntaxNode[] {\n const alternatives: Parser.SyntaxNode[] = [];\n for (let index = 0; index < node.childCount; index += 1) {\n const child = node.child(index);\n if (child && node.fieldNameForChild(index) === 'alternative' && !elseClauseNodeTypes.has(child.type)) {\n alternatives.push(child);\n }\n }\n return alternatives;\n}\n"],"mappings":"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"}
|