Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Make Anki packages #4

Merged
merged 1 commit into from
Feb 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deps/deno-anki.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "https://raw.githubusercontent.com/takker99/deno-anki/0.0.2/mod.ts";
export * from "https://raw.githubusercontent.com/takker99/deno-anki/0.0.3/mod.ts";
1 change: 1 addition & 0 deletions deps/jsZip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { default as JSZip } from "https://esm.sh/jszip@3.9.1";
4 changes: 2 additions & 2 deletions deps/scrapbox.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.19.0/rest/mod.ts";
export * from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.19.0/title.ts";
export * from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.19.2/rest/mod.ts";
export * from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.19.2/title.ts";
export * from "https://raw.githubusercontent.com/scrapbox-jp/types/0.3.8/rest.ts";
1 change: 1 addition & 0 deletions deps/sql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { SqlJsStatic } from "https://esm.sh/sql.js@1.8.0";
136 changes: 136 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import {
getTable,
NotFoundError,
NotLoggedInError,
NotMemberError,
Page,
Result,
} from "./deps/scrapbox.ts";
import {
Deck,
makeCollection,
makePackage,
NoteType,
} from "./deps/deno-anki.ts";
import { JSZip } from "./deps/jsZip.ts";
import { SqlJsStatic } from "./deps/sql.ts";
import { parseNotes, Path } from "./note.ts";
import { InvalidDeckError, parseDeck } from "./deck.ts";
import { InvalidNoteTypeError, parseNoteType } from "./noteType.ts";

/** 既定のdeck */
const defaultDeck: Deck = {
name: "default",
id: 1,
};

/** 既定のNote type */
// @ts-ignore NoteTypeしか帰ってこないはず
const defaultNoteType: NoteType = parseNoteType(`name,Basic (Cloze)
id,1677417085373
isCloze,true`).value;

type DeckResult = Result<
Deck,
InvalidDeckError | NotFoundError | NotLoggedInError | NotMemberError
>;

/** 読み込んだdecksのcache
*
* Promiseを入れることで、重複読み込みを避ける
*/
const decks = new Map<Key, Promise<DeckResult>>();

/** 指定されたdeckをcacheから読み込む
*
* cacheになければfetchする
*
* `path`が`undefined`のときはdefaultのdeckを返す
*/
const getDeck = (path: Path | undefined): Promise<DeckResult> => {
if (!path) return Promise.resolve({ ok: true, value: defaultDeck });
const deck = decks.get(toKey(path));
// すでにfetch中のがあれば、それを待つ
if (deck) return deck;

const promise = (async () => {
const result = await getTable(path.project, path.title, "deck");
if (!result.ok) return result;
return parseDeck(result.value);
})();
decks.set(toKey(path), promise);
return promise;
};

type NoteTypeResult = Result<
NoteType,
InvalidNoteTypeError | NotFoundError | NotLoggedInError | NotMemberError
>;

/** 読み込んだnote typesのcache
*
* Promiseを入れることで、重複読み込みを避ける
*/
const noteTypes = new Map<Key, Promise<NoteTypeResult>>();

/** 指定されたnote typeをcacheから読み込む
*
* cacheになければfetchする
*/
const getNoteType = (path: Path | undefined): Promise<NoteTypeResult> => {
if (!path) return Promise.resolve({ ok: true, value: defaultNoteType });
const noteType = noteTypes.get(toKey(path));
// すでにfetch中のがあれば、それを待つ
if (noteType) return noteType;

const promise = (async () => {
const result = await getTable(path.project, path.title, "note type");
if (!result.ok) return result;
return parseNoteType(result.value);
})();
noteTypes.set(toKey(path), promise);
return promise;
};

export interface MakeApkgInit {
jsZip: typeof JSZip;
sql: SqlJsStatic;
}

export const makeApkg = async (
project: string,
pages: Page[],
init: MakeApkgInit,
): Promise<{ ok: true; value: Blob }> => {
const notes = (await Promise.all(pages.map(async (page) => {
const { deckRef, noteTypeRef, notes: notes_ } = parseNotes(
project,
page.title,
page.lines,
);

const deckRes = await getDeck(deckRef);
if (!deckRes.ok) {
console.warn(`${deckRes.value.name} ${deckRes.value.message}`);
}
const deck = deckRes.ok ? deckRes.value : defaultDeck;
const noteTypeRes = await getNoteType(noteTypeRef);
if (!noteTypeRes.ok) {
console.warn(`${noteTypeRes.value.name} ${noteTypeRes.value.message}`);
}
const noteType = noteTypeRes.ok ? noteTypeRes.value : defaultNoteType;
return notes_.map((note) => ({ deck, noteType, ...note }));
}))).flat();

return {
ok: true,
value: await makePackage(
makeCollection(notes, init.sql),
{},
init.jsZip,
),
};
};

type Key = `/${string}/${string}`;
const toKey = (path: Path): Key => `/${path.project}/${path.title}`;