mdrefine 0.2.0-rc.2 → 0.2.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/CHANGELOG.md +577 -0
- package/CODE_OF_CONDUCT.md +30 -0
- package/CONTRIBUTING.md +189 -16
- package/README.md +100 -154
- package/SECURITY.md +15 -13
- package/config.schema.json +91 -0
- package/dist/cli/diff.d.ts +10 -0
- package/dist/cli/diff.js +247 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/main.js +270 -150
- package/dist/cli/main.js.map +1 -1
- package/dist/config/load.js +67 -6
- package/dist/config/load.js.map +1 -1
- package/dist/config/problems.d.ts +7 -0
- package/dist/config/problems.js +208 -0
- package/dist/config/problems.js.map +1 -0
- package/dist/config/resolve.d.ts +42 -109
- package/dist/config/resolve.js +193 -52
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/directives.d.ts +7 -0
- package/dist/core/directives.js +25 -13
- package/dist/core/directives.js.map +1 -1
- package/dist/core/engine.d.ts +42 -4
- package/dist/core/engine.js +518 -79
- package/dist/core/engine.js.map +1 -1
- package/dist/core/frozen.d.ts +6 -0
- package/dist/core/frozen.js +15 -0
- package/dist/core/frozen.js.map +1 -0
- package/dist/core/scope.d.ts +30 -0
- package/dist/core/scope.js +122 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/types.d.ts +202 -23
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +3 -0
- package/dist/node.js +4 -0
- package/dist/node.js.map +1 -0
- package/dist/rules/blocks.d.ts +34 -0
- package/dist/rules/blocks.js +550 -0
- package/dist/rules/blocks.js.map +1 -0
- package/dist/rules/content.d.ts +2 -0
- package/dist/rules/content.js +85 -0
- package/dist/rules/content.js.map +1 -0
- package/dist/rules/dialects.js +10 -6
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/links.js +302 -6
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/lists.d.ts +2 -0
- package/dist/rules/lists.js +924 -0
- package/dist/rules/lists.js.map +1 -0
- package/dist/rules/structure.js +127 -1
- package/dist/rules/structure.js.map +1 -1
- package/dist/rules/style.d.ts +8 -2
- package/dist/rules/style.js +1137 -165
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/block-identifier.d.ts +18 -0
- package/dist/syntax/block-identifier.js +24 -0
- package/dist/syntax/block-identifier.js.map +1 -0
- package/dist/syntax/obsidian.d.ts +29 -11
- package/dist/syntax/obsidian.js +9 -10
- package/dist/syntax/obsidian.js.map +1 -1
- package/dist/syntax/parse.d.ts +87 -3
- package/dist/syntax/parse.js +247 -19
- package/dist/syntax/parse.js.map +1 -1
- package/dist/workspace/access.d.ts +12 -3
- package/dist/workspace/access.js +33 -5
- package/dist/workspace/access.js.map +1 -1
- package/dist/workspace/discovery.d.ts +85 -0
- package/dist/workspace/discovery.js +155 -0
- package/dist/workspace/discovery.js.map +1 -0
- package/dist/workspace/encoding.d.ts +24 -0
- package/dist/workspace/encoding.js +113 -0
- package/dist/workspace/encoding.js.map +1 -0
- package/dist/workspace/files.d.ts +92 -6
- package/dist/workspace/files.js +715 -59
- package/dist/workspace/files.js.map +1 -1
- package/dist/workspace/git.d.ts +54 -0
- package/dist/workspace/git.js +198 -0
- package/dist/workspace/git.js.map +1 -0
- package/dist/workspace/index.d.ts +58 -3
- package/dist/workspace/index.js +136 -37
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/selection.d.ts +2 -2
- package/dist/workspace/selection.js +3 -3
- package/dist/workspace/selection.js.map +1 -1
- package/dist/workspace/slug.js +8 -1
- package/dist/workspace/slug.js.map +1 -1
- package/dist/workspace/vault.js +3 -1
- package/dist/workspace/vault.js.map +1 -1
- package/docs/api.md +239 -0
- package/docs/benchmarks.md +140 -200
- package/docs/changelog.md +1 -0
- package/docs/cli.md +281 -89
- package/docs/configuration.md +193 -480
- package/docs/design.md +163 -284
- package/docs/dialects.md +169 -0
- package/docs/examples.md +151 -109
- package/docs/index.md +21 -37
- package/docs/integrations.md +144 -0
- package/docs/obsidian.md +91 -115
- package/docs/plugins.md +147 -212
- package/docs/quick-start.md +68 -74
- package/docs/rules.md +1237 -0
- package/docs/versions.md +56 -0
- package/examples/README.md +17 -14
- package/examples/automation/summarize-report.mjs +5 -2
- package/examples/library/lint-folder.mjs +5 -4
- package/examples/library/lint-workspace.mjs +32 -0
- package/examples/plugin/notes/release-checklist.md +2 -2
- package/examples/plugin/rules.mjs +17 -23
- package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +2 -2
- package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +2 -2
- package/examples/workspaces/repository-docs/after/README.md +2 -2
- package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +0 -2
- package/examples/workspaces/repository-docs/before/README.md +2 -2
- package/examples/workspaces/repository-docs/before/docs/guide.md +7 -7
- package/examples/workspaces/repository-docs/before/docs/reference.md +3 -1
- package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +0 -2
- package/package.json +38 -12
- package/docs/contributing.md +0 -218
- package/docs/releases/0.1.0-beta.1.md +0 -39
- package/docs/releases/0.1.0-beta.2.md +0 -83
- package/docs/releases/0.1.0-beta.3.md +0 -61
- package/docs/releases/0.1.0-beta.4.md +0 -50
- package/docs/releases/0.1.0-rc.1.md +0 -99
- package/docs/releases/0.2.0-rc.1.md +0 -141
- package/docs/releases/0.2.0-rc.2.md +0 -225
- package/docs/releases.md +0 -144
- package/docs/testing.md +0 -93
package/dist/workspace/index.js
CHANGED
|
@@ -3,10 +3,22 @@ import GithubSlugger from "github-slugger";
|
|
|
3
3
|
import { decodeNamedCharacterReference } from "decode-named-character-reference";
|
|
4
4
|
import { walk } from "../syntax/walk.js";
|
|
5
5
|
import { fallbackDialect } from "../config/resolve.js";
|
|
6
|
+
import { blockIdentifier } from "../syntax/block-identifier.js";
|
|
6
7
|
import { parse, range, textContent } from "../syntax/parse.js";
|
|
7
8
|
import { headingAttributes, lastLineSource } from "./heading-attributes.js";
|
|
8
9
|
import { createForgejoSlugger, createGiteaSlugger, giteaAnchor } from "./slug.js";
|
|
9
10
|
import { refusal } from "./access.js";
|
|
11
|
+
import { isNotUtf8 } from "./encoding.js";
|
|
12
|
+
/**
|
|
13
|
+
* Whether a file is Markdown by its name: one ending in `.md` or `.markdown`,
|
|
14
|
+
* in any letter case, both of which GitHub, Forgejo, and Gitea render.
|
|
15
|
+
* Discovery selects such files in folders, and their headings are link
|
|
16
|
+
* targets. For the Obsidian dialect only `.md` counts: Obsidian opens no
|
|
17
|
+
* other file as a note.
|
|
18
|
+
*/
|
|
19
|
+
export function isMarkdownName(name, dialect) {
|
|
20
|
+
return (dialect === "obsidian" ? /\.md$/i : /\.(?:md|markdown)$/i).test(name);
|
|
21
|
+
}
|
|
10
22
|
/** GFM's tagfilter makes GitHub show these tags as text; a hard break adds nothing. */
|
|
11
23
|
const github = {
|
|
12
24
|
shownTags: /^<\/?(?:iframe|noembed|noframes|plaintext|script|style|title|textarea|xmp)(?=[\t\n\f\r />])/i,
|
|
@@ -250,33 +262,57 @@ function htmlParts(document) {
|
|
|
250
262
|
});
|
|
251
263
|
return parts;
|
|
252
264
|
}
|
|
253
|
-
/**
|
|
265
|
+
/**
|
|
266
|
+
* Whether the outline holds a chain of headings that `parts` name, each one
|
|
267
|
+
* below the one before, within its section: `Setup#Mobile` is a heading
|
|
268
|
+
* `Mobile` of any lower level after a heading `Setup` and before the next
|
|
269
|
+
* heading of `Setup`'s level or higher. Every heading that a part names is
|
|
270
|
+
* tried, so the link is missing only when no such chain exists.
|
|
271
|
+
*/
|
|
272
|
+
function hasHeadingPath(outline, parts) {
|
|
273
|
+
const named = (names, part) => names.some((name) => name === part || name.toLowerCase() === part.toLowerCase());
|
|
274
|
+
const below = (part, from, depth) => {
|
|
275
|
+
for (let at = from; at < outline.length && outline[at].depth > depth; at++)
|
|
276
|
+
if (named(outline[at].names, parts[part]) &&
|
|
277
|
+
(part === parts.length - 1 || below(part + 1, at + 1, outline[at].depth)))
|
|
278
|
+
return true;
|
|
279
|
+
return false;
|
|
280
|
+
};
|
|
281
|
+
return below(0, 0, 0);
|
|
282
|
+
}
|
|
283
|
+
/** A target's anchors for Obsidian. */
|
|
254
284
|
function obsidianAnchors(document) {
|
|
255
285
|
const anchors = {
|
|
256
286
|
headings: new Set(),
|
|
257
287
|
foldedHeadings: new Set(),
|
|
288
|
+
outline: [],
|
|
258
289
|
blocks: new Set(),
|
|
259
290
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
291
|
+
const { source } = document;
|
|
292
|
+
walk(document.tree, (node) => {
|
|
293
|
+
const block = blockIdentifier(source, node);
|
|
294
|
+
if (block)
|
|
295
|
+
anchors.blocks.add(block.id);
|
|
296
|
+
if (node.type !== "heading")
|
|
297
|
+
return;
|
|
298
|
+
// A link may name a heading by the text it shows or as its source writes
|
|
299
|
+
// it: the heading `` `move` `` is both `move` and `` `move` ``.
|
|
300
|
+
const first = node.children[0];
|
|
301
|
+
const written = first ? source.slice(range(first)[0], range(node.children.at(-1))[1]) : "";
|
|
302
|
+
const names = [...new Set([textContent(node), written])].map(detach);
|
|
303
|
+
for (const name of names) {
|
|
304
|
+
anchors.headings.add(name);
|
|
305
|
+
anchors.foldedHeadings.add(name.toLowerCase());
|
|
306
|
+
}
|
|
307
|
+
anchors.outline.push({ depth: node.depth, names });
|
|
271
308
|
});
|
|
272
|
-
|
|
309
|
+
// The names are detached already, and so is their lowercase form.
|
|
310
|
+
detachAll(anchors.blocks);
|
|
273
311
|
return anchors;
|
|
274
312
|
}
|
|
275
|
-
/** A target's anchors for GitHub and CommonMark
|
|
313
|
+
/** A target's anchors for GitHub and CommonMark. */
|
|
276
314
|
function githubAnchors(document) {
|
|
277
315
|
const anchors = { slugs: new Set(), explicit: new Set() };
|
|
278
|
-
if (!document)
|
|
279
|
-
return anchors;
|
|
280
316
|
const slugger = new GithubSlugger();
|
|
281
317
|
const parts = htmlParts(document);
|
|
282
318
|
// GitHub numbers the anchors of Markdown and HTML headings together, in
|
|
@@ -298,15 +334,13 @@ function githubAnchors(document) {
|
|
|
298
334
|
detachAll(anchors.slugs, anchors.explicit);
|
|
299
335
|
return anchors;
|
|
300
336
|
}
|
|
301
|
-
/** A target's anchors for Forgejo and Gitea
|
|
337
|
+
/** A target's anchors for Forgejo and Gitea. */
|
|
302
338
|
function forgeAnchors(document) {
|
|
303
339
|
const anchors = {
|
|
304
340
|
forgejoSlugs: new Set(),
|
|
305
341
|
giteaSlugs: new Set(),
|
|
306
342
|
explicit: new Set(),
|
|
307
343
|
};
|
|
308
|
-
if (!document)
|
|
309
|
-
return anchors;
|
|
310
344
|
const { source } = document;
|
|
311
345
|
const forgejoSlugger = createForgejoSlugger();
|
|
312
346
|
const giteaSlugger = createGiteaSlugger();
|
|
@@ -380,6 +414,13 @@ export function splitDestination(destination) {
|
|
|
380
414
|
fragment: decode(hash < 0 ? "" : destination.slice(hash + 1)),
|
|
381
415
|
};
|
|
382
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* The workspace of `files`, which maps each path, relative to the workspace
|
|
419
|
+
* root with forward slashes, to a `WorkspaceSource`. Link checks, link
|
|
420
|
+
* rewriting, and Obsidian wrapping need one. A Markdown target is parsed in
|
|
421
|
+
* `options.dialect` when a link's fragment needs its anchors, which are kept
|
|
422
|
+
* and its text is not.
|
|
423
|
+
*/
|
|
383
424
|
export function createWorkspace(files, options = {}) {
|
|
384
425
|
const sources = new Map(Object.entries(files).map(([name, source]) => [
|
|
385
426
|
name.replaceAll("\\", "/").replace(/^\.\//, ""),
|
|
@@ -480,25 +521,39 @@ export function createWorkspace(files, options = {}) {
|
|
|
480
521
|
return foldedDirectories.has(name.toLowerCase());
|
|
481
522
|
}
|
|
482
523
|
const targets = new Map();
|
|
524
|
+
/** What the host's lookup found, by the matching asked for and the path. */
|
|
525
|
+
const lookedUp = new Map();
|
|
526
|
+
/** The sources of the files that the lookup found, by path. */
|
|
527
|
+
const foundSources = new Map();
|
|
528
|
+
/** What the host finds at a path that the index does not hold, asked once per path. */
|
|
529
|
+
function lookup(name, folded) {
|
|
530
|
+
if (!options.lookup)
|
|
531
|
+
return undefined;
|
|
532
|
+
const key = `${folded ? "i" : "s"}:${name}`;
|
|
533
|
+
if (lookedUp.has(key))
|
|
534
|
+
return lookedUp.get(key);
|
|
535
|
+
const result = options.lookup(name, folded);
|
|
536
|
+
lookedUp.set(key, result);
|
|
537
|
+
if (result?.type === "file")
|
|
538
|
+
foundSources.set(result.path, result.source);
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
483
541
|
/**
|
|
484
|
-
* One group of a target's anchors, from the target read and parsed
|
|
485
|
-
* nothing keeps the parsed document.
|
|
486
|
-
*
|
|
487
|
-
*
|
|
542
|
+
* One group of a Markdown target's anchors, from the target read and parsed
|
|
543
|
+
* for it; nothing keeps the parsed document. Undefined for a target that the
|
|
544
|
+
* system refuses to let be read, or whose text is not UTF-8, which is marked
|
|
545
|
+
* and not read again.
|
|
488
546
|
*/
|
|
489
|
-
function compute(target, name, anchors) {
|
|
547
|
+
function compute(target, value, name, anchors) {
|
|
490
548
|
if (target.unreadable)
|
|
491
549
|
return undefined;
|
|
492
|
-
const value = sources.get(name);
|
|
493
|
-
if (value === null || value === undefined || !/\.md$/i.test(name))
|
|
494
|
-
return anchors(null);
|
|
495
550
|
let source;
|
|
496
551
|
try {
|
|
497
552
|
source = typeof value === "function" ? value() : value;
|
|
498
553
|
}
|
|
499
554
|
catch (error) {
|
|
500
555
|
// The file exists, but its anchors are unknown; any other error is a fault.
|
|
501
|
-
if (!refusal(error))
|
|
556
|
+
if (!refusal(error) && !isNotUtf8(error))
|
|
502
557
|
throw error;
|
|
503
558
|
target.unreadable = true;
|
|
504
559
|
return undefined;
|
|
@@ -515,20 +570,30 @@ export function createWorkspace(files, options = {}) {
|
|
|
515
570
|
* page script also retries a fragment lowercased, and its anchors are.
|
|
516
571
|
*/
|
|
517
572
|
function hasAnchor(name, dialect, fragment, anchor) {
|
|
573
|
+
const value = sources.has(name) ? sources.get(name) : foundSources.get(name);
|
|
574
|
+
// Only a Markdown file's anchors are known. Other files take fragments of
|
|
575
|
+
// their own, such as an image's `#gh-dark-mode-only`, a PDF's `#page=2`, or
|
|
576
|
+
// a forge's line range `#L10-L20` in source code, which are not checked; so
|
|
577
|
+
// is a file that is linted as Markdown only because it was named.
|
|
578
|
+
if (value === null || value === undefined || !isMarkdownName(name, dialect))
|
|
579
|
+
return true;
|
|
518
580
|
let target = targets.get(name);
|
|
519
581
|
if (!target)
|
|
520
582
|
targets.set(name, (target = {}));
|
|
521
583
|
if (dialect === "obsidian") {
|
|
522
|
-
const anchors = target.obsidian ?? compute(target, name, obsidianAnchors);
|
|
584
|
+
const anchors = target.obsidian ?? compute(target, value, name, obsidianAnchors);
|
|
523
585
|
if (!anchors)
|
|
524
586
|
return undefined;
|
|
525
587
|
target.obsidian = anchors;
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
588
|
+
if (fragment.startsWith("^"))
|
|
589
|
+
return anchors.blocks.has(fragment.slice(1));
|
|
590
|
+
if (anchors.headings.has(fragment) || anchors.foldedHeadings.has(fragment.toLowerCase()))
|
|
591
|
+
return true;
|
|
592
|
+
// A path of headings, each below the one before, such as `Setup#Mobile`.
|
|
593
|
+
return fragment.includes("#") && hasHeadingPath(anchors.outline, fragment.split("#"));
|
|
529
594
|
}
|
|
530
595
|
if (dialect === "forgejo" || dialect === "gitea") {
|
|
531
|
-
const forge = target.forge ?? compute(target, name, forgeAnchors);
|
|
596
|
+
const forge = target.forge ?? compute(target, value, name, forgeAnchors);
|
|
532
597
|
if (!forge)
|
|
533
598
|
return undefined;
|
|
534
599
|
target.forge = forge;
|
|
@@ -537,7 +602,7 @@ export function createWorkspace(files, options = {}) {
|
|
|
537
602
|
return true;
|
|
538
603
|
return forge.explicit.has(anchor) || forge.explicit.has(fragment);
|
|
539
604
|
}
|
|
540
|
-
const anchors = target.github ?? compute(target, name, githubAnchors);
|
|
605
|
+
const anchors = target.github ?? compute(target, value, name, githubAnchors);
|
|
541
606
|
if (!anchors)
|
|
542
607
|
return undefined;
|
|
543
608
|
target.github = anchors;
|
|
@@ -639,7 +704,7 @@ export function createWorkspace(files, options = {}) {
|
|
|
639
704
|
...(options.strictLineBreaks !== undefined
|
|
640
705
|
? { strictLineBreaks: options.strictLineBreaks }
|
|
641
706
|
: {}),
|
|
642
|
-
resolve(source, destination, dialect) {
|
|
707
|
+
resolve(source, destination, dialect, wiki = false) {
|
|
643
708
|
if (/^[a-z][a-z\d+.-]*:/i.test(destination) || destination.startsWith("//"))
|
|
644
709
|
return { status: "external" };
|
|
645
710
|
source = source.replaceAll("\\", "/");
|
|
@@ -664,6 +729,22 @@ export function createWorkspace(files, options = {}) {
|
|
|
664
729
|
const partial = folded && !vault && rootFolders.length > 0;
|
|
665
730
|
// Whether Obsidian would look in a place of that vault outside the workspace.
|
|
666
731
|
let beyond = false;
|
|
732
|
+
// A link on the disk that the path leads through to outside the workspace root.
|
|
733
|
+
let leaves;
|
|
734
|
+
/** What the host finds at a path that the index does not hold. */
|
|
735
|
+
const look = (name, file = true) => {
|
|
736
|
+
const found = lookup(name, folded);
|
|
737
|
+
if (found?.type === "file") {
|
|
738
|
+
if (file)
|
|
739
|
+
candidates.add(found.path);
|
|
740
|
+
}
|
|
741
|
+
else if (found?.type === "directory")
|
|
742
|
+
directory ||= !wiki;
|
|
743
|
+
else if (found?.type === "outside")
|
|
744
|
+
leaves ??= found.through;
|
|
745
|
+
else if (found?.type === "unreadable")
|
|
746
|
+
blocking.add(found.at);
|
|
747
|
+
};
|
|
667
748
|
const add = (candidate) => {
|
|
668
749
|
const normalized = path.posix.normalize(candidate);
|
|
669
750
|
if (normalized === ".." || normalized.startsWith("../")) {
|
|
@@ -681,10 +762,11 @@ export function createWorkspace(files, options = {}) {
|
|
|
681
762
|
outside ??=
|
|
682
763
|
namesMatching(bare)[0] ??
|
|
683
764
|
(path.posix.extname(bare) ? undefined : namesMatching(`${bare}.md`)[0]) ??
|
|
684
|
-
(isDirectory(bare, true) ? bare : undefined);
|
|
765
|
+
(!wiki && isDirectory(bare, true) ? bare : undefined);
|
|
685
766
|
return;
|
|
686
767
|
}
|
|
687
|
-
|
|
768
|
+
// Obsidian links a wikilink to a note or a file, never to a folder.
|
|
769
|
+
if (!wiki && isDirectory(bare, folded))
|
|
688
770
|
directory = true;
|
|
689
771
|
const above = unreadableAbove(bare, folded);
|
|
690
772
|
if (above !== undefined)
|
|
@@ -692,14 +774,23 @@ export function createWorkspace(files, options = {}) {
|
|
|
692
774
|
if (!folded) {
|
|
693
775
|
if (sources.has(normalized))
|
|
694
776
|
candidates.add(normalized);
|
|
777
|
+
// A path that ends with a slash names a folder only.
|
|
778
|
+
else if (!directory)
|
|
779
|
+
look(bare, bare === normalized);
|
|
695
780
|
return;
|
|
696
781
|
}
|
|
697
782
|
// Names differing only by case all count, so such vaults report ambiguity.
|
|
783
|
+
const before = candidates.size;
|
|
698
784
|
for (const name of namesMatching(normalized))
|
|
699
785
|
candidates.add(name);
|
|
700
786
|
if (!path.posix.extname(normalized))
|
|
701
787
|
for (const name of namesMatching(`${normalized}.md`))
|
|
702
788
|
candidates.add(name);
|
|
789
|
+
if (candidates.size === before && !directory) {
|
|
790
|
+
look(bare);
|
|
791
|
+
if (!path.posix.extname(bare))
|
|
792
|
+
look(`${bare}.md`);
|
|
793
|
+
}
|
|
703
794
|
};
|
|
704
795
|
if (!targetPath)
|
|
705
796
|
add(source);
|
|
@@ -750,6 +841,14 @@ export function createWorkspace(files, options = {}) {
|
|
|
750
841
|
// A match is uncertain when a place that could not be looked in may hold a
|
|
751
842
|
// note that Obsidian would choose instead, or as well.
|
|
752
843
|
const uncertain = [...(unsearched ? hidden : obstructed ? blocking : []), ...unseen];
|
|
844
|
+
// The tool reads nothing outside the workspace root, where the link leads.
|
|
845
|
+
if (candidates.size === 0 && leaves !== undefined)
|
|
846
|
+
return {
|
|
847
|
+
status: "unreadable",
|
|
848
|
+
unreadable: [leaves],
|
|
849
|
+
leavesRoot: leaves,
|
|
850
|
+
...(vault ? { vault } : {}),
|
|
851
|
+
};
|
|
753
852
|
if (candidates.size === 0 && (blocking.size > 0 || unsearched || unseen.length > 0))
|
|
754
853
|
return {
|
|
755
854
|
status: "unreadable",
|