This commit is contained in:
2026-07-18 15:36:28 +02:00
parent 21e06b8a4c
commit d4e48c6bb4
5 changed files with 63 additions and 22 deletions

View File

@@ -7,7 +7,7 @@ const { data: pages } = await useAsyncData('nav', () =>
<template>
<nav>
<h2>Notas</h2>
<h2>Índice</h2>
<TreeNode :nodes="pages" />
</nav>
</template>

View File

@@ -48,6 +48,7 @@ if (!page.value) {
width: 100%;
max-width: 800px;
padding: 0 1rem;
margin-bottom: 300px;
}
.page-tree-sidebar {

View File

@@ -6,4 +6,6 @@ visible: true
Hola que tal! Hauria de funcionar
Test
Test
<a href="/razas" class="dead-link" data-hidden="Razas">Razas</a>

View File

@@ -19,7 +19,9 @@
*
* VISIBILITY GATE:
* Only notes whose frontmatter contains `visible: true` are converted.
* Any note missing the field, or with `visible: false`, is silently skipped.
* Any note missing the field, or with `visible: false`, is skipped.
* Wiki links that point to a hidden (or missing) note are rendered as dead
* links: <a class="dead-link" data-missing="…"> instead of a normal link.
*
* Usage:
* node obsidian-to-nuxt.js --vault ./my-vault --out ./content --base-path /notes
@@ -108,6 +110,11 @@ function collectMarkdownFiles(dir) {
return results;
}
/**
* Build a lookup map of stem/title/slug → URL for every **visible** note.
* Hidden notes (visible !== true) are intentionally excluded so that wiki
* links pointing at them resolve to nothing and are rendered as dead links.
*/
function buildNoteIndex(files) {
const candidates = new Map();
@@ -118,27 +125,31 @@ function buildNoteIndex(files) {
}
for (const file of files) {
const stem = path.basename(file, ".md");
const rel = path.relative(VAULT_DIR, file).replace(/\.md$/, "");
const url = BASE_PATH + "/" + rel.split(path.sep).map(slugify).join("/");
const depth = rel.split(path.sep).length;
const content = fs.readFileSync(file, "utf8");
// Skip hidden notes — links to them must become dead links.
const { frontmatter, body } = parseFrontmatter(content);
if (!isVisible(frontmatter)) continue;
const stem = path.basename(file, ".md");
const rel = path.relative(VAULT_DIR, file).replace(/\.md$/, "");
const url = BASE_PATH + "/" + rel.split(path.sep).map(slugify).join("/");
const depth = rel.split(path.sep).length;
// Key 1: exact lowercase filename stem e.g. "note2"
addCandidate(stem.toLowerCase(), url, depth);
// Key 2: slugified filename stem e.g. "note-two" from "Note Two.md"
addCandidate(slugify(stem), url, depth);
// Key 3: frontmatter title
const fmMatch = content.match(/^---[\s\S]*?^title:\s*(.+)/m);
if (fmMatch) {
const title = fmMatch[1].trim().replace(/^["']|["']$/g, "");
// Key 3: frontmatter title (already parsed above — no second regex needed)
if (frontmatter.title) {
const title = String(frontmatter.title);
addCandidate(title.toLowerCase(), url, depth);
addCandidate(slugify(title), url, depth);
}
// Key 4: first H1 heading
const h1Match = content.match(/^#\s+(.+)/m);
const h1Match = body.match(/^#\s+(.+)/m);
if (h1Match) {
const h1 = h1Match[1].trim();
addCandidate(h1.toLowerCase(), url, depth);
@@ -151,6 +162,24 @@ function buildNoteIndex(files) {
return result;
}
/**
* Collect the lowercase stems (and slugified stems) of every note that exists
* in the vault but is NOT visible. Used to distinguish "hidden" dead links
* from "truly missing" dead links when rendering wiki links.
*/
function buildHiddenStems(files) {
const hidden = new Set();
for (const file of files) {
const content = fs.readFileSync(file, "utf8");
const { frontmatter } = parseFrontmatter(content);
if (isVisible(frontmatter)) continue;
const stem = path.basename(file, ".md");
hidden.add(stem.toLowerCase());
hidden.add(slugify(stem));
}
return hidden;
}
// ─── Frontmatter ─────────────────────────────────────────────────────────────
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
@@ -299,7 +328,12 @@ function convertEmbeds(body, noteIndex) {
// ─── Wiki links [[...]] ──────────────────────────────────────────────────────
function convertWikiLinks(body, noteIndex) {
/**
* @param {string} body
* @param {Map} noteIndex - visible notes only (stem/title → url)
* @param {Set} hiddenStems - stems of notes that exist but are not visible
*/
function convertWikiLinks(body, noteIndex, hiddenStems) {
return body.replace(/\[\[([^\]]+?)\]\]/g, function(_, ref) {
const pipeIdx = ref.indexOf("|");
let target, alias;
@@ -334,7 +368,12 @@ function convertWikiLinks(body, noteIndex) {
const label = alias || (anchor ? stem + " \u203a " + anchor : stem);
if (dead) {
return '<a href="' + url + '" class="dead-link" data-missing="' + stem + '">' + label + '</a>';
// Distinguish between a note that is hidden vs one that doesn't exist at all.
const isHidden = hiddenStems.has(stem.toLowerCase()) || hiddenStems.has(slugify(stem));
const extraAttr = isHidden
? ' data-hidden="' + stem + '"'
: ' data-missing="' + stem + '"';
return '<a href="' + url + '" class="dead-link"' + extraAttr + '>' + label + '</a>';
}
return "[" + label + "](" + url + ")";
});
@@ -342,7 +381,7 @@ function convertWikiLinks(body, noteIndex) {
// ─── Convert one file ────────────────────────────────────────────────────────
function convertFile(content, noteIndex, filename) {
function convertFile(content, noteIndex, hiddenStems, filename) {
let { frontmatter, body } = parseFrontmatter(content);
const inlineTags = extractInlineTags(body);
@@ -358,16 +397,14 @@ function convertFile(content, noteIndex, filename) {
body = convertCallouts(body);
body = convertBlockIds(body);
body = convertEmbeds(body, noteIndex);
body = convertWikiLinks(body, noteIndex);
body = convertWikiLinks(body, noteIndex, hiddenStems);
// Prepend a title if the body doesn't already start with an H1 heading
const trimmed = body.trimStart();
const hasTitle = /^#[^#]/.test(trimmed);
if (!hasTitle) {
const title = frontmatter.title || path.basename(filename, ".md");
if (title != "index") {
body = "# " + title + "\n\n" + trimmed;
}
body = "# " + title + "\n\n" + trimmed;
}
const fmString = Object.keys(frontmatter).length
@@ -385,8 +422,9 @@ function main() {
process.exit(1);
}
const files = collectMarkdownFiles(VAULT_DIR);
const noteIndex = buildNoteIndex(files);
const files = collectMarkdownFiles(VAULT_DIR);
const noteIndex = buildNoteIndex(files);
const hiddenStems = buildHiddenStems(files);
console.log("Found " + files.length + " markdown file(s) in " + VAULT_DIR);
@@ -416,7 +454,7 @@ function main() {
let result;
try {
result = convertFile(content, noteIndex, path.basename(file));
result = convertFile(content, noteIndex, hiddenStems, path.basename(file));
} catch (err) {
console.warn(" \u26A0 Skipping " + rel + ": " + err.message);
skipCount++;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB