yanki 2.0.11 → 2.0.12
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/bin/cli.js +154 -154
- package/dist/lib/index.js +80 -39
- package/dist/standalone/index.js +83 -83
- package/package.json +4 -4
package/dist/lib/index.js
CHANGED
|
@@ -509,7 +509,7 @@ function getFileExtensionForMimeType(mimeType) {
|
|
|
509
509
|
//#endregion
|
|
510
510
|
//#region src/lib/utilities/path.ts
|
|
511
511
|
const WINDOWS_DRIVE_LETTER_REGEX = /* @__PURE__ */ new RegExp("^[A-Z]:", "iv");
|
|
512
|
-
const QUERY_FRAGMENT_START_REGEX = /* @__PURE__ */ new RegExp("[
|
|
512
|
+
const QUERY_FRAGMENT_START_REGEX = /* @__PURE__ */ new RegExp("[#^]", "v");
|
|
513
513
|
/**
|
|
514
514
|
* The browserify polyfill doesn't implement win32 absolute path detection...
|
|
515
515
|
*
|
|
@@ -575,26 +575,37 @@ function getBaseAndQueryParts(filePath) {
|
|
|
575
575
|
const [base, query] = splitAtFirstMatch(path.basename(filePath), QUERY_FRAGMENT_START_REGEX);
|
|
576
576
|
return [path.join(directoryPath, base), query];
|
|
577
577
|
}
|
|
578
|
+
/**
|
|
579
|
+
* Get every plausible base and anchor interpretation of a file path whose name
|
|
580
|
+
* may contain anchor delimiter characters (`#`, `^`), ordered from the longest
|
|
581
|
+
* literal file name (no anchor at all) to the shortest (anchor starts at the
|
|
582
|
+
* first delimiter in the file name, matching Obsidian's anchor syntax).
|
|
583
|
+
* Delimiters in directory names are never treated as anchor starts.
|
|
584
|
+
*
|
|
585
|
+
* File names may legitimately contain these characters, so callers should try
|
|
586
|
+
* candidates in order against a list of real files instead of assuming the
|
|
587
|
+
* first delimiter starts an anchor.
|
|
588
|
+
* https://github.com/kitschpatrol/yanki/issues/20
|
|
589
|
+
*/
|
|
590
|
+
function getBaseAndQueryCandidates(filePath) {
|
|
591
|
+
const directoryPath = path.dirname(filePath);
|
|
592
|
+
const fileName = path.basename(filePath);
|
|
593
|
+
const candidates = [{
|
|
594
|
+
base: path.join(directoryPath, fileName),
|
|
595
|
+
query: void 0
|
|
596
|
+
}];
|
|
597
|
+
for (let index = fileName.length - 1; index >= 0; index--) if (QUERY_FRAGMENT_START_REGEX.test(fileName.charAt(index))) candidates.push({
|
|
598
|
+
base: path.join(directoryPath, fileName.slice(0, index)),
|
|
599
|
+
query: fileName.slice(index)
|
|
600
|
+
});
|
|
601
|
+
return candidates;
|
|
602
|
+
}
|
|
578
603
|
function getBase(filePath) {
|
|
579
604
|
return getBaseAndQueryParts(filePath)[0];
|
|
580
605
|
}
|
|
581
606
|
function getQuery(filePath) {
|
|
582
607
|
return getBaseAndQueryParts(filePath).at(1) ?? "";
|
|
583
608
|
}
|
|
584
|
-
function hasExtension(filePath) {
|
|
585
|
-
return getExtension(filePath) !== "";
|
|
586
|
-
}
|
|
587
|
-
function getExtension(filePath) {
|
|
588
|
-
return path.extname(getBase(filePath));
|
|
589
|
-
}
|
|
590
|
-
function addExtensionIfMissing(filePath, extension) {
|
|
591
|
-
if (hasExtension(filePath)) return filePath;
|
|
592
|
-
return addExtension(filePath, extension);
|
|
593
|
-
}
|
|
594
|
-
function addExtension(filePath, extension) {
|
|
595
|
-
const [base, query] = getBaseAndQueryParts(filePath);
|
|
596
|
-
return `${base}.${extension}${query ?? ""}`;
|
|
597
|
-
}
|
|
598
609
|
//#endregion
|
|
599
610
|
//#region src/lib/utilities/url.ts
|
|
600
611
|
const DRIVE_LETTER_REGEX = /* @__PURE__ */ new RegExp("^[a-z]:", "iv");
|
|
@@ -1792,7 +1803,9 @@ const defaultResolveLinkOptions = {
|
|
|
1792
1803
|
* @returns Resolved absolute path or URL One of:
|
|
1793
1804
|
*
|
|
1794
1805
|
* - Resolved absolute POSIX-style paths
|
|
1795
|
-
* - Removes any
|
|
1806
|
+
* - Removes any Obsidian-style heading and block anchors, unless the anchor
|
|
1807
|
+
* characters are a literal part of a matched file's name (`?` is always
|
|
1808
|
+
* treated as a literal file name character in local paths)
|
|
1796
1809
|
* - Not URI-encoded
|
|
1797
1810
|
* - Retains original case
|
|
1798
1811
|
* - HTTP protocol URL
|
|
@@ -1807,8 +1820,7 @@ function resolveLink(filePathOrUrl, options) {
|
|
|
1807
1820
|
const decodedUrl = safeDecodeURI(currentPathOrUrl) ?? currentPathOrUrl;
|
|
1808
1821
|
const sourceType = getSrcType(decodedUrl);
|
|
1809
1822
|
if (sourceType === "localFileName") {
|
|
1810
|
-
|
|
1811
|
-
resolvedUrl = resolveNameLink(resolvedUrl, cwd, allFilePaths ?? []) ?? resolveWithBasePath(decodedUrl, {
|
|
1823
|
+
const resolvedUrl = resolveNameLink(normalize(decodedUrl), cwd, allFilePaths ?? []) ?? resolveWithBasePath(decodedUrl, {
|
|
1812
1824
|
basePath,
|
|
1813
1825
|
cwd
|
|
1814
1826
|
});
|
|
@@ -1856,13 +1868,21 @@ function resolveLocalFilePath(decodedUrl, options) {
|
|
|
1856
1868
|
basePath,
|
|
1857
1869
|
cwd
|
|
1858
1870
|
});
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1871
|
+
let matchedPath;
|
|
1872
|
+
let matchedQuery;
|
|
1873
|
+
for (const { base, query } of getBaseAndQueryCandidates(resolvedUrl)) {
|
|
1874
|
+
matchedPath = (path.extname(base) === "" ? [`${base}.md`] : [base, `${base}.md`]).find((candidate) => pathExistsInAllFiles(candidate, allFilePaths ?? []));
|
|
1875
|
+
if (matchedPath !== void 0) {
|
|
1876
|
+
matchedQuery = query;
|
|
1877
|
+
break;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
if (matchedPath !== void 0) {
|
|
1881
|
+
if (convertFilePathsToProtocol !== "none" && (type === "link" || type === "embed" && [".md", ".pdf"].includes(path.extname(matchedPath)))) {
|
|
1882
|
+
if (convertFilePathsToProtocol === "obsidian" && obsidianVaultName !== void 0) return createObsidianVaultLink(`${matchedPath}${matchedQuery ?? ""}`, basePath ?? "", obsidianVaultName);
|
|
1883
|
+
if (convertFilePathsToProtocol === "file") return createFileLink(`${matchedPath}${matchedQuery ?? ""}`);
|
|
1864
1884
|
}
|
|
1865
|
-
return
|
|
1885
|
+
return matchedPath;
|
|
1866
1886
|
}
|
|
1867
1887
|
return getBase(resolvedUrl);
|
|
1868
1888
|
}
|
|
@@ -1875,9 +1895,10 @@ function resolveLocalFilePath(decodedUrl, options) {
|
|
|
1875
1895
|
* Obsidian seems to treat note links slightly differently from image / asset
|
|
1876
1896
|
* links.
|
|
1877
1897
|
*
|
|
1878
|
-
* @param name Non-URI-encoded name of the file,
|
|
1879
|
-
*
|
|
1880
|
-
*
|
|
1898
|
+
* @param name Non-URI-encoded, normalized name of the file, with or without a
|
|
1899
|
+
* file extension. Extension-less names are assumed to be .md files. May
|
|
1900
|
+
* include an anchor suffix, or anchor delimiter characters that are a literal
|
|
1901
|
+
* part of the file name. (POSIX-style paths.)
|
|
1881
1902
|
* @param cwd Absolute path to the current working directory of the file from
|
|
1882
1903
|
* which we're resolving the link. (POSIX-style paths)
|
|
1883
1904
|
* @param allFilePaths Array of absolute paths to all other files in the paths
|
|
@@ -1888,14 +1909,34 @@ function resolveLocalFilePath(decodedUrl, options) {
|
|
|
1888
1909
|
*/
|
|
1889
1910
|
function resolveNameLink(name, cwd, allFilePaths) {
|
|
1890
1911
|
if (allFilePaths.length === 0) return;
|
|
1891
|
-
const
|
|
1912
|
+
for (const { base, query } of getBaseAndQueryCandidates(name)) {
|
|
1913
|
+
const match = findBestNameMatch(path.extname(base) === "" ? `${base}.md` : base, cwd, allFilePaths);
|
|
1914
|
+
if (match !== void 0) return `${match}${query ?? ""}`;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Find the best matching file for a name in the list of all file paths.
|
|
1919
|
+
* Extracted from `resolveNameLink` so each base and anchor interpretation of
|
|
1920
|
+
* the name can be attempted in turn.
|
|
1921
|
+
*
|
|
1922
|
+
* @param base Non-URI-encoded name of the file with a file extension and no
|
|
1923
|
+
* anchor suffix. (POSIX-style paths.)
|
|
1924
|
+
* @param cwd Absolute path to the current working directory of the file from
|
|
1925
|
+
* which we're resolving the link. (POSIX-style paths)
|
|
1926
|
+
* @param allFilePaths Array of absolute paths to all other files in the paths
|
|
1927
|
+
* to be considered. (POSIX-style paths.)
|
|
1928
|
+
*
|
|
1929
|
+
* @returns Absolute path to the best matching file, or undefined if there's no
|
|
1930
|
+
* valid match. (POSIX-style paths.)
|
|
1931
|
+
*/
|
|
1932
|
+
function findBestNameMatch(base, cwd, allFilePaths) {
|
|
1892
1933
|
const baseWithoutMd = base.replace(MD_EXTENSION_REGEX, "").toLowerCase();
|
|
1893
1934
|
const pathsToName = allFilePaths.filter((filePath) => {
|
|
1894
1935
|
return filePath.replace(MD_EXTENSION_REGEX, "").toLowerCase().endsWith(baseWithoutMd);
|
|
1895
1936
|
});
|
|
1896
1937
|
if (pathsToName.length === 0) return;
|
|
1897
|
-
if (pathsToName.length === 1) return
|
|
1898
|
-
return
|
|
1938
|
+
if (pathsToName.length === 1) return pathsToName[0];
|
|
1939
|
+
return pathsToName.toSorted((a, b) => {
|
|
1899
1940
|
if (!base.endsWith(".md") || base.includes(path.sep)) {
|
|
1900
1941
|
const aHasCwd = a.startsWith(cwd);
|
|
1901
1942
|
if (aHasCwd !== b.startsWith(cwd)) return aHasCwd ? -1 : 1;
|
|
@@ -1904,22 +1945,22 @@ function resolveNameLink(name, cwd, allFilePaths) {
|
|
|
1904
1945
|
const bDepth = b.split(path.sep).length;
|
|
1905
1946
|
if (aDepth !== bDepth) return aDepth - bDepth;
|
|
1906
1947
|
return a.localeCompare(b);
|
|
1907
|
-
})[0]
|
|
1948
|
+
})[0];
|
|
1908
1949
|
}
|
|
1909
1950
|
/**
|
|
1910
|
-
* Check for presence of a path in a list in a case-
|
|
1911
|
-
*
|
|
1951
|
+
* Check for presence of a path in a list in a case-agnostic manner, since
|
|
1952
|
+
* Obsidian is not case sensitive.
|
|
1912
1953
|
*
|
|
1913
|
-
* @param filePath
|
|
1954
|
+
* @param filePath Literal file path with file extension and no anchor suffix.
|
|
1955
|
+
* (POSIX-style path.)
|
|
1914
1956
|
* @param allFilePaths Array of absolute file paths to check. (POSIX-style
|
|
1915
1957
|
* paths.)
|
|
1916
1958
|
*
|
|
1917
|
-
* @returns
|
|
1918
|
-
* undefined if it is not.
|
|
1959
|
+
* @returns Whether the file path is present in the list of all file paths.
|
|
1919
1960
|
*/
|
|
1920
1961
|
function pathExistsInAllFiles(filePath, allFilePaths) {
|
|
1921
|
-
const
|
|
1922
|
-
|
|
1962
|
+
const filePathLowerCase = filePath.toLowerCase();
|
|
1963
|
+
return allFilePaths.some((file) => file.toLowerCase().endsWith(filePathLowerCase));
|
|
1923
1964
|
}
|
|
1924
1965
|
function createFileLink(absolutePath) {
|
|
1925
1966
|
return `file://${absolutePath}`;
|
|
@@ -1948,7 +1989,7 @@ const plugin$1 = function(options) {
|
|
|
1948
1989
|
obsidianVaultName: obsidianVault,
|
|
1949
1990
|
type: "link"
|
|
1950
1991
|
});
|
|
1951
|
-
node.url = isUrl(resolvedLink) ? resolvedLink : encodeURI(resolvedLink);
|
|
1992
|
+
node.url = isUrl(resolvedLink) ? resolvedLink : encodeURI(resolvedLink).replaceAll("?", "%3F").replaceAll("#", "%23");
|
|
1952
1993
|
});
|
|
1953
1994
|
visit(tree, "image", (node) => {
|
|
1954
1995
|
node.data ??= {};
|