mdrefine 0.1.0-beta.1 → 0.1.0-beta.3
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/CONTRIBUTING.md +6 -0
- package/README.md +10 -6
- package/dist/cli/main.js +9 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/config/resolve.d.ts +12 -0
- package/dist/config/resolve.js +7 -1
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/engine.js +29 -9
- package/dist/core/engine.js.map +1 -1
- package/dist/core/types.d.ts +7 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/rules/links.js +28 -5
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/structure.d.ts +2 -0
- package/dist/rules/structure.js +24 -0
- package/dist/rules/structure.js.map +1 -0
- package/dist/rules/style.js +138 -29
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/obsidian.js +17 -0
- package/dist/syntax/obsidian.js.map +1 -1
- package/dist/workspace/files.d.ts +5 -2
- package/dist/workspace/files.js +15 -6
- package/dist/workspace/files.js.map +1 -1
- package/dist/workspace/index.d.ts +4 -1
- package/dist/workspace/index.js +53 -15
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/selection.d.ts +2 -0
- package/dist/workspace/selection.js +31 -0
- package/dist/workspace/selection.js.map +1 -0
- package/docs/cli.md +18 -0
- package/docs/configuration.md +91 -25
- package/docs/contributing.md +87 -5
- package/docs/design.md +28 -5
- package/docs/plugins.md +11 -4
- package/docs/quick-start.md +1 -1
- package/docs/releases/0.1.0-beta.2.md +83 -0
- package/docs/releases/0.1.0-beta.3.md +61 -0
- package/docs/releases.md +34 -10
- package/package.json +1 -1
package/dist/workspace/files.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { lstat, readdir, readFile, realpath, open, rename, unlink } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
4
5
|
import ignore from "ignore";
|
|
5
6
|
import { minimatch } from "minimatch";
|
|
6
7
|
import { exists } from "../config/load.js";
|
|
7
|
-
export async function discover(rootPath, supplied, excluded) {
|
|
8
|
+
export async function discover(rootPath, supplied, excluded, resolve = {}) {
|
|
8
9
|
const root = await realpath(rootPath);
|
|
9
10
|
const files = {};
|
|
11
|
+
const directories = [];
|
|
10
12
|
const selected = [];
|
|
11
13
|
const requests = [];
|
|
12
14
|
for (const input of supplied.length ? supplied : [root]) {
|
|
@@ -19,7 +21,7 @@ export async function discover(rootPath, supplied, excluded) {
|
|
|
19
21
|
throw new Error(`Input is outside the workspace: ${input}`);
|
|
20
22
|
requests.push(relative);
|
|
21
23
|
}
|
|
22
|
-
async function walk(relative, inherited) {
|
|
24
|
+
async function walk(relative, inherited, targetOnly = false) {
|
|
23
25
|
const directory = path.join(root, relative);
|
|
24
26
|
const layers = [...inherited];
|
|
25
27
|
if (await exists(path.join(directory, ".gitignore")))
|
|
@@ -32,17 +34,23 @@ export async function discover(rootPath, supplied, excluded) {
|
|
|
32
34
|
entry.isSymbolicLink())
|
|
33
35
|
continue;
|
|
34
36
|
const name = relative ? `${relative}/${entry.name}` : entry.name;
|
|
35
|
-
|
|
37
|
+
const gitIgnored = layers.some((layer) => layer.matcher.ignores(name.slice(layer.base ? layer.base.length + 1 : 0) + (entry.isDirectory() ? "/" : "")));
|
|
38
|
+
if (gitIgnored && resolve.gitIgnored !== true)
|
|
36
39
|
continue;
|
|
37
40
|
const full = path.join(root, name);
|
|
38
41
|
if (entry.isDirectory()) {
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
directories.push(name);
|
|
43
|
+
const nested = await exists(path.join(full, ".git"));
|
|
44
|
+
if (!nested || resolve.nestedRepositories === true)
|
|
45
|
+
await walk(name, layers, targetOnly || gitIgnored || nested);
|
|
41
46
|
}
|
|
42
47
|
else if (entry.isFile()) {
|
|
43
48
|
const markdown = /\.md$/i.test(name);
|
|
44
|
-
|
|
49
|
+
let cached;
|
|
50
|
+
files[name] = markdown ? () => (cached ??= readFileSync(full, "utf8")) : null;
|
|
45
51
|
if (markdown &&
|
|
52
|
+
!targetOnly &&
|
|
53
|
+
!gitIgnored &&
|
|
46
54
|
requests.some((request) => !request || name === request || name.startsWith(`${request}/`)) &&
|
|
47
55
|
!excluded.some((pattern) => minimatch(name, pattern, { dot: true })))
|
|
48
56
|
selected.push(name);
|
|
@@ -63,6 +71,7 @@ export async function discover(rootPath, supplied, excluded) {
|
|
|
63
71
|
return {
|
|
64
72
|
root,
|
|
65
73
|
files,
|
|
74
|
+
directories,
|
|
66
75
|
selected: selected.sort(),
|
|
67
76
|
...(strictLineBreaks !== undefined ? { strictLineBreaks } : {}),
|
|
68
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/workspace/files.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, readdir, readFile, realpath, open, rename, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport ignore from \"ignore\";\nimport { minimatch } from \"minimatch\";\nimport { exists } from \"../config/load.js\";\n\nexport interface FileSet {\n root: string;\n files: Record<string, string | null>;\n selected: string[];\n strictLineBreaks?: boolean;\n}\nexport async function discover(\n rootPath: string,\n supplied: string[],\n excluded: string[],\n): Promise<FileSet> {\n const root = await realpath(rootPath);\n const files: Record<string, string | null> = {};\n const selected: string[] = [];\n const requests: string[] = [];\n for (const input of supplied.length ? supplied : [root]) {\n const absolute = path.resolve(input);\n if ((await lstat(absolute)).isSymbolicLink())\n throw new Error(`Refusing symbolic-link input: ${input}`);\n const resolved = await realpath(absolute);\n const relative = path.relative(root, resolved).split(path.sep).join(\"/\");\n if (relative.startsWith(\"../\") || relative === \"..\" || path.isAbsolute(relative))\n throw new Error(`Input is outside the workspace: ${input}`);\n requests.push(relative);\n }\n interface IgnoreLayer {\n base: string;\n matcher: ReturnType<typeof ignore>;\n }\n async function walk(relative: string, inherited: IgnoreLayer[]) {\n const directory = path.join(root, relative);\n const layers = [...inherited];\n if (await exists(path.join(directory, \".gitignore\")))\n layers.push({\n base: relative,\n matcher: ignore().add(await readFile(path.join(directory, \".gitignore\"), \"utf8\")),\n });\n for (const entry of await readdir(directory, { withFileTypes: true })) {\n if (\n [\".git\", \".obsidian\", \"node_modules\", \".npm-cache\", \"dist\", \"coverage\"].includes(\n entry.name,\n ) ||\n entry.isSymbolicLink()\n )\n continue;\n const name = relative ? `${relative}/${entry.name}` : entry.name;\n if (\n layers.some((layer) =>\n layer.matcher.ignores(\n name.slice(layer.base ? layer.base.length + 1 : 0) + (entry.isDirectory() ? \"/\" : \"\"),\n ),\n )\n )\n continue;\n const full = path.join(root, name);\n if (entry.isDirectory()) {\n if (!(await exists(path.join(full, \".git\")))) await walk(name, layers);\n } else if (entry.isFile()) {\n const markdown = /\\.md$/i.test(name);\n files[name] = markdown ? await readFile(full, \"utf8\") : null;\n if (\n markdown &&\n requests.some(\n (request) => !request || name === request || name.startsWith(`${request}/`),\n ) &&\n !excluded.some((pattern) => minimatch(name, pattern, { dot: true }))\n )\n selected.push(name);\n }\n }\n }\n await walk(\"\", []);\n let strictLineBreaks: boolean | undefined;\n const app = path.join(root, \".obsidian\", \"app.json\");\n if (await exists(app)) {\n const settings: unknown = JSON.parse(await readFile(app, \"utf8\"));\n strictLineBreaks =\n typeof settings === \"object\" &&\n settings !== null &&\n \"strictLineBreaks\" in settings &&\n settings.strictLineBreaks === true;\n }\n return {\n root,\n files,\n selected: selected.sort(),\n ...(strictLineBreaks !== undefined ? { strictLineBreaks } : {}),\n };\n}\nexport async function writeAtomic(file: string, before: string, after: string): Promise<void> {\n const info = await lstat(file);\n if (!info.isFile() || info.isSymbolicLink())\n throw new Error(`Refusing non-regular file: ${file}`);\n if ((await readFile(file, \"utf8\")) !== before)\n throw new Error(`File changed during formatting: ${file}`);\n const temporary = path.join(path.dirname(file), `.mdtools-${randomUUID()}.tmp`);\n const handle = await open(temporary, \"wx\", info.mode);\n try {\n await handle.writeFile(after, \"utf8\");\n await handle.chmod(info.mode);\n await handle.sync();\n await handle.close();\n const current = await lstat(file);\n if (\n current.isSymbolicLink() ||\n current.ino !== info.ino ||\n (await readFile(file, \"utf8\")) !== before\n )\n throw new Error(`File changed before replacement: ${file}`);\n await rename(temporary, file);\n } finally {\n await handle.close();\n await unlink(temporary).catch((error: NodeJS.ErrnoException) => {\n if (error.code !== \"ENOENT\") throw error;\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/workspace/files.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, readdir, readFile, realpath, open, rename, unlink } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport type { ResolveOptions } from \"../core/types.js\";\nimport type { WorkspaceSource } from \"./index.js\";\nimport { randomUUID } from \"node:crypto\";\nimport ignore from \"ignore\";\nimport { minimatch } from \"minimatch\";\nimport { exists } from \"../config/load.js\";\n\nexport interface FileSet {\n root: string;\n files: Record<string, WorkspaceSource>;\n directories: string[];\n selected: string[];\n strictLineBreaks?: boolean;\n}\nexport async function discover(\n rootPath: string,\n supplied: string[],\n excluded: string[],\n resolve: ResolveOptions = {},\n): Promise<FileSet> {\n const root = await realpath(rootPath);\n const files: Record<string, WorkspaceSource> = {};\n const directories: string[] = [];\n const selected: string[] = [];\n const requests: string[] = [];\n for (const input of supplied.length ? supplied : [root]) {\n const absolute = path.resolve(input);\n if ((await lstat(absolute)).isSymbolicLink())\n throw new Error(`Refusing symbolic-link input: ${input}`);\n const resolved = await realpath(absolute);\n const relative = path.relative(root, resolved).split(path.sep).join(\"/\");\n if (relative.startsWith(\"../\") || relative === \"..\" || path.isAbsolute(relative))\n throw new Error(`Input is outside the workspace: ${input}`);\n requests.push(relative);\n }\n interface IgnoreLayer {\n base: string;\n matcher: ReturnType<typeof ignore>;\n }\n async function walk(relative: string, inherited: IgnoreLayer[], targetOnly = false) {\n const directory = path.join(root, relative);\n const layers = [...inherited];\n if (await exists(path.join(directory, \".gitignore\")))\n layers.push({\n base: relative,\n matcher: ignore().add(await readFile(path.join(directory, \".gitignore\"), \"utf8\")),\n });\n for (const entry of await readdir(directory, { withFileTypes: true })) {\n if (\n [\".git\", \".obsidian\", \"node_modules\", \".npm-cache\", \"dist\", \"coverage\"].includes(\n entry.name,\n ) ||\n entry.isSymbolicLink()\n )\n continue;\n const name = relative ? `${relative}/${entry.name}` : entry.name;\n const gitIgnored = layers.some((layer) =>\n layer.matcher.ignores(\n name.slice(layer.base ? layer.base.length + 1 : 0) + (entry.isDirectory() ? \"/\" : \"\"),\n ),\n );\n if (gitIgnored && resolve.gitIgnored !== true) continue;\n const full = path.join(root, name);\n if (entry.isDirectory()) {\n directories.push(name);\n const nested = await exists(path.join(full, \".git\"));\n if (!nested || resolve.nestedRepositories === true)\n await walk(name, layers, targetOnly || gitIgnored || nested);\n } else if (entry.isFile()) {\n const markdown = /\\.md$/i.test(name);\n let cached: string | undefined;\n files[name] = markdown ? () => (cached ??= readFileSync(full, \"utf8\")) : null;\n if (\n markdown &&\n !targetOnly &&\n !gitIgnored &&\n requests.some(\n (request) => !request || name === request || name.startsWith(`${request}/`),\n ) &&\n !excluded.some((pattern) => minimatch(name, pattern, { dot: true }))\n )\n selected.push(name);\n }\n }\n }\n await walk(\"\", []);\n let strictLineBreaks: boolean | undefined;\n const app = path.join(root, \".obsidian\", \"app.json\");\n if (await exists(app)) {\n const settings: unknown = JSON.parse(await readFile(app, \"utf8\"));\n strictLineBreaks =\n typeof settings === \"object\" &&\n settings !== null &&\n \"strictLineBreaks\" in settings &&\n settings.strictLineBreaks === true;\n }\n return {\n root,\n files,\n directories,\n selected: selected.sort(),\n ...(strictLineBreaks !== undefined ? { strictLineBreaks } : {}),\n };\n}\nexport async function writeAtomic(file: string, before: string, after: string): Promise<void> {\n const info = await lstat(file);\n if (!info.isFile() || info.isSymbolicLink())\n throw new Error(`Refusing non-regular file: ${file}`);\n if ((await readFile(file, \"utf8\")) !== before)\n throw new Error(`File changed during formatting: ${file}`);\n const temporary = path.join(path.dirname(file), `.mdtools-${randomUUID()}.tmp`);\n const handle = await open(temporary, \"wx\", info.mode);\n try {\n await handle.writeFile(after, \"utf8\");\n await handle.chmod(info.mode);\n await handle.sync();\n await handle.close();\n const current = await lstat(file);\n if (\n current.isSymbolicLink() ||\n current.ino !== info.ino ||\n (await readFile(file, \"utf8\")) !== before\n )\n throw new Error(`File changed before replacement: ${file}`);\n await rename(temporary, file);\n } finally {\n await handle.close();\n await unlink(temporary).catch((error: NodeJS.ErrnoException) => {\n if (error.code !== \"ENOENT\") throw error;\n });\n }\n}\n"]}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Dialect, Workspace } from "../core/types.js";
|
|
2
|
+
export type WorkspaceSource = string | null | (() => string);
|
|
2
3
|
export interface WorkspaceOptions {
|
|
3
4
|
dialect?: Dialect;
|
|
4
5
|
strictLineBreaks?: boolean;
|
|
6
|
+
/** Existing directories, including empty ones; never treated as note targets. */
|
|
7
|
+
directories?: string[];
|
|
5
8
|
}
|
|
6
9
|
export declare function splitDestination(destination: string): {
|
|
7
10
|
path: string;
|
|
8
11
|
fragment: string;
|
|
9
12
|
};
|
|
10
|
-
export declare function createWorkspace(files: Record<string,
|
|
13
|
+
export declare function createWorkspace(files: Record<string, WorkspaceSource>, options?: WorkspaceOptions): Workspace;
|
package/dist/workspace/index.js
CHANGED
|
@@ -18,10 +18,28 @@ export function splitDestination(destination) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
export function createWorkspace(files, options = {}) {
|
|
21
|
+
const sources = new Map(Object.entries(files).map(([name, source]) => [
|
|
22
|
+
name.replaceAll("\\", "/").replace(/^\.\//, ""),
|
|
23
|
+
source,
|
|
24
|
+
]));
|
|
25
|
+
const directories = new Set((options.directories ?? []).map((name) => name.replaceAll("\\", "/")));
|
|
26
|
+
directories.add(".");
|
|
27
|
+
for (const name of sources.keys()) {
|
|
28
|
+
let directory = path.posix.dirname(name);
|
|
29
|
+
while (directory !== "." && directory !== path.posix.dirname(directory)) {
|
|
30
|
+
directories.add(directory);
|
|
31
|
+
directory = path.posix.dirname(directory);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
21
34
|
const entries = new Map();
|
|
22
|
-
|
|
35
|
+
function fragments(name) {
|
|
36
|
+
const cached = entries.get(name);
|
|
37
|
+
if (cached)
|
|
38
|
+
return cached;
|
|
23
39
|
const entry = { headings: new Set(), slugs: new Set(), blocks: new Set() };
|
|
24
|
-
|
|
40
|
+
const value = sources.get(name);
|
|
41
|
+
if (value !== null && value !== undefined && /\.md$/i.test(name)) {
|
|
42
|
+
const source = typeof value === "function" ? value() : value;
|
|
25
43
|
const document = parse(source, options.dialect ?? "commonmark", name);
|
|
26
44
|
const slugger = new GithubSlugger();
|
|
27
45
|
visit(document.tree, "heading", (node) => {
|
|
@@ -35,7 +53,26 @@ export function createWorkspace(files, options = {}) {
|
|
|
35
53
|
entry.blocks.add(match[1]);
|
|
36
54
|
});
|
|
37
55
|
}
|
|
38
|
-
entries.set(name
|
|
56
|
+
entries.set(name, entry);
|
|
57
|
+
return entry;
|
|
58
|
+
}
|
|
59
|
+
let suffixes;
|
|
60
|
+
function suffixCandidates(target) {
|
|
61
|
+
if (!suffixes) {
|
|
62
|
+
suffixes = new Map();
|
|
63
|
+
for (const name of sources.keys()) {
|
|
64
|
+
const parts = name.split("/");
|
|
65
|
+
for (let i = 0; i < parts.length; i++) {
|
|
66
|
+
const suffix = parts.slice(i).join("/");
|
|
67
|
+
for (const key of suffix.endsWith(".md") ? [suffix, suffix.slice(0, -3)] : [suffix]) {
|
|
68
|
+
if (!suffixes.has(key))
|
|
69
|
+
suffixes.set(key, new Set());
|
|
70
|
+
suffixes.get(key).add(name);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return suffixes.get(target);
|
|
39
76
|
}
|
|
40
77
|
return {
|
|
41
78
|
...(options.strictLineBreaks !== undefined
|
|
@@ -44,20 +81,24 @@ export function createWorkspace(files, options = {}) {
|
|
|
44
81
|
resolve(source, destination, dialect) {
|
|
45
82
|
if (/^[a-z][a-z\d+.-]*:/i.test(destination) || destination.startsWith("//"))
|
|
46
83
|
return { status: "external" };
|
|
84
|
+
source = source.replaceAll("\\", "/");
|
|
47
85
|
const parts = splitDestination(destination);
|
|
48
86
|
const targetPath = parts.path;
|
|
49
87
|
if (dialect !== "obsidian" && (targetPath.startsWith("/") || targetPath.includes("?")))
|
|
50
88
|
return { status: "unavailable" };
|
|
51
89
|
const candidates = new Set();
|
|
90
|
+
let directory = false;
|
|
52
91
|
const add = (candidate) => {
|
|
53
92
|
const normalized = path.posix.normalize(candidate);
|
|
54
93
|
if (normalized.startsWith("../") || path.posix.isAbsolute(normalized))
|
|
55
94
|
return;
|
|
56
|
-
if (
|
|
95
|
+
if (directories.has(normalized.replace(/\/$/, "")))
|
|
96
|
+
directory = true;
|
|
97
|
+
if (sources.has(normalized))
|
|
57
98
|
candidates.add(normalized);
|
|
58
99
|
if (dialect === "obsidian" &&
|
|
59
100
|
!path.posix.extname(normalized) &&
|
|
60
|
-
|
|
101
|
+
sources.has(`${normalized}.md`))
|
|
61
102
|
candidates.add(`${normalized}.md`);
|
|
62
103
|
};
|
|
63
104
|
if (!targetPath)
|
|
@@ -67,26 +108,23 @@ export function createWorkspace(files, options = {}) {
|
|
|
67
108
|
add(path.posix.join(path.posix.dirname(source), targetPath));
|
|
68
109
|
else {
|
|
69
110
|
add(targetPath.replace(/^\//, ""));
|
|
70
|
-
if (!targetPath.startsWith("/"))
|
|
111
|
+
if (candidates.size === 0 && !directory && !targetPath.startsWith("/"))
|
|
71
112
|
add(path.posix.join(path.posix.dirname(source), targetPath));
|
|
72
|
-
if (candidates.size === 0) {
|
|
73
|
-
for (const name of
|
|
74
|
-
|
|
75
|
-
name.endsWith(`/${targetPath}`) ||
|
|
76
|
-
name === `${targetPath}.md` ||
|
|
77
|
-
name.endsWith(`/${targetPath}.md`))
|
|
78
|
-
candidates.add(name);
|
|
79
|
-
}
|
|
113
|
+
if (candidates.size === 0 && !directory && !targetPath.startsWith("/")) {
|
|
114
|
+
for (const name of suffixCandidates(targetPath) ?? [])
|
|
115
|
+
candidates.add(name);
|
|
80
116
|
}
|
|
81
117
|
}
|
|
82
118
|
}
|
|
83
119
|
else
|
|
84
120
|
add(path.posix.join(path.posix.dirname(source), targetPath));
|
|
121
|
+
if (candidates.size === 0 && directory)
|
|
122
|
+
return { status: "directory" };
|
|
85
123
|
if (candidates.size !== 1)
|
|
86
124
|
return { status: candidates.size ? "ambiguous" : "missing" };
|
|
87
125
|
const target = [...candidates][0];
|
|
88
|
-
const entry = entries.get(target);
|
|
89
126
|
const fragment = parts.fragment;
|
|
127
|
+
const entry = fragment ? fragments(target) : undefined;
|
|
90
128
|
const fragmentExists = !fragment ||
|
|
91
129
|
(dialect === "obsidian"
|
|
92
130
|
? fragment.startsWith("^")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport GithubSlugger from \"github-slugger\";\nimport { visit } from \"unist-util-visit\";\nimport type { Dialect, LinkResolution, Workspace } from \"../core/types.js\";\nimport { parse, textContent } from \"../syntax/parse.js\";\n\ninterface Entry {\n headings: Set<string>;\n slugs: Set<string>;\n blocks: Set<string>;\n}\nexport interface WorkspaceOptions {\n dialect?: Dialect;\n strictLineBreaks?: boolean;\n}\nexport function splitDestination(destination: string): { path: string; fragment: string } {\n const hash = destination.indexOf(\"#\");\n const decode = (value: string) => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n };\n return {\n path: decode(hash < 0 ? destination : destination.slice(0, hash)),\n fragment: decode(hash < 0 ? \"\" : destination.slice(hash + 1)),\n };\n}\nexport function createWorkspace(\n files: Record<string, string | null>,\n options: WorkspaceOptions = {},\n): Workspace {\n const entries = new Map<string, Entry>();\n for (const [name, source] of Object.entries(files)) {\n const entry: Entry = { headings: new Set(), slugs: new Set(), blocks: new Set() };\n if (source !== null && /\\.md$/i.test(name)) {\n const document = parse(source, options.dialect ?? \"commonmark\", name);\n const slugger = new GithubSlugger();\n visit(document.tree, \"heading\", (node) => {\n const text = textContent(node);\n entry.headings.add(text);\n entry.slugs.add(slugger.slug(text));\n });\n visit(document.tree, \"text\", (node) => {\n const match = /(?:^|\\s)\\^([A-Za-z0-9-]+)\\s*$/.exec(node.value);\n if (match) entry.blocks.add(match[1]!);\n });\n }\n entries.set(name.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\"), entry);\n }\n return {\n ...(options.strictLineBreaks !== undefined\n ? { strictLineBreaks: options.strictLineBreaks }\n : {}),\n resolve(source, destination, dialect): LinkResolution {\n if (/^[a-z][a-z\\d+.-]*:/i.test(destination) || destination.startsWith(\"//\"))\n return { status: \"external\" };\n const parts = splitDestination(destination);\n const targetPath = parts.path;\n if (dialect !== \"obsidian\" && (targetPath.startsWith(\"/\") || targetPath.includes(\"?\")))\n return { status: \"unavailable\" };\n const candidates = new Set<string>();\n const add = (candidate: string) => {\n const normalized = path.posix.normalize(candidate);\n if (normalized.startsWith(\"../\") || path.posix.isAbsolute(normalized)) return;\n if (entries.has(normalized)) candidates.add(normalized);\n if (\n dialect === \"obsidian\" &&\n !path.posix.extname(normalized) &&\n entries.has(`${normalized}.md`)\n )\n candidates.add(`${normalized}.md`);\n };\n if (!targetPath) add(source);\n else if (dialect === \"obsidian\") {\n if (/^\\.{1,2}\\//.test(targetPath))\n add(path.posix.join(path.posix.dirname(source), targetPath));\n else {\n add(targetPath.replace(/^\\//, \"\"));\n if (!targetPath.startsWith(\"/\"))\n add(path.posix.join(path.posix.dirname(source), targetPath));\n if (candidates.size === 0) {\n for (const name of entries.keys()) {\n if (\n name === targetPath ||\n name.endsWith(`/${targetPath}`) ||\n name === `${targetPath}.md` ||\n name.endsWith(`/${targetPath}.md`)\n )\n candidates.add(name);\n }\n }\n }\n } else add(path.posix.join(path.posix.dirname(source), targetPath));\n if (candidates.size !== 1) return { status: candidates.size ? \"ambiguous\" : \"missing\" };\n const target = [...candidates][0]!;\n const entry = entries.get(target)!;\n const fragment = parts.fragment;\n const fragmentExists =\n !fragment ||\n (dialect === \"obsidian\"\n ? fragment.startsWith(\"^\")\n ? entry.blocks.has(fragment.slice(1))\n : entry.headings.has(fragment)\n : entry.slugs.has(fragment));\n return { status: \"resolved\", target, fragment, fragmentExists };\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport GithubSlugger from \"github-slugger\";\nimport { visit } from \"unist-util-visit\";\nimport type { Dialect, LinkResolution, Workspace } from \"../core/types.js\";\nimport { parse, textContent } from \"../syntax/parse.js\";\n\nexport type WorkspaceSource = string | null | (() => string);\n\ninterface Entry {\n headings: Set<string>;\n slugs: Set<string>;\n blocks: Set<string>;\n}\nexport interface WorkspaceOptions {\n dialect?: Dialect;\n strictLineBreaks?: boolean;\n /** Existing directories, including empty ones; never treated as note targets. */\n directories?: string[];\n}\nexport function splitDestination(destination: string): { path: string; fragment: string } {\n const hash = destination.indexOf(\"#\");\n const decode = (value: string) => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n };\n return {\n path: decode(hash < 0 ? destination : destination.slice(0, hash)),\n fragment: decode(hash < 0 ? \"\" : destination.slice(hash + 1)),\n };\n}\nexport function createWorkspace(\n files: Record<string, WorkspaceSource>,\n options: WorkspaceOptions = {},\n): Workspace {\n const sources = new Map(\n Object.entries(files).map(([name, source]) => [\n name.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\//, \"\"),\n source,\n ]),\n );\n const directories = new Set(\n (options.directories ?? []).map((name) => name.replaceAll(\"\\\\\", \"/\")),\n );\n directories.add(\".\");\n for (const name of sources.keys()) {\n let directory = path.posix.dirname(name);\n while (directory !== \".\" && directory !== path.posix.dirname(directory)) {\n directories.add(directory);\n directory = path.posix.dirname(directory);\n }\n }\n const entries = new Map<string, Entry>();\n function fragments(name: string): Entry {\n const cached = entries.get(name);\n if (cached) return cached;\n const entry: Entry = { headings: new Set(), slugs: new Set(), blocks: new Set() };\n const value = sources.get(name);\n if (value !== null && value !== undefined && /\\.md$/i.test(name)) {\n const source = typeof value === \"function\" ? value() : value;\n const document = parse(source, options.dialect ?? \"commonmark\", name);\n const slugger = new GithubSlugger();\n visit(document.tree, \"heading\", (node) => {\n const text = textContent(node);\n entry.headings.add(text);\n entry.slugs.add(slugger.slug(text));\n });\n visit(document.tree, \"text\", (node) => {\n const match = /(?:^|\\s)\\^([A-Za-z0-9-]+)\\s*$/.exec(node.value);\n if (match) entry.blocks.add(match[1]!);\n });\n }\n entries.set(name, entry);\n return entry;\n }\n let suffixes: Map<string, Set<string>> | undefined;\n function suffixCandidates(target: string): Set<string> | undefined {\n if (!suffixes) {\n suffixes = new Map();\n for (const name of sources.keys()) {\n const parts = name.split(\"/\");\n for (let i = 0; i < parts.length; i++) {\n const suffix = parts.slice(i).join(\"/\");\n for (const key of suffix.endsWith(\".md\") ? [suffix, suffix.slice(0, -3)] : [suffix]) {\n if (!suffixes.has(key)) suffixes.set(key, new Set());\n suffixes.get(key)!.add(name);\n }\n }\n }\n }\n return suffixes.get(target);\n }\n return {\n ...(options.strictLineBreaks !== undefined\n ? { strictLineBreaks: options.strictLineBreaks }\n : {}),\n resolve(source, destination, dialect): LinkResolution {\n if (/^[a-z][a-z\\d+.-]*:/i.test(destination) || destination.startsWith(\"//\"))\n return { status: \"external\" };\n source = source.replaceAll(\"\\\\\", \"/\");\n const parts = splitDestination(destination);\n const targetPath = parts.path;\n if (dialect !== \"obsidian\" && (targetPath.startsWith(\"/\") || targetPath.includes(\"?\")))\n return { status: \"unavailable\" };\n const candidates = new Set<string>();\n let directory = false;\n const add = (candidate: string) => {\n const normalized = path.posix.normalize(candidate);\n if (normalized.startsWith(\"../\") || path.posix.isAbsolute(normalized)) return;\n if (directories.has(normalized.replace(/\\/$/, \"\"))) directory = true;\n if (sources.has(normalized)) candidates.add(normalized);\n if (\n dialect === \"obsidian\" &&\n !path.posix.extname(normalized) &&\n sources.has(`${normalized}.md`)\n )\n candidates.add(`${normalized}.md`);\n };\n if (!targetPath) add(source);\n else if (dialect === \"obsidian\") {\n if (/^\\.{1,2}\\//.test(targetPath))\n add(path.posix.join(path.posix.dirname(source), targetPath));\n else {\n add(targetPath.replace(/^\\//, \"\"));\n if (candidates.size === 0 && !directory && !targetPath.startsWith(\"/\"))\n add(path.posix.join(path.posix.dirname(source), targetPath));\n if (candidates.size === 0 && !directory && !targetPath.startsWith(\"/\")) {\n for (const name of suffixCandidates(targetPath) ?? []) candidates.add(name);\n }\n }\n } else add(path.posix.join(path.posix.dirname(source), targetPath));\n if (candidates.size === 0 && directory) return { status: \"directory\" };\n if (candidates.size !== 1) return { status: candidates.size ? \"ambiguous\" : \"missing\" };\n const target = [...candidates][0]!;\n const fragment = parts.fragment;\n const entry = fragment ? fragments(target) : undefined;\n const fragmentExists =\n !fragment ||\n (dialect === \"obsidian\"\n ? fragment.startsWith(\"^\")\n ? entry!.blocks.has(fragment.slice(1))\n : entry!.headings.has(fragment)\n : entry!.slugs.has(fragment));\n return { status: \"resolved\", target, fragment, fragmentExists };\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
3
|
+
/** Exclusions are literal paths relative to cwd, never glob patterns. */
|
|
4
|
+
export async function excludeSelection(root, selected, exclusions) {
|
|
5
|
+
// Resolve filesystem aliases (including Windows short names) consistently
|
|
6
|
+
// with discovery, while allowing exclusions for paths that do not exist yet.
|
|
7
|
+
async function canonical(file) {
|
|
8
|
+
try {
|
|
9
|
+
return await realpath(file);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code !== "ENOENT")
|
|
13
|
+
throw error;
|
|
14
|
+
const parent = path.dirname(file);
|
|
15
|
+
if (parent === file)
|
|
16
|
+
throw error;
|
|
17
|
+
return path.join(await canonical(parent), path.basename(file));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const relative = await Promise.all(exclusions.map(async (input) => {
|
|
21
|
+
const name = path
|
|
22
|
+
.relative(root, await canonical(path.resolve(input)))
|
|
23
|
+
.split(path.sep)
|
|
24
|
+
.join("/");
|
|
25
|
+
if (name === ".." || name.startsWith("../") || path.isAbsolute(name))
|
|
26
|
+
throw new Error(`Excluded path is outside the workspace: ${input}`);
|
|
27
|
+
return name;
|
|
28
|
+
}));
|
|
29
|
+
return selected.filter((name) => !relative.some((excluded) => !excluded || name === excluded || name.startsWith(`${excluded}/`)));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/workspace/selection.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAY,EACZ,QAAkB,EAClB,UAAoB;IAEpB,0EAA0E;IAC1E,6EAA6E;IAC7E,KAAK,UAAU,SAAS,CAAC,IAAY;QACnC,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM,KAAK,CAAC;YACjC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAI;aACd,QAAQ,CAAC,IAAI,EAAE,MAAM,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;aACpD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAClE,MAAM,IAAI,KAAK,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CACH,CAAC;IACF,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,QAAQ,CAAC,IAAI,CACZ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,CAChF,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import path from \"node:path\";\nimport { realpath } from \"node:fs/promises\";\n\n/** Exclusions are literal paths relative to cwd, never glob patterns. */\nexport async function excludeSelection(\n root: string,\n selected: string[],\n exclusions: string[],\n): Promise<string[]> {\n // Resolve filesystem aliases (including Windows short names) consistently\n // with discovery, while allowing exclusions for paths that do not exist yet.\n async function canonical(file: string): Promise<string> {\n try {\n return await realpath(file);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n const parent = path.dirname(file);\n if (parent === file) throw error;\n return path.join(await canonical(parent), path.basename(file));\n }\n }\n const relative = await Promise.all(\n exclusions.map(async (input) => {\n const name = path\n .relative(root, await canonical(path.resolve(input)))\n .split(path.sep)\n .join(\"/\");\n if (name === \"..\" || name.startsWith(\"../\") || path.isAbsolute(name))\n throw new Error(`Excluded path is outside the workspace: ${input}`);\n return name;\n }),\n );\n return selected.filter(\n (name) =>\n !relative.some(\n (excluded) => !excluded || name === excluded || name.startsWith(`${excluded}/`),\n ),\n );\n}\n"]}
|
package/docs/cli.md
CHANGED
|
@@ -19,6 +19,24 @@ honors `.gitignore` and configuration ignore patterns, skips symlinks and nested
|
|
|
19
19
|
repositories, and does not expand path globs itself. See the
|
|
20
20
|
[selection details](configuration.md#selection-output-and-exit-codes).
|
|
21
21
|
|
|
22
|
+
Use a caller or Git hook to choose changed or staged paths and pass them as
|
|
23
|
+
explicit file arguments. The CLI processes working-tree contents, not the Git
|
|
24
|
+
index. Pass paths as separate arguments (after `--` when they could begin with
|
|
25
|
+
`-`), retain `--root` for workspace-wide link resolution, and skip the
|
|
26
|
+
invocation when the selection is empty: no file arguments means the whole
|
|
27
|
+
workspace.
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
node dist/cli/main.js lint --exclude generated
|
|
31
|
+
node dist/cli/main.js format --exclude notes/archive.md --exclude vendor --diff
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Repeat `--exclude <path>` to omit exact files or directory subtrees from any
|
|
35
|
+
file selection. Exclusions are literal paths relative to the current directory
|
|
36
|
+
(or absolute paths), must be inside the workspace, and do not remove link
|
|
37
|
+
targets from the index. They cannot be combined with stdin. Config ignores and
|
|
38
|
+
repository boundaries still apply to all selections.
|
|
39
|
+
|
|
22
40
|
## Use an explicit configuration
|
|
23
41
|
|
|
24
42
|
```sh
|
package/docs/configuration.md
CHANGED
|
@@ -57,22 +57,67 @@ Problem rules report findings and never become formatting edits.
|
|
|
57
57
|
|
|
58
58
|
## Built-in rules
|
|
59
59
|
|
|
60
|
-
| Rule | Kind | Options
|
|
61
|
-
| ----------------------------- | ------- |
|
|
62
|
-
| `style/wrap` | Style | `width
|
|
63
|
-
| `style/
|
|
64
|
-
| `style/
|
|
65
|
-
| `style/
|
|
66
|
-
| `style/
|
|
67
|
-
| `style/
|
|
68
|
-
| `
|
|
69
|
-
| `links/
|
|
70
|
-
| `links/
|
|
71
|
-
| `
|
|
72
|
-
| `github/
|
|
73
|
-
| `
|
|
74
|
-
| `obsidian/
|
|
75
|
-
| `obsidian/
|
|
60
|
+
| Rule | Kind | Options |
|
|
61
|
+
| ----------------------------- | ------- | ---------------------------------------------------------------------- |
|
|
62
|
+
| `style/wrap` | Style | `width`, `measure`, `reportUnreflowed`, `reportUnbreakable`; see below |
|
|
63
|
+
| `style/inline-code` | Style | None; joins multiline code spans, opt-in |
|
|
64
|
+
| `style/emphasis` | Style | `marker`: `_` (default) or `*` |
|
|
65
|
+
| `style/strong` | Style | `marker`: `*` (default) or `_` |
|
|
66
|
+
| `style/final-newline` | Style | None; adds a missing final newline |
|
|
67
|
+
| `style/table` | Style | None; aligns top-level GFM tables |
|
|
68
|
+
| `style/heading` | Style | None; converts eligible Setext headings to ATX |
|
|
69
|
+
| `links/valid` | Problem | None; local files and heading/block fragments |
|
|
70
|
+
| `links/path` | Style | `style`, `brackets`, `extension`, `leadingDot`; see below |
|
|
71
|
+
| `links/notation` | Style | `style`: `markdown` or `wiki`; Obsidian only |
|
|
72
|
+
| `github/task-marker` | Style | None; `[X]` becomes `[x]` |
|
|
73
|
+
| `github/alert-marker` | Style | None; known GitHub alert types become uppercase |
|
|
74
|
+
| `obsidian/callout-marker` | Style | None; Obsidian callout types become lowercase |
|
|
75
|
+
| `obsidian/block-reference` | Problem | None; duplicate trailing `^block-id` markers |
|
|
76
|
+
| `obsidian/strict-line-breaks` | Problem | None; report unverified/incompatible reflow settings |
|
|
77
|
+
|
|
78
|
+
## Document structure
|
|
79
|
+
|
|
80
|
+
Enable `structure/initial-heading` explicitly with severity `warn` or `error`;
|
|
81
|
+
no preset enables it. It accepts no options and reports problems without
|
|
82
|
+
changing content. Use path `overrides` to exempt generated documents or trees
|
|
83
|
+
with different conventions.
|
|
84
|
+
|
|
85
|
+
`structure/initial-heading` skips YAML/TOML front matter and requires a level-1
|
|
86
|
+
heading as the first content block. Both ATX and Setext headings count; use
|
|
87
|
+
`style/heading` separately to require ATX spelling. Empty documents are
|
|
88
|
+
reported. Comments and other content before the title are not skipped.
|
|
89
|
+
|
|
90
|
+
Filename/title matching, metadata placement, and guesses about accidental
|
|
91
|
+
Markdown markers are consumer policies. Implement those checks through
|
|
92
|
+
[rule plugins](plugins.md#rule-plugins), which receive the document source,
|
|
93
|
+
parsed tree, and path. Modified-date updates belong in a consumer wrapper or
|
|
94
|
+
library workflow; formatting rules must preserve document meaning.
|
|
95
|
+
|
|
96
|
+
## Wrapping and inline code
|
|
97
|
+
|
|
98
|
+
`style/wrap` accepts widths from 20 through 500 and defaults to `width: 80` and
|
|
99
|
+
`measure: "columns"` (display columns). Set `measure: "codepoints"` to count
|
|
100
|
+
Unicode scalar values instead; astral characters count once and combining marks
|
|
101
|
+
count separately. Container prefixes count toward the width in either mode.
|
|
102
|
+
|
|
103
|
+
`reportUnreflowed: true` reports breakable over-width lines in paragraphs
|
|
104
|
+
protected by hard breaks, block IDs, inline HTML, callout headers, or
|
|
105
|
+
unsupported multiline syntax or containers. `reportUnbreakable: true` reports
|
|
106
|
+
overflow from an atom that cannot be split, including in protected paragraphs.
|
|
107
|
+
Whitespace inside a link, code span, or other protected inline node is not a
|
|
108
|
+
wrapping opportunity. A protected paragraph with both breakable prose and an
|
|
109
|
+
over-width atom can report both causes; an atom-only overflow does not trigger
|
|
110
|
+
`reportUnreflowed`. Hard-break markers are excluded from protected-line width.
|
|
111
|
+
Both options default to false, propose no edits, and use the rule's severity.
|
|
112
|
+
Use severity `error` or `--max-warnings 0` to make these diagnostics fail a
|
|
113
|
+
check; `--check` does not change rule settings.
|
|
114
|
+
|
|
115
|
+
Enable `style/inline-code` to join multiline code spans before paragraph reflow.
|
|
116
|
+
This opt-in rule handles single and multiple backticks, including list and quote
|
|
117
|
+
containers. It preserves the parsed code value, converting each line ending to
|
|
118
|
+
one space under CommonMark's code-span rules. Other code whitespace remains
|
|
119
|
+
significant. It is useful for Obsidian editors that display split spans
|
|
120
|
+
differently from Reading view.
|
|
76
121
|
|
|
77
122
|
## Link policies
|
|
78
123
|
|
|
@@ -95,6 +140,9 @@ vault-relative path. `leadingDot: true` prefixes ordinary relative paths with
|
|
|
95
140
|
Markdown destinations are percent-encoded. `extension` accepts `preserve`
|
|
96
141
|
(default), `include`, or `omit`; omission is Obsidian specific. Fragments are
|
|
97
142
|
resolved according to the dialect, separately from percent-decoded filenames.
|
|
143
|
+
Already compliant destinations retain their percent-encoding spelling, including
|
|
144
|
+
literal `?` and `%` in resolving Obsidian paths. Newly generated paths encode
|
|
145
|
+
literal percent signs and hashes to preserve target identity.
|
|
98
146
|
|
|
99
147
|
Path edits require a resolved target and valid fragment. Unaliased ordinary
|
|
100
148
|
wikilinks retain their target spelling because changing it may change the
|
|
@@ -110,10 +158,15 @@ separate passes.
|
|
|
110
158
|
|
|
111
159
|
Network URLs are recognized but never fetched. Website-root paths and
|
|
112
160
|
query-bearing destinations in CommonMark/GitHub are outside local resolution.
|
|
113
|
-
Missing or ambiguous targets are reported, never guessed.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
the
|
|
161
|
+
Missing or ambiguous targets are reported, never guessed. Obsidian resolution
|
|
162
|
+
uses explicit `./` or `../` paths as source-relative; otherwise an exact
|
|
163
|
+
vault-root match precedes an exact source-relative match, followed by unique
|
|
164
|
+
suffix matching. Multiple candidates within the chosen tier remain ambiguous.
|
|
165
|
+
Relative rewrites use `./` when needed to avoid a vault-root collision. Existing
|
|
166
|
+
directories get a distinct diagnostic in Obsidian; they are not rewritten to
|
|
167
|
+
README or index notes. CommonMark/GitHub directory links are accepted because a
|
|
168
|
+
hosting site can serve them. Shortest-name/alias resolution is not a complete
|
|
169
|
+
clone of Obsidian.
|
|
117
170
|
|
|
118
171
|
## Suppressions
|
|
119
172
|
|
|
@@ -139,11 +192,24 @@ it directly above the content to suppress.
|
|
|
139
192
|
|
|
140
193
|
Commands accept explicit files/directories, or no paths to select the workspace.
|
|
141
194
|
The CLI honors `.gitignore` files and configured ignore patterns. Config-ignored
|
|
142
|
-
documents remain available for link resolution
|
|
143
|
-
indexed.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
195
|
+
documents remain available for link resolution. Git-ignored content and nested
|
|
196
|
+
repositories are not indexed by default. To make their files available only as
|
|
197
|
+
link targets, set:
|
|
198
|
+
|
|
199
|
+
```jsonc
|
|
200
|
+
{
|
|
201
|
+
"resolve": { "gitIgnored": true, "nestedRepositories": true },
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
These options are independent and default to false. They never select those
|
|
206
|
+
files for linting or formatting, including when a path is supplied explicitly.
|
|
207
|
+
Markdown targets are read and parsed only for fragment checks; attachments need
|
|
208
|
+
only a discovered file entry. Built-in traversal exclusions still apply. `.git`,
|
|
209
|
+
`.obsidian`, `node_modules`, `.npm-cache`, `dist`, and `coverage` are excluded
|
|
210
|
+
from traversal, as are symlinks. Nested Git repositories are traversed for
|
|
211
|
+
targets only when opted in. Explicit symlink inputs are rejected. Shell-expanded
|
|
212
|
+
globs work; the CLI does not expand path globs.
|
|
147
213
|
|
|
148
214
|
`lint --json` and `format --json` print a single object with `version`, `mode`,
|
|
149
215
|
`files`, and `written`. Each file carries diagnostics with rule ID, severity,
|