@shrkcrft/graph 0.1.0-alpha.10
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/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/indexer/detect-workspace.d.ts +18 -0
- package/dist/indexer/detect-workspace.d.ts.map +1 -0
- package/dist/indexer/detect-workspace.js +80 -0
- package/dist/indexer/extract-csharp-file.d.ts +27 -0
- package/dist/indexer/extract-csharp-file.d.ts.map +1 -0
- package/dist/indexer/extract-csharp-file.js +163 -0
- package/dist/indexer/extract-dart-file.d.ts +28 -0
- package/dist/indexer/extract-dart-file.d.ts.map +1 -0
- package/dist/indexer/extract-dart-file.js +167 -0
- package/dist/indexer/extract-elixir-file.d.ts +27 -0
- package/dist/indexer/extract-elixir-file.d.ts.map +1 -0
- package/dist/indexer/extract-elixir-file.js +164 -0
- package/dist/indexer/extract-go-file.d.ts +28 -0
- package/dist/indexer/extract-go-file.d.ts.map +1 -0
- package/dist/indexer/extract-go-file.js +156 -0
- package/dist/indexer/extract-java-file.d.ts +25 -0
- package/dist/indexer/extract-java-file.d.ts.map +1 -0
- package/dist/indexer/extract-java-file.js +140 -0
- package/dist/indexer/extract-kotlin-file.d.ts +20 -0
- package/dist/indexer/extract-kotlin-file.d.ts.map +1 -0
- package/dist/indexer/extract-kotlin-file.js +158 -0
- package/dist/indexer/extract-php-file.d.ts +26 -0
- package/dist/indexer/extract-php-file.d.ts.map +1 -0
- package/dist/indexer/extract-php-file.js +161 -0
- package/dist/indexer/extract-python-file.d.ts +30 -0
- package/dist/indexer/extract-python-file.d.ts.map +1 -0
- package/dist/indexer/extract-python-file.js +196 -0
- package/dist/indexer/extract-ruby-file.d.ts +29 -0
- package/dist/indexer/extract-ruby-file.d.ts.map +1 -0
- package/dist/indexer/extract-ruby-file.js +151 -0
- package/dist/indexer/extract-rust-file.d.ts +27 -0
- package/dist/indexer/extract-rust-file.d.ts.map +1 -0
- package/dist/indexer/extract-rust-file.js +186 -0
- package/dist/indexer/extract-swift-file.d.ts +27 -0
- package/dist/indexer/extract-swift-file.d.ts.map +1 -0
- package/dist/indexer/extract-swift-file.js +168 -0
- package/dist/indexer/extract-ts-file.d.ts +79 -0
- package/dist/indexer/extract-ts-file.d.ts.map +1 -0
- package/dist/indexer/extract-ts-file.js +403 -0
- package/dist/indexer/incremental-updater.d.ts +41 -0
- package/dist/indexer/incremental-updater.d.ts.map +1 -0
- package/dist/indexer/incremental-updater.js +395 -0
- package/dist/indexer/index-builder.d.ts +23 -0
- package/dist/indexer/index-builder.d.ts.map +1 -0
- package/dist/indexer/index-builder.js +289 -0
- package/dist/indexer/resolve-imports.d.ts +36 -0
- package/dist/indexer/resolve-imports.d.ts.map +1 -0
- package/dist/indexer/resolve-imports.js +144 -0
- package/dist/indexer/unresolved-imports.d.ts +20 -0
- package/dist/indexer/unresolved-imports.d.ts.map +1 -0
- package/dist/indexer/unresolved-imports.js +32 -0
- package/dist/query/cycle-detection.d.ts +40 -0
- package/dist/query/cycle-detection.d.ts.map +1 -0
- package/dist/query/cycle-detection.js +135 -0
- package/dist/query/query-api.d.ts +87 -0
- package/dist/query/query-api.d.ts.map +1 -0
- package/dist/query/query-api.js +232 -0
- package/dist/schema/edge-kind.d.ts +31 -0
- package/dist/schema/edge-kind.d.ts.map +1 -0
- package/dist/schema/edge-kind.js +35 -0
- package/dist/schema/edge.d.ts +22 -0
- package/dist/schema/edge.d.ts.map +1 -0
- package/dist/schema/edge.js +1 -0
- package/dist/schema/file-fingerprint.d.ts +22 -0
- package/dist/schema/file-fingerprint.d.ts.map +1 -0
- package/dist/schema/file-fingerprint.js +1 -0
- package/dist/schema/graph-snapshot.d.ts +18 -0
- package/dist/schema/graph-snapshot.d.ts.map +1 -0
- package/dist/schema/graph-snapshot.js +1 -0
- package/dist/schema/manifest.d.ts +47 -0
- package/dist/schema/manifest.d.ts.map +1 -0
- package/dist/schema/manifest.js +1 -0
- package/dist/schema/node-kind.d.ts +21 -0
- package/dist/schema/node-kind.d.ts.map +1 -0
- package/dist/schema/node-kind.js +27 -0
- package/dist/schema/node.d.ts +26 -0
- package/dist/schema/node.d.ts.map +1 -0
- package/dist/schema/node.js +1 -0
- package/dist/schema/schema-version.d.ts +10 -0
- package/dist/schema/schema-version.d.ts.map +1 -0
- package/dist/schema/schema-version.js +8 -0
- package/dist/store/file-fingerprint.d.ts +8 -0
- package/dist/store/file-fingerprint.d.ts.map +1 -0
- package/dist/store/file-fingerprint.js +64 -0
- package/dist/store/graph-store.d.ts +48 -0
- package/dist/store/graph-store.d.ts.map +1 -0
- package/dist/store/graph-store.js +194 -0
- package/package.json +54 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export * from './schema/schema-version.js';
|
|
2
|
+
export * from './schema/node-kind.js';
|
|
3
|
+
export * from './schema/edge-kind.js';
|
|
4
|
+
export * from './schema/node.js';
|
|
5
|
+
export * from './schema/edge.js';
|
|
6
|
+
export * from './schema/manifest.js';
|
|
7
|
+
export * from './schema/file-fingerprint.js';
|
|
8
|
+
export * from './schema/graph-snapshot.js';
|
|
9
|
+
export * from './store/file-fingerprint.js';
|
|
10
|
+
export * from './store/graph-store.js';
|
|
11
|
+
export * from './indexer/extract-ts-file.js';
|
|
12
|
+
export * from './indexer/extract-python-file.js';
|
|
13
|
+
export * from './indexer/extract-go-file.js';
|
|
14
|
+
export * from './indexer/extract-java-file.js';
|
|
15
|
+
export * from './indexer/extract-rust-file.js';
|
|
16
|
+
export * from './indexer/extract-kotlin-file.js';
|
|
17
|
+
export * from './indexer/extract-ruby-file.js';
|
|
18
|
+
export * from './indexer/extract-csharp-file.js';
|
|
19
|
+
export * from './indexer/extract-elixir-file.js';
|
|
20
|
+
export * from './indexer/extract-php-file.js';
|
|
21
|
+
export * from './indexer/extract-dart-file.js';
|
|
22
|
+
export * from './indexer/extract-swift-file.js';
|
|
23
|
+
export * from './indexer/detect-workspace.js';
|
|
24
|
+
export * from './indexer/resolve-imports.js';
|
|
25
|
+
export * from './indexer/index-builder.js';
|
|
26
|
+
export * from './indexer/incremental-updater.js';
|
|
27
|
+
export * from './indexer/unresolved-imports.js';
|
|
28
|
+
export * from './query/query-api.js';
|
|
29
|
+
export * from './query/cycle-detection.js';
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAE3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AAEvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,iCAAiC,CAAC;AAChD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAEhD,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Public surface of @shrkcrft/graph.
|
|
2
|
+
//
|
|
3
|
+
// Schema first; store, extractor, indexer, query API follow.
|
|
4
|
+
export * from "./schema/schema-version.js";
|
|
5
|
+
export * from "./schema/node-kind.js";
|
|
6
|
+
export * from "./schema/edge-kind.js";
|
|
7
|
+
export * from "./schema/node.js";
|
|
8
|
+
export * from "./schema/edge.js";
|
|
9
|
+
export * from "./schema/manifest.js";
|
|
10
|
+
export * from "./schema/file-fingerprint.js";
|
|
11
|
+
export * from "./schema/graph-snapshot.js";
|
|
12
|
+
export * from "./store/file-fingerprint.js";
|
|
13
|
+
export * from "./store/graph-store.js";
|
|
14
|
+
export * from "./indexer/extract-ts-file.js";
|
|
15
|
+
export * from "./indexer/extract-python-file.js";
|
|
16
|
+
export * from "./indexer/extract-go-file.js";
|
|
17
|
+
export * from "./indexer/extract-java-file.js";
|
|
18
|
+
export * from "./indexer/extract-rust-file.js";
|
|
19
|
+
export * from "./indexer/extract-kotlin-file.js";
|
|
20
|
+
export * from "./indexer/extract-ruby-file.js";
|
|
21
|
+
export * from "./indexer/extract-csharp-file.js";
|
|
22
|
+
export * from "./indexer/extract-elixir-file.js";
|
|
23
|
+
export * from "./indexer/extract-php-file.js";
|
|
24
|
+
export * from "./indexer/extract-dart-file.js";
|
|
25
|
+
export * from "./indexer/extract-swift-file.js";
|
|
26
|
+
export * from "./indexer/detect-workspace.js";
|
|
27
|
+
export * from "./indexer/resolve-imports.js";
|
|
28
|
+
export * from "./indexer/index-builder.js";
|
|
29
|
+
export * from "./indexer/incremental-updater.js";
|
|
30
|
+
export * from "./indexer/unresolved-imports.js";
|
|
31
|
+
export * from "./query/query-api.js";
|
|
32
|
+
export * from "./query/cycle-detection.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface IWorkspacePackage {
|
|
2
|
+
/** package.json name field. */
|
|
3
|
+
name: string;
|
|
4
|
+
/** Project-relative directory (POSIX separators). */
|
|
5
|
+
dir: string;
|
|
6
|
+
/** Entry point (project-relative) — best-effort. */
|
|
7
|
+
entry?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Discover workspace packages from `package.json`'s `workspaces` field.
|
|
11
|
+
* Supports both the array and the `{ packages: [...] }` form.
|
|
12
|
+
*
|
|
13
|
+
* Nx integration is deliberately out of scope here — Nx's project graph
|
|
14
|
+
* is a separate optional input considered later (see code-intelligence.md
|
|
15
|
+
* §8.3).
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectWorkspacePackages(projectRoot: string): readonly IWorkspacePackage[];
|
|
18
|
+
//# sourceMappingURL=detect-workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-workspace.d.ts","sourceRoot":"","sources":["../../src/indexer/detect-workspace.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,iBAAiB,EAAE,CAoDzF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import * as nodePath from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Discover workspace packages from `package.json`'s `workspaces` field.
|
|
5
|
+
* Supports both the array and the `{ packages: [...] }` form.
|
|
6
|
+
*
|
|
7
|
+
* Nx integration is deliberately out of scope here — Nx's project graph
|
|
8
|
+
* is a separate optional input considered later (see code-intelligence.md
|
|
9
|
+
* §8.3).
|
|
10
|
+
*/
|
|
11
|
+
export function detectWorkspacePackages(projectRoot) {
|
|
12
|
+
const pkgPath = nodePath.join(projectRoot, 'package.json');
|
|
13
|
+
if (!existsSync(pkgPath))
|
|
14
|
+
return [];
|
|
15
|
+
let raw;
|
|
16
|
+
try {
|
|
17
|
+
raw = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const wsRaw = raw.workspaces;
|
|
23
|
+
const patterns = normalizeWorkspaces(wsRaw);
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const pattern of patterns) {
|
|
26
|
+
const dir = pattern.replace(/\/\*?$/, '');
|
|
27
|
+
const full = nodePath.join(projectRoot, dir);
|
|
28
|
+
if (!existsSync(full))
|
|
29
|
+
continue;
|
|
30
|
+
let entries;
|
|
31
|
+
try {
|
|
32
|
+
entries = readdirSync(full);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
for (const child of entries) {
|
|
38
|
+
const inner = nodePath.join(full, child);
|
|
39
|
+
try {
|
|
40
|
+
if (!statSync(inner).isDirectory())
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const childPkg = nodePath.join(inner, 'package.json');
|
|
47
|
+
if (!existsSync(childPkg))
|
|
48
|
+
continue;
|
|
49
|
+
try {
|
|
50
|
+
const pj = JSON.parse(readFileSync(childPkg, 'utf8'));
|
|
51
|
+
if (!pj.name)
|
|
52
|
+
continue;
|
|
53
|
+
const relDir = nodePath.relative(projectRoot, inner).split(nodePath.sep).join('/');
|
|
54
|
+
const entry = pj.main ?? pj.module ?? pj.types;
|
|
55
|
+
out.push({
|
|
56
|
+
name: pj.name,
|
|
57
|
+
dir: relDir,
|
|
58
|
+
...(entry ? { entry: `${relDir}/${entry.replace(/^\.\//, '')}` } : {}),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* ignore */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
67
|
+
}
|
|
68
|
+
function normalizeWorkspaces(value) {
|
|
69
|
+
if (!value)
|
|
70
|
+
return [];
|
|
71
|
+
if (Array.isArray(value))
|
|
72
|
+
return value.filter((v) => typeof v === 'string');
|
|
73
|
+
if (typeof value === 'object') {
|
|
74
|
+
const packages = value.packages;
|
|
75
|
+
if (Array.isArray(packages)) {
|
|
76
|
+
return packages.filter((v) => typeof v === 'string');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IFileFingerprint } from '../schema/file-fingerprint.js';
|
|
2
|
+
import type { IExtractedFile } from './extract-ts-file.js';
|
|
3
|
+
export declare const EXTRACT_CSHARP_FILE_SOURCE = "extract-csharp-file@v1";
|
|
4
|
+
/**
|
|
5
|
+
* Regex-based C# extractor.
|
|
6
|
+
*
|
|
7
|
+
* Captured (top-level only — nested types are skipped to stay tight):
|
|
8
|
+
* - `[public] class Name`, `[public] sealed class`, `[public] static
|
|
9
|
+
* class`, `[public] abstract class`, `[public] partial class`
|
|
10
|
+
* - `[public] interface Name`
|
|
11
|
+
* - `[public] struct Name`, `[public] readonly struct`
|
|
12
|
+
* - `[public] record Name(...)`, `[public] record class`, `[public]
|
|
13
|
+
* record struct`
|
|
14
|
+
* - `[public] enum Name`
|
|
15
|
+
* - `namespace Name { … }` and `namespace Name;` (file-scoped)
|
|
16
|
+
*
|
|
17
|
+
* Visibility: `public` → exported. `internal`/`private`/`protected`
|
|
18
|
+
* → local. C# defaults to `internal` at the namespace level, so the
|
|
19
|
+
* `isExported` flag honours the explicit modifier; unmodified types
|
|
20
|
+
* are treated as `internal` (local).
|
|
21
|
+
*
|
|
22
|
+
* Imports: classic `using X.Y;`, `using static X.Y.Z;`,
|
|
23
|
+
* `using alias = X.Y;`. We capture the right-hand path; aliases are
|
|
24
|
+
* dropped.
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractCsharpFile(fingerprint: IFileFingerprint, absPath: string, content?: string): IExtractedFile;
|
|
27
|
+
//# sourceMappingURL=extract-csharp-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-csharp-file.d.ts","sourceRoot":"","sources":["../../src/indexer/extract-csharp-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CA6ChB"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { EdgeKind } from "../schema/edge-kind.js";
|
|
4
|
+
import { NodeKind } from "../schema/node-kind.js";
|
|
5
|
+
export const EXTRACT_CSHARP_FILE_SOURCE = 'extract-csharp-file@v1';
|
|
6
|
+
/**
|
|
7
|
+
* Regex-based C# extractor.
|
|
8
|
+
*
|
|
9
|
+
* Captured (top-level only — nested types are skipped to stay tight):
|
|
10
|
+
* - `[public] class Name`, `[public] sealed class`, `[public] static
|
|
11
|
+
* class`, `[public] abstract class`, `[public] partial class`
|
|
12
|
+
* - `[public] interface Name`
|
|
13
|
+
* - `[public] struct Name`, `[public] readonly struct`
|
|
14
|
+
* - `[public] record Name(...)`, `[public] record class`, `[public]
|
|
15
|
+
* record struct`
|
|
16
|
+
* - `[public] enum Name`
|
|
17
|
+
* - `namespace Name { … }` and `namespace Name;` (file-scoped)
|
|
18
|
+
*
|
|
19
|
+
* Visibility: `public` → exported. `internal`/`private`/`protected`
|
|
20
|
+
* → local. C# defaults to `internal` at the namespace level, so the
|
|
21
|
+
* `isExported` flag honours the explicit modifier; unmodified types
|
|
22
|
+
* are treated as `internal` (local).
|
|
23
|
+
*
|
|
24
|
+
* Imports: classic `using X.Y;`, `using static X.Y.Z;`,
|
|
25
|
+
* `using alias = X.Y;`. We capture the right-hand path; aliases are
|
|
26
|
+
* dropped.
|
|
27
|
+
*/
|
|
28
|
+
export function extractCsharpFile(fingerprint, absPath, content) {
|
|
29
|
+
const text = content ?? readFileSync(absPath, 'utf8');
|
|
30
|
+
const fileNode = makeFileNode(fingerprint, text);
|
|
31
|
+
const symbolNodes = [];
|
|
32
|
+
const edges = [];
|
|
33
|
+
const lines = text.split('\n');
|
|
34
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
35
|
+
const raw = lines[i];
|
|
36
|
+
if (raw.length === 0)
|
|
37
|
+
continue;
|
|
38
|
+
if (raw.startsWith(' ') || raw.startsWith('\t'))
|
|
39
|
+
continue;
|
|
40
|
+
const trimmed = raw.trimStart();
|
|
41
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
42
|
+
continue;
|
|
43
|
+
// Strip leading attributes like `[Serializable]` or `[Obsolete("…")]`.
|
|
44
|
+
const stripped = trimmed.replace(/^(?:\[[^\]]*\]\s*)+/, '');
|
|
45
|
+
// Pull access modifiers + variant modifiers.
|
|
46
|
+
const visMatch = /^(public|internal|private|protected|protected\s+internal|internal\s+protected|private\s+protected)\s+/.exec(stripped);
|
|
47
|
+
const isExported = !!visMatch && /\bpublic\b/.test(visMatch[1]);
|
|
48
|
+
const rest = visMatch ? stripped.slice(visMatch[0].length) : stripped;
|
|
49
|
+
const noModifiers = rest.replace(/^(?:static\s+|sealed\s+|abstract\s+|partial\s+|readonly\s+|ref\s+|unsafe\s+|new\s+|virtual\s+|override\s+|async\s+)+/, '');
|
|
50
|
+
let m = /^class\s+([A-Za-z_]\w*)/.exec(noModifiers);
|
|
51
|
+
if (m) {
|
|
52
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'class', i + 1, isExported);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
m = /^interface\s+([A-Za-z_]\w*)/.exec(noModifiers);
|
|
56
|
+
if (m) {
|
|
57
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'interface', i + 1, isExported);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
m = /^struct\s+([A-Za-z_]\w*)/.exec(noModifiers);
|
|
61
|
+
if (m) {
|
|
62
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'class', i + 1, isExported);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
m = /^record(?:\s+(?:class|struct))?\s+([A-Za-z_]\w*)/.exec(noModifiers);
|
|
66
|
+
if (m) {
|
|
67
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'class', i + 1, isExported);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
m = /^enum\s+([A-Za-z_]\w*)/.exec(noModifiers);
|
|
71
|
+
if (m) {
|
|
72
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'enum', i + 1, isExported);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
m = /^namespace\s+([\w.]+)\s*[;{]/.exec(noModifiers);
|
|
76
|
+
if (m) {
|
|
77
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'namespace', i + 1, true);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
fileNode,
|
|
82
|
+
symbolNodes,
|
|
83
|
+
edges,
|
|
84
|
+
rawImportSpecifiers: scanUsings(text),
|
|
85
|
+
importBindings: [],
|
|
86
|
+
identifierReferences: [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function pushSymbol(fp, nodes, edges, fileId, name, declKind, line, isExported) {
|
|
90
|
+
const sym = {
|
|
91
|
+
id: `symbol:${fp.path}#${name}`,
|
|
92
|
+
kind: NodeKind.Symbol,
|
|
93
|
+
label: name,
|
|
94
|
+
path: fp.path,
|
|
95
|
+
line,
|
|
96
|
+
data: { declKind, visibility: isExported ? 'export' : 'local', isExported, language: 'csharp' },
|
|
97
|
+
};
|
|
98
|
+
nodes.push(sym);
|
|
99
|
+
edges.push({
|
|
100
|
+
id: createHash('sha1').update(`${fileId}|${sym.id}|${EdgeKind.DeclaresSymbol}`).digest('hex'),
|
|
101
|
+
from: fileId,
|
|
102
|
+
to: sym.id,
|
|
103
|
+
kind: EdgeKind.DeclaresSymbol,
|
|
104
|
+
source: EXTRACT_CSHARP_FILE_SOURCE,
|
|
105
|
+
data: { visibility: isExported ? 'export' : 'local', declKind, line },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function makeFileNode(fp, text) {
|
|
109
|
+
const label = fp.path.split('/').pop() ?? fp.path;
|
|
110
|
+
const tags = ['csharp'];
|
|
111
|
+
if (isCsharpTestPath(fp.path))
|
|
112
|
+
tags.push('test');
|
|
113
|
+
const nsMatch = /^namespace\s+([\w.]+)/m.exec(text);
|
|
114
|
+
return {
|
|
115
|
+
id: fp.nodeId,
|
|
116
|
+
kind: NodeKind.File,
|
|
117
|
+
label,
|
|
118
|
+
path: fp.path,
|
|
119
|
+
tags,
|
|
120
|
+
data: {
|
|
121
|
+
language: 'csharp',
|
|
122
|
+
sizeBytes: fp.sizeBytes,
|
|
123
|
+
sha1: fp.sha1,
|
|
124
|
+
hasDefaultExport: false,
|
|
125
|
+
exportCount: 0,
|
|
126
|
+
localCount: 0,
|
|
127
|
+
reExportCount: 0,
|
|
128
|
+
...(nsMatch ? { csharpNamespace: nsMatch[1] } : {}),
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function isCsharpTestPath(rel) {
|
|
133
|
+
return /(?:^|\/)[\w.-]+Tests?\.cs$/.test(rel) || /(?:^|\/)test\//.test(rel);
|
|
134
|
+
}
|
|
135
|
+
function scanUsings(text) {
|
|
136
|
+
const out = [];
|
|
137
|
+
// `using X.Y;`, `using static X.Y.Z;`, `using alias = X.Y.Z;`,
|
|
138
|
+
// `using alias = X.Y.Generic<int, string>;`
|
|
139
|
+
const re = /^using\s+(?:static\s+)?(?:[\w]+\s*=\s*)?([\w.]+)(?:\s*<[^;]*>)?\s*;/gm;
|
|
140
|
+
let m;
|
|
141
|
+
while ((m = re.exec(text)) !== null) {
|
|
142
|
+
const line = lineFromOffset(text, m.index);
|
|
143
|
+
out.push({ specifier: m[1], line, kind: 'csharp-using' });
|
|
144
|
+
}
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
const deduped = [];
|
|
147
|
+
for (const it of out) {
|
|
148
|
+
const k = `${it.specifier}|${it.line}`;
|
|
149
|
+
if (seen.has(k))
|
|
150
|
+
continue;
|
|
151
|
+
seen.add(k);
|
|
152
|
+
deduped.push(it);
|
|
153
|
+
}
|
|
154
|
+
return deduped;
|
|
155
|
+
}
|
|
156
|
+
function lineFromOffset(text, offset) {
|
|
157
|
+
let line = 1;
|
|
158
|
+
for (let i = 0; i < offset && i < text.length; i++) {
|
|
159
|
+
if (text.charCodeAt(i) === 10)
|
|
160
|
+
line += 1;
|
|
161
|
+
}
|
|
162
|
+
return line;
|
|
163
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IFileFingerprint } from '../schema/file-fingerprint.js';
|
|
2
|
+
import type { IExtractedFile } from './extract-ts-file.js';
|
|
3
|
+
export declare const EXTRACT_DART_FILE_SOURCE = "extract-dart-file@v1";
|
|
4
|
+
/**
|
|
5
|
+
* Regex-based Dart extractor.
|
|
6
|
+
*
|
|
7
|
+
* Top-level declarations only (column-0). Detected:
|
|
8
|
+
* - `[abstract] class Name [extends X] [with M] [implements I]`
|
|
9
|
+
* - `mixin Name`, `mixin Name on X`
|
|
10
|
+
* - `enum Name { … }`
|
|
11
|
+
* - `typedef Name = …`
|
|
12
|
+
* - `extension Name on X { … }`
|
|
13
|
+
* - `[Type] name(…) { … }`, `void name(…) { … }` — file-scope functions
|
|
14
|
+
*
|
|
15
|
+
* Imports: `import 'package:foo/bar.dart' [as X] [show A, B] [hide C]`;
|
|
16
|
+
* `export 'package:foo/bar.dart'`. Relative imports captured as-is.
|
|
17
|
+
*
|
|
18
|
+
* Visibility: Dart marks private members with a leading underscore.
|
|
19
|
+
* Public symbols (no leading `_`) get `isExported: true`; otherwise
|
|
20
|
+
* `local`.
|
|
21
|
+
*
|
|
22
|
+
* Out of scope:
|
|
23
|
+
* - `part of` / `part` directive handling.
|
|
24
|
+
* - Inline `@Annotation()` capture.
|
|
25
|
+
* - Class body walking (methods).
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractDartFile(fingerprint: IFileFingerprint, absPath: string, content?: string): IExtractedFile;
|
|
28
|
+
//# sourceMappingURL=extract-dart-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-dart-file.d.ts","sourceRoot":"","sources":["../../src/indexer/extract-dart-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CA8DhB"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { EdgeKind } from "../schema/edge-kind.js";
|
|
4
|
+
import { NodeKind } from "../schema/node-kind.js";
|
|
5
|
+
export const EXTRACT_DART_FILE_SOURCE = 'extract-dart-file@v1';
|
|
6
|
+
/**
|
|
7
|
+
* Regex-based Dart extractor.
|
|
8
|
+
*
|
|
9
|
+
* Top-level declarations only (column-0). Detected:
|
|
10
|
+
* - `[abstract] class Name [extends X] [with M] [implements I]`
|
|
11
|
+
* - `mixin Name`, `mixin Name on X`
|
|
12
|
+
* - `enum Name { … }`
|
|
13
|
+
* - `typedef Name = …`
|
|
14
|
+
* - `extension Name on X { … }`
|
|
15
|
+
* - `[Type] name(…) { … }`, `void name(…) { … }` — file-scope functions
|
|
16
|
+
*
|
|
17
|
+
* Imports: `import 'package:foo/bar.dart' [as X] [show A, B] [hide C]`;
|
|
18
|
+
* `export 'package:foo/bar.dart'`. Relative imports captured as-is.
|
|
19
|
+
*
|
|
20
|
+
* Visibility: Dart marks private members with a leading underscore.
|
|
21
|
+
* Public symbols (no leading `_`) get `isExported: true`; otherwise
|
|
22
|
+
* `local`.
|
|
23
|
+
*
|
|
24
|
+
* Out of scope:
|
|
25
|
+
* - `part of` / `part` directive handling.
|
|
26
|
+
* - Inline `@Annotation()` capture.
|
|
27
|
+
* - Class body walking (methods).
|
|
28
|
+
*/
|
|
29
|
+
export function extractDartFile(fingerprint, absPath, content) {
|
|
30
|
+
const text = content ?? readFileSync(absPath, 'utf8');
|
|
31
|
+
const fileNode = makeFileNode(fingerprint);
|
|
32
|
+
const symbolNodes = [];
|
|
33
|
+
const edges = [];
|
|
34
|
+
const lines = text.split('\n');
|
|
35
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
36
|
+
const raw = lines[i];
|
|
37
|
+
if (raw.length === 0)
|
|
38
|
+
continue;
|
|
39
|
+
if (raw.startsWith(' ') || raw.startsWith('\t'))
|
|
40
|
+
continue;
|
|
41
|
+
const trimmed = raw.trimStart();
|
|
42
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*'))
|
|
43
|
+
continue;
|
|
44
|
+
// class / abstract class / sealed class / interface class / final class / base class / mixin class
|
|
45
|
+
let m = /^(?:abstract\s+|sealed\s+|base\s+|interface\s+|final\s+|mixin\s+)*class\s+([A-Za-z_]\w*)/.exec(trimmed);
|
|
46
|
+
if (m) {
|
|
47
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'class', i + 1);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
m = /^mixin\s+([A-Za-z_]\w*)/.exec(trimmed);
|
|
51
|
+
if (m) {
|
|
52
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'class', i + 1);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
m = /^enum\s+([A-Za-z_]\w*)/.exec(trimmed);
|
|
56
|
+
if (m) {
|
|
57
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'enum', i + 1);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
m = /^typedef\s+([A-Za-z_]\w*)/.exec(trimmed);
|
|
61
|
+
if (m) {
|
|
62
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'type-alias', i + 1);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
m = /^extension\s+([A-Za-z_]\w*)/.exec(trimmed);
|
|
66
|
+
if (m) {
|
|
67
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, m[1], 'class', i + 1);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// File-scope functions: `<Type> name(…)` or `void name(…)`.
|
|
71
|
+
// Require either a `{` to open a body (anywhere on the line) or
|
|
72
|
+
// an arrow `=>` so we don't sweep up variable declarations or
|
|
73
|
+
// function-type fields.
|
|
74
|
+
m = /^([A-Za-z_]\w*(?:<[^>]*>)?\??)\s+([A-Za-z_]\w*)\s*\(/.exec(trimmed);
|
|
75
|
+
if (m && (/\{/.test(trimmed) || /=>/.test(trimmed))) {
|
|
76
|
+
const name = m[2];
|
|
77
|
+
// Skip constructor-style names that begin with `Type.` (those are
|
|
78
|
+
// class members, not file-scope functions).
|
|
79
|
+
if (!name.includes('.')) {
|
|
80
|
+
pushSymbol(fingerprint, symbolNodes, edges, fileNode.id, name, 'function', i + 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
fileNode,
|
|
86
|
+
symbolNodes,
|
|
87
|
+
edges,
|
|
88
|
+
rawImportSpecifiers: scanDartImports(text),
|
|
89
|
+
importBindings: [],
|
|
90
|
+
identifierReferences: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function pushSymbol(fp, nodes, edges, fileId, name, declKind, line) {
|
|
94
|
+
const isExported = !name.startsWith('_');
|
|
95
|
+
const sym = {
|
|
96
|
+
id: `symbol:${fp.path}#${name}`,
|
|
97
|
+
kind: NodeKind.Symbol,
|
|
98
|
+
label: name,
|
|
99
|
+
path: fp.path,
|
|
100
|
+
line,
|
|
101
|
+
data: { declKind, visibility: isExported ? 'export' : 'local', isExported, language: 'dart' },
|
|
102
|
+
};
|
|
103
|
+
nodes.push(sym);
|
|
104
|
+
edges.push({
|
|
105
|
+
id: createHash('sha1').update(`${fileId}|${sym.id}|${EdgeKind.DeclaresSymbol}`).digest('hex'),
|
|
106
|
+
from: fileId,
|
|
107
|
+
to: sym.id,
|
|
108
|
+
kind: EdgeKind.DeclaresSymbol,
|
|
109
|
+
source: EXTRACT_DART_FILE_SOURCE,
|
|
110
|
+
data: { visibility: isExported ? 'export' : 'local', declKind, line },
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function makeFileNode(fp) {
|
|
114
|
+
const label = fp.path.split('/').pop() ?? fp.path;
|
|
115
|
+
const tags = ['dart'];
|
|
116
|
+
if (isDartTestPath(fp.path))
|
|
117
|
+
tags.push('test');
|
|
118
|
+
return {
|
|
119
|
+
id: fp.nodeId,
|
|
120
|
+
kind: NodeKind.File,
|
|
121
|
+
label,
|
|
122
|
+
path: fp.path,
|
|
123
|
+
tags,
|
|
124
|
+
data: {
|
|
125
|
+
language: 'dart',
|
|
126
|
+
sizeBytes: fp.sizeBytes,
|
|
127
|
+
sha1: fp.sha1,
|
|
128
|
+
hasDefaultExport: false,
|
|
129
|
+
exportCount: 0,
|
|
130
|
+
localCount: 0,
|
|
131
|
+
reExportCount: 0,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function isDartTestPath(rel) {
|
|
136
|
+
return (/(?:^|\/)test\//.test(rel) ||
|
|
137
|
+
/(?:^|\/)[\w-]+_test\.dart$/.test(rel));
|
|
138
|
+
}
|
|
139
|
+
function scanDartImports(text) {
|
|
140
|
+
const out = [];
|
|
141
|
+
// `import 'package:foo/bar.dart' [as X] [show A] [hide B];`
|
|
142
|
+
// `export 'package:foo/bar.dart';`
|
|
143
|
+
const re = /^\s*(import|export)\s+['"]([^'"]+)['"](?:\s+(?:as\s+\w+|show\s+[^;]+|hide\s+[^;]+|deferred\s+as\s+\w+))*\s*;/gm;
|
|
144
|
+
let m;
|
|
145
|
+
while ((m = re.exec(text)) !== null) {
|
|
146
|
+
const line = lineFromOffset(text, m.index);
|
|
147
|
+
out.push({ specifier: m[2], line, kind: `dart-${m[1]}` });
|
|
148
|
+
}
|
|
149
|
+
const seen = new Set();
|
|
150
|
+
const deduped = [];
|
|
151
|
+
for (const it of out) {
|
|
152
|
+
const k = `${it.kind}|${it.specifier}|${it.line}`;
|
|
153
|
+
if (seen.has(k))
|
|
154
|
+
continue;
|
|
155
|
+
seen.add(k);
|
|
156
|
+
deduped.push(it);
|
|
157
|
+
}
|
|
158
|
+
return deduped;
|
|
159
|
+
}
|
|
160
|
+
function lineFromOffset(text, offset) {
|
|
161
|
+
let line = 1;
|
|
162
|
+
for (let i = 0; i < offset && i < text.length; i++) {
|
|
163
|
+
if (text.charCodeAt(i) === 10)
|
|
164
|
+
line += 1;
|
|
165
|
+
}
|
|
166
|
+
return line;
|
|
167
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IFileFingerprint } from '../schema/file-fingerprint.js';
|
|
2
|
+
import type { IExtractedFile } from './extract-ts-file.js';
|
|
3
|
+
export declare const EXTRACT_ELIXIR_FILE_SOURCE = "extract-elixir-file@v1";
|
|
4
|
+
/**
|
|
5
|
+
* Regex-based Elixir extractor.
|
|
6
|
+
*
|
|
7
|
+
* Top-level constructs only (column-0):
|
|
8
|
+
* - `defmodule Path.To.Mod do` → module
|
|
9
|
+
* - `def name(...)` → function (exported)
|
|
10
|
+
* - `defp name(...)` → function (local)
|
|
11
|
+
* - `defstruct …` and `defprotocol` / `defimpl` headers → struct / protocol
|
|
12
|
+
*
|
|
13
|
+
* Nested `def`s under a `defmodule` show up at column 0 in this
|
|
14
|
+
* extractor's view; that's intentional because they're the publicly
|
|
15
|
+
* callable functions on that module.
|
|
16
|
+
*
|
|
17
|
+
* Imports: `alias Foo.Bar`, `alias Foo.{A, B}`, `import Enum`,
|
|
18
|
+
* `require Logger`, `use MyAppWeb, :controller`. Aliases inside
|
|
19
|
+
* `{...}` are expanded.
|
|
20
|
+
*
|
|
21
|
+
* Out of scope:
|
|
22
|
+
* - Pipe operator chains.
|
|
23
|
+
* - Macro definitions (`defmacro`/`defmacrop`).
|
|
24
|
+
* - Inline guard clauses (`when ...`).
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractElixirFile(fingerprint: IFileFingerprint, absPath: string, content?: string): IExtractedFile;
|
|
27
|
+
//# sourceMappingURL=extract-elixir-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-elixir-file.d.ts","sourceRoot":"","sources":["../../src/indexer/extract-elixir-file.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,KAAK,EACV,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CAiDhB"}
|