mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
Revert "feat(links): resolve [[wikilink]] frontmatter values via global_basename (#2406)"
This reverts commit 503f61e6e4.
This commit is contained in:
@@ -567,7 +567,7 @@ export async function extractPageLinks(
|
||||
// path needed `resolveBasenameMatches` on the real resolver.
|
||||
let fmUnresolved: UnresolvedFrontmatterRef[] = [];
|
||||
if (!opts.skipFrontmatter) {
|
||||
const fm = await extractFrontmatterLinks(slug, pageType, frontmatter, resolver, opts.globalBasename);
|
||||
const fm = await extractFrontmatterLinks(slug, pageType, frontmatter, resolver);
|
||||
candidates.push(...fm.candidates);
|
||||
fmUnresolved = fm.unresolved;
|
||||
}
|
||||
@@ -1042,24 +1042,11 @@ export interface FrontmatterExtractResult {
|
||||
* Arrays of objects: uses the `name` or `slug` property (codex tension 6.3).
|
||||
* Non-string / non-object entries: silently skipped (log-only).
|
||||
*/
|
||||
/**
|
||||
* Unwrap an Obsidian-style `[[wikilink]]` frontmatter value to its bare link
|
||||
* target: drops the surrounding brackets, any `|alias`, and `#heading` /
|
||||
* `^block` suffixes. Non-bracketed values pass through unchanged. Used to feed
|
||||
* the basename index when global_basename is on (see extractFrontmatterLinks).
|
||||
*/
|
||||
function unwrapWikilink(value: string): string {
|
||||
const match = /^\s*\[\[(.+?)\]\]\s*$/.exec(value);
|
||||
if (!match) return value;
|
||||
return match[1].split('|')[0].split('#')[0].split('^')[0].trim();
|
||||
}
|
||||
|
||||
export async function extractFrontmatterLinks(
|
||||
slug: string,
|
||||
pageType: PageType,
|
||||
frontmatter: Record<string, unknown>,
|
||||
resolver: SlugResolver,
|
||||
globalBasename = false,
|
||||
): Promise<FrontmatterExtractResult> {
|
||||
const candidates: LinkCandidate[] = [];
|
||||
const unresolved: UnresolvedFrontmatterRef[] = [];
|
||||
@@ -1092,23 +1079,7 @@ export async function extractFrontmatterLinks(
|
||||
}
|
||||
if (!name) continue; // skip numbers, nulls, malformed objects
|
||||
|
||||
let resolved = await resolver.resolve(name, mapping.dirHint);
|
||||
if (!resolved && globalBasename && typeof resolver.resolveBasenameMatches === 'function') {
|
||||
// Issue #972 follow-up: extend global_basename resolution to
|
||||
// frontmatter link fields. resolve() can't reach a bare-title
|
||||
// wikilink value (e.g. `sources: "[[2025-12-25_mentor-extraction]]"`)
|
||||
// — it has no '/', so the slug-direct getPage is skipped, and the
|
||||
// field's dirHint may name folders that don't exist in this brain,
|
||||
// so the dir-scoped exact + fuzzy steps miss too. When
|
||||
// link_resolution.global_basename is on, fall back to the SAME
|
||||
// basename index the body bare-wikilink pass uses, after unwrapping
|
||||
// the brackets. Unique-match-only: ambiguous basenames (e.g. archive
|
||||
// duplicates, generic hubs like `_index`) stay unresolved rather than
|
||||
// create a wrong edge.
|
||||
const matches = (await resolver.resolveBasenameMatches(unwrapWikilink(name)))
|
||||
.filter((s) => s !== slug);
|
||||
if (matches.length === 1) resolved = matches[0];
|
||||
}
|
||||
const resolved = await resolver.resolve(name, mapping.dirHint);
|
||||
if (!resolved) {
|
||||
unresolved.push({ field, name });
|
||||
continue;
|
||||
|
||||
@@ -270,53 +270,6 @@ describe('extractPageLinks', () => {
|
||||
expect(sourceLink!.targetSlug).toBe('meetings/2026-01-15');
|
||||
});
|
||||
|
||||
// ─── global_basename for frontmatter link fields (issue #972 follow-up) ───
|
||||
|
||||
test('frontmatter [[wikilink]] resolves via global_basename when resolve() misses', async () => {
|
||||
// `sources: [[2025-12-25_mentor-extraction]]` — bare title, no '/', so the
|
||||
// standard resolver misses; the basename index finds the single match.
|
||||
const resolver: SlugResolver = {
|
||||
resolve: async () => null,
|
||||
resolveBasenameMatches: async (name) =>
|
||||
name === '2025-12-25_mentor-extraction'
|
||||
? ['trading/raw/2025-12-25_mentor-extraction']
|
||||
: [],
|
||||
};
|
||||
const { candidates } = await extractPageLinks(
|
||||
'trading/wiki/backtesting', 'Body.',
|
||||
{ sources: ['[[2025-12-25_mentor-extraction]]'] },
|
||||
'concept', resolver, { globalBasename: true },
|
||||
);
|
||||
// `sources` is direction:'incoming' → edge is resolved → page.
|
||||
const edge = candidates.find(c => c.linkType === 'discussed_in');
|
||||
expect(edge).toBeDefined();
|
||||
expect(edge!.fromSlug).toBe('trading/raw/2025-12-25_mentor-extraction');
|
||||
expect(edge!.targetSlug).toBe('trading/wiki/backtesting');
|
||||
});
|
||||
|
||||
test('frontmatter basename fallback stays unresolved when ambiguous (>1 match)', async () => {
|
||||
const resolver: SlugResolver = {
|
||||
resolve: async () => null,
|
||||
resolveBasenameMatches: async () => ['a/dup', 'b/dup'],
|
||||
};
|
||||
const { candidates, unresolved } = await extractPageLinks(
|
||||
'wiki/x', 'Body.', { sources: ['[[dup]]'] }, 'concept', resolver, { globalBasename: true },
|
||||
);
|
||||
expect(candidates.find(c => c.linkType === 'discussed_in')).toBeUndefined();
|
||||
expect(unresolved.some(u => u.field === 'sources')).toBe(true);
|
||||
});
|
||||
|
||||
test('frontmatter basename fallback is gated OFF when globalBasename is false', async () => {
|
||||
const resolver: SlugResolver = {
|
||||
resolve: async () => null,
|
||||
resolveBasenameMatches: async () => ['raw/note'],
|
||||
};
|
||||
const { candidates } = await extractPageLinks(
|
||||
'wiki/x', 'Body.', { sources: ['[[note]]'] }, 'concept', resolver, // globalBasename omitted = false
|
||||
);
|
||||
expect(candidates.find(c => c.linkType === 'discussed_in')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('extracts bare slug references in text', async () => {
|
||||
const { candidates } = await extractPageLinks(
|
||||
'docs/x', 'See companies/acme for details.', {}, 'concept', nullResolver,
|
||||
|
||||
Reference in New Issue
Block a user