diff --git a/README.md b/README.md index 34664689b..0fc8e562b 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,17 @@ pnpm install pnpm edit-docs:edit-docs ``` +Alternatively, if you have Nix installed: +```shell +# Run directly +nix run .#edit-docs + +# Or install to your profile +nix profile install .#edit-docs +trilium-edit-docs +``` + + ### Building the Executable Download the repository, install dependencies using `pnpm` and then build the desktop app for Windows: ```shell diff --git a/apps/edit-docs/package.json b/apps/edit-docs/package.json index 31d7e51ec..2282522f7 100644 --- a/apps/edit-docs/package.json +++ b/apps/edit-docs/package.json @@ -16,6 +16,8 @@ "fs-extra": "11.3.3" }, "scripts": { + "build": "tsx scripts/build.ts", + "test-build": "vitest --config vitest.build.config.mts", "edit-docs": "cross-env TRILIUM_PORT=37741 TRILIUM_DATA_DIR=data TRILIUM_INTEGRATION_TEST=memory-no-store tsx ../../scripts/electron-start.mts src/edit-docs.ts", "edit-demo": "cross-env TRILIUM_PORT=37744 TRILIUM_DATA_DIR=data TRILIUM_INTEGRATION_TEST=memory-no-store DOCS_ROOT=../../../docs USER_GUIDE_ROOT=\"../../server/src/assets/doc_notes/en/User Guide\" tsx ../../scripts/electron-start.mts src/edit-demo.ts" } diff --git a/apps/edit-docs/scripts/build.ts b/apps/edit-docs/scripts/build.ts new file mode 100644 index 000000000..251d96f7b --- /dev/null +++ b/apps/edit-docs/scripts/build.ts @@ -0,0 +1,40 @@ +import { writeFileSync } from "fs"; +import { join } from "path"; + +import BuildHelper from "../../../scripts/build-utils"; +import originalPackageJson from "../package.json" with { type: "json" }; + +const build = new BuildHelper("apps/edit-docs"); + +async function main() { + await build.buildBackend(["src/edit-docs.ts", "src/utils.ts"]); + + // Copy assets from server (needed for DB initialization) + build.copy("/apps/server/src/assets", "assets/"); + build.triggerBuildAndCopyTo("packages/share-theme", "share-theme/assets/"); + build.copy("/packages/share-theme/src/templates", "share-theme/templates/"); + build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css"); + build.buildFrontend(); + + // Copy node modules dependencies + build.copyNodeModules(["better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote"]); + + generatePackageJson(); +} + +function generatePackageJson() { + const { version, author, license, description, dependencies, devDependencies } = originalPackageJson; + const packageJson = { + name: "trilium-edit-docs", + main: "edit-docs.cjs", + version, + author, + license, + description, + dependencies: {"better-sqlite3": dependencies["better-sqlite3"]}, + devDependencies: {electron: devDependencies.electron}, + }; + writeFileSync(join(build.outDir, "package.json"), JSON.stringify(packageJson, null, "\t"), "utf-8"); +} + +main(); diff --git a/apps/edit-docs/spec/build-checks/artifacts.spec.ts b/apps/edit-docs/spec/build-checks/artifacts.spec.ts new file mode 100644 index 000000000..ae2e17196 --- /dev/null +++ b/apps/edit-docs/spec/build-checks/artifacts.spec.ts @@ -0,0 +1,48 @@ +import { globSync } from "fs"; +import { join } from "path"; +import { it, describe, expect } from "vitest"; + +describe("Check artifacts are present", () => { + const distPath = join(__dirname, "../../dist"); + + it("has the necessary node modules", async () => { + const paths = [ + "node_modules/better-sqlite3", + "node_modules/bindings", + "node_modules/file-uri-to-path", + "node_modules/@electron/remote" + ]; + + ensurePathsExist(paths); + }); + + it("includes the client", async () => { + const paths = [ + "public/assets", + "public/fonts", + "public/node_modules", + "public/src", + "public/stylesheets", + "public/translations" + ]; + + ensurePathsExist(paths); + }); + + it("includes necessary assets", async () => { + const paths = [ + "assets", + "share-theme", + "ckeditor5-content.css" + ]; + + ensurePathsExist(paths); + }); + + function ensurePathsExist(paths: string[]) { + for (const path of paths) { + const result = globSync(join(distPath, path, "**")); + expect(result, path).not.toHaveLength(0); + } + } +}); diff --git a/apps/edit-docs/src/edit-docs.ts b/apps/edit-docs/src/edit-docs.ts index c15a2f086..8cb6b898d 100644 --- a/apps/edit-docs/src/edit-docs.ts +++ b/apps/edit-docs/src/edit-docs.ts @@ -36,6 +36,10 @@ function parseArgs() { for (let i = 0; i < args.length; i++) { if (args[i] === '--config' || args[i] === '-c') { configPath = args[i + 1]; + if (!configPath) { + console.error("Error: --config/-c requires a path argument"); + process.exit(1); + } i++; // Skip the next argument as it's the value } else if (args[i] === '--help' || args[i] === '-h') { showHelp = true; @@ -86,9 +90,15 @@ let NOTE_MAPPINGS: NoteMapping[]; // Load configuration from edit-docs-config.yaml async function loadConfig() { - const CONFIG_PATH = configPath + let CONFIG_PATH = configPath ? path.resolve(configPath) - : path.join(__dirname, "../../../edit-docs-config.yaml"); + : path.join(process.cwd(), "edit-docs-config.yaml"); + + const exists = await fs.access(CONFIG_PATH).then(() => true).catch(() => false); + if (!exists && !configPath) { + // Fallback to project root if running from within a subproject + CONFIG_PATH = path.join(__dirname, "../../../edit-docs-config.yaml"); + } const configContent = await fs.readFile(CONFIG_PATH, "utf-8"); const config = yaml.load(configContent) as Config; @@ -106,12 +116,18 @@ async function main() { await loadConfig(); const initializedPromise = startElectron(() => { // Wait for the import to be finished and the application to be loaded before we listen to changes. - setTimeout(() => registerHandlers(), 10_000); + setTimeout(() => { + registerHandlers(); + }, 10_000); }); await initializeTranslations(); await initializeDatabase(true); + // Wait for becca to be loaded before importing data + const beccaLoader = await import("@triliumnext/server/src/becca/becca_loader.js"); + await beccaLoader.beccaLoaded; + cls.init(async () => { for (const mapping of NOTE_MAPPINGS) { if (!mapping.exportOnly) { @@ -248,7 +264,6 @@ async function registerHandlers() { return; } - console.log("Got entity changed", e.entityName, e.entity.title); debouncer(); }); } diff --git a/apps/edit-docs/src/utils.ts b/apps/edit-docs/src/utils.ts index 84e62ba39..3b41a70fd 100644 --- a/apps/edit-docs/src/utils.ts +++ b/apps/edit-docs/src/utils.ts @@ -7,18 +7,21 @@ import type { WriteStream } from "fs"; import fs from "fs/promises"; import fsExtra from "fs-extra"; import path from "path"; -import { resolve } from "path"; -import { deferred, DeferredPromise } from "../../../packages/commons/src"; +import { deferred, type DeferredPromise } from "../../../packages/commons/src/index.js"; -export function initializeDatabase(skipDemoDb: boolean) { - return new Promise(async (resolve) => { - const sqlInit = (await import("@triliumnext/server/src/services/sql_init.js")).default; - cls.init(async () => { - if (!sqlInit.isDbInitialized()) { - await sqlInit.createInitialDatabase(skipDemoDb); - } - resolve(); +export function initializeDatabase(skipDemoDb: boolean): Promise { + return new Promise((resolve) => { + import("@triliumnext/server/src/services/sql_init.js").then((m) => { + const sqlInit = m.default; + cls.init(async () => { + if (!sqlInit.isDbInitialized()) { + sqlInit.createInitialDatabase(skipDemoDb).then(() => resolve()); + } else { + sqlInit.dbReady.resolve(); + resolve(); + } + }); }); }); } @@ -78,7 +81,6 @@ async function createImportZip(path: string) { zlib: { level: 0 } }); - console.log("Archive path is ", resolve(path)); archive.directory(path, "/"); const outputStream = fsExtra.createWriteStream(inputFile); @@ -93,9 +95,11 @@ async function createImportZip(path: string) { } function waitForEnd(archive: Archiver, stream: WriteStream) { - return new Promise(async (res, rej) => { - stream.on("finish", () => res()); - await archive.finalize(); + return new Promise((res, rej) => { + stream.on("finish", res); + stream.on("error", rej); + archive.on("error", rej); + archive.finalize().catch(rej); }); } diff --git a/apps/edit-docs/vitest.build.config.mts b/apps/edit-docs/vitest.build.config.mts new file mode 100644 index 000000000..9dff36fc0 --- /dev/null +++ b/apps/edit-docs/vitest.build.config.mts @@ -0,0 +1,17 @@ +/// +import { defineConfig } from 'vite'; + +export default defineConfig(() => ({ + root: __dirname, + cacheDir: '../../node_modules/.vite/apps/edit-docs', + plugins: [], + test: { + watch: false, + globals: true, + environment: "node", + include: ['spec/build-checks/**'], + reporters: [ + "verbose" + ] + }, +})); diff --git a/flake.nix b/flake.nix index 72cc8946b..ed1548b9e 100644 --- a/flake.nix +++ b/flake.nix @@ -112,7 +112,7 @@ nodejs.python removeReferencesTo ] - ++ lib.optionals (app == "desktop") [ + ++ lib.optionals (app == "desktop" || app == "edit-docs") [ copyDesktopItems # required for NIXOS_OZONE_WL expansion # https://github.com/NixOS/nixpkgs/issues/172583 @@ -252,10 +252,33 @@ --add-flags $out/opt/trilium-server/main.cjs ''; }; + + edit-docs = makeApp { + app = "edit-docs"; + preBuildCommands = '' + export npm_config_nodedir=${electron.headers} + pnpm postinstall + ''; + buildTask = "edit-docs:build"; + mainProgram = "trilium-edit-docs"; + installCommands = '' + #remove-references-to -t ${electron.headers} apps/edit-docs/dist/node_modules/better-sqlite3/build/config.gypi + #remove-references-to -t ${nodejs.python} apps/edit-docs/dist/node_modules/better-sqlite3/build/config.gypi + + mkdir -p $out/{bin,opt/trilium-edit-docs} + cp --archive apps/edit-docs/dist/* $out/opt/trilium-edit-docs + makeShellWrapper ${lib.getExe electron} $out/bin/trilium-edit-docs \ + --set-default ELECTRON_IS_DEV 0 \ + --set TRILIUM_RESOURCE_DIR $out/opt/trilium-edit-docs \ + --add-flags $out/opt/trilium-edit-docs/edit-docs.cjs + ''; + }; + in { packages.desktop = desktop; packages.server = server; + packages.edit-docs = edit-docs; packages.default = desktop; diff --git a/package.json b/package.json index 95b5c1345..a6cc47c5f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "desktop:start": "pnpm run --filter desktop dev", "desktop:build": "pnpm run --filter desktop build", "desktop:start-prod": "pnpm run --filter desktop start-prod", + "edit-docs:edit-docs": "pnpm run --filter edit-docs edit-docs", + "edit-docs:build": "pnpm run --filter edit-docs build", "website:start": "pnpm run --filter website dev", "website:build": "pnpm run --filter website build", "electron:build": "pnpm desktop:build", @@ -28,7 +30,6 @@ "chore:update-version": "tsx ./scripts/update-version.ts", "docs:build": "pnpm run --filter build-docs start", "docs:preview": "pnpm http-server site -p 9000", - "edit-docs:edit-docs": "pnpm run --filter edit-docs edit-docs", "edit-docs:edit-demo": "pnpm run --filter edit-docs edit-demo", "test:all": "pnpm test:parallel && pnpm test:sequential", "test:parallel": "pnpm --filter=!server --filter=!ckeditor5-mermaid --filter=!ckeditor5-math --parallel test",