Skip to content

Commit

Permalink
import and convert wiki-style note links
Browse files Browse the repository at this point in the history
- convert wiki-style note links to markdown-style note links
- update internal note links to updated note location
- make import type (Notion, Other) configurable via UI
- use birthtime, else mtime, for note creation date
  • Loading branch information
cloverich committed Dec 1, 2024
1 parent 9b8cb73 commit 07f7c72
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 473 deletions.
60 changes: 49 additions & 11 deletions src/preload/client/importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type IImporterClient = ImporterClient;

import { uuidv7obj } from "uuidv7";
import { mdastToString, parseMarkdown as stringToMdast } from "../../markdown";
import { SourceType } from "./importer/SourceType";
import { WikiFileResolver } from "./importer/WikiFileResolver";
import { parseTitleAndFrontMatter } from "./importer/frontmatter";

Expand Down Expand Up @@ -76,11 +77,6 @@ interface StagedNote {
error?: string | null;
}

export enum SourceType {
Notion = "notion",
Other = "other",
}

export class ImporterClient {
constructor(
private db: Database,
Expand Down Expand Up @@ -222,9 +218,15 @@ export class ImporterClient {
);

// Prefer front-matter supplied create and update times, but fallback to file stats
// todo: check updatedAt, "Updated At", "Last Edited", etc.
// todo: check updatedAt, "Updated At", "Last Edited", etc. i.e. support more possible
// front-matter keys for dates; probably needs to be configurable:
// 1. Which key(s) to check
// 2. Whether to use birthtime or mtime
// 3. Which timezone to use
// 4. Whether to use the front-matter date or the file date
if (!frontMatter.createdAt) {
frontMatter.createdAt = file.stats.ctime.toISOString();
frontMatter.createdAt =
file.stats.birthtime.toISOString() || file.stats.mtime.toISOString();
}

if (!frontMatter.updatedAt) {
Expand Down Expand Up @@ -283,6 +285,7 @@ export class ImporterClient {
await resolver.moveStagedFiles(chroniclesRoot, importerId, importDir);

const linkMapping = await this.noteLinksMapping(importerId);
const wikiLinkMapping = await this.noteLinksWikiMapping(importerId);

const items = await this.knex<StagedNote>("import_notes").where({
importerId,
Expand All @@ -291,10 +294,8 @@ export class ImporterClient {
for await (const item of items) {
const frontMatter = JSON.parse(item.frontMatter);

// todo: can I store the mdast in JSON? If so, should I just do this on the first
// pass since I already parsed it to mdast once?
const mdast = stringToMdast(item.content) as any as mdast.Root;
this.updateNoteLinks(mdast, item, linkMapping);
await this.updateNoteLinks(mdast, item, linkMapping, wikiLinkMapping);

await resolver.updateFileLinks(item.sourcePath, mdast);
this.convertWikiLinks(mdast);
Expand Down Expand Up @@ -381,6 +382,26 @@ export class ImporterClient {
return linkMapping;
};

// Pull all staged notes and generate a mapping of original note title
// to the new file path (chroniclesPath). This is used to update
// wikilinks that point to other notes to chronicles-style markdown links.
private noteLinksWikiMapping = async (importerId: string) => {
let linkMapping: Record<string, { journal: string; chroniclesId: string }> =
{};

const importedItems = await this.knex("import_notes")
.where({ importerId })
.select("title", "journal", "chroniclesId");

for (const item of importedItems) {
if ("error" in item && item.error) continue;
const { journal, chroniclesId, title } = item;
linkMapping[title] = { journal, chroniclesId };
}

return linkMapping;
};

// check if a markdown link is a link to a (markdown) note
private isNoteLink = (url: string) => {
// we are only interested in markdown links
Expand All @@ -395,8 +416,13 @@ export class ImporterClient {
private updateNoteLinks = async (
mdast: mdast.Root | mdast.Content,
item: StagedNote,
// mapping of sourcePath to new journal and chroniclesId
linkMapping: Record<string, { journal: string; chroniclesId: string }>,
// mapping of note title to new journal and chroniclesId
linkMappingWiki: Record<string, { journal: string; chroniclesId: string }>,
) => {
// todo: update ofmWikilink
// todo: update links that point to local files
if (mdast.type === "link" && this.isNoteLink(mdast.url)) {
const url = decodeURIComponent(mdast.url);
const sourceFolderPath = path.dirname(item.sourcePath);
Expand All @@ -409,9 +435,20 @@ export class ImporterClient {
mdast.url = `../${mapped.journal}/${mapped.chroniclesId}.md`;
}

if (mdast.type === "ofmWikilink") {
const title = mdast.value;
const mapped = linkMappingWiki[title];

if (!mapped) return;

// NOTE: This updates the url, but assumes the node type
// will be converted to regular link in later step
mdast.url = `../${mapped.journal}/${mapped.chroniclesId}.md`;
}

if ("children" in mdast) {
for (const child of mdast.children as any) {
this.updateNoteLinks(child, item, linkMapping);
this.updateNoteLinks(child, item, linkMapping, linkMappingWiki);
}
}
};
Expand Down Expand Up @@ -558,6 +595,7 @@ export class ImporterClient {
mdast.alt = mdast.value;
mdast.url = mdast.url;
} else if (mdast.type === "ofmWikilink") {
mdast.children = [{ type: "text", value: mdast.value }];
(mdast as any).type = "link";
} else {
if ("children" in mdast) {
Expand Down
5 changes: 5 additions & 0 deletions src/preload/client/importer/SourceType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Included here so non-preload (preference) can import
export enum SourceType {
Notion = "notion",
Other = "other",
}
7 changes: 0 additions & 7 deletions src/preload/client/importer/WikiFileResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,6 @@ export class WikiFileResolver {
const updatedUrl = await this.resolveToChroniclesByName(mdast.value);
if (updatedUrl) {
mdast.url = updatedUrl;
console.log(
"updated url (old)",
mdast.value,
"(new)",
mdast.url,
mdast,
);
}
} else {
const updatedUrl = await this.resolveMarkdownFileLinkToChroniclesPath(
Expand Down
2 changes: 1 addition & 1 deletion src/preload/client/importer/frontmatter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import yaml from "yaml";
import { SourceType } from "../importer";
import { SourceType } from "../importer/SourceType";

interface ParseTitleAndFrontMatterRes {
title: string;
Expand Down
164 changes: 0 additions & 164 deletions src/preload/importer/legacy/importChronicles.ts

This file was deleted.

Loading

0 comments on commit 07f7c72

Please sign in to comment.