Notepad/enter/.obsidian/plugins/find-unlinked-files/main.js

947 lines
161 KiB
JavaScript
Raw Normal View History

2023-07-05 18:29:11 +00:00
'use strict';
var obsidian = require('obsidian');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
/** @deprecated */
function __spreadArrays() {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
}
var DeleteFilesModal = /** @class */ (function (_super) {
__extends(DeleteFilesModal, _super);
function DeleteFilesModal(app, filesToDelete) {
var _this = _super.call(this, app) || this;
_this.filesToDelete = filesToDelete;
return _this;
}
DeleteFilesModal.prototype.onOpen = function () {
var _this = this;
var _a = this, contentEl = _a.contentEl, titleEl = _a.titleEl;
titleEl.setText('Move ' + this.filesToDelete.length + ' files to system trash?');
contentEl
.createEl("button", { text: "Cancel" })
.addEventListener("click", function () { return _this.close(); });
contentEl
.setAttr("margin", "auto");
contentEl
.createEl("button", {
cls: "mod-cta",
text: "Confirm"
})
.addEventListener("click", function () { return __awaiter(_this, void 0, void 0, function () {
var _i, _a, file;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_i = 0, _a = this.filesToDelete;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
file = _a[_i];
return [4 /*yield*/, this.app.vault.trash(file, true)];
case 2:
_b.sent();
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4:
this.close();
return [2 /*return*/];
}
});
}); });
};
DeleteFilesModal.prototype.onClose = function () {
var contentEl = this.contentEl;
contentEl.empty();
};
return DeleteFilesModal;
}(obsidian.Modal));
var SettingsTab = /** @class */ (function (_super) {
__extends(SettingsTab, _super);
function SettingsTab(app, plugin, defaultSettings) {
var _this = _super.call(this, app, plugin) || this;
_this.defaultSettings = defaultSettings;
_this.plugin = plugin;
return _this;
}
// Add trailing slash to catch files named like the directory. See https://github.com/Vinzent03/find-unlinked-files/issues/24
SettingsTab.prototype.formatPath = function (path, addDirectorySlash) {
if (path.length == 0)
return path;
path = obsidian.normalizePath(path);
if (addDirectorySlash)
return path + "/";
else
return path;
};
SettingsTab.prototype.display = function () {
var _this = this;
var containerEl = this.containerEl;
containerEl.empty();
containerEl.createEl("h2", { text: this.plugin.manifest.name });
containerEl.createEl("h4", { text: "Settings for finding orphaned files" });
new obsidian.Setting(containerEl)
.setName("Open output file")
.addToggle(function (cb) {
return cb.setValue(_this.plugin.settings.openOutputFile)
.onChange(function (value) {
_this.plugin.settings.openOutputFile = value;
_this.plugin.saveSettings();
});
});
new obsidian.Setting(containerEl)
.setName('Output file name')
.setDesc('Set name of output file (without file extension). Make sure no file exists with this name because it will be overwritten! If the name is empty, the default name is set.')
.addText(function (cb) { return cb.onChange(function (value) {
if (value.length == 0) {
_this.plugin.settings.outputFileName = _this.defaultSettings.outputFileName;
}
else {
_this.plugin.settings.outputFileName = value;
}
_this.plugin.saveSettings();
}).setValue(_this.plugin.settings.outputFileName); });
new obsidian.Setting(containerEl)
.setName('Disable working links')
.setDesc('Indent lines to disable the link and to clean up the graph view')
.addToggle(function (cb) { return cb.onChange(function (value) {
_this.plugin.settings.disableWorkingLinks = value;
_this.plugin.saveSettings();
}).setValue(_this.plugin.settings.disableWorkingLinks); });
new obsidian.Setting(containerEl)
.setName("Exclude files in the given directories")
.setDesc("Enable to exclude files in the given directories. Disable to only include files in the given directories")
.addToggle(function (cb) {
return cb.setValue(_this.plugin.settings.ignoreDirectories)
.onChange(function (value) {
_this.plugin.settings.ignoreDirectories = value;
_this.plugin.saveSettings();
});
});
new obsidian.Setting(containerEl)
.setName("Directories")
.setDesc("Add each directory path in a new line")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/Subdirectory")
.setValue(_this.plugin.settings.directoriesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, true); });
_this.plugin.settings.directoriesToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude files")
.setDesc("Add each file path in a new line (with file extension!)")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/file.md")
.setValue(_this.plugin.settings.filesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, false); });
_this.plugin.settings.filesToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude links")
.setDesc("Exclude files, which contain the given file as link. Add each file path in a new line (with file extension!). Set it to `*` to exclude files with links.")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/file.md")
.setValue(_this.plugin.settings.linksToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, false); });
_this.plugin.settings.linksToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude files with the given filetypes")
.setDesc("Enable to exclude files with the given filetypes. Disable to only include files with the given filetypes")
.addToggle(function (cb) {
return cb.setValue(_this.plugin.settings.ignoreFileTypes)
.onChange(function (value) {
_this.plugin.settings.ignoreFileTypes = value;
_this.plugin.saveSettings();
});
});
new obsidian.Setting(containerEl)
.setName("File types")
.setDesc("Effect depends on toggle above")
.addTextArea(function (cb) { return cb
.setPlaceholder("docx,txt")
.setValue(_this.plugin.settings.fileTypesToIgnore.join(","))
.onChange(function (value) {
var extensions = value.trim().split(",");
_this.plugin.settings.fileTypesToIgnore = extensions;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude tags")
.setDesc("Exclude files, which contain the given tag. Add each tag separated by comma (without `#`)")
.addTextArea(function (cb) { return cb
.setPlaceholder("todo,unfinished")
.setValue(_this.plugin.settings.tagsToIgnore.join(","))
.onChange(function (value) {
var tags = value.trim().split(",");
_this.plugin.settings.tagsToIgnore = tags;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Filetypes to delete per command. See README.")
.setDesc("Add each filetype separated by comma. Set to `*` to delete all files.")
.addTextArea(function (cb) { return cb
.setPlaceholder("jpg,png")
.setValue(_this.plugin.settings.fileTypesToDelete.join(","))
.onChange(function (value) {
var extensions = value.trim().split(",");
_this.plugin.settings.fileTypesToDelete = extensions;
_this.plugin.saveSettings();
}); });
/// Settings for find brokenLinks
containerEl.createEl("h4", { text: "Settings for finding broken links" });
new obsidian.Setting(containerEl)
.setName('Output file name')
.setDesc('Set name of output file (without file extension). Make sure no file exists with this name because it will be overwritten! If the name is empty, the default name is set.')
.addText(function (cb) { return cb.onChange(function (value) {
if (value.length == 0) {
_this.plugin.settings.unresolvedLinksOutputFileName = _this.defaultSettings.unresolvedLinksOutputFileName;
}
else {
_this.plugin.settings.unresolvedLinksOutputFileName = value;
}
_this.plugin.saveSettings();
}).setValue(_this.plugin.settings.unresolvedLinksOutputFileName); });
new obsidian.Setting(containerEl)
.setName("Exclude files in the given directories")
.setDesc("Enable to exclude files in the given directories. Disable to only include files in the given directories")
.addToggle(function (cb) {
return cb.setValue(_this.plugin.settings.unresolvedLinksIgnoreDirectories)
.onChange(function (value) {
_this.plugin.settings.unresolvedLinksIgnoreDirectories = value;
_this.plugin.saveSettings();
});
});
new obsidian.Setting(containerEl)
.setName("Directories")
.setDesc("Add each directory path in a new line")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/Subdirectory")
.setValue(_this.plugin.settings.unresolvedLinksDirectoriesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, true); });
_this.plugin.settings.unresolvedLinksDirectoriesToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude files")
.setDesc("Exclude links in the specified file. Add each file path in a new line (with file extension!)")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/file.md")
.setValue(_this.plugin.settings.unresolvedLinksFilesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, false); });
_this.plugin.settings.unresolvedLinksFilesToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude links")
.setDesc("Exclude files, which contain the given file as link. Add each file path in a new line (with file extension!). Set it to `*` to exclude files with links.")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/file.md")
.setValue(_this.plugin.settings.unresolvedLinksLinksToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, false); });
_this.plugin.settings.unresolvedLinksLinksToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude filetypes")
.setDesc("Exclude links with the specified filetype. Add each filetype separated by comma")
.addTextArea(function (cb) { return cb
.setPlaceholder("docx,txt")
.setValue(_this.plugin.settings.unresolvedLinksFileTypesToIgnore.join(","))
.onChange(function (value) {
var extensions = value.trim().split(",");
_this.plugin.settings.unresolvedLinksFileTypesToIgnore = extensions;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude tags")
.setDesc("Exclude links in files, which contain the given tag. Add each tag separated by comma (without `#`)")
.addTextArea(function (cb) { return cb
.setPlaceholder("todo,unfinished")
.setValue(_this.plugin.settings.unresolvedLinksTagsToIgnore.join(","))
.onChange(function (value) {
var tags = value.trim().split(",");
_this.plugin.settings.unresolvedLinksTagsToIgnore = tags;
_this.plugin.saveSettings();
}); });
containerEl.createEl("h4", { text: "Settings for finding files without tags" });
new obsidian.Setting(containerEl)
.setName('Output file name')
.setDesc('Set name of output file (without file extension). Make sure no file exists with this name because it will be overwritten! If the name is empty, the default name is set.')
.addText(function (cb) { return cb.onChange(function (value) {
if (value.length == 0) {
_this.plugin.settings.withoutTagsOutputFileName = _this.defaultSettings.withoutTagsOutputFileName;
}
else {
_this.plugin.settings.withoutTagsOutputFileName = value;
}
_this.plugin.saveSettings();
}).setValue(_this.plugin.settings.withoutTagsOutputFileName); });
new obsidian.Setting(containerEl)
.setName("Exclude files")
.setDesc("Exclude the specific files. Add each file path in a new line (with file extension!)")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/file.md")
.setValue(_this.plugin.settings.withoutTagsFilesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, false); });
_this.plugin.settings.withoutTagsFilesToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude directories")
.setDesc("Exclude files in the specified directories. Add each directory path in a new line")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/Subdirectory")
.setValue(_this.plugin.settings.withoutTagsDirectoriesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, true); });
_this.plugin.settings.withoutTagsDirectoriesToIgnore = paths;
_this.plugin.saveSettings();
}); });
/// Settings for empty files
containerEl.createEl("h4", { text: "Settings for finding empty files" });
new obsidian.Setting(containerEl)
.setName("Exclude files in the given directories")
.setDesc("Enable to exclude files in the given directories. Disable to only include files in the given directories")
.addToggle(function (cb) {
return cb.setValue(_this.plugin.settings.emptyFilesIgnoreDirectories)
.onChange(function (value) {
_this.plugin.settings.emptyFilesIgnoreDirectories = value;
_this.plugin.saveSettings();
});
});
new obsidian.Setting(containerEl)
.setName("Directories")
.setDesc("Add each directory path in a new line")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/Subdirectory")
.setValue(_this.plugin.settings.emptyFilesDirectories.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, true); });
_this.plugin.settings.emptyFilesDirectories = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName("Exclude files")
.setDesc("Add each file path in a new line (with file extension!)")
.addTextArea(function (cb) { return cb
.setPlaceholder("Directory/file.md")
.setValue(_this.plugin.settings.emptyFilesFilesToIgnore.join("\n"))
.onChange(function (value) {
var paths = value.trim().split("\n").map(function (value) { return _this.formatPath(value, false); });
_this.plugin.settings.emptyFilesFilesToIgnore = paths;
_this.plugin.saveSettings();
}); });
new obsidian.Setting(containerEl)
.setName('Donate')
.setDesc('If you like this Plugin, consider donating to support continued development.')
.addButton(function (bt) {
bt.buttonEl.outerHTML = "<a href='https://ko-fi.com/F1F195IQ5' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi3.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>";
});
};
return SettingsTab;
}(obsidian.PluginSettingTab));
var Utils = /** @class */ (function () {
/**
* Checks for the given settings. Is used for `Find orphaned files` and `Find broken links`
* @param app
* @param filePath
* @param tagsToIgnore
* @param linksToIgnore
* @param directoriesToIgnore
* @param filesToIgnore
* @param ignoreDirectories
*/
function Utils(app, filePath, tagsToIgnore, linksToIgnore, directoriesToIgnore, filesToIgnore, ignoreDirectories, dir) {
if (ignoreDirectories === void 0) { ignoreDirectories = true; }
this.app = app;
this.filePath = filePath;
this.tagsToIgnore = tagsToIgnore;
this.linksToIgnore = linksToIgnore;
this.directoriesToIgnore = directoriesToIgnore;
this.filesToIgnore = filesToIgnore;
this.ignoreDirectories = ignoreDirectories;
this.dir = dir;
this.fileCache = app.metadataCache.getCache(filePath);
}
Utils.prototype.hasTagsToIgnore = function () {
var _this = this;
var tags = obsidian.getAllTags(this.fileCache);
return (tags === null || tags === void 0 ? void 0 : tags.find(function (tag) { return _this.tagsToIgnore.contains(tag.substring(1)); })) !== undefined;
};
Utils.prototype.hasLinksToIgnore = function () {
var _this = this;
var _a, _b;
if ((((_a = this.fileCache) === null || _a === void 0 ? void 0 : _a.embeds) != null || ((_b = this.fileCache) === null || _b === void 0 ? void 0 : _b.links) != null) && this.linksToIgnore[0] == "*") {
return true;
}
return obsidian.iterateCacheRefs(this.fileCache, function (cb) {
var _a;
var link = (_a = _this.app.metadataCache.getFirstLinkpathDest(cb.link, _this.filePath)) === null || _a === void 0 ? void 0 : _a.path;
return _this.linksToIgnore.contains(link);
});
};
Utils.prototype.checkDirectory = function () {
var _this = this;
if (this.dir) {
if (!this.filePath.startsWith(this.dir)) {
return true;
}
}
var contains = this.directoriesToIgnore.find(function (value) { return value.length != 0 && _this.filePath.startsWith(value); }) !== undefined;
if (this.ignoreDirectories) {
return contains;
}
else {
return !contains;
}
};
Utils.prototype.isFileToIgnore = function () {
return this.filesToIgnore.contains(this.filePath);
};
Utils.prototype.isValid = function () {
return !this.hasTagsToIgnore() && !this.hasLinksToIgnore() && !this.checkDirectory() && !this.isFileToIgnore();
};
/**
* Writes the text to the file and opens the file in a new pane if it is not opened yet
* @param app
* @param outputFileName name of the output file
* @param text data to be written to the file
*/
Utils.writeAndOpenFile = function (app, outputFileName, text, openFile) {
return __awaiter(this, void 0, void 0, function () {
var fileIsAlreadyOpened, newPane, file;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, app.vault.adapter.write(outputFileName, text)];
case 1:
_a.sent();
if (!openFile)
return [2 /*return*/];
fileIsAlreadyOpened = false;
app.workspace.iterateAllLeaves(function (leaf) {
if (leaf.getDisplayText() != "" && outputFileName.startsWith(leaf.getDisplayText())) {
fileIsAlreadyOpened = true;
}
});
if (!!fileIsAlreadyOpened) return [3 /*break*/, 5];
newPane = app.workspace.getLeavesOfType("empty").length == 0;
if (!newPane) return [3 /*break*/, 2];
app.workspace.openLinkText(outputFileName, "/", true);
return [3 /*break*/, 5];
case 2:
file = app.vault.getAbstractFileByPath(outputFileName);
if (!(file instanceof obsidian.TFile)) return [3 /*break*/, 4];
return [4 /*yield*/, app.workspace.getLeavesOfType("empty")[0].openFile(file)];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4:
app.workspace.openLinkText(outputFileName, "/", true);
_a.label = 5;
case 5: return [2 /*return*/];
}
});
});
};
return Utils;
}());
var DEFAULT_SETTINGS = {
outputFileName: "orphaned files output",
disableWorkingLinks: false,
directoriesToIgnore: [],
filesToIgnore: [],
fileTypesToIgnore: [],
linksToIgnore: [],
tagsToIgnore: [],
fileTypesToDelete: [],
ignoreFileTypes: true,
ignoreDirectories: true,
unresolvedLinksIgnoreDirectories: true,
unresolvedLinksOutputFileName: "broken links output",
unresolvedLinksDirectoriesToIgnore: [],
unresolvedLinksFilesToIgnore: [],
unresolvedLinksFileTypesToIgnore: [],
unresolvedLinksLinksToIgnore: [],
unresolvedLinksTagsToIgnore: [],
withoutTagsDirectoriesToIgnore: [],
withoutTagsFilesToIgnore: [],
withoutTagsOutputFileName: "files without tags",
emptyFilesOutputFileName: "empty files",
emptyFilesDirectories: [],
emptyFilesFilesToIgnore: [],
emptyFilesIgnoreDirectories: true,
openOutputFile: true,
};
var FindOrphanedFilesPlugin = /** @class */ (function (_super) {
__extends(FindOrphanedFilesPlugin, _super);
function FindOrphanedFilesPlugin() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.findExtensionRegex = /(\.[^.]+)$/;
return _this;
}
FindOrphanedFilesPlugin.prototype.onload = function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.log('loading ' + this.manifest.name + " plugin");
return [4 /*yield*/, this.loadSettings()];
case 1:
_a.sent();
this.addCommand({
id: 'find-unlinked-files',
name: 'Find orphaned files',
callback: function () { return _this.findOrphanedFiles(); },
});
this.addCommand({
id: 'find-unresolved-link',
name: 'Find broken links',
callback: function () { return _this.findBrokenLinks(); },
});
this.addCommand({
id: "delete-unlinked-files",
name: "Delete orphaned files with certain extension. See README",
callback: function () { return _this.deleteOrphanedFiles(); }
});
this.addCommand({
id: "create-files-of-broken-links",
name: "Create files of broken links",
callback: function () { return _this.createFilesOfBrokenLinks(); }
});
this.addCommand({
id: "find-files-without-tags",
name: "Find files without tags",
callback: function () { return _this.findFilesWithoutTags(); }
});
this.addCommand({
id: "find-empty-files",
name: "Find empty files",
callback: function () { return _this.findEmptyFiles(); }
});
this.addCommand({
id: "delete-empty-files",
name: "Delete empty files",
callback: function () { return _this.deleteEmptyFiles(); }
});
this.addSettingTab(new SettingsTab(this.app, this, DEFAULT_SETTINGS));
this.app.workspace.on("file-menu", function (menu, file, source, leaf) {
if (file instanceof obsidian.TFolder) {
menu.addItem(function (cb) {
cb.setIcon("search");
cb.setTitle("Find orphaned files");
// Add trailing slash to catch files named like the directory. See https://github.com/Vinzent03/find-unlinked-files/issues/24
cb.onClick(function (e) { _this.findOrphanedFiles(file.path + "/"); });
});
}
});
return [2 /*return*/];
}
});
});
};
FindOrphanedFilesPlugin.prototype.createFilesOfBrokenLinks = function () {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var links, filesToCreate, _i, links_1, link, file, foundType, _c, filesToCreate_1, file;
return __generator(this, function (_d) {
switch (_d.label) {
case 0: return [4 /*yield*/, this.app.vault.adapter.exists(this.settings.unresolvedLinksOutputFileName + ".md")];
case 1:
if (!(_d.sent())) {
new obsidian.Notice("Can't find file - Please run the `Find broken files' command before");
return [2 /*return*/];
}
links = (_a = this.app.metadataCache.getCache(this.settings.unresolvedLinksOutputFileName + ".md")) === null || _a === void 0 ? void 0 : _a.links;
if (!links) {
new obsidian.Notice("No broken links found");
return [2 /*return*/];
}
filesToCreate = [];
for (_i = 0, links_1 = links; _i < links_1.length; _i++) {
link = links_1[_i];
file = this.app.metadataCache.getFirstLinkpathDest(link.link, "/");
if (file)
continue;
foundType = (_b = this.findExtensionRegex.exec(link.link)) === null || _b === void 0 ? void 0 : _b[0];
if ((foundType !== null && foundType !== void 0 ? foundType : ".md") == ".md") {
if (foundType) {
filesToCreate.push(link.link);
}
else {
filesToCreate.push(link.link + ".md");
}
}
}
if (!filesToCreate) return [3 /*break*/, 5];
_c = 0, filesToCreate_1 = filesToCreate;
_d.label = 2;
case 2:
if (!(_c < filesToCreate_1.length)) return [3 /*break*/, 5];
file = filesToCreate_1[_c];
return [4 /*yield*/, this.app.vault.create(file, "")];
case 3:
_d.sent();
_d.label = 4;
case 4:
_c++;
return [3 /*break*/, 2];
case 5: return [2 /*return*/];
}
});
});
};
FindOrphanedFilesPlugin.prototype.findEmptyFiles = function () {
return __awaiter(this, void 0, void 0, function () {
var files, emptyFiles, _i, files_1, file, content, trimmedContent, cache, frontmatter, lines, prefix, text;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
files = this.app.vault.getFiles();
emptyFiles = [];
_i = 0, files_1 = files;
_a.label = 1;
case 1:
if (!(_i < files_1.length)) return [3 /*break*/, 4];
file = files_1[_i];
if (!new Utils(this.app, file.path, [], [], this.settings.emptyFilesDirectories, this.settings.emptyFilesFilesToIgnore, this.settings.emptyFilesIgnoreDirectories).isValid()) {
return [3 /*break*/, 3];
}
return [4 /*yield*/, this.app.vault.read(file)];
case 2:
content = _a.sent();
trimmedContent = content.trim();
if (!trimmedContent) {
emptyFiles.push(file);
}
cache = app.metadataCache.getFileCache(file);
frontmatter = cache === null || cache === void 0 ? void 0 : cache.frontmatter;
if (frontmatter) {
lines = content.trimRight().split("\n").length;
if (frontmatter.position.end.line == lines - 1) {
emptyFiles.push(file);
}
}
_a.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4:
console.log(emptyFiles);
if (this.settings.disableWorkingLinks)
prefix = " ";
else
prefix = "";
text = emptyFiles.map(function (file) { return prefix + "- [[" + file.path + "]]"; }).join("\n");
Utils.writeAndOpenFile(this.app, this.settings.emptyFilesOutputFileName + ".md", text, this.settings.openOutputFile);
return [2 /*return*/];
}
});
});
};
FindOrphanedFilesPlugin.prototype.findOrphanedFiles = function (dir) {
var _this = this;
var outFileName = this.settings.outputFileName + ".md";
var outFile;
var files = this.app.vault.getFiles();
var markdownFiles = this.app.vault.getMarkdownFiles();
var links = [];
markdownFiles.forEach(function (markFile) {
if (markFile.path == outFileName) {
outFile = markFile;
return;
}
obsidian.iterateCacheRefs(_this.app.metadataCache.getFileCache(markFile), function (cb) {
var txt = _this.app.metadataCache.getFirstLinkpathDest(obsidian.getLinkpath(cb.link), markFile.path);
if (txt != null)
links.push(txt.path);
});
});
var notLinkedFiles = files.filter(function (file) { return _this.isValid(file, links, dir); });
notLinkedFiles.remove(outFile);
var text = "";
var prefix;
if (this.settings.disableWorkingLinks)
prefix = " ";
else
prefix = "";
notLinkedFiles.forEach(function (file) {
text += prefix + "- [[" + _this.app.metadataCache.fileToLinktext(file, "/", false) + "]]\n";
});
Utils.writeAndOpenFile(this.app, outFileName, text, this.settings.openOutputFile);
};
FindOrphanedFilesPlugin.prototype.deleteOrphanedFiles = function () {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var links, filesToDelete;
var _this = this;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, this.app.vault.adapter.exists(this.settings.outputFileName + ".md")];
case 1:
if (!(_c.sent())) {
new obsidian.Notice("Can't find file - Please run the `Find orphaned files' command before");
return [2 /*return*/];
}
links = (_b = (_a = this.app.metadataCache.getCache(this.settings.outputFileName + ".md")) === null || _a === void 0 ? void 0 : _a.links) !== null && _b !== void 0 ? _b : [];
filesToDelete = [];
links.forEach(function (link) {
var file = _this.app.metadataCache.getFirstLinkpathDest(link.link, "/");
if (!file)
return;
if (_this.settings.fileTypesToDelete[0] == "*" || _this.settings.fileTypesToDelete.contains(file.extension)) {
filesToDelete.push(file);
}
});
if (filesToDelete.length > 0)
new DeleteFilesModal(this.app, filesToDelete).open();
return [2 /*return*/];
}
});
});
};
FindOrphanedFilesPlugin.prototype.deleteEmptyFiles = function () {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var links, filesToDelete, _i, links_2, link, file;
return __generator(this, function (_c) {
switch (_c.label) {
case 0: return [4 /*yield*/, this.app.vault.adapter.exists(this.settings.emptyFilesOutputFileName + ".md")];
case 1:
if (!(_c.sent())) {
new obsidian.Notice("Can't find file - Please run the `Find orphaned files' command before");
return [2 /*return*/];
}
links = (_b = (_a = this.app.metadataCache.getCache(this.settings.emptyFilesOutputFileName + ".md")) === null || _a === void 0 ? void 0 : _a.links) !== null && _b !== void 0 ? _b : [];
filesToDelete = [];
for (_i = 0, links_2 = links; _i < links_2.length; _i++) {
link = links_2[_i];
file = this.app.metadataCache.getFirstLinkpathDest(link.link, "/");
if (!file)
return [2 /*return*/];
filesToDelete.push(file);
}
if (filesToDelete.length > 0)
new DeleteFilesModal(this.app, filesToDelete).open();
return [2 /*return*/];
}
});
});
};
FindOrphanedFilesPlugin.prototype.findBrokenLinks = function () {
var outFileName = this.settings.unresolvedLinksOutputFileName + ".md";
var links = [];
var brokenLinks = this.app.metadataCache.unresolvedLinks;
for (var sourceFilepath in brokenLinks) {
if (sourceFilepath == this.settings.unresolvedLinksOutputFileName + ".md")
continue;
var fileType = sourceFilepath.substring(sourceFilepath.lastIndexOf(".") + 1);
var utils = new Utils(this.app, sourceFilepath, this.settings.unresolvedLinksTagsToIgnore, this.settings.unresolvedLinksLinksToIgnore, this.settings.unresolvedLinksDirectoriesToIgnore, this.settings.unresolvedLinksFilesToIgnore, this.settings.unresolvedLinksIgnoreDirectories);
if (!utils.isValid())
continue;
var _loop_1 = function (link) {
var linkFileType = link.substring(link.lastIndexOf(".") + 1);
if (this_1.settings.unresolvedLinksFileTypesToIgnore.contains(linkFileType))
return "continue";
var formattedFilePath = sourceFilepath;
if (fileType == "md") {
formattedFilePath = sourceFilepath.substring(0, sourceFilepath.lastIndexOf(".md"));
}
var brokenLink = { files: [formattedFilePath], link: link };
if (links.contains(brokenLink))
return "continue";
var duplication = links.find(function (e) { return e.link == link; });
if (duplication) {
duplication.files.push(formattedFilePath);
}
else {
links.push(brokenLink);
}
};
var this_1 = this;
for (var link in brokenLinks[sourceFilepath]) {
_loop_1(link);
}
}
Utils.writeAndOpenFile(this.app, outFileName, __spreadArrays([
"Don't forget that creating the file from here may create the file in the wrong directory!"
], links.map(function (e) { return "- [[" + e.link + "]] in [[" + e.files.join("]], [[") + "]]"; })).join("\n"), this.settings.openOutputFile);
};
FindOrphanedFilesPlugin.prototype.findFilesWithoutTags = function () {
var _this = this;
var outFileName = this.settings.withoutTagsOutputFileName + ".md";
var outFile;
var files = this.app.vault.getMarkdownFiles();
var withoutFiles = files.filter(function (file) {
var _a;
if (new Utils(_this.app, file.path, [], [], _this.settings.withoutTagsDirectoriesToIgnore, _this.settings.withoutTagsFilesToIgnore, true).isValid()) {
return ((_a = obsidian.getAllTags(_this.app.metadataCache.getFileCache(file)).length) !== null && _a !== void 0 ? _a : 0) <= 0;
}
else {
return false;
}
});
withoutFiles.remove(outFile);
var prefix;
if (this.settings.disableWorkingLinks)
prefix = " ";
else
prefix = "";
var text = withoutFiles.map(function (file) { return prefix + "- [[" + file.path + "]]"; }).join("\n");
Utils.writeAndOpenFile(this.app, outFileName, text, this.settings.openOutputFile);
};
/**
* Checks if the given file in an orphaned file
*
* @param file file to check
* @param links all links in the vault
*/
FindOrphanedFilesPlugin.prototype.isValid = function (file, links, dir) {
if (links.contains(file.path))
return false;
//filetypes to ignore by default
if (file.extension == "css")
return false;
if (this.settings.fileTypesToIgnore[0] !== "") {
var containsFileType = this.settings.fileTypesToIgnore.contains(file.extension);
if (this.settings.ignoreFileTypes) {
if (containsFileType)
return;
}
else {
if (!containsFileType)
return;
}
}
var utils = new Utils(this.app, file.path, this.settings.tagsToIgnore, this.settings.linksToIgnore, this.settings.directoriesToIgnore, this.settings.filesToIgnore, this.settings.ignoreDirectories, dir);
if (!utils.isValid())
return false;
return true;
};
FindOrphanedFilesPlugin.prototype.onunload = function () {
console.log('unloading ' + this.manifest.name + " plugin");
};
FindOrphanedFilesPlugin.prototype.loadSettings = function () {
return __awaiter(this, void 0, void 0, function () {
var _a, _b, _c, _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
_a = this;
_c = (_b = Object).assign;
_d = [DEFAULT_SETTINGS];
return [4 /*yield*/, this.loadData()];
case 1:
_a.settings = _c.apply(_b, _d.concat([_e.sent()]));
return [2 /*return*/];
}
});
});
};
FindOrphanedFilesPlugin.prototype.saveSettings = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.saveData(this.settings)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
return FindOrphanedFilesPlugin;
}(obsidian.Plugin));
module.exports = FindOrphanedFilesPlugin;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInNyYy9kZWxldGVGaWxlc01vZGFsLnRzIiwic3JjL3NldHRpbmdzVGFiLnRzIiwic3JjL3V0aWxzLnRzIiwic3JjL21haW4udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5cclxuXHJcblBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG5wdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQuXHJcblxyXG5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiIEFORCBUSEUgQVVUSE9SIERJU0NMQUlNUyBBTEwgV0FSUkFOVElFUyBXSVRIXHJcblJFR0FSRCBUTyBUSElTIFNPRlRXQVJFIElOQ0xVRElORyBBTEwgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWVxyXG5BTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsXHJcbklORElSRUNULCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgT1IgQU5ZIERBTUFHRVMgV0hBVFNPRVZFUiBSRVNVTFRJTkcgRlJPTVxyXG5MT1NTIE9GIFVTRSwgREFUQSBPUiBQUk9GSVRTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgTkVHTElHRU5DRSBPUlxyXG5PVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SXHJcblBFUkZPUk1BTkNFIE9GIFRISVMgU09GVFdBUkUuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlICovXHJcblxyXG52YXIgZXh0ZW5kU3RhdGljcyA9IGZ1bmN0aW9uKGQsIGIpIHtcclxuICAgIGV4dGVuZFN0YXRpY3MgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHxcclxuICAgICAgICAoeyBfX3Byb3RvX186IFtdIH0gaW5zdGFuY2VvZiBBcnJheSAmJiBmdW5jdGlvbiAoZCwgYikgeyBkLl9fcHJvdG9fXyA9IGI7IH0pIHx8XHJcbiAgICAgICAgZnVuY3Rpb24gKGQsIGIpIHsgZm9yICh2YXIgcCBpbiBiKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGIsIHApKSBkW3BdID0gYltwXTsgfTtcclxuICAgIHJldHVybiBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG59O1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZXh0ZW5kcyhkLCBiKSB7XHJcbiAgICBpZiAodHlwZW9mIGIgIT09IFwiZnVuY3Rpb25cIiAmJiBiICE9PSBudWxsKVxyXG4gICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJDbGFzcyBleHRlbmRzIHZhbHVlIFwiICsgU3RyaW5nKGIpICsgXCIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbFwiKTtcclxuICAgIGV4dGVuZFN0YXRpY3MoZCwgYik7XHJcbiAgICBmdW5jdGlvbiBfXygpIHsgdGhpcy5jb25zdHJ1Y3RvciA9IGQ7IH1cclxuICAgIGQucHJvdG90eXBlID0gYiA9PT0gbnVsbCA/IE9iamVjdC5jcmVhdGUoYikgOiAoX18ucHJvdG90eXBlID0gYi5wcm90b3R5cGUsIG5ldyBfXygpKTtcclxufVxyXG5cclxuZXhwb3J0IHZhciBfX2Fzc2lnbiA9IGZ1bmN0aW9uKCkge1xyXG4gICAgX19hc3NpZ24gPSBPYmplY3QuYXNzaWduIHx8IGZ1bmN0aW9uIF9fYXNzaWduKHQpIHtcclxuICAgICAgICBmb3IgKHZhciBzLCBpID0gMSwgbiA9IGFyZ3VtZW50cy5sZW5ndGg7IGkgPCBuOyBpKyspIHtcclxuICAgICAgICAgICAgcyA9IGFyZ3VtZW50c1tpXTtcclxuICAgICAgICAgICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApKSB0W3BdID0gc1twXTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHQ7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gX19hc3NpZ24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fcmVzdChzLCBlKSB7XHJcbiAgICB2YXIgdCA9IHt9O1xyXG4gICAgZm9yICh2YXIgcCBpbiBzKSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHMsIHApICYmIGUuaW5kZXhPZihwKSA8IDApXHJcbiAgICAgICAgdFtwXSA9IHNbcF07XHJcbiAgICBpZiAocyAhPSBudWxsICYmIHR5cGVvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzID09PSBcImZ1bmN0aW9uXCIpXHJcbiAgICAgICAgZm9yICh2YXIgaSA9IDAsIHAgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKHMpOyBpIDwgcC5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICBpZiAoZS5pbmRleE9mKHBbaV0pIDwgMCAmJiBPYmplY3QucHJvdG90eXBlLnByb3BlcnR5SXNFbnVtZXJhYmxlLmNhbGwocywgcFtpXSkpXHJcbiAgICAgICAgICAgICAgICB0W3BbaV1dID0gc1twW2ldXTtcclxuICAgICAgICB9XHJcbiAgICByZXR1cm4gdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIF9fZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpIHtcclxuICAgIHZhciBjID0gYXJndW1lbnRzLmxlbmd0aCwgciA9IGMgPCAzID8gdGFyZ2V0IDogZGVzYyA9PT0gbnVsbCA/IGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHRhcmdldCwga2V5KSA6IGRlc2MsIGQ7XHJcbiAgICBpZiAodHlwZW9mIFJlZmxlY3QgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUgPT09IFwiZnVuY3Rpb25cIikgciA9IFJlZmxlY3QuZGVjb3JhdGUoZGVjb3JhdG9ycywgdGFyZ2V0LCBrZXksIGRlc2MpO1xyXG4gICAgZWxzZSBmb3IgKHZhciBpID0gZGVjb3JhdG9ycy5