lat.md 0.10.1 → 0.10.2
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/src/cli/check.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync } from 'node:fs';
|
|
|
3
3
|
import { basename, dirname, extname, join, relative } from 'node:path';
|
|
4
4
|
import { listLatticeFiles, loadAllSections, extractRefs, flattenSections, parseFrontmatter, parseSections, buildFileIndex, resolveRef, } from '../lattice.js';
|
|
5
5
|
import { scanCodeRefs } from '../code-refs.js';
|
|
6
|
-
import { SOURCE_EXTENSIONS } from '../source-parser.js';
|
|
6
|
+
import { SOURCE_EXTENSIONS, clearSymbolCache } from '../source-parser.js';
|
|
7
7
|
import { walkEntries } from '../walk.js';
|
|
8
8
|
import { INIT_VERSION, readInitVersion } from '../init-version.js';
|
|
9
9
|
function filePart(id) {
|
|
@@ -82,6 +82,7 @@ async function tryResolveSourceRef(target, projectRoot) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
export async function checkMd(latticeDir) {
|
|
85
|
+
clearSymbolCache();
|
|
85
86
|
const projectRoot = dirname(latticeDir);
|
|
86
87
|
const files = await listLatticeFiles(latticeDir);
|
|
87
88
|
const allSections = await loadAllSections(latticeDir);
|
|
@@ -10,6 +10,8 @@ export type SourceSymbol = {
|
|
|
10
10
|
/** All source file extensions that lat can parse (derived from grammarMap). */
|
|
11
11
|
export declare const SOURCE_EXTENSIONS: ReadonlySet<string>;
|
|
12
12
|
export declare function parseSourceSymbols(filePath: string, content: string): Promise<SourceSymbol[]>;
|
|
13
|
+
/** Clear the symbol cache. Call between top-level operations. */
|
|
14
|
+
export declare function clearSymbolCache(): void;
|
|
13
15
|
/**
|
|
14
16
|
* Check whether a source file path (relative to projectRoot) has a given symbol.
|
|
15
17
|
* Used by lat check to validate source code wiki links lazily.
|
|
@@ -477,8 +477,17 @@ function extractGoSymbols(tree) {
|
|
|
477
477
|
* Handles plain identifiers and pointer declarators (*name).
|
|
478
478
|
*/
|
|
479
479
|
function cFuncName(declarator) {
|
|
480
|
-
|
|
481
|
-
|
|
480
|
+
// Unwrap pointer_declarator layers (for functions returning pointers,
|
|
481
|
+
// e.g. `JSRuntime *JS_NewRuntime(void)` → pointer_declarator > function_declarator)
|
|
482
|
+
let node = declarator;
|
|
483
|
+
while (node.type === 'pointer_declarator') {
|
|
484
|
+
const child = node.childForFieldName('declarator');
|
|
485
|
+
if (!child)
|
|
486
|
+
return null;
|
|
487
|
+
node = child;
|
|
488
|
+
}
|
|
489
|
+
if (node.type === 'function_declarator') {
|
|
490
|
+
const inner = node.childForFieldName('declarator');
|
|
482
491
|
if (!inner)
|
|
483
492
|
return null;
|
|
484
493
|
if (inner.type === 'identifier')
|
|
@@ -510,6 +519,13 @@ function cVarName(declarator) {
|
|
|
510
519
|
return null;
|
|
511
520
|
node = inner;
|
|
512
521
|
}
|
|
522
|
+
// Unwrap array_declarator (e.g. `char js_version[]`)
|
|
523
|
+
if (node.type === 'array_declarator') {
|
|
524
|
+
const inner = node.childForFieldName('declarator');
|
|
525
|
+
if (!inner)
|
|
526
|
+
return null;
|
|
527
|
+
node = inner;
|
|
528
|
+
}
|
|
513
529
|
if (node.type === 'identifier')
|
|
514
530
|
return node.text;
|
|
515
531
|
if (node.type === 'pointer_declarator') {
|
|
@@ -601,7 +617,8 @@ function collectCNodes(parent, symbols) {
|
|
|
601
617
|
});
|
|
602
618
|
}
|
|
603
619
|
}
|
|
604
|
-
else if (node.type === 'preproc_def'
|
|
620
|
+
else if (node.type === 'preproc_def' ||
|
|
621
|
+
node.type === 'preproc_function_def') {
|
|
605
622
|
const name = extractName(node);
|
|
606
623
|
if (name) {
|
|
607
624
|
symbols.push({
|
|
@@ -634,19 +651,32 @@ export async function parseSourceSymbols(filePath, content) {
|
|
|
634
651
|
const tree = p.parse(content);
|
|
635
652
|
if (!tree)
|
|
636
653
|
return [];
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
654
|
+
try {
|
|
655
|
+
if (ext === '.py') {
|
|
656
|
+
return extractPySymbols(tree);
|
|
657
|
+
}
|
|
658
|
+
if (ext === '.rs') {
|
|
659
|
+
return extractRustSymbols(tree);
|
|
660
|
+
}
|
|
661
|
+
if (ext === '.go') {
|
|
662
|
+
return extractGoSymbols(tree);
|
|
663
|
+
}
|
|
664
|
+
if (ext === '.c' || ext === '.h') {
|
|
665
|
+
return extractCSymbols(tree);
|
|
666
|
+
}
|
|
667
|
+
return extractTsSymbols(tree);
|
|
645
668
|
}
|
|
646
|
-
|
|
647
|
-
|
|
669
|
+
finally {
|
|
670
|
+
tree.delete();
|
|
648
671
|
}
|
|
649
|
-
|
|
672
|
+
}
|
|
673
|
+
// Per-invocation cache for parsed source symbols, keyed by absolute file path.
|
|
674
|
+
// Prevents re-parsing the same file when multiple wiki links reference it
|
|
675
|
+
// (e.g. 20+ links to quickjs.c would otherwise parse a 60K-line file 20 times).
|
|
676
|
+
const symbolCache = new Map();
|
|
677
|
+
/** Clear the symbol cache. Call between top-level operations. */
|
|
678
|
+
export function clearSymbolCache() {
|
|
679
|
+
symbolCache.clear();
|
|
650
680
|
}
|
|
651
681
|
/**
|
|
652
682
|
* Check whether a source file path (relative to projectRoot) has a given symbol.
|
|
@@ -654,24 +684,33 @@ export async function parseSourceSymbols(filePath, content) {
|
|
|
654
684
|
*/
|
|
655
685
|
export async function resolveSourceSymbol(filePath, symbolPath, projectRoot) {
|
|
656
686
|
const absPath = join(projectRoot, filePath);
|
|
657
|
-
let
|
|
658
|
-
|
|
659
|
-
content
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
687
|
+
let cached = symbolCache.get(absPath);
|
|
688
|
+
if (!cached) {
|
|
689
|
+
let content;
|
|
690
|
+
try {
|
|
691
|
+
content = readFileSync(absPath, 'utf-8');
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
cached = { symbols: [] };
|
|
695
|
+
symbolCache.set(absPath, cached);
|
|
696
|
+
return { found: false, symbols: [] };
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
const symbols = await parseSourceSymbols(filePath, content);
|
|
700
|
+
cached = { symbols };
|
|
701
|
+
}
|
|
702
|
+
catch (err) {
|
|
703
|
+
cached = {
|
|
704
|
+
symbols: [],
|
|
705
|
+
error: `failed to parse "${filePath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
symbolCache.set(absPath, cached);
|
|
663
709
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
symbols = await parseSourceSymbols(filePath, content);
|
|
667
|
-
}
|
|
668
|
-
catch (err) {
|
|
669
|
-
return {
|
|
670
|
-
found: false,
|
|
671
|
-
symbols: [],
|
|
672
|
-
error: `failed to parse "${filePath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
673
|
-
};
|
|
710
|
+
if (cached.error) {
|
|
711
|
+
return { found: false, symbols: cached.symbols, error: cached.error };
|
|
674
712
|
}
|
|
713
|
+
const { symbols } = cached;
|
|
675
714
|
const parts = symbolPath.split('#');
|
|
676
715
|
if (parts.length === 1) {
|
|
677
716
|
// Simple symbol: getConfigDir
|