@real1ty-obsidian-plugins/utils 2.2.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -9
- package/dist/{async-utils.d.ts → async/async.d.ts} +1 -1
- package/dist/async/async.d.ts.map +1 -0
- package/dist/{async-utils.js → async/async.js} +1 -1
- package/dist/async/async.js.map +1 -0
- package/dist/async/batch-operations.d.ts.map +1 -0
- package/dist/async/batch-operations.js.map +1 -0
- package/dist/async/index.d.ts +3 -0
- package/dist/async/index.d.ts.map +1 -0
- package/dist/async/index.js +3 -0
- package/dist/async/index.js.map +1 -0
- package/dist/core/evaluator-base.d.ts.map +1 -0
- package/dist/core/evaluator-base.js.map +1 -0
- package/dist/core/generate.d.ts.map +1 -0
- package/dist/core/generate.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/{date-recurrence-utils.d.ts → date/date-recurrence.d.ts} +1 -1
- package/dist/date/date-recurrence.d.ts.map +1 -0
- package/dist/{date-recurrence-utils.js → date/date-recurrence.js} +1 -1
- package/dist/date/date-recurrence.js.map +1 -0
- package/dist/{date-utils.d.ts → date/date.d.ts} +1 -1
- package/dist/date/date.d.ts.map +1 -0
- package/dist/{date-utils.js → date/date.js} +1 -1
- package/dist/date/date.js.map +1 -0
- package/dist/date/index.d.ts +3 -0
- package/dist/date/index.d.ts.map +1 -0
- package/dist/date/index.js +3 -0
- package/dist/date/index.js.map +1 -0
- package/dist/{child-reference-utils.d.ts → file/child-reference.d.ts} +1 -1
- package/dist/file/child-reference.d.ts.map +1 -0
- package/dist/{child-reference-utils.js → file/child-reference.js} +1 -1
- package/dist/file/child-reference.js.map +1 -0
- package/dist/file/file-operations.d.ts.map +1 -0
- package/dist/{file-operations.js → file/file-operations.js} +2 -2
- package/dist/file/file-operations.js.map +1 -0
- package/dist/file/file.d.ts +263 -0
- package/dist/file/file.d.ts.map +1 -0
- package/dist/file/file.js +466 -0
- package/dist/file/file.js.map +1 -0
- package/dist/{frontmatter-utils.d.ts → file/frontmatter.d.ts} +1 -1
- package/dist/file/frontmatter.d.ts.map +1 -0
- package/dist/{frontmatter-utils.js → file/frontmatter.js} +1 -1
- package/dist/file/frontmatter.js.map +1 -0
- package/dist/file/index.d.ts +7 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/file/index.js +7 -0
- package/dist/file/index.js.map +1 -0
- package/dist/file/link-parser.d.ts.map +1 -0
- package/dist/file/link-parser.js.map +1 -0
- package/dist/{templater-utils.d.ts → file/templater.d.ts} +1 -1
- package/dist/file/templater.d.ts.map +1 -0
- package/dist/{templater-utils.js → file/templater.js} +1 -1
- package/dist/file/templater.js.map +1 -0
- package/dist/index.d.ts +6 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -15
- package/dist/index.js.map +1 -1
- package/dist/settings/index.d.ts +3 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +3 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/settings-store.d.ts.map +1 -0
- package/dist/settings/settings-store.js.map +1 -0
- package/dist/settings/settings-ui-builder.d.ts.map +1 -0
- package/dist/settings/settings-ui-builder.js.map +1 -0
- package/dist/string/index.d.ts +2 -0
- package/dist/string/index.d.ts.map +1 -0
- package/dist/string/index.js +2 -0
- package/dist/string/index.js.map +1 -0
- package/dist/{string-utils.d.ts → string/string.d.ts} +1 -1
- package/dist/string/string.d.ts.map +1 -0
- package/dist/{string-utils.js → string/string.js} +1 -1
- package/dist/string/string.js.map +1 -0
- package/dist/testing/mocks/obsidian.d.ts +1 -0
- package/dist/testing/mocks/obsidian.d.ts.map +1 -1
- package/dist/testing/mocks/obsidian.js +6 -0
- package/dist/testing/mocks/obsidian.js.map +1 -1
- package/package.json +3 -5
- package/src/async/async.ts +117 -0
- package/src/async/batch-operations.ts +53 -0
- package/src/async/index.ts +2 -0
- package/src/core/evaluator-base.ts +118 -0
- package/src/core/generate.ts +22 -0
- package/src/core/index.ts +2 -0
- package/src/date/date-recurrence.ts +244 -0
- package/src/date/date.ts +111 -0
- package/src/date/index.ts +2 -0
- package/src/file/child-reference.ts +76 -0
- package/src/file/file-operations.ts +197 -0
- package/src/file/file.ts +570 -0
- package/src/file/frontmatter.ts +80 -0
- package/src/file/index.ts +6 -0
- package/src/file/link-parser.ts +18 -0
- package/src/file/templater.ts +75 -0
- package/src/index.ts +14 -0
- package/src/settings/index.ts +2 -0
- package/src/settings/settings-store.ts +88 -0
- package/src/settings/settings-ui-builder.ts +507 -0
- package/src/string/index.ts +1 -0
- package/src/string/string.ts +26 -0
- package/src/testing/index.ts +23 -0
- package/src/testing/mocks/obsidian.ts +331 -0
- package/src/testing/mocks/utils.ts +113 -0
- package/src/testing/setup.ts +19 -0
- package/dist/async-utils.d.ts.map +0 -1
- package/dist/async-utils.js.map +0 -1
- package/dist/batch-operations.d.ts.map +0 -1
- package/dist/batch-operations.js.map +0 -1
- package/dist/child-reference-utils.d.ts.map +0 -1
- package/dist/child-reference-utils.js.map +0 -1
- package/dist/date-recurrence-utils.d.ts.map +0 -1
- package/dist/date-recurrence-utils.js.map +0 -1
- package/dist/date-utils.d.ts.map +0 -1
- package/dist/date-utils.js.map +0 -1
- package/dist/evaluator-base.d.ts.map +0 -1
- package/dist/evaluator-base.js.map +0 -1
- package/dist/file-operations.d.ts.map +0 -1
- package/dist/file-operations.js.map +0 -1
- package/dist/file-utils.d.ts +0 -6
- package/dist/file-utils.d.ts.map +0 -1
- package/dist/file-utils.js +0 -25
- package/dist/file-utils.js.map +0 -1
- package/dist/frontmatter-utils.d.ts.map +0 -1
- package/dist/frontmatter-utils.js.map +0 -1
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js.map +0 -1
- package/dist/link-parser.d.ts.map +0 -1
- package/dist/link-parser.js.map +0 -1
- package/dist/settings-store.d.ts.map +0 -1
- package/dist/settings-store.js.map +0 -1
- package/dist/settings-ui-builder.d.ts.map +0 -1
- package/dist/settings-ui-builder.js.map +0 -1
- package/dist/string-utils.d.ts.map +0 -1
- package/dist/string-utils.js.map +0 -1
- package/dist/templater-utils.d.ts.map +0 -1
- package/dist/templater-utils.js.map +0 -1
- /package/dist/{batch-operations.d.ts → async/batch-operations.d.ts} +0 -0
- /package/dist/{batch-operations.js → async/batch-operations.js} +0 -0
- /package/dist/{evaluator-base.d.ts → core/evaluator-base.d.ts} +0 -0
- /package/dist/{evaluator-base.js → core/evaluator-base.js} +0 -0
- /package/dist/{generate.d.ts → core/generate.d.ts} +0 -0
- /package/dist/{generate.js → core/generate.js} +0 -0
- /package/dist/{file-operations.d.ts → file/file-operations.d.ts} +0 -0
- /package/dist/{link-parser.d.ts → file/link-parser.d.ts} +0 -0
- /package/dist/{link-parser.js → file/link-parser.js} +0 -0
- /package/dist/{settings-store.d.ts → settings/settings-store.d.ts} +0 -0
- /package/dist/{settings-store.js → settings/settings-store.js} +0 -0
- /package/dist/{settings-ui-builder.d.ts → settings/settings-ui-builder.d.ts} +0 -0
- /package/dist/{settings-ui-builder.js → settings/settings-ui-builder.js} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { type App, Notice, TFile } from "obsidian";
|
|
4
|
+
import { generateZettelId } from "../core";
|
|
5
|
+
import { generateUniqueFilePath } from "./file";
|
|
6
|
+
import { extractFilePathFromLink } from "./link-parser";
|
|
7
|
+
|
|
8
|
+
export const fromRoot = (relativePath: string): string => {
|
|
9
|
+
return path.resolve(__dirname, `../../../${relativePath}`);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getActiveFileOrThrow = (app: App): TFile => {
|
|
13
|
+
const activeFile: TFile | null = app.workspace.getActiveFile();
|
|
14
|
+
if (!activeFile) {
|
|
15
|
+
new Notice(`⚠️ Open a note first.`);
|
|
16
|
+
throw new Error(`Open a note first.`);
|
|
17
|
+
}
|
|
18
|
+
return activeFile;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getTemplateContent = async (app: App, templatePath: string): Promise<string> => {
|
|
22
|
+
const templateFile = app.vault.getAbstractFileByPath(templatePath);
|
|
23
|
+
if (!templateFile) {
|
|
24
|
+
new Notice(`❌ Template not found: ${templatePath}`);
|
|
25
|
+
throw new Error(`Template not found: ${templatePath}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return await app.vault.read(templateFile as TFile);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const ensureFolderExists = async (app: App, folderPath: string): Promise<void> => {
|
|
32
|
+
if (!app.vault.getAbstractFileByPath(folderPath)) {
|
|
33
|
+
await app.vault.createFolder(folderPath).catch(() => {});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const openFileInNewLeaf = async (app: App, file: TFile): Promise<void> => {
|
|
38
|
+
await app.workspace.getLeaf(true).openFile(file);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const getNoteFilesFromDir = async (directoryPath: string): Promise<string[]> => {
|
|
42
|
+
const files = await fs.readdir(directoryPath);
|
|
43
|
+
const directoryName = path.basename(directoryPath);
|
|
44
|
+
|
|
45
|
+
return files.filter((file) => {
|
|
46
|
+
if (!file.endsWith(".md")) return false;
|
|
47
|
+
|
|
48
|
+
const fileNameWithoutExt = path.parse(file).name;
|
|
49
|
+
if (fileNameWithoutExt === directoryName) {
|
|
50
|
+
console.log(`⏭️ Skipping directory-level file: ${file}`);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getTargetFileFromLink = (app: App, relationshipLink: string): TFile | null => {
|
|
59
|
+
const targetFilePath = extractFilePathFromLink(relationshipLink);
|
|
60
|
+
if (!targetFilePath) {
|
|
61
|
+
console.warn(`Failed to extract file path from link: ${relationshipLink}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const targetFile = app.vault.getAbstractFileByPath(targetFilePath) as TFile;
|
|
66
|
+
if (!targetFile) {
|
|
67
|
+
console.warn(`Target file not found for link: ${relationshipLink}`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return targetFile;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const createFileLink = (file: TFile): string => {
|
|
75
|
+
const folder = file.parent?.path && file.parent.path !== "/" ? file.parent.path : "";
|
|
76
|
+
return folder ? `[[${folder}/${file.basename}|${file.basename}]]` : `[[${file.basename}]]`;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const normalizeArray = (value: string | string[] | undefined): string[] => {
|
|
80
|
+
if (!value) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
if (typeof value === "string") {
|
|
84
|
+
return [value];
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
return [];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const arraysEqual = (a: string[], b: string[]): boolean => {
|
|
93
|
+
return a.length === b.length && a.every((val, index) => val === b[index]);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Normalizes frontmatter content by converting quoted numeric _ZettelIDs to numbers.
|
|
98
|
+
* This handles edge cases where YAML parsers treat numeric strings inconsistently.
|
|
99
|
+
*/
|
|
100
|
+
export const normalizeContent = (content: string): string => {
|
|
101
|
+
let normalized = content;
|
|
102
|
+
let hasChanges = false;
|
|
103
|
+
|
|
104
|
+
// Normalize _ZettelID: "string" → number (remove quotes)
|
|
105
|
+
const zettelIdMatch = normalized.match(/^_ZettelID:\s*"(\d+)"/m);
|
|
106
|
+
if (zettelIdMatch) {
|
|
107
|
+
const [fullMatch, numericId] = zettelIdMatch;
|
|
108
|
+
const replacement = `_ZettelID: ${numericId}`;
|
|
109
|
+
normalized = normalized.replace(fullMatch, replacement);
|
|
110
|
+
hasChanges = true;
|
|
111
|
+
console.log(` ✅ _ZettelID: "${numericId}" → ${numericId}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return hasChanges ? normalized : content;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Safely performs a file operation with error handling and file validation.
|
|
119
|
+
* Reduces boilerplate for common file operations.
|
|
120
|
+
*/
|
|
121
|
+
export const withFileOperation = async <T>(
|
|
122
|
+
app: App,
|
|
123
|
+
event: any,
|
|
124
|
+
operation: (file: TFile) => Promise<T>,
|
|
125
|
+
errorMessage: string = "Operation failed"
|
|
126
|
+
): Promise<T | null> => {
|
|
127
|
+
try {
|
|
128
|
+
const filePath = event.extendedProps.filePath;
|
|
129
|
+
const file = app.vault.getAbstractFileByPath(filePath);
|
|
130
|
+
|
|
131
|
+
if (!(file instanceof TFile)) {
|
|
132
|
+
new Notice("Could not find the file");
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return await operation(file);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error(`Error in file operation:`, error);
|
|
139
|
+
new Notice(errorMessage);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Safely performs a file operation by file path with error handling and file validation.
|
|
146
|
+
*/
|
|
147
|
+
export const withFile = async <T>(
|
|
148
|
+
app: App,
|
|
149
|
+
filePath: string,
|
|
150
|
+
operation: (file: TFile) => Promise<T>,
|
|
151
|
+
errorMessage: string = "Operation failed"
|
|
152
|
+
): Promise<T | null> => {
|
|
153
|
+
try {
|
|
154
|
+
const file = app.vault.getAbstractFileByPath(filePath);
|
|
155
|
+
|
|
156
|
+
if (!(file instanceof TFile)) {
|
|
157
|
+
new Notice("Could not find the file");
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return await operation(file);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error(`Error in file operation:`, error);
|
|
164
|
+
new Notice(errorMessage);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Duplicates a file with a new ZettelID, preserving the original content
|
|
171
|
+
* but updating the ZettelID in frontmatter if configured.
|
|
172
|
+
*/
|
|
173
|
+
export const duplicateFileWithNewZettelId = async (
|
|
174
|
+
app: App,
|
|
175
|
+
file: TFile,
|
|
176
|
+
zettelIdProp?: string
|
|
177
|
+
): Promise<TFile> => {
|
|
178
|
+
const content = await app.vault.read(file);
|
|
179
|
+
|
|
180
|
+
const parentPath = file.parent?.path || "";
|
|
181
|
+
const baseNameWithoutZettel = file.basename.replace(/-\d{14}$/, "");
|
|
182
|
+
const zettelId = generateZettelId();
|
|
183
|
+
const newBasename = `${baseNameWithoutZettel}-${zettelId}`;
|
|
184
|
+
const newFilePath = generateUniqueFilePath(app, parentPath, newBasename);
|
|
185
|
+
|
|
186
|
+
// Create the new file with original content
|
|
187
|
+
const newFile = await app.vault.create(newFilePath, content);
|
|
188
|
+
|
|
189
|
+
// Update the ZettelID in frontmatter if configured
|
|
190
|
+
if (zettelIdProp) {
|
|
191
|
+
await app.fileManager.processFrontMatter(newFile, (fm) => {
|
|
192
|
+
fm[zettelIdProp] = zettelId;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return newFile;
|
|
197
|
+
};
|