49278723b1
- Use Node filesystem APIs instead of shell commands for temp cleanup - Add absolute path helpers for cross-platform compatibility - Generate both main.js and dist/main.js artifacts - Improve error handling in document downloader - Track additional document metadata in sync tracker - Replace Unix-specific `find` with recursive directory traversal The build system now generates two output files (main.js and dist/main.js) for better compatibility with Obsidian's plugin loading. Path handling has been centralized in new utilities to ensure cross-platform behavior, replacing shell commands that had Windows compatibility issues.
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
import { runCommand } from "./process";
|
|
import { readdir } from "fs/promises";
|
|
import { join } from "path";
|
|
|
|
/**
|
|
* Extract a .rm file (which is a zip archive) to a temporary directory.
|
|
* Returns the path to the extraction directory.
|
|
*/
|
|
export async function extractRmFile(rmPath: string, outputDir: string): Promise<string> {
|
|
// .rm files are zip archives
|
|
const { code, stderr } = await runCommand("unzip", ["-o", rmPath, "-d", outputDir]);
|
|
if (code !== 0) {
|
|
throw new Error(`Failed to extract .rm file: ${stderr}`);
|
|
}
|
|
return outputDir;
|
|
}
|
|
|
|
/**
|
|
* List the contents of the extracted .rm archive.
|
|
*/
|
|
export async function listRmContents(extractedDir: string): Promise<string[]> {
|
|
const files: string[] = [];
|
|
|
|
async function walk(dir: string): Promise<void> {
|
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
for (const entry of entries) {
|
|
const entryPath = join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
await walk(entryPath);
|
|
} else if (entry.isFile()) {
|
|
files.push(entryPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
await walk(extractedDir);
|
|
return files;
|
|
}
|
|
|
|
/**
|
|
* Check if a document is a notebook (handwritten) or a PDF/ebook.
|
|
* On reMarkable v3.x, notebooks have .rm page files and no content.pdf.
|
|
* Falls back to rendering a test page if ambiguous.
|
|
*/
|
|
export async function isNotebook(extractedDir: string): Promise<boolean> {
|
|
const contents = await listRmContents(extractedDir);
|
|
const hasRmPages = contents.some((f) => f.endsWith(".rm") && !f.endsWith(".zip"));
|
|
const hasContentPdf = contents.some((f) => f.endsWith("content.pdf") || f.endsWith(".pdf"));
|
|
// PDF-with-annotations documents have both; treat them as non-notebooks since the PDF is the source
|
|
return hasRmPages && !hasContentPdf;
|
|
}
|
|
|
|
/**
|
|
* Get the list of page .rm files from an extracted notebook.
|
|
* Returns basenames only.
|
|
* Recursively searches for .rm files (not just root).
|
|
*/
|
|
export async function getPageFiles(extractedDir: string): Promise<string[]> {
|
|
const contents = await listRmContents(extractedDir);
|
|
return contents.filter((f) => f.endsWith(".rm") && !f.endsWith(".zip")).sort();
|
|
}
|