Merge branch 'develop' into renovate/electron-36.x

This commit is contained in:
Elian Doran 2025-05-25 21:13:29 +03:00 committed by GitHub
commit 13a8c6c488
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 824 additions and 1752 deletions

View File

@ -1,2 +1,7 @@
_regroup _regroup
_regroup_monorepo _regroup_monorepo
# Asset copying respects .gitignore / .nxignore for some reason.
# See https://github.com/nrwl/nx/issues/20309
!dist
!node_modules

View File

@ -24,5 +24,7 @@
}, },
"github-actions.workflows.pinned.workflows": [ "github-actions.workflows.pinned.workflows": [
".github/workflows/nightly.yml" ".github/workflows/nightly.yml"
] ],
"typescript.validate.enable": true,
"typescript.tsserver.experimental.enableProjectDiagnostics": true
} }

View File

@ -36,7 +36,7 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.52.0", "@playwright/test": "1.52.0",
"@stylistic/eslint-plugin": "4.2.0", "@stylistic/eslint-plugin": "4.4.0",
"@types/express": "5.0.1", "@types/express": "5.0.1",
"@types/node": "22.15.21", "@types/node": "22.15.21",
"@types/yargs": "17.0.33", "@types/yargs": "17.0.33",

View File

@ -1010,9 +1010,10 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
font-size: var(--detail-font-size) !important; font-size: var(--detail-font-size) !important;
} }
.ck-mentions .ck-button.ck-on { .ck-mentions {
background-color: var(--active-item-background-color) !important; --ck-color-list-button-on-background: var(--active-item-background-color);
color: var(--active-item-text-color) !important; --ck-color-list-button-on-background-focus: var(--ck-color-list-button-on-background);
--ck-color-list-button-on-text: var(--active-item-text-color);
} }
.ck-mentions .ck-button b { .ck-mentions .ck-button b {

View File

@ -5,6 +5,12 @@
* Color scheme * Color scheme
*/ */
:root { :root {
/*
* NOTICE: This theme is currently in the beta stage of development.
* The names and purposes of these CSS variables are subject to frequent changes.
*/
--theme-style: dark; --theme-style: dark;
--native-titlebar-background: #00000000; --native-titlebar-background: #00000000;

View File

@ -5,6 +5,12 @@
* Color scheme * Color scheme
*/ */
:root { :root {
/*
* NOTICE: This theme is currently in the beta stage of development.
* The names and purposes of these CSS variables are subject to frequent changes.
*/
--theme-style: light; --theme-style: light;
--native-titlebar-background: #ffffff00; --native-titlebar-background: #ffffff00;

View File

@ -79,7 +79,7 @@ button.btn.btn-success kbd {
*/ */
:root .icon-action:not(.global-menu-button), :root .icon-action:not(.global-menu-button),
:root .tn-tool-button, :root .btn.tn-tool-button,
:root .btn-group .tn-tool-button:not(:last-child), :root .btn-group .tn-tool-button:not(:last-child),
:root .btn-group .tn-tool-button:last-child { :root .btn-group .tn-tool-button:last-child {
width: var(--icon-button-size); width: var(--icon-button-size);

View File

@ -373,6 +373,20 @@
background: var(--hover-item-background-color); background: var(--hover-item-background-color);
} }
/* Mention list (the autocompletion list for emojis, labels and relations) */
:root .ck-mentions {
--ck-color-list-button-on-background: var(--hover-item-background-color);
--ck-color-list-button-on-text: var(--hover-item-text-color);
--ck-color-list-button-hover-background: var(--ck-editor-toolbar-dropdown-button-open-background);
--ck-focus-ring: 1px solid transparent;
}
/* "Keep on typing to see the emoji" placeholder */
#mention-list-item-id\:__EMOJI_HINT {
background: transparent;
}
/* /*
* EDITOR'S CONTENT * EDITOR'S CONTENT
*/ */

View File

@ -110,6 +110,8 @@ export default class NoteTypeChooserDialog extends BasicWidget {
//@ts-ignore //@ts-ignore
if (e.clickEvent) { if (e.clickEvent) {
e.preventDefault(); e.preventDefault();
} else {
this.modal.hide();
} }
}); });
} }

View File

@ -5,7 +5,7 @@
"description": "Tool to compare content of Trilium databases. Useful for debugging sync problems.", "description": "Tool to compare content of Trilium databases. Useful for debugging sync problems.",
"dependencies": { "dependencies": {
"colors": "1.4.0", "colors": "1.4.0",
"diff": "8.0.1", "diff": "8.0.2",
"sqlite": "5.1.1", "sqlite": "5.1.1",
"sqlite3": "5.1.7" "sqlite3": "5.1.7"
}, },

View File

@ -51,8 +51,8 @@ async function getMap(db: Database, query: string, params: any[] = []) {
return map; return map;
} }
async function getFlattenedResults(db: Database, key: string, query: string, params: any[] = []) { async function getFlattenedResults<T>(db: Database, key: string, query: string, params: any[] = []) {
const list = []; const list: T[] = [];
const result = await getResults(db, query, params); const result = await getResults(db, query, params);
for (const row of result) { for (const row of result) {

View File

@ -1,8 +0,0 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2016"
}
}

View File

@ -68,8 +68,8 @@ module.exports = {
] ]
}, },
rebuildConfig: { rebuildConfig: {
force: true, force: false,
extraModules: [ "better-sqlite3" ] onlyModules: []
}, },
makers: [ makers: [
{ {

View File

@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"description": "Build your personal knowledge base with TriliumNext Notes", "description": "Build your personal knowledge base with TriliumNext Notes",
"private": true, "private": true,
"main": "main.js", "main": "main.cjs",
"dependencies": { "dependencies": {
"@electron/remote": "2.1.2", "@electron/remote": "2.1.2",
"better-sqlite3": "^11.9.1", "better-sqlite3": "^11.9.1",
@ -29,7 +29,7 @@
"prebuild-install": "^7.1.1" "prebuild-install": "^7.1.1"
}, },
"config": { "config": {
"forge": "../electron-forge/forge.config.cjs" "forge": "./electron-forge/forge.config.cjs"
}, },
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977", "packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977",
"scripts": { "scripts": {
@ -44,6 +44,69 @@
"nx": { "nx": {
"name": "desktop", "name": "desktop",
"targets": { "targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"options": {
"main": "apps/desktop/src/electron-main.ts",
"outputPath": "apps/desktop/dist",
"outputFileName": "main.js",
"tsConfig": "apps/desktop/tsconfig.app.json",
"platform": "node",
"external": [
"electron",
"@electron/remote",
"better-sqlite3",
"./xhr-sync-worker.js"
],
"format": [
"cjs"
],
"minify": true,
"thirdParty": true,
"esbuildOptions": {
"splitting": false,
"loader": {
".css": "text"
}
},
"assets": [
{
"glob": "**/*",
"input": "apps/server/dist/node_modules",
"output": "node_modules"
},
{
"glob": "**/*",
"input": "apps/desktop/node_modules/@electron/remote",
"output": "node_modules/@electron/remote"
},
{
"glob": "**/*",
"input": "apps/server/dist/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "apps/desktop/src/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "apps/server/dist/public",
"output": "public"
},
{
"glob": "xhr-sync-worker.js",
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
"output": ""
}
],
"declarationRootDir": "apps/desktop/src"
}
},
"rebuild-deps": { "rebuild-deps": {
"executor": "nx:run-commands", "executor": "nx:run-commands",
"dependsOn": [ "dependsOn": [
@ -68,11 +131,11 @@
"defaultConfiguration": "default", "defaultConfiguration": "default",
"configurations": { "configurations": {
"default": { "default": {
"command": "electron .", "command": "electron main.cjs",
"cwd": "{projectRoot}/dist" "cwd": "{projectRoot}/dist"
}, },
"nixos": { "nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.js\"", "command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
"cwd": ".", "cwd": ".",
"forwardAllArgs": false "forwardAllArgs": false
} }
@ -86,11 +149,11 @@
"defaultConfiguration": "default", "defaultConfiguration": "default",
"configurations": { "configurations": {
"default": { "default": {
"command": "electron .", "command": "electron main.cjs",
"cwd": "{projectRoot}/dist" "cwd": "{projectRoot}/dist"
}, },
"nixos": { "nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.js\"", "command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
"cwd": ".", "cwd": ".",
"forwardAllArgs": false "forwardAllArgs": false
} }
@ -98,16 +161,25 @@
}, },
"electron-forge:make": { "electron-forge:make": {
"dependsOn": [ "dependsOn": [
"build" "build",
"rebuild-deps"
], ],
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge make dist" "command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge make dist"
}, },
"electron-forge:package": { "electron-forge:package": {
"dependsOn": [ "dependsOn": [
"build" "build",
"rebuild-deps"
], ],
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge package dist" "command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm electron-forge package dist"
},
"electron-forge:start": {
"dependsOn": [
"build",
"rebuild-deps"
],
"command": "pnpm -C apps/desktop exec cross-env NODE_INSTALLER=npm TRILIUM_DATA_DIR=./data electron-forge start dist"
} }
} }
} }
} }

View File

@ -44,7 +44,7 @@ async function main() {
await import("@triliumnext/server/src/main.js"); await import("@triliumnext/server/src/main.js");
} }
export async function onReady() { async function onReady() {
// electron.app.setAppUserModelId('com.github.zadam.trilium'); // electron.app.setAppUserModelId('com.github.zadam.trilium');
// if db is not initialized -> setup process // if db is not initialized -> setup process

View File

@ -1,17 +1,21 @@
{ {
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2020",
"outDir": "dist", "outDir": "dist",
"strict": false,
"types": [ "types": [
"node", "node",
"express" "express"
], ],
"rootDir": "src", "rootDir": "src",
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
"verbatimModuleSyntax": false
}, },
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts",
"../server/src/*.d.ts"
], ],
"exclude": [ "exclude": [
"eslint.config.js", "eslint.config.js",

View File

@ -1,67 +0,0 @@
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { join } = require('path');
const outputDir = join(__dirname, 'dist');
module.exports = {
output: {
path: outputDir,
},
module: {
rules: [
{
test: /\.css$/i,
type: "asset/source"
}
]
},
target: [ "node" ],
plugins: [
new NxAppWebpackPlugin({
target: 'node',
compiler: 'tsc',
main: './src/electron-main.ts',
tsConfig: './tsconfig.app.json',
assets: ["./src/assets"],
optimization: false,
outputHashing: 'none',
generatePackageJson: false,
externalDependencies: [
"electron/main",
"@electron/remote/main",
"electron",
"@electron/remote",
"better-sqlite3"
]
}),
new CopyPlugin({
patterns: [
{
from: "../client/dist",
to: join(outputDir, "public")
},
{
from: "../server/dist/node_modules",
to: join(outputDir, "node_modules")
},
{
from: "../server/dist/assets",
to: join(outputDir, "assets")
},
{
from: "node_modules/@electron/remote",
to: join(outputDir, "node_modules/@electron/remote")
},
{
from: "node_modules/prebuild-install",
to: join(outputDir, "node_modules/better-sqlite3/node_modules/prebuild-install")
},
{
from: "package.json",
to: join(outputDir, "package.json")
}
]
})
]
};

View File

@ -3,6 +3,10 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"description": "Desktop version of Trilium which imports the demo database (presented to new users at start-up) or the user guide and other documentation and saves the modifications for committing.", "description": "Desktop version of Trilium which imports the demo database (presented to new users at start-up) or the user guide and other documentation and saves the modifications for committing.",
"dependencies": {
"archiver": "7.0.1",
"better-sqlite3": "^11.9.1"
},
"devDependencies": { "devDependencies": {
"@triliumnext/client": "workspace:*", "@triliumnext/client": "workspace:*",
"@triliumnext/desktop": "workspace:*", "@triliumnext/desktop": "workspace:*",
@ -17,6 +21,59 @@
"server" "server"
], ],
"targets": { "targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"options": {
"main": "apps/edit-docs/src/electron-docs-main.ts",
"outputPath": "apps/edit-docs/dist",
"outputFileName": "main.js",
"tsConfig": "apps/edit-docs/tsconfig.app.json",
"platform": "node",
"external": [
"electron",
"@electron/remote",
"better-sqlite3",
"./xhr-sync-worker.js"
],
"format": [
"cjs"
],
"minify": true,
"thirdParty": true,
"esbuildOptions": {
"splitting": false,
"loader": {
".css": "text"
}
},
"assets": [
{
"glob": "**/*",
"input": "apps/server/dist/node_modules",
"output": "node_modules"
},
{
"glob": "**/*",
"input": "apps/server/dist/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "apps/server/dist/public",
"output": "public"
},
{
"glob": "xhr-sync-worker.js",
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
"output": ""
}
],
"declarationRootDir": "apps/edit-docs/src"
}
},
"rebuild-deps": { "rebuild-deps": {
"executor": "nx:run-commands", "executor": "nx:run-commands",
"dependsOn": [ "dependsOn": [
@ -41,19 +98,34 @@
"defaultConfiguration": "default", "defaultConfiguration": "default",
"configurations": { "configurations": {
"default": { "default": {
"command": "electron .", "command": "electron main.cjs",
"cwd": "./apps/edit-docs/dist" "cwd": "{projectRoot}/dist"
}, },
"nixos": { "nixos": {
"command": "nix-shell -p electron_35 --run \"electron .\"", "command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
"forwardAllArgs": false, "cwd": ".",
"cwd": "./apps/edit-docs/dist" "forwardAllArgs": false
}
}
},
"serve-nodir": {
"executor": "nx:run-commands",
"dependsOn": [
"rebuild-deps"
],
"defaultConfiguration": "default",
"configurations": {
"default": {
"command": "electron main.cjs",
"cwd": "{projectRoot}/dist"
},
"nixos": {
"command": "nix-shell -p electron_35 --run \"electron {projectRoot}/dist/main.cjs\"",
"cwd": ".",
"forwardAllArgs": false
} }
} }
} }
} }
}, }
"dependencies": { }
"archiver": "7.0.1"
}
}

View File

@ -13,8 +13,9 @@ import TaskContext from "@triliumnext/server/src/services/task_context.js";
import { deferred } from "@triliumnext/server/src/services/utils.js"; import { deferred } from "@triliumnext/server/src/services/utils.js";
import { parseNoteMetaFile } from "@triliumnext/server/src/services/in_app_help.js"; import { parseNoteMetaFile } from "@triliumnext/server/src/services/in_app_help.js";
import { resolve } from "path"; import { resolve } from "path";
import type NoteMeta from "@triliumnext/server/src/services/meta/note_meta.js";
import electron from "electron"; import electron from "electron";
import { onReady } from "@triliumnext/desktop/src/electron-main.js"; import windowService from "@triliumnext/server/src/services/window.js";
interface NoteMapping { interface NoteMapping {
rootNoteId: string; rootNoteId: string;
@ -55,12 +56,25 @@ const NOTE_MAPPINGS: NoteMapping[] = [
]; ];
async function main() { async function main() {
electron.app.on("ready", onReady); const initializedPromise = deferred<void>();
electron.app.on("ready", async () => {
await initializedPromise;
console.log("Electron is ready!");
// Start the server.
await import("@triliumnext/server/src/main.js");
// Create the main window.
await windowService.createMainWindow(electron.app);
// Wait for the import to be finished and the application to be loaded before we listen to changes.
setTimeout(() => registerHandlers(), 10_000);
});
await initializeTranslations(); await initializeTranslations();
await initializeDatabase(true); await initializeDatabase(true);
const initializedPromise = deferred<void>();
cls.init(async () => { cls.init(async () => {
for (const mapping of NOTE_MAPPINGS) { for (const mapping of NOTE_MAPPINGS) {
if (!mapping.exportOnly) { if (!mapping.exportOnly) {
@ -70,11 +84,6 @@ async function main() {
setOptions(); setOptions();
initializedPromise.resolve(); initializedPromise.resolve();
}); });
await initializedPromise;
// Wait for the import to be finished and the application to be loaded before we listen to changes.
setTimeout(() => registerHandlers(), 10_000);
} }
async function setOptions() { async function setOptions() {

View File

@ -40,4 +40,4 @@ async function exportData() {
await exportToZipFile("root", "html", DEMO_ZIP_PATH); await exportToZipFile("root", "html", DEMO_ZIP_PATH);
} }
await main(); main();

View File

@ -1,16 +1,21 @@
{ {
"extends": "../../tsconfig.base.json", "extends": "../../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2020",
"outDir": "dist", "outDir": "dist",
"strict": false,
"types": [ "types": [
"node" "node",
"express"
], ],
"rootDir": "src", "rootDir": "src",
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
"verbatimModuleSyntax": false
}, },
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts",
"../server/src/*.d.ts"
], ],
"exclude": [ "exclude": [
"eslint.config.js", "eslint.config.js",

View File

@ -1,53 +0,0 @@
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { join } = require('path');
const outputDir = join(__dirname, 'dist');
module.exports = {
output: {
path: join(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
type: "asset/source"
}
]
},
plugins: [
new NxAppWebpackPlugin({
target: 'node',
compiler: 'tsc',
main: './src/electron-docs-main.ts',
tsConfig: './tsconfig.app.json',
optimization: false,
outputHashing: 'none',
generatePackageJson: true,
externalDependencies: [
"electron/main",
"@electron/remote/main",
"electron",
"@electron/remote",
"better-sqlite3"
]
}),
new CopyPlugin({
patterns: [
{
from: "../desktop/dist/node_modules",
to: join(outputDir, "node_modules")
},
{
from: "../desktop/dist/assets",
to: join(outputDir, "assets")
},
{
from: "../desktop/dist/public",
to: join(outputDir, "public")
},
]
})
],
};

View File

@ -26,7 +26,8 @@ export default defineConfig({
command: 'pnpm server:start-prod', command: 'pnpm server:start-prod',
url: baseURL, url: baseURL,
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
cwd: workspaceRoot cwd: workspaceRoot,
timeout: 5 * 60 * 1000
} : undefined, } : undefined,
projects: [ projects: [
{ {

View File

@ -1,3 +1,4 @@
TRILIUM_ENV=dev TRILIUM_ENV=dev
TRILIUM_DATA_DIR=./apps/server/data TRILIUM_DATA_DIR=./apps/server/data
TRILIUM_RESOURCE_DIR=./apps/server/dist
TRILIUM_PUBLIC_SERVER=http://localhost:4200 TRILIUM_PUBLIC_SERVER=http://localhost:4200

View File

@ -3,7 +3,7 @@ FROM node:22.16.0-bullseye-slim AS builder
# Install native dependencies since we might be building cross-platform. # Install native dependencies since we might be building cross-platform.
WORKDIR /usr/src/app/build WORKDIR /usr/src/app/build
COPY ./dist/package.json ./dist/pnpm-lock.yaml ./docker/pnpm-workspace.yaml /usr/src/app/ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches # We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
@ -25,4 +25,4 @@ FROM node:22.16.0-bullseye-slim
# Configure container # Configure container
EXPOSE 8080 EXPOSE 8080
CMD [ "sh", "./start-docker.sh" ] CMD [ "sh", "./start-docker.sh" ]
HEALTHCHECK --start-period=10s CMD exec gosu node node /usr/src/app/docker_healthcheck.js HEALTHCHECK --start-period=10s CMD exec gosu node node /usr/src/app/docker_healthcheck.cjs

View File

@ -3,7 +3,7 @@ FROM node:22.16.0-alpine AS builder
# Install native dependencies since we might be building cross-platform. # Install native dependencies since we might be building cross-platform.
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY ./dist/package.json ./dist/pnpm-lock.yaml ./docker/pnpm-workspace.yaml /usr/src/app/ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches # We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
@ -23,4 +23,4 @@ FROM node:22.16.0-alpine
# Configure container # Configure container
EXPOSE 8080 EXPOSE 8080
CMD [ "sh", "./start-docker.sh" ] CMD [ "sh", "./start-docker.sh" ]
HEALTHCHECK --start-period=10s CMD exec su-exec node node /usr/src/app/docker_healthcheck.js HEALTHCHECK --start-period=10s CMD exec su-exec node node /usr/src/app/docker_healthcheck.cjs

View File

@ -0,0 +1,5 @@
{
"dependencies": {
"better-sqlite3": "11.10.0"
}
}

View File

@ -39,7 +39,7 @@
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@types/xml2js": "0.4.14", "@types/xml2js": "0.4.14",
"express-http-proxy": "2.1.1", "express-http-proxy": "2.1.1",
"@anthropic-ai/sdk": "0.51.0", "@anthropic-ai/sdk": "0.52.0",
"@braintree/sanitize-url": "7.1.1", "@braintree/sanitize-url": "7.1.1",
"@triliumnext/commons": "workspace:*", "@triliumnext/commons": "workspace:*",
"@triliumnext/express-partial-content": "workspace:*", "@triliumnext/express-partial-content": "workspace:*",
@ -88,7 +88,7 @@
"multer": "2.0.0", "multer": "2.0.0",
"normalize-strings": "1.1.1", "normalize-strings": "1.1.1",
"ollama": "0.5.15", "ollama": "0.5.15",
"openai": "4.102.0", "openai": "4.103.0",
"rand-token": "1.0.1", "rand-token": "1.0.1",
"safe-compare": "1.1.4", "safe-compare": "1.1.4",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
@ -105,38 +105,21 @@
"tmp": "0.2.3", "tmp": "0.2.3",
"turndown": "7.2.0", "turndown": "7.2.0",
"unescape": "1.0.1", "unescape": "1.0.1",
"webpack": "5.99.9",
"ws": "8.18.2", "ws": "8.18.2",
"xml2js": "0.6.2", "xml2js": "0.6.2",
"yauzl": "3.2.0", "yauzl": "3.2.0"
"copy-webpack-plugin": "13.0.0"
}, },
"nx": { "nx": {
"name": "server", "name": "server",
"targets": { "targets": {
"build": {
"dependsOn": [
"^build",
"client:build"
]
},
"serve": { "serve": {
"executor": "@nx/js:node", "executor": "@nx/js:node",
"defaultConfiguration": "development",
"dependsOn": [ "dependsOn": [
"build" "build-without-client"
], ],
"options": { "options": {
"buildTarget": "server:build", "buildTarget": "server:build-without-client:development",
"runBuildTargetDependencies": false "runBuildTargetDependencies": false
},
"configurations": {
"development": {
"buildTarget": "server:build:development"
},
"production": {
"buildTarget": "server:build:production"
}
} }
}, },
"package": { "package": {
@ -149,7 +132,7 @@
"dependsOn": [ "dependsOn": [
"build" "build"
], ],
"command": "node apps/server/dist/main.js" "command": "node apps/server/dist/main.cjs"
}, },
"docker-build": { "docker-build": {
"dependsOn": [ "dependsOn": [
@ -183,7 +166,118 @@
"command": "docker run -p 8081:8080 triliumnext-alpine" "command": "docker run -p 8081:8080 triliumnext-alpine"
} }
} }
},
"build-without-client": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"options": {
"main": "apps/server/src/main.ts",
"outputPath": "apps/server/dist",
"outputFileName": "main.js",
"tsConfig": "apps/server/tsconfig.app.json",
"platform": "node",
"format": [
"cjs"
],
"declarationRootDir": "apps/server/src"
},
"configurations": {
"development": {
"minify": false,
"assets": [
{
"glob": "**/*",
"input": "apps/server/src/assets",
"output": "assets"
}
]
}
}
},
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": [
"{options.outputPath}"
],
"dependsOn": [
"^build",
"client:build"
],
"options": {
"main": "apps/server/src/main.ts",
"outputPath": "apps/server/dist",
"tsConfig": "apps/server/tsconfig.app.json",
"platform": "node",
"external": [
"electron",
"@electron/remote",
"better-sqlite3",
"./xhr-sync-worker.js"
],
"format": [
"cjs"
],
"declarationRootDir": "apps/server/src",
"minify": true,
"thirdParty": true,
"esbuildOptions": {
"splitting": false,
"loader": {
".css": "text"
}
},
"additionalEntryPoints": [
"apps/server/src/docker_healthcheck.ts"
],
"assets": [
{
"glob": "**/*",
"input": "apps/server/src/assets",
"output": "assets"
},
{
"glob": "**/*",
"input": "apps/client/dist",
"output": "public"
},
{
"glob": "**/*",
"input": "apps/server/node_modules/better-sqlite3",
"output": "node_modules/better-sqlite3"
},
{
"glob": "**/*",
"input": "apps/server/node_modules/bindings",
"output": "node_modules/bindings"
},
{
"glob": "**/*",
"input": "apps/server/node_modules/file-uri-to-path",
"output": "node_modules/file-uri-to-path"
},
{
"glob": "xhr-sync-worker.js",
"input": "apps/server/node_modules/jsdom/lib/jsdom/living/xhr",
"output": ""
}
]
}
} }
} }
} },
"exports": {
"./package.json": "./package.json",
"./src/*": "./src/*",
".": {
"development": "./src/main.ts",
"types": "./dist/main.d.ts",
"import": "./dist/main.js",
"default": "./dist/main.js"
}
},
"types": "./dist/main.d.ts",
"module": "./dist/main.js",
"main": "./dist/main.js"
} }

View File

@ -137,7 +137,7 @@ export default async function buildApp() {
startScheduledCleanup(); startScheduledCleanup();
if (utils.isElectron) { if (utils.isElectron) {
(await import("@electron/remote/main")).initialize(); (await import("@electron/remote/main/index.js")).initialize();
} }
return app; return app;

View File

@ -21,7 +21,7 @@
<body> <body>
<div class="container login-page"> <div class="container login-page">
<div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto pt-4"> <div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto pt-4">
<img class="img-fluid d-block mx-auto" style="height: 8rem;" src="<%= assetPath %>/images/icon-color.svg" aria-hidden="true" draggable="false" > <img class="img-fluid d-block mx-auto" style="height: 8rem;" src="<%= assetPathFragment %>/images/icon-color.svg" aria-hidden="true" draggable="false" >
<h1 class="text-center"><%= t("login.heading") %></h1> <h1 class="text-center"><%= t("login.heading") %></h1>
<% if (ssoEnabled) { %> <% if (ssoEnabled) { %>

View File

@ -36,7 +36,7 @@ interface DateLimits {
maxDate: string; maxDate: string;
} }
interface SimilarNote { export interface SimilarNote {
score: number; score: number;
notePath: string[]; notePath: string[];
noteId: string; noteId: string;

View File

@ -6,7 +6,7 @@ import etapiTokenService from "../services/etapi_tokens.js";
import config from "../services/config.js"; import config from "../services/config.js";
import type { NextFunction, Request, RequestHandler, Response, Router } from "express"; import type { NextFunction, Request, RequestHandler, Response, Router } from "express";
import type { ValidatorMap } from "./etapi-interface.js"; import type { ValidatorMap } from "./etapi-interface.js";
import type { ApiRequestHandler } from "../routes/routes.js"; import type { ApiRequestHandler } from "../routes/route_api.js";
const GENERIC_CODE = "GENERIC"; const GENERIC_CODE = "GENERIC";
type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head"; type HttpMethod = "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head";

View File

@ -116,7 +116,7 @@ function getLinkMap(req: Request) {
}).notes; }).notes;
} }
const noteIds = new Set(unfilteredNotes.filter((note) => ignoreExcludeFromNoteMap || !note.isLabelTruthy("excludeFromNoteMap")).map((note) => note.noteId)); const noteIds = new Set<string>(unfilteredNotes.filter((note) => ignoreExcludeFromNoteMap || !note.isLabelTruthy("excludeFromNoteMap")).map((note) => note.noteId));
if (mapRootNote.type === "search") { if (mapRootNote.type === "search") {
noteIds.delete(mapRootNote.noteId); noteIds.delete(mapRootNote.noteId);

View File

@ -118,7 +118,7 @@ function createNote(req: Request) {
throw new ValidationError("Missing or incorrect type for target branch ID."); throw new ValidationError("Missing or incorrect type for target branch ID.");
} }
const { note, branch } = noteService.createNewNoteWithTarget(target, targetBranchId, params); const { note, branch } = noteService.createNewNoteWithTarget(target, String(targetBranchId), params);
return { return {
note, note,

View File

@ -1,10 +1,10 @@
import { assetUrlFragment } from "../services/asset_path.js"; import { assetUrlFragment } from "../services/asset_path.js";
import path from "path"; import path from "path";
import { fileURLToPath } from "url";
import express from "express"; import express from "express";
import { getResourceDir, isDev } from "../services/utils.js"; import { getResourceDir, isDev } from "../services/utils.js";
import type serveStatic from "serve-static"; import type serveStatic from "serve-static";
import proxy from "express-http-proxy"; import proxy from "express-http-proxy";
import { existsSync } from "fs";
const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<unknown, Record<string, unknown>>>) => { const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOptions<express.Response<unknown, Record<string, unknown>>>) => {
if (!isDev) { if (!isDev) {
@ -17,7 +17,7 @@ const persistentCacheStatic = (root: string, options?: serveStatic.ServeStaticOp
}; };
async function register(app: express.Application) { async function register(app: express.Application) {
const srcRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); const srcRoot = path.join(__dirname, "..");
const resourceDir = getResourceDir(); const resourceDir = getResourceDir();
if (isDev) { if (isDev) {
@ -29,14 +29,19 @@ async function register(app: express.Application) {
proxyReqPathResolver: (req) => "/" + assetUrlFragment + `/@fs` + req.url proxyReqPathResolver: (req) => "/" + assetUrlFragment + `/@fs` + req.url
})); }));
} else { } else {
app.use(`/${assetUrlFragment}/src`, persistentCacheStatic(path.join(resourceDir, "public", "src"))); const publicDir = path.join(resourceDir, "public");
app.use(`/${assetUrlFragment}/stylesheets`, persistentCacheStatic(path.join(resourceDir, "public", "stylesheets"))); if (!existsSync(publicDir)) {
app.use(`/${assetUrlFragment}/libraries`, persistentCacheStatic(path.join(resourceDir, "public", "libraries"))); throw new Error("Public directory is missing at: " + path.resolve(publicDir));
app.use(`/${assetUrlFragment}/fonts`, persistentCacheStatic(path.join(resourceDir, "public", "fonts"))); }
app.use(`/${assetUrlFragment}/translations/`, persistentCacheStatic(path.join(resourceDir, "public", "translations")));
app.use(`/${assetUrlFragment}/images`, persistentCacheStatic(path.join(resourceDir, "assets", "images"))); app.use(`/${assetUrlFragment}/src`, persistentCacheStatic(path.join(publicDir, "src")));
app.use(`/node_modules/`, persistentCacheStatic(path.join(resourceDir, "public/node_modules"))); app.use(`/${assetUrlFragment}/stylesheets`, persistentCacheStatic(path.join(publicDir, "stylesheets")));
app.use(`/${assetUrlFragment}/libraries`, persistentCacheStatic(path.join(publicDir, "libraries")));
app.use(`/${assetUrlFragment}/fonts`, persistentCacheStatic(path.join(publicDir, "fonts")));
app.use(`/${assetUrlFragment}/translations/`, persistentCacheStatic(path.join(publicDir, "translations")));
app.use(`/node_modules/`, persistentCacheStatic(path.join(publicDir, "node_modules")));
} }
app.use(`/${assetUrlFragment}/images`, persistentCacheStatic(path.join(resourceDir, "assets", "images")));
app.use(`/${assetUrlFragment}/doc_notes`, persistentCacheStatic(path.join(resourceDir, "assets", "doc_notes"))); app.use(`/${assetUrlFragment}/doc_notes`, persistentCacheStatic(path.join(resourceDir, "assets", "doc_notes")));
app.use(`/assets/vX/fonts`, express.static(path.join(srcRoot, "public/fonts"))); app.use(`/assets/vX/fonts`, express.static(path.join(srcRoot, "public/fonts")));
app.use(`/assets/vX/images`, express.static(path.join(srcRoot, "..", "images"))); app.use(`/assets/vX/images`, express.static(path.join(srcRoot, "..", "images")));

View File

@ -1,4 +1,4 @@
import { ipcMain } from "electron"; import electron from "electron";
interface Response { interface Response {
statusCode: number; statusCode: number;
@ -10,7 +10,7 @@ interface Response {
} }
function init(app: Express.Application) { function init(app: Express.Application) {
ipcMain.on("server-request", (event, arg) => { electron.ipcMain.on("server-request", (event, arg) => {
const req = { const req = {
url: arg.url, url: arg.url,
method: arg.method, method: arg.method,
@ -50,7 +50,7 @@ function init(app: Express.Application) {
} }
}; };
return app._router.handle(req, res, () => {}); return (app as any)._router.handle(req, res, () => {});
}); });
} }

View File

@ -3,7 +3,7 @@ import optionService from "../services/options.js";
import myScryptService from "../services/encryption/my_scrypt.js"; import myScryptService from "../services/encryption/my_scrypt.js";
import log from "../services/log.js"; import log from "../services/log.js";
import passwordService from "../services/encryption/password.js"; import passwordService from "../services/encryption/password.js";
import assetPath from "../services/asset_path.js"; import assetPath, { assetUrlFragment } from "../services/asset_path.js";
import appPath from "../services/app_path.js"; import appPath from "../services/app_path.js";
import ValidationError from "../errors/validation_error.js"; import ValidationError from "../errors/validation_error.js";
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
@ -13,12 +13,14 @@ import openID from '../services/open_id.js';
import openIDEncryption from '../services/encryption/open_id_encryption.js'; import openIDEncryption from '../services/encryption/open_id_encryption.js';
function loginPage(req: Request, res: Response) { function loginPage(req: Request, res: Response) {
// Login page is triggered twice. Once here, and another time if the password is failed.
res.render('login', { res.render('login', {
wrongPassword: false, wrongPassword: false,
wrongTotp: false, wrongTotp: false,
totpEnabled: totp.isTotpEnabled(), totpEnabled: totp.isTotpEnabled(),
ssoEnabled: openID.isOpenIDEnabled(), ssoEnabled: openID.isOpenIDEnabled(),
assetPath: assetPath, assetPath: assetPath,
assetPathFragment: assetUrlFragment,
appPath: appPath, appPath: appPath,
}); });
} }
@ -169,6 +171,7 @@ function sendLoginError(req: Request, res: Response, errorType: 'password' | 'to
totpEnabled: totp.isTotpEnabled(), totpEnabled: totp.isTotpEnabled(),
ssoEnabled: openID.isOpenIDEnabled(), ssoEnabled: openID.isOpenIDEnabled(),
assetPath: assetPath, assetPath: assetPath,
assetPathFragment: assetUrlFragment,
appPath: appPath, appPath: appPath,
}); });
} }

View File

@ -1,4 +1,4 @@
import express from "express"; import express, { type RequestHandler } from "express";
import multer from "multer"; import multer from "multer";
import log from "../services/log.js"; import log from "../services/log.js";
import cls from "../services/cls.js"; import cls from "../services/cls.js";
@ -166,7 +166,7 @@ function handleException(e: unknown | Error, method: HttpMethod, path: string, r
} }
export function createUploadMiddleware() { export function createUploadMiddleware(): RequestHandler {
const multerOptions: multer.Options = { const multerOptions: multer.Options = {
fileFilter: (req: express.Request, file, cb) => { fileFilter: (req: express.Request, file, cb) => {
// UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side. // UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side.

View File

@ -3,6 +3,7 @@ import session, { Store } from "express-session";
import sessionSecret from "../services/session_secret.js"; import sessionSecret from "../services/session_secret.js";
import config from "../services/config.js"; import config from "../services/config.js";
import log from "../services/log.js"; import log from "../services/log.js";
import type express from "express";
class SQLiteSessionStore extends Store { class SQLiteSessionStore extends Store {
@ -51,7 +52,7 @@ class SQLiteSessionStore extends Store {
} }
const sessionParser = session({ const sessionParser: express.RequestHandler = session({
secret: sessionSecret, secret: sessionSecret,
resave: false, // true forces the session to be saved back to the session store, even if the session was never modified during the request. resave: false, // true forces the session to be saved back to the session store, even if the session was never modified during the request.
saveUninitialized: false, // true forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified. saveUninitialized: false, // true forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified.

View File

@ -1,5 +1,5 @@
import BUILTIN_ATTRIBUTES from "./builtin_attributes.js"; import BUILTIN_ATTRIBUTES from "./builtin_attributes.js";
import fs from "fs-extra"; import fs from "fs";
import dataDir from "./data_dir.js"; import dataDir from "./data_dir.js";
import dateUtils from "./date_utils.js"; import dateUtils from "./date_utils.js";
import Database from "better-sqlite3"; import Database from "better-sqlite3";

View File

@ -224,14 +224,14 @@ export interface Api {
* @param date in YYYY-MM-DD format * @param date in YYYY-MM-DD format
* @param rootNote - specify calendar root note, normally leave empty to use the default calendar * @param rootNote - specify calendar root note, normally leave empty to use the default calendar
*/ */
getDayNote(date: string, rootNote?: BNote): Promise<BNote | null>; getDayNote(date: string, rootNote?: BNote): BNote | null;
/** /**
* Returns today's day note. If such note doesn't exist, it is created. * Returns today's day note. If such note doesn't exist, it is created.
* *
* @param rootNote specify calendar root note, normally leave empty to use the default calendar * @param rootNote specify calendar root note, normally leave empty to use the default calendar
*/ */
getTodayNote(rootNote?: BNote): Promise<BNote | null>; getTodayNote(rootNote?: BNote): BNote | null;
/** /**
* Returns note for the first date of the week of the given date. * Returns note for the first date of the week of the given date.
@ -239,7 +239,7 @@ export interface Api {
* @param date in YYYY-MM-DD format * @param date in YYYY-MM-DD format
* @param rootNote - specify calendar root note, normally leave empty to use the default calendar * @param rootNote - specify calendar root note, normally leave empty to use the default calendar
*/ */
getWeekFirstDayNote(date: string, rootNote: BNote): Promise<BNote | null>; getWeekFirstDayNote(date: string, rootNote: BNote): BNote | null;
/** /**
* Returns week note for given date. If such a note doesn't exist, it is created. * Returns week note for given date. If such a note doesn't exist, it is created.
@ -247,7 +247,7 @@ export interface Api {
* @param date in YYYY-MM-DD format * @param date in YYYY-MM-DD format
* @param rootNote - specify calendar root note, normally leave empty to use the default calendar * @param rootNote - specify calendar root note, normally leave empty to use the default calendar
*/ */
getWeekNote(date: string, rootNote: BNote): Promise<BNote | null>; getWeekNote(date: string, rootNote: BNote): BNote | null;
/** /**
* Returns month note for given date. If such a note doesn't exist, it is created. * Returns month note for given date. If such a note doesn't exist, it is created.
@ -255,7 +255,7 @@ export interface Api {
* @param date in YYYY-MM format * @param date in YYYY-MM format
* @param rootNote - specify calendar root note, normally leave empty to use the default calendar * @param rootNote - specify calendar root note, normally leave empty to use the default calendar
*/ */
getMonthNote(date: string, rootNote: BNote): Promise<BNote | null>; getMonthNote(date: string, rootNote: BNote): BNote | null;
/** /**
* Returns quarter note for given date. If such a note doesn't exist, it is created. * Returns quarter note for given date. If such a note doesn't exist, it is created.
@ -263,7 +263,7 @@ export interface Api {
* @param date in YYYY-MM format * @param date in YYYY-MM format
* @param rootNote - specify calendar root note, normally leave empty to use the default calendar * @param rootNote - specify calendar root note, normally leave empty to use the default calendar
*/ */
getQuarterNote(date: string, rootNote: BNote): Promise<BNote | null>; getQuarterNote(date: string, rootNote: BNote): BNote | null;
/** /**
* Returns year note for given year. If such a note doesn't exist, it is created. * Returns year note for given year. If such a note doesn't exist, it is created.

View File

@ -2,7 +2,7 @@
import dateUtils from "./date_utils.js"; import dateUtils from "./date_utils.js";
import optionService from "./options.js"; import optionService from "./options.js";
import fs from "fs-extra"; import fs from "fs";
import dataDir from "./data_dir.js"; import dataDir from "./data_dir.js";
import log from "./log.js"; import log from "./log.js";
import syncMutexService from "./sync_mutex.js"; import syncMutexService from "./sync_mutex.js";

View File

@ -4,19 +4,26 @@ import sql_init from "./sql_init.js";
import { join } from "path"; import { join } from "path";
import { getResourceDir } from "./utils.js"; import { getResourceDir } from "./utils.js";
import hidden_subtree from "./hidden_subtree.js"; import hidden_subtree from "./hidden_subtree.js";
import { LOCALES, type Locale } from "@triliumnext/commons"; import { LOCALES, type Locale, type LOCALE_IDS } from "@triliumnext/commons";
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
const DAYJS_LOCALE_MAP: Record<string, string> = { const DAYJS_LOADER: Record<LOCALE_IDS, () => Promise<typeof import("dayjs/locale/en.js")>> = {
cn: "zh-cn", "ar": () => import("dayjs/locale/ar.js"),
tw: "zh-tw" "cn": () => import("dayjs/locale/zh-cn.js"),
}; "de": () => import("dayjs/locale/de.js"),
"en": () => import("dayjs/locale/en.js"),
let dayjsLocale: string; "es": () => import("dayjs/locale/es.js"),
"fa": () => import("dayjs/locale/fa.js"),
"fr": () => import("dayjs/locale/fr.js"),
"he": () => import("dayjs/locale/he.js"),
"ku": () => import("dayjs/locale/ku.js"),
"ro": () => import("dayjs/locale/ro.js"),
"tw": () => import("dayjs/locale/zh-tw.js")
}
export async function initializeTranslations() { export async function initializeTranslations() {
const resourceDir = getResourceDir(); const resourceDir = getResourceDir();
const Backend = (await import("i18next-fs-backend")).default; const Backend = (await import("i18next-fs-backend/cjs")).default;
const locale = getCurrentLanguage(); const locale = getCurrentLanguage();
// Initialize translations // Initialize translations
@ -30,12 +37,7 @@ export async function initializeTranslations() {
}); });
// Initialize dayjs locale. // Initialize dayjs locale.
dayjsLocale = DAYJS_LOCALE_MAP[locale] ?? locale; const dayjsLocale = await DAYJS_LOADER[locale]();
try {
await import(`dayjs/locale/${dayjsLocale}.js`);
} catch (err) {
console.warn(`Could not load locale ${dayjsLocale}`, err);
}
dayjs.locale(dayjsLocale); dayjs.locale(dayjsLocale);
} }
@ -48,8 +50,8 @@ export function getLocales(): Locale[] {
return LOCALES; return LOCALES;
} }
function getCurrentLanguage() { function getCurrentLanguage(): LOCALE_IDS {
let language; let language: string;
if (sql_init.isDbInitialized()) { if (sql_init.isDbInitialized()) {
language = options.getOptionOrNull("locale"); language = options.getOptionOrNull("locale");
} }
@ -59,7 +61,7 @@ function getCurrentLanguage() {
language = "en"; language = "en";
} }
return language; return language as LOCALE_IDS;
} }
export async function changeLanguage(locale: string) { export async function changeLanguage(locale: string) {

View File

@ -26,7 +26,7 @@ import type {
* *
* Manages and provides access to all available agent tools. * Manages and provides access to all available agent tools.
*/ */
class AgentToolsManager { export class AgentToolsManager {
private vectorSearchTool: VectorSearchTool | null = null; private vectorSearchTool: VectorSearchTool | null = null;
private noteNavigatorTool: NoteNavigatorTool | null = null; private noteNavigatorTool: NoteNavigatorTool | null = null;
private queryDecompositionTool: QueryDecompositionTool | null = null; private queryDecompositionTool: QueryDecompositionTool | null = null;

View File

@ -16,14 +16,12 @@ import vectorStore from "./embeddings/index.js";
import providerManager from "./providers/providers.js"; import providerManager from "./providers/providers.js";
import { ContextExtractor } from "./context/index.js"; import { ContextExtractor } from "./context/index.js";
import eventService from "../events.js"; import eventService from "../events.js";
import type { NoteEmbeddingContext } from "./embeddings/embeddings_interface.js";
import type { OptionDefinitions } from "@triliumnext/commons";
import sql from "../sql.js"; import sql from "../sql.js";
import sqlInit from "../sql_init.js"; import sqlInit from "../sql_init.js";
import { CONTEXT_PROMPTS } from './constants/llm_prompt_constants.js'; import { CONTEXT_PROMPTS } from './constants/llm_prompt_constants.js';
import { SEARCH_CONSTANTS } from './constants/search_constants.js'; import { SEARCH_CONSTANTS } from './constants/search_constants.js';
class IndexService { export class IndexService {
private initialized = false; private initialized = false;
private indexingInProgress = false; private indexingInProgress = false;
private contextExtractor = new ContextExtractor(); private contextExtractor = new ContextExtractor();

View File

@ -1,6 +1,6 @@
import backupService from "./backup.js"; import backupService from "./backup.js";
import sql from "./sql.js"; import sql from "./sql.js";
import fs from "fs-extra"; import fs from "fs";
import log from "./log.js"; import log from "./log.js";
import { crash } from "./utils.js"; import { crash } from "./utils.js";
import resourceDir from "./resource_dir.js"; import resourceDir from "./resource_dir.js";

View File

@ -207,7 +207,7 @@ async function getClient(opts: ClientOpts): Promise<Client> {
// it's not clear how to explicitly configure proxy (as opposed to system proxy), // it's not clear how to explicitly configure proxy (as opposed to system proxy),
// so in that case, we always use node's modules // so in that case, we always use node's modules
if (isElectron && !opts.proxy) { if (isElectron && !opts.proxy) {
return (await import("electron")).net as Client; return (await import("electron")).net as unknown as Client;
} else { } else {
const { protocol } = url.parse(opts.url); const { protocol } = url.parse(opts.url);

View File

@ -5,7 +5,7 @@ import becca from "../becca/becca.js";
import type BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
import type { ApiParams } from "./backend_script_api_interface.js"; import type { ApiParams } from "./backend_script_api_interface.js";
interface Bundle { export interface Bundle {
note?: BNote; note?: BNote;
noteId?: string; noteId?: string;
script: string; script: string;

View File

@ -8,7 +8,7 @@ import log from "./log.js";
import type { Statement, Database as DatabaseType, RunResult } from "better-sqlite3"; import type { Statement, Database as DatabaseType, RunResult } from "better-sqlite3";
import dataDir from "./data_dir.js"; import dataDir from "./data_dir.js";
import cls from "./cls.js"; import cls from "./cls.js";
import fs from "fs-extra"; import fs from "fs";
import Database from "better-sqlite3"; import Database from "better-sqlite3";
import ws from "./ws.js"; import ws from "./ws.js";
import becca_loader from "../becca/becca_loader.js"; import becca_loader from "../becca/becca_loader.js";
@ -352,7 +352,6 @@ function disableSlowQueryLogging<T>(cb: () => T) {
} }
export default { export default {
dbConnection,
insert, insert,
replace, replace,

View File

@ -1,7 +1,7 @@
import { BrowserWindow, Menu, Tray, ipcMain, nativeTheme } from "electron"; import electron from "electron";
import type { BrowserWindow, Tray } from "electron";
import { default as i18next, t } from "i18next"; import { default as i18next, t } from "i18next";
import path from "path"; import path from "path";
import { fileURLToPath } from "url";
import becca from "../becca/becca.js"; import becca from "../becca/becca.js";
import becca_service from "../becca/becca_service.js"; import becca_service from "../becca/becca_service.js";
@ -33,7 +33,7 @@ function getTrayIconPath() {
} }
function getIconPath(name: string) { function getIconPath(name: string) {
const suffix = !isMac && nativeTheme.shouldUseDarkColors ? "-inverted" : ""; const suffix = !isMac && electron.nativeTheme.shouldUseDarkColors ? "-inverted" : "";
return path.resolve(path.join(getResourceDir(), "assets", "images", "tray", `${name}Template${suffix}.png`)); return path.resolve(path.join(getResourceDir(), "assets", "images", "tray", `${name}Template${suffix}.png`));
} }
@ -216,7 +216,7 @@ function updateTrayMenu() {
} }
const contextMenu = Menu.buildFromTemplate([ const contextMenu = electron.Menu.buildFromTemplate([
...windowVisibilityMenuItems, ...windowVisibilityMenuItems,
{ type: "separator" }, { type: "separator" },
{ {
@ -255,7 +255,7 @@ function updateTrayMenu() {
type: "normal", type: "normal",
icon: getIconPath("close"), icon: getIconPath("close"),
click: () => { click: () => {
const windows = BrowserWindow.getAllWindows(); const windows = electron.BrowserWindow.getAllWindows();
windows.forEach(window => { windows.forEach(window => {
window.close(); window.close();
}); });
@ -287,7 +287,7 @@ function createTray() {
return; return;
} }
tray = new Tray(getTrayIconPath()); tray = new electron.Tray(getTrayIconPath());
tray.setToolTip(t("tray.tooltip")); tray.setToolTip(t("tray.tooltip"));
// Restore focus // Restore focus
tray.on("click", changeVisibility); tray.on("click", changeVisibility);
@ -295,9 +295,9 @@ function createTray() {
if (!isMac) { if (!isMac) {
// macOS uses template icons which work great on dark & light themes. // macOS uses template icons which work great on dark & light themes.
nativeTheme.on("updated", updateTrayMenu); electron.nativeTheme.on("updated", updateTrayMenu);
} }
ipcMain.on("reload-tray", updateTrayMenu); electron.ipcMain.on("reload-tray", updateTrayMenu);
i18next.on("languageChanged", updateTrayMenu); i18next.on("languageChanged", updateTrayMenu);
} }

View File

@ -7,7 +7,7 @@ import entityChangesService from "./entity_changes.js";
import becca from "../becca/becca.js"; import becca from "../becca/becca.js";
import type BNote from "../becca/entities/bnote.js"; import type BNote from "../becca/entities/bnote.js";
interface ValidationResponse { export interface ValidationResponse {
branch: BBranch | null; branch: BBranch | null;
success: boolean; success: boolean;
message?: string; message?: string;

View File

@ -9,8 +9,6 @@ import escape from "escape-html";
import sanitize from "sanitize-filename"; import sanitize from "sanitize-filename";
import mimeTypes from "mime-types"; import mimeTypes from "mime-types";
import path from "path"; import path from "path";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import type NoteMeta from "./meta/note_meta.js"; import type NoteMeta from "./meta/note_meta.js";
import log from "./log.js"; import log from "./log.js";
import { t } from "i18next"; import { t } from "i18next";
@ -226,7 +224,7 @@ export function timeLimit<T>(promise: Promise<T>, limitMs: number, errorMessage?
}); });
} }
interface DeferredPromise<T> extends Promise<T> { export interface DeferredPromise<T> extends Promise<T> {
resolve: (value: T | PromiseLike<T>) => void; resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void; reject: (reason?: any) => void;
} }
@ -299,7 +297,7 @@ export function getResourceDir() {
return path.dirname(process.argv[1]); return path.dirname(process.argv[1]);
} }
return join(dirname(fileURLToPath(import.meta.url)), ".."); return path.join(__dirname, "..");
} }
// TODO: Deduplicate with src/public/app/services/utils.ts // TODO: Deduplicate with src/public/app/services/utils.ts

View File

@ -7,14 +7,11 @@ import log from "./log.js";
import sqlInit from "./sql_init.js"; import sqlInit from "./sql_init.js";
import cls from "./cls.js"; import cls from "./cls.js";
import keyboardActionsService from "./keyboard_actions.js"; import keyboardActionsService from "./keyboard_actions.js";
import * as remoteMain from "@electron/remote/main"; import electron from "electron";
import { BrowserWindow, shell, type App, type BrowserWindowConstructorOptions, type WebContents } from "electron"; import type { App, BrowserWindowConstructorOptions, BrowserWindow, WebContents } from "electron";
import { dialog, ipcMain } from "electron";
import { formatDownloadTitle, isDev, isMac, isWindows } from "./utils.js"; import { formatDownloadTitle, isDev, isMac, isWindows } from "./utils.js";
import { fileURLToPath } from "url";
import { dirname } from "path";
import { t } from "i18next"; import { t } from "i18next";
import { RESOURCE_DIR } from "./resource_dir.js";
// Prevent the window being garbage collected // Prevent the window being garbage collected
let mainWindow: BrowserWindow | null; let mainWindow: BrowserWindow | null;
@ -28,14 +25,14 @@ function trackWindowFocus(win: BrowserWindow) {
allWindows = allWindows.filter(w => !w.isDestroyed() && w !== win); allWindows = allWindows.filter(w => !w.isDestroyed() && w !== win);
allWindows.push(win); allWindows.push(win);
if (!optionService.getOptionBool("disableTray")) { if (!optionService.getOptionBool("disableTray")) {
ipcMain.emit("reload-tray"); electron.ipcMain.emit("reload-tray");
} }
}); });
win.on("closed", () => { win.on("closed", () => {
allWindows = allWindows.filter(w => !w.isDestroyed()); allWindows = allWindows.filter(w => !w.isDestroyed());
if (!optionService.getOptionBool("disableTray")) { if (!optionService.getOptionBool("disableTray")) {
ipcMain.emit("reload-tray"); electron.ipcMain.emit("reload-tray");
} }
}); });
} }
@ -66,7 +63,7 @@ async function createExtraWindow(extraWindowHash: string) {
trackWindowFocus(win); trackWindowFocus(win);
} }
ipcMain.on("create-extra-window", (event, arg) => { electron.ipcMain.on("create-extra-window", (event, arg) => {
createExtraWindow(arg.extraWindowHash); createExtraWindow(arg.extraWindowHash);
}); });
@ -76,13 +73,13 @@ interface ExportAsPdfOpts {
pageSize: "A0" | "A1" | "A2" | "A3" | "A4" | "A5" | "A6" | "Legal" | "Letter" | "Tabloid" | "Ledger"; pageSize: "A0" | "A1" | "A2" | "A3" | "A4" | "A5" | "A6" | "Legal" | "Letter" | "Tabloid" | "Ledger";
} }
ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => { electron.ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
const browserWindow = BrowserWindow.fromWebContents(e.sender); const browserWindow = electron.BrowserWindow.fromWebContents(e.sender);
if (!browserWindow) { if (!browserWindow) {
return; return;
} }
const filePath = dialog.showSaveDialogSync(browserWindow, { const filePath = electron.dialog.showSaveDialogSync(browserWindow, {
defaultPath: formatDownloadTitle(opts.title, "file", "application/pdf"), defaultPath: formatDownloadTitle(opts.title, "file", "application/pdf"),
filters: [ filters: [
{ {
@ -111,18 +108,18 @@ ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => {
` `
}); });
} catch (e) { } catch (e) {
dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-export-message")); electron.dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-export-message"));
return; return;
} }
try { try {
await fs.writeFile(filePath, buffer); await fs.writeFile(filePath, buffer);
} catch (e) { } catch (e) {
dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-save-message")); electron.dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-save-message"));
return; return;
} }
shell.openPath(filePath); electron.shell.openPath(filePath);
}); });
async function createMainWindow(app: App) { async function createMainWindow(app: App) {
@ -226,7 +223,8 @@ function getWindowExtraOpts() {
return extraOpts; return extraOpts;
} }
function configureWebContents(webContents: WebContents, spellcheckEnabled: boolean) { async function configureWebContents(webContents: WebContents, spellcheckEnabled: boolean) {
const remoteMain = (await import("@electron/remote/main/index.js")).default;
remoteMain.enable(webContents); remoteMain.enable(webContents);
webContents.setWindowOpenHandler((details) => { webContents.setWindowOpenHandler((details) => {
@ -259,7 +257,7 @@ function configureWebContents(webContents: WebContents, spellcheckEnabled: boole
} }
function getIcon() { function getIcon() {
return path.join(dirname(fileURLToPath(import.meta.url)), "../../images/app-icons/png/256x256" + (isDev ? "-dev" : "") + ".png"); return path.join(RESOURCE_DIR, "images/app-icons/png/256x256" + (isDev ? "-dev" : "") + ".png");
} }
async function createSetupWindow() { async function createSetupWindow() {

View File

@ -195,9 +195,11 @@ function register(router: Router) {
try { try {
const content = templateNote.getContent(); const content = templateNote.getContent();
if (typeof content === "string") { if (typeof content === "string") {
const ejsResult = ejs.render(content, opts, { includer }); import("ejs").then((ejs) => {
res.send(ejsResult); const ejsResult = ejs.render(content, opts, { includer });
useDefaultView = false; // Rendering went okay, don't use default view res.send(ejsResult);
useDefaultView = false; // Rendering went okay, don't use default view
});
} }
} catch (e: unknown) { } catch (e: unknown) {
const [errMessage, errStack] = safeExtractMessageAndStackFromError(e); const [errMessage, errStack] = safeExtractMessageAndStackFromError(e);

View File

@ -4,4 +4,4 @@
[ ! -z "${USER_GID}" ] && groupmod -og ${USER_GID} node || echo "No USER_GID specified, leaving 1000" [ ! -z "${USER_GID}" ] && groupmod -og ${USER_GID} node || echo "No USER_GID specified, leaving 1000"
chown -R node:node /home/node chown -R node:node /home/node
exec su -c "node ./main" node exec su -c "node ./main.cjs" node

View File

@ -5,6 +5,7 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"target": "ES2020", "target": "ES2020",
"outDir": "dist", "outDir": "dist",
"strict": false,
"types": [ "types": [
"node", "node",
"express" "express"

View File

@ -1,69 +0,0 @@
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const { join } = require('path');
const outputDir = join(__dirname, 'dist');
function buildFilesToCopy() {
const files = [];
files.push({
from: "../client/dist",
to: join(outputDir, "public")
});
const nodePaths = [
// Required as they are native dependencies and cannot be well bundled.
"better-sqlite3",
"bindings",
"file-uri-to-path"
];
for (const nodePath of nodePaths) {
files.push({
from: join("node_modules", nodePath),
to: join(outputDir, "node_modules", nodePath)
})
}
return files;
}
module.exports = {
output: {
path: outputDir
},
module: {
rules: [
{
test: /\.css$/i,
type: "asset/source"
}
]
},
plugins: [
new NxAppWebpackPlugin({
target: 'node',
compiler: 'tsc',
main: './src/main.ts',
tsConfig: './tsconfig.app.json',
assets: ["./src/assets"],
optimization: false,
outputHashing: 'none',
generatePackageJson: true,
additionalEntryPoints: [
"./src/docker_healthcheck.ts"
],
externalDependencies: [
"electron/main",
"@electron/remote/main",
"electron",
"@electron/remote",
"better-sqlite3"
]
}),
new CopyPlugin({
patterns: buildFilesToCopy()
})
]
};

View File

@ -1,4 +1,7 @@
# v0.94.0 # v0.94.0
> [!CAUTION]
> **For (advanced) manual setups only:** Following a tooling change, the main entry point has changed from `main.js` to `main.cjs`. Same goes for the Electron build.
> [!IMPORTANT] > [!IMPORTANT]
> If you enjoyed this release, consider showing a token of appreciation by: > If you enjoyed this release, consider showing a token of appreciation by:
> >
@ -38,21 +41,7 @@
* Added the Nix language (and also in code blocks for text notes). * Added the Nix language (and also in code blocks for text notes).
* Added an indentation marker. * Added an indentation marker.
* Note: syntax highlighting for some languages (mostly HTML-template languages such as EJS, JSP) is no longer supported due to lack of upstream support. If this is a problem, feel free to report an issue and we can see what can be done about it. * Note: syntax highlighting for some languages (mostly HTML-template languages such as EJS, JSP) is no longer supported due to lack of upstream support. If this is a problem, feel free to report an issue and we can see what can be done about it.
* Syntax highlighting in code blocks for text notes: * Added support for additional syntax highlighting for code blocks in text notes: Cypher, XML-DTD, Jinja2, ClojureScript, Perl, Scala, Scheme, Swift, SystemVerilog, mIRC, Cobol, Dylan, RPM Specfile, TCCN3.
* Added support for Cypher.
* Added support for XML-DTD.
* Added support for Jinja2.
* Added support for ClojureScript.
* Added support for Perl.
* Added support for Scala.
* Added support for Scheme.
* Added support for Swift.
* Added support for SystemVerilog.
* Added support for mIRC.
* Added support for Cobol.
* Added support for Dylan.
* Added support for RPM Specfile.
* Added support for TCCN3.
* Mermaid diagrams: basic syntax highlight (not all diagram types are supported) and code folding. * Mermaid diagrams: basic syntax highlight (not all diagram types are supported) and code folding.
* Slight organization in Appearance settings: code block themes are now in "Text Notes", added a "Related settings" section in Appearance. * Slight organization in Appearance settings: code block themes are now in "Text Notes", added a "Related settings" section in Appearance.
* [Added support for opening and activating a note in a new tab using Ctrl+Shift+click on notes in the launcher pane, note tree, or note images](https://github.com/TriliumNext/Notes/pull/1854) by @SiriusXT * [Added support for opening and activating a note in a new tab using Ctrl+Shift+click on notes in the launcher pane, note tree, or note images](https://github.com/TriliumNext/Notes/pull/1854) by @SiriusXT

10
nx.json
View File

@ -54,16 +54,6 @@
"watchDepsTargetName": "watch-deps" "watchDepsTargetName": "watch-deps"
} }
}, },
{
"plugin": "@nx/webpack/plugin",
"options": {
"buildTargetName": "build",
"serveTargetName": "serve",
"previewTargetName": "preview",
"buildDepsTargetName": "build-deps",
"watchDepsTargetName": "watch-deps"
}
},
{ {
"plugin": "@nx/playwright/plugin", "plugin": "@nx/playwright/plugin",
"options": { "options": {

View File

@ -27,19 +27,17 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@electron/rebuild": "4.0.1", "@electron/rebuild": "4.0.1",
"@nx/devkit": "21.1.1", "@nx/devkit": "21.1.2",
"@nx/esbuild": "21.1.1", "@nx/esbuild": "21.1.2",
"@nx/eslint": "21.1.1", "@nx/eslint": "21.1.2",
"@nx/eslint-plugin": "21.1.1", "@nx/eslint-plugin": "21.1.2",
"@nx/express": "21.1.1", "@nx/express": "21.1.2",
"@nx/js": "21.1.1", "@nx/js": "21.1.2",
"@nx/node": "21.1.1", "@nx/node": "21.1.2",
"@nx/playwright": "21.1.1", "@nx/playwright": "21.1.2",
"@nx/vite": "21.1.1", "@nx/vite": "21.1.2",
"@nx/web": "21.1.1", "@nx/web": "21.1.2",
"@nx/webpack": "21.1.1",
"@playwright/test": "^1.36.0", "@playwright/test": "^1.36.0",
"@svgr/webpack": "^8.0.1",
"@swc-node/register": "~1.10.0", "@swc-node/register": "~1.10.0",
"@swc/cli": "~0.7.0", "@swc/cli": "~0.7.0",
"@swc/core": "~1.11.0", "@swc/core": "~1.11.0",
@ -59,7 +57,7 @@
"jiti": "2.4.2", "jiti": "2.4.2",
"jsdom": "~26.1.0", "jsdom": "~26.1.0",
"jsonc-eslint-parser": "^2.1.0", "jsonc-eslint-parser": "^2.1.0",
"nx": "21.1.1", "nx": "21.1.2",
"react-refresh": "^0.17.0", "react-refresh": "^0.17.0",
"swc-loader": "0.2.6", "swc-loader": "0.2.6",
"tslib": "^2.3.0", "tslib": "^2.3.0",
@ -69,8 +67,7 @@
"upath": "2.0.1", "upath": "2.0.1",
"vite": "^6.0.0", "vite": "^6.0.0",
"vite-plugin-dts": "~4.5.0", "vite-plugin-dts": "~4.5.0",
"vitest": "^3.0.0", "vitest": "^3.0.0"
"webpack-cli": "^6.0.0"
}, },
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"author": { "author": {

View File

@ -9,7 +9,7 @@ export interface Locale {
electronLocale?: string; electronLocale?: string;
} }
export const LOCALES: Locale[] = [ const UNSORTED_LOCALES = [
{ {
id: "en", id: "en",
name: "English", name: "English",
@ -75,4 +75,9 @@ export const LOCALES: Locale[] = [
rtl: true, rtl: true,
contentOnly: true contentOnly: true
} }
].sort((a, b) => a.name.localeCompare(b.name)); ] as const;
export const LOCALES: Locale[] = Array.from(UNSORTED_LOCALES)
.sort((a, b) => a.name.localeCompare(b.name));
export type LOCALE_IDS = typeof UNSORTED_LOCALES[number]["id"];

View File

@ -1,22 +0,0 @@
{
"jsc": {
"target": "es2017",
"parser": {
"syntax": "typescript",
"decorators": true,
"dynamicImport": true
},
"transform": {
"decoratorMetadata": true,
"legacyDecorator": true
},
"keepClassNames": true,
"externalHelpers": true,
"loose": true
},
"module": {
"type": "commonjs"
},
"sourceMaps": true,
"exclude": ["jest.config.ts",".*\\.spec.tsx?$",".*\\.test.tsx?$","./src/jest-setup.ts$","./**/jest-setup.ts$",".*.js$"]
}

View File

@ -5,16 +5,16 @@
"version": "1.1.0", "version": "1.1.0",
"type": "module", "type": "module",
"private": true, "private": true,
"main": "./dist/index.js", "main": "./dist/main.js",
"module": "./dist/index.js", "module": "./dist/main.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"exports": { "exports": {
"./package.json": "./package.json", "./package.json": "./package.json",
".": { ".": {
"development": "./src/index.ts", "development": "./src/index.ts",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"import": "./dist/index.js", "import": "./dist/main.js",
"default": "./dist/index.js" "default": "./dist/main.js"
} }
}, },
"keywords": [ "keywords": [
@ -27,16 +27,29 @@
"name": "express-partial-content", "name": "express-partial-content",
"targets": { "targets": {
"build": { "build": {
"executor": "@nx/js:swc", "executor": "@nx/esbuild:esbuild",
"outputs": [ "outputs": [
"{options.outputPath}" "{options.outputPath}"
], ],
"defaultConfiguration": "production",
"options": { "options": {
"outputPath": "packages/express-partial-content/dist",
"tsConfig": "packages/express-partial-content/tsconfig.lib.json",
"packageJson": "packages/express-partial-content/package.json",
"main": "packages/express-partial-content/src/index.ts", "main": "packages/express-partial-content/src/index.ts",
"stripLeadingPaths": true "outputPath": "packages/express-partial-content/dist",
"outputFileName": "main.js",
"tsConfig": "packages/express-partial-content/tsconfig.lib.json",
"platform": "node",
"format": [
"esm"
],
"declarationRootDir": "packages/express-partial-content/src"
},
"configurations": {
"development": {
"minify": false
},
"production": {
"minify": true
}
} }
} }
} }

1554
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@ onlyBuiltDependencies:
overrides: overrides:
"node-abi": 4.8.0 "node-abi": 4.8.0
"@types/express-serve-static-core": 5.0.6
shamefullyHoist: true shamefullyHoist: true
nodeLinker: isolated nodeLinker: isolated

View File

@ -4,27 +4,64 @@
* This script is used internally by the `rebuild-deps` target of the `desktop`. Normally we could use * This script is used internally by the `rebuild-deps` target of the `desktop`. Normally we could use
* `electron-rebuild` CLI directly, but it would rebuild the monorepo-level dependencies and breaks * `electron-rebuild` CLI directly, but it would rebuild the monorepo-level dependencies and breaks
* the server build (and it doesn't expose a CLI option to override this). * the server build (and it doesn't expose a CLI option to override this).
*
* A side purpose is to generate a fake `package.json` file in the `dist` directory
* that contains only the native dependencies. This is used by `electron-forge`.
*/ */
import { join, resolve } from "path"; import { join, resolve } from "path";
import { rebuild } from "@electron/rebuild" import { rebuild } from "@electron/rebuild"
import { readFileSync } from "fs"; import { readFileSync, rmSync, writeFileSync } from "fs";
function getElectronVersion(distDir: string) { const nativeDependencies = [
if (process.argv[3]) { "better-sqlite3"
return process.argv[3]; ];
}
const packageJsonPath = join(distDir, "package.json"); function parsePackageJson(distDir: string) {
const packageJsonPath = join(distDir, "../package.json");
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
let electronVersion: string;
const electronVersion = packageJson?.devDependencies?.electron ?? packageJson?.dependencies?.electron; if (process.argv[3]) {
if (!electronVersion) { electronVersion = process.argv[3];
console.error(`Unable to retrieve Electron version in '${resolve(packageJsonPath)}'.`); } else {
process.exit(3); electronVersion = packageJson?.devDependencies?.electron ?? packageJson?.dependencies?.electron;
if (!electronVersion) {
console.error(`Unable to retrieve Electron version in '${resolve(packageJsonPath)}'.`);
process.exit(3);
}
}
return {
electronVersion,
packageJson
};
}
function createFakePackageJson(distPath: string, packageJson: any) {
const finalDependencies = {};
for (const dep of nativeDependencies) {
finalDependencies[dep] = packageJson.dependencies[dep];
} }
return electronVersion; const fakePackageJson: any = {
name: "trilium",
version: packageJson.version,
main: packageJson.main,
author: packageJson.author,
license: packageJson.license,
description: packageJson.description,
dependencies: finalDependencies,
devDependencies: {
"electron": packageJson.devDependencies?.electron || packageJson.dependencies?.electron,
}
};
if (packageJson?.config?.forge) {
fakePackageJson.config = {
forge: join("..", packageJson.config.forge)
};
}
writeFileSync(distPath, JSON.stringify(fakePackageJson, null, 2), "utf-8");
} }
function main() { function main() {
@ -34,7 +71,10 @@ function main() {
process.exit(1); process.exit(1);
} }
const electronVersion = getElectronVersion(distDir); const { electronVersion, packageJson } = parsePackageJson(distDir);
const packageJsonPath = join(distDir, "package.json");
createFakePackageJson(packageJsonPath, packageJson);
console.log(`Rebuilding ${distDir} with version ${electronVersion}...`); console.log(`Rebuilding ${distDir} with version ${electronVersion}...`);
rebuild({ rebuild({

View File

@ -10,7 +10,7 @@
"moduleResolution": "nodenext", "moduleResolution": "nodenext",
"noEmitOnError": true, "noEmitOnError": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noImplicitAny": true, "noImplicitAny": false, // TODO: Re-enable it at some point.
"noImplicitOverride": false, // TODO: Re-enable it at some point. "noImplicitOverride": false, // TODO: Re-enable it at some point.
"noImplicitReturns": false, // TODO: Re-enable it at some point. "noImplicitReturns": false, // TODO: Re-enable it at some point.
"noUnusedLocals": false, // TODO: Re-enable it at some point. "noUnusedLocals": false, // TODO: Re-enable it at some point.