Begin adding tests
This commit is contained in:
parent
e089616deb
commit
bc5ddf3c23
1
.npmignore
Normal file
1
.npmignore
Normal file
|
@ -0,0 +1 @@
|
|||
bin/_test
|
|
@ -9,7 +9,7 @@
|
|||
"build": "npm run clean && tsc",
|
||||
"clean": "shx rm -rf bin",
|
||||
"dev": "npm run build && npm start",
|
||||
"start": "node ./bin/hammerstone.js"
|
||||
"test": "npm run build && shx cp -r src/_test/test-vault bin/_test/test-vault && node ./bin/_test/test-runner.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
|
29
src/_test/main.test.ts
Normal file
29
src/_test/main.test.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import test, { describe } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import Vault from "../vault.js";
|
||||
|
||||
describe("Vault", () => {
|
||||
test("load a vault", () => {
|
||||
const vault = new Vault("./bin/_test/test-vault");
|
||||
assert.equal(vault.size, 4, "Unexpected number of documents in vault");
|
||||
});
|
||||
|
||||
test("ignore paths", () => {
|
||||
const vault = new Vault("./bin/_test/test-vault", {
|
||||
ignorePatterns: ["**/Ignoreme/**"],
|
||||
});
|
||||
assert.equal(vault.size, 3, "Unexpected number of documents in vault");
|
||||
});
|
||||
|
||||
test("reading tags", () => {
|
||||
const vault = new Vault("./bin/_test/test-vault");
|
||||
const taggedFile = vault.index["directory-subdirectory-i-have-tags"];
|
||||
|
||||
if (taggedFile === undefined) {
|
||||
assert.fail("Expected file with tags");
|
||||
}
|
||||
|
||||
const expectedTags = ["propertytag", "tags", "hashtags"];
|
||||
assert.deepStrictEqual(taggedFile.tags, expectedTags);
|
||||
});
|
||||
});
|
13
src/_test/test-runner.ts
Normal file
13
src/_test/test-runner.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { tap } from "node:test/reporters";
|
||||
import { run } from "node:test";
|
||||
import process from "node:process";
|
||||
import path from "node:path";
|
||||
|
||||
run({
|
||||
files: [path.resolve("./bin/_test/main.test.js")],
|
||||
})
|
||||
.on("test:fail", () => {
|
||||
process.exitCode = 1;
|
||||
})
|
||||
.compose(tap)
|
||||
.pipe(process.stdout);
|
1
src/_test/test-vault/.obsidian/app.json
vendored
Normal file
1
src/_test/test-vault/.obsidian/app.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
3
src/_test/test-vault/.obsidian/appearance.json
vendored
Normal file
3
src/_test/test-vault/.obsidian/appearance.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"accentColor": ""
|
||||
}
|
30
src/_test/test-vault/.obsidian/core-plugins-migration.json
vendored
Normal file
30
src/_test/test-vault/.obsidian/core-plugins-migration.json
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"properties": false,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": false
|
||||
}
|
20
src/_test/test-vault/.obsidian/core-plugins.json
vendored
Normal file
20
src/_test/test-vault/.obsidian/core-plugins.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
"file-explorer",
|
||||
"global-search",
|
||||
"switcher",
|
||||
"graph",
|
||||
"backlink",
|
||||
"canvas",
|
||||
"outgoing-link",
|
||||
"tag-pane",
|
||||
"page-preview",
|
||||
"daily-notes",
|
||||
"templates",
|
||||
"note-composer",
|
||||
"command-palette",
|
||||
"editor-status",
|
||||
"bookmarks",
|
||||
"outline",
|
||||
"word-count",
|
||||
"file-recovery"
|
||||
]
|
22
src/_test/test-vault/.obsidian/graph.json
vendored
Normal file
22
src/_test/test-vault/.obsidian/graph.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"collapse-filter": true,
|
||||
"search": "",
|
||||
"showTags": false,
|
||||
"showAttachments": false,
|
||||
"hideUnresolved": false,
|
||||
"showOrphans": true,
|
||||
"collapse-color-groups": true,
|
||||
"colorGroups": [],
|
||||
"collapse-display": true,
|
||||
"showArrow": false,
|
||||
"textFadeMultiplier": 0,
|
||||
"nodeSizeMultiplier": 1,
|
||||
"lineSizeMultiplier": 1,
|
||||
"collapse-forces": true,
|
||||
"centerStrength": 0.518713248970312,
|
||||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 1,
|
||||
"close": true
|
||||
}
|
160
src/_test/test-vault/.obsidian/workspace.json
vendored
Normal file
160
src/_test/test-vault/.obsidian/workspace.json
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
{
|
||||
"main": {
|
||||
"id": "0d432b44da4e24cf",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "6bba5a91ffd45e55",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "c548f27295fc9f89",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Ignoreme/Unimportant file.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "eb8f617338bff33a",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "e9168ba4d39b9811",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "0714e101e8d2e95b",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "d8fde6378b886f78",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2f84c2d6a1864258",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "5dc6da21227cd234",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "7ce4d629d6f2274c",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "996e1a5e0748190e",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "Ignoreme/Unimportant file.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "aaed9afdd72608ad",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "Ignoreme/Unimportant file.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "71cf6badf7dcaf51",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "93936f2b9e8b5004",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "Ignoreme/Unimportant file.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false
|
||||
}
|
||||
},
|
||||
"active": "c548f27295fc9f89",
|
||||
"lastOpenFiles": [
|
||||
"Another Directory/Cool note.md",
|
||||
"Ignoreme/Unimportant file.md",
|
||||
"Ignoreme",
|
||||
"Directory/Subdirectory/I have tags.md",
|
||||
"Another Directory",
|
||||
"Welcome.md",
|
||||
"Directory/Subdirectory",
|
||||
"Directory"
|
||||
]
|
||||
}
|
1
src/_test/test-vault/Another Directory/Cool note.md
Normal file
1
src/_test/test-vault/Another Directory/Cool note.md
Normal file
|
@ -0,0 +1 @@
|
|||
This is a cool cool note, it also links to the [[Welcome]] note.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
tags:
|
||||
- propertytag
|
||||
---
|
||||
I am a file that has some #tags in it, using inline #hashtags as well as the properties.
|
1
src/_test/test-vault/Ignoreme/Unimportant file.md
Normal file
1
src/_test/test-vault/Ignoreme/Unimportant file.md
Normal file
|
@ -0,0 +1 @@
|
|||
This file should not be read because it is not important.
|
1
src/_test/test-vault/Welcome.md
Normal file
1
src/_test/test-vault/Welcome.md
Normal file
|
@ -0,0 +1 @@
|
|||
This is the home document of this here test vault.
|
165
src/document.ts
165
src/document.ts
|
@ -1,116 +1,14 @@
|
|||
import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||
import path from "node:path";
|
||||
import slug from "slug";
|
||||
import YAML from "yaml";
|
||||
import type Vault from "./vault.js";
|
||||
|
||||
export type FrontmatterShape = Record<string, string | string[]>;
|
||||
|
||||
const FRONTMATTER_REGEX = /^---[\s\S]*?---/gm;
|
||||
|
||||
function getBlockTags(blockName: string): [string, string] {
|
||||
return [`%% {${blockName}} %%`, `%% {/${blockName}} %%`];
|
||||
}
|
||||
|
||||
function documentContainsBlock(document: MarkdownDocument, blockName: string) {
|
||||
const [openTag, closeTag] = getBlockTags(blockName);
|
||||
|
||||
const openLoc = document.content.indexOf(openTag);
|
||||
const closeLoc = document.content.indexOf(closeTag);
|
||||
|
||||
if (openLoc === -1 || closeLoc === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (closeLoc < openLoc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function readDocumentBlock(
|
||||
document: MarkdownDocument,
|
||||
blockName: string,
|
||||
): string | undefined {
|
||||
const [openTag, closeTag] = getBlockTags(blockName);
|
||||
|
||||
const openLoc = document.content.indexOf(openTag);
|
||||
const closeLoc = document.content.indexOf(closeTag);
|
||||
|
||||
if (openLoc === -1 || closeLoc === -1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return document.content.slice(openLoc + openTag.length, closeLoc).trim();
|
||||
}
|
||||
|
||||
function writeDocumentBlock(
|
||||
document: MarkdownDocument,
|
||||
blockName: string,
|
||||
blockContent: string = "",
|
||||
): MarkdownDocument {
|
||||
const [openTag, closeTag] = getBlockTags(blockName);
|
||||
|
||||
const openLoc = document.content.indexOf(openTag);
|
||||
const closeLoc = document.content.indexOf(closeTag);
|
||||
|
||||
if (openLoc === -1 || closeLoc === -1) {
|
||||
return document;
|
||||
}
|
||||
|
||||
const newContent =
|
||||
document.content.slice(0, openLoc + openTag.length) +
|
||||
"\n" +
|
||||
blockContent +
|
||||
"\n" +
|
||||
document.content.slice(closeLoc);
|
||||
|
||||
document.content = newContent;
|
||||
return document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse YAML frontmatter from a markdown document
|
||||
* @param contents The raw markdown content
|
||||
* @returns Any successfully parsed frontmatter
|
||||
*/
|
||||
function processDocumentFrontmatter(contents: string): FrontmatterShape {
|
||||
if (contents.trim().indexOf("---") !== 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
let frontmatterData = {};
|
||||
|
||||
if (FRONTMATTER_REGEX.test(contents)) {
|
||||
const frontmatterString = contents.match(FRONTMATTER_REGEX)![0];
|
||||
const cleanFrontmatter = frontmatterString.replaceAll("---", "").trim();
|
||||
frontmatterData = YAML.parse(cleanFrontmatter);
|
||||
}
|
||||
|
||||
return {
|
||||
...frontmatterData,
|
||||
};
|
||||
}
|
||||
|
||||
function getDocumentMarkdown(content: string): string {
|
||||
if (content.trim().indexOf("---") !== 0) {
|
||||
return content;
|
||||
}
|
||||
return content.replace(FRONTMATTER_REGEX, "").trim();
|
||||
}
|
||||
|
||||
function combineMarkdownAndFrontmatter(
|
||||
markdown: string,
|
||||
frontmatter: FrontmatterShape,
|
||||
) {
|
||||
return `---
|
||||
${YAML.stringify(frontmatter)}
|
||||
---
|
||||
|
||||
${markdown.trim()}
|
||||
`;
|
||||
}
|
||||
import {
|
||||
combineMarkdownAndFrontmatter,
|
||||
extractFrontmatter,
|
||||
extractMarkdown,
|
||||
extractTags,
|
||||
type FrontmatterShape,
|
||||
} from "./markdown.js";
|
||||
|
||||
export default class MarkdownDocument {
|
||||
/** A reference to the vault containing this document */
|
||||
|
@ -119,12 +17,10 @@ export default class MarkdownDocument {
|
|||
/** The original file path to this document */
|
||||
readonly path: string;
|
||||
|
||||
/** The path to the root of the vault */
|
||||
readonly vaultRootPath: string;
|
||||
|
||||
/** The directory/folder that this Document is in, relative to the vault root */
|
||||
readonly dirname: string;
|
||||
|
||||
/** An array of directory names this document is found under */
|
||||
readonly taxonomy: string[];
|
||||
|
||||
/** The name of this document's file, without the file extension */
|
||||
|
@ -148,6 +44,8 @@ export default class MarkdownDocument {
|
|||
/** The markdown portion of the file (without frontmatter) */
|
||||
private _markdown: string;
|
||||
|
||||
tags: string[];
|
||||
|
||||
constructor(filePath: string, vault: Vault) {
|
||||
if (!existsSync(filePath)) {
|
||||
throw new Error(`File not found: ${filePath}`);
|
||||
|
@ -156,30 +54,23 @@ export default class MarkdownDocument {
|
|||
const rawFileContent = readFileSync(filePath, "utf-8");
|
||||
const fileDirname = path.dirname(filePath);
|
||||
const vaultPath = vault.vaultPath;
|
||||
const frontmatter = extractFrontmatter(rawFileContent);
|
||||
|
||||
this.path = filePath;
|
||||
this.dirname = path.relative(vaultPath, fileDirname);
|
||||
this.taxonomy = this.dirname.split(path.sep);
|
||||
this.vaultRootPath = vaultPath;
|
||||
this.filename = path.basename(filePath, ".md");
|
||||
this.slug = slug(`${this.taxonomy.join("-")}-${this.filename}`);
|
||||
this._content = rawFileContent;
|
||||
this.contentHistory = [rawFileContent];
|
||||
this._frontmatter = processDocumentFrontmatter(rawFileContent);
|
||||
this._markdown = getDocumentMarkdown(rawFileContent);
|
||||
this._frontmatter = frontmatter;
|
||||
this._markdown = extractMarkdown(rawFileContent);
|
||||
this.vault = vault;
|
||||
}
|
||||
|
||||
containsBlock(blockName: string): boolean {
|
||||
return documentContainsBlock(this, blockName);
|
||||
}
|
||||
|
||||
readBlock(blockName: string): string | undefined {
|
||||
return readDocumentBlock(this, blockName);
|
||||
}
|
||||
|
||||
setBlockContent(blockName: string, newContent: string): MarkdownDocument {
|
||||
return writeDocumentBlock(this, blockName, newContent);
|
||||
// Identify tags from content and frontmatter
|
||||
const frontmatterTags = frontmatter.tags;
|
||||
const contentTags = extractTags(this._markdown);
|
||||
this.tags = frontmatterTags.concat(contentTags);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
@ -226,11 +117,29 @@ export default class MarkdownDocument {
|
|||
|
||||
set content(newValue: string) {
|
||||
this._content = newValue;
|
||||
this._frontmatter = processDocumentFrontmatter(newValue);
|
||||
this._markdown = getDocumentMarkdown(newValue);
|
||||
this._frontmatter = extractFrontmatter(newValue);
|
||||
this._markdown = extractMarkdown(newValue);
|
||||
this.contentHistory.push(this._content);
|
||||
}
|
||||
|
||||
hasTag(tag: string) {
|
||||
return this.tags.includes(tag);
|
||||
}
|
||||
|
||||
hasTaxonomy(dirs: string[]) {
|
||||
if (dirs.length < this.taxonomy.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
if (dirs[i] !== this.taxonomy[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
write() {
|
||||
writeFileSync(
|
||||
this.path,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Vault from "./vault.js";
|
||||
import MarkdownDocument, { type FrontmatterShape } from "./document.js";
|
||||
import MarkdownDocument from "./document.js";
|
||||
import type { FrontmatterShape } from "./markdown.js";
|
||||
|
||||
export { Vault, MarkdownDocument, FrontmatterShape };
|
||||
|
|
74
src/markdown.ts
Normal file
74
src/markdown.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import YAML from "yaml";
|
||||
|
||||
export interface FrontmatterShape {
|
||||
tags: string[];
|
||||
aliases: string[];
|
||||
cssclasses: string[];
|
||||
[key: string]: string | string[];
|
||||
}
|
||||
|
||||
const EMPTY_FRONTMATTER: FrontmatterShape = {
|
||||
tags: [],
|
||||
aliases: [],
|
||||
cssclasses: [],
|
||||
};
|
||||
|
||||
const FRONTMATTER_REGEX = /^---[\s\S]*?---/gm;
|
||||
|
||||
/**
|
||||
* Attempt to parse YAML frontmatter from a mixed yaml/md doc
|
||||
* @param content The raw markdown content
|
||||
* @returns Any successfully parsed frontmatter
|
||||
*/
|
||||
export function extractFrontmatter(content: string): FrontmatterShape {
|
||||
// If it does not start with `---`, it is invalid for frontmatter
|
||||
if (content.trim().indexOf("---") !== 0) {
|
||||
return {
|
||||
...EMPTY_FRONTMATTER,
|
||||
};
|
||||
}
|
||||
|
||||
let frontmatterData = {};
|
||||
|
||||
if (FRONTMATTER_REGEX.test(content)) {
|
||||
const frontmatterString = content.match(FRONTMATTER_REGEX)![0];
|
||||
const cleanFrontmatter = frontmatterString.replaceAll("---", "").trim();
|
||||
frontmatterData = YAML.parse(cleanFrontmatter);
|
||||
}
|
||||
|
||||
return {
|
||||
...EMPTY_FRONTMATTER,
|
||||
...frontmatterData,
|
||||
};
|
||||
}
|
||||
|
||||
export function extractMarkdown(content: string): string {
|
||||
if (content.trim().indexOf("---") !== 0) {
|
||||
return content;
|
||||
}
|
||||
return content.replace(FRONTMATTER_REGEX, "").trim();
|
||||
}
|
||||
|
||||
export function combineMarkdownAndFrontmatter(
|
||||
markdown: string,
|
||||
frontmatter: FrontmatterShape,
|
||||
) {
|
||||
return `---
|
||||
${YAML.stringify(frontmatter)}
|
||||
---
|
||||
|
||||
${markdown.trim()}
|
||||
`;
|
||||
}
|
||||
|
||||
const TAG_REGEX = /#[A-Za-z0-9/\-_]+/g;
|
||||
|
||||
export function extractTags(content: string): string[] {
|
||||
const results = content.match(TAG_REGEX);
|
||||
|
||||
if (results === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return results.map((result) => result.replace("#", ""));
|
||||
}
|
47
src/vault.ts
47
src/vault.ts
|
@ -1,3 +1,4 @@
|
|||
import path from "node:path";
|
||||
import { globSync } from "glob";
|
||||
import MarkdownDocument from "./document.js";
|
||||
|
||||
|
@ -47,22 +48,61 @@ function buildVaultIndex(
|
|||
return index;
|
||||
}
|
||||
|
||||
export class VaultView {
|
||||
/** An array of all discovered Markdown documents */
|
||||
documents: MarkdownDocument[] = [];
|
||||
|
||||
/** An array of all generated document slugs */
|
||||
slugs: string[] = [];
|
||||
|
||||
/** A map of generated document slugs to their associated document */
|
||||
index: Record<string, MarkdownDocument> = {};
|
||||
|
||||
/** The absolute path of this fault on disk */
|
||||
readonly vault: Vault;
|
||||
|
||||
/** The number of documents in this vault */
|
||||
readonly size: number = 0;
|
||||
|
||||
constructor(documents: MarkdownDocument[], vault: Vault) {
|
||||
this.documents = documents;
|
||||
this.slugs = documents.map((doc) => doc.slug);
|
||||
this.index = buildVaultIndex(documents);
|
||||
this.vault = vault;
|
||||
this.size = documents.length;
|
||||
}
|
||||
|
||||
map(fn: (document: MarkdownDocument) => MarkdownDocument) {
|
||||
this.documents = this.documents.map(fn);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
interface VaultOptions {
|
||||
ignorePatterns?: string[];
|
||||
}
|
||||
|
||||
export default class Vault {
|
||||
/** An array of all discovered Markdown documents */
|
||||
documents: MarkdownDocument[] = [];
|
||||
|
||||
/** An array of all generated document slugs */
|
||||
slugs: string[] = [];
|
||||
|
||||
/** A map of generated document slugs to their associated document */
|
||||
index: Record<string, MarkdownDocument> = {};
|
||||
|
||||
/** The absolute path of this fault on disk */
|
||||
readonly vaultPath: string;
|
||||
|
||||
/** The number of documents in this vault */
|
||||
readonly size: number = 0;
|
||||
|
||||
/** File patterns to ignore when discovering vault files */
|
||||
private ignorePatterns: string[] = [];
|
||||
|
||||
constructor(vaultRootPath: string, options?: VaultOptions) {
|
||||
this.vaultPath = vaultRootPath;
|
||||
this.vaultPath = path.resolve(vaultRootPath);
|
||||
this.ignorePatterns = options?.ignorePatterns ?? [];
|
||||
|
||||
const allFiles = loadVaultDocuments(this, this.ignorePatterns);
|
||||
|
@ -74,6 +114,11 @@ export default class Vault {
|
|||
this.index = buildVaultIndex(allFiles);
|
||||
}
|
||||
|
||||
view(fn: (document: MarkdownDocument) => boolean) {
|
||||
const matchingDocs = this.documents.filter(fn);
|
||||
return new VaultView(matchingDocs, this);
|
||||
}
|
||||
|
||||
map(fn: (document: MarkdownDocument) => MarkdownDocument) {
|
||||
this.documents = this.documents.map(fn);
|
||||
return this;
|
||||
|
|
Loading…
Reference in a new issue