mirror of
https://github.com/zadam/trilium.git
synced 2025-12-04 22:44:25 +01:00
Unify Dayjs between client and server (#7930)
This commit is contained in:
commit
c081a596df
@ -38,8 +38,6 @@
|
|||||||
"boxicons": "2.1.4",
|
"boxicons": "2.1.4",
|
||||||
"clsx": "2.1.1",
|
"clsx": "2.1.1",
|
||||||
"color": "5.0.3",
|
"color": "5.0.3",
|
||||||
"dayjs": "1.11.19",
|
|
||||||
"dayjs-plugin-utc": "0.1.2",
|
|
||||||
"debounce": "3.0.0",
|
"debounce": "3.0.0",
|
||||||
"draggabilly": "3.0.0",
|
"draggabilly": "3.0.0",
|
||||||
"force-graph": "1.51.0",
|
"force-graph": "1.51.0",
|
||||||
|
|||||||
44
apps/client/src/services/bundle.spec.ts
Normal file
44
apps/client/src/services/bundle.spec.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { Bundle, executeBundle } from "./bundle";
|
||||||
|
import { buildNote } from "../test/easy-froca";
|
||||||
|
|
||||||
|
describe("Script bundle", () => {
|
||||||
|
it("dayjs is available", async () => {
|
||||||
|
const script = /* js */`return api.dayjs().format("YYYY-MM-DD");`;
|
||||||
|
const bundle = getBundle(script);
|
||||||
|
const result = await executeBundle(bundle, null, $());
|
||||||
|
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("dayjs is-same-or-before plugin exists", async () => {
|
||||||
|
const script = /* js */`return api.dayjs("2023-10-01").isSameOrBefore(api.dayjs("2023-10-02"));`;
|
||||||
|
const bundle = getBundle(script);
|
||||||
|
const result = await executeBundle(bundle, null, $());
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getBundle(script: string) {
|
||||||
|
const id = buildNote({
|
||||||
|
title: "Script note"
|
||||||
|
}).noteId;
|
||||||
|
const bundle: Bundle = {
|
||||||
|
script: [
|
||||||
|
'',
|
||||||
|
`apiContext.modules['${id}'] = { exports: {} };`,
|
||||||
|
`return await ((async function(exports, module, require, api) {`,
|
||||||
|
`try {`,
|
||||||
|
`${script}`,
|
||||||
|
`;`,
|
||||||
|
`} catch (e) { throw new Error(\"Load of script note \\\"Client\\\" (${id}) failed with: \" + e.message); }`,
|
||||||
|
`for (const exportKey in exports) module.exports[exportKey] = exports[exportKey];`,
|
||||||
|
`return module.exports;`,
|
||||||
|
`}).call({}, {}, apiContext.modules['${id}'], apiContext.require([]), apiContext.apis['${id}']));`,
|
||||||
|
''
|
||||||
|
].join('\n'),
|
||||||
|
html: "",
|
||||||
|
noteId: id,
|
||||||
|
allNoteIds: [ id ]
|
||||||
|
};
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
@ -27,7 +27,7 @@ async function getAndExecuteBundle(noteId: string, originEntity = null, script =
|
|||||||
return await executeBundle(bundle, originEntity);
|
return await executeBundle(bundle, originEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery<HTMLElement>) {
|
export async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery<HTMLElement>) {
|
||||||
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container);
|
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type { FNoteRow } from "../entities/fnote.js";
|
import type { FNoteRow } from "../entities/fnote.js";
|
||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import shortcutService from "./shortcuts.js";
|
|||||||
import dialogService from "./dialog.js";
|
import dialogService from "./dialog.js";
|
||||||
import type FNote from "../entities/fnote.js";
|
import type FNote from "../entities/fnote.js";
|
||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type NoteContext from "../components/note_context.js";
|
import type NoteContext from "../components/note_context.js";
|
||||||
import type Component from "../components/component.js";
|
import type Component from "../components/component.js";
|
||||||
import { formatLogMessage } from "@triliumnext/commons";
|
import { formatLogMessage } from "@triliumnext/commons";
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import options from "./options.js";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import i18nextHttpBackend from "i18next-http-backend";
|
import i18nextHttpBackend from "i18next-http-backend";
|
||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import type { Locale } from "@triliumnext/commons";
|
import { LOCALE_IDS, setDayjsLocale, type Locale } from "@triliumnext/commons";
|
||||||
import { initReactI18next } from "react-i18next";
|
import { initReactI18next } from "react-i18next";
|
||||||
|
|
||||||
let locales: Locale[] | null;
|
let locales: Locale[] | null;
|
||||||
@ -13,7 +13,7 @@ let locales: Locale[] | null;
|
|||||||
export let translationsInitializedPromise = $.Deferred();
|
export let translationsInitializedPromise = $.Deferred();
|
||||||
|
|
||||||
export async function initLocale() {
|
export async function initLocale() {
|
||||||
const locale = (options.get("locale") as string) || "en";
|
const locale = ((options.get("locale") as string) || "en") as LOCALE_IDS;
|
||||||
|
|
||||||
locales = await server.get<Locale[]>("options/locales");
|
locales = await server.get<Locale[]>("options/locales");
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ export async function initLocale() {
|
|||||||
returnEmptyString: false
|
returnEmptyString: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await setDayjsLocale(locale);
|
||||||
translationsInitializedPromise.resolve();
|
translationsInitializedPromise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type { ViewScope } from "./link.js";
|
import type { ViewScope } from "./link.js";
|
||||||
import FNote from "../entities/fnote";
|
import FNote from "../entities/fnote";
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,8 @@ function mockServer() {
|
|||||||
attributes: []
|
attributes: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.warn(`Unsupported GET to mocked server: ${url}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
async post(url: string, data: object) {
|
async post(url: string, data: object) {
|
||||||
|
|||||||
@ -7,17 +7,10 @@ import toastService from "../../services/toast.js";
|
|||||||
import options from "../../services/options.js";
|
import options from "../../services/options.js";
|
||||||
import { Dropdown } from "bootstrap";
|
import { Dropdown } from "bootstrap";
|
||||||
import type { EventData } from "../../components/app_context.js";
|
import type { EventData } from "../../components/app_context.js";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import { dayjs, type Dayjs } from "@triliumnext/commons";
|
||||||
import isoWeek from "dayjs/plugin/isoWeek.js";
|
|
||||||
import utc from "dayjs/plugin/utc.js";
|
|
||||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
|
|
||||||
import "../../stylesheets/calendar.css";
|
import "../../stylesheets/calendar.css";
|
||||||
import type { AttributeRow, OptionDefinitions } from "@triliumnext/commons";
|
import type { AttributeRow, OptionDefinitions } from "@triliumnext/commons";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
|
||||||
dayjs.extend(isSameOrAfter);
|
|
||||||
dayjs.extend(isoWeek);
|
|
||||||
|
|
||||||
const MONTHS = [
|
const MONTHS = [
|
||||||
t("calendar.january"),
|
t("calendar.january"),
|
||||||
t("calendar.february"),
|
t("calendar.february"),
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { t } from "../services/i18n.js";
|
|||||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||||
import server from "../services/server.js";
|
import server from "../services/server.js";
|
||||||
import fileWatcher from "../services/file_watcher.js";
|
import fileWatcher from "../services/file_watcher.js";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import type { EventData } from "../components/app_context.js";
|
import type { EventData } from "../components/app_context.js";
|
||||||
import type FNote from "../entities/fnote.js";
|
import type FNote from "../entities/fnote.js";
|
||||||
|
|
||||||
|
|||||||
@ -76,7 +76,6 @@
|
|||||||
"compression": "1.8.1",
|
"compression": "1.8.1",
|
||||||
"cookie-parser": "1.4.7",
|
"cookie-parser": "1.4.7",
|
||||||
"csrf-csrf": "3.2.2",
|
"csrf-csrf": "3.2.2",
|
||||||
"dayjs": "1.11.19",
|
|
||||||
"debounce": "3.0.0",
|
"debounce": "3.0.0",
|
||||||
"debug": "4.4.3",
|
"debug": "4.4.3",
|
||||||
"ejs": "3.1.10",
|
"ejs": "3.1.10",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { beforeAll } from "vitest";
|
import { beforeAll } from "vitest";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import dayjs from "dayjs";
|
import { setDayjsLocale } from "@triliumnext/commons";
|
||||||
|
|
||||||
// Initialize environment variables.
|
// Initialize environment variables.
|
||||||
process.env.TRILIUM_DATA_DIR = join(__dirname, "db");
|
process.env.TRILIUM_DATA_DIR = join(__dirname, "db");
|
||||||
@ -25,6 +25,5 @@ beforeAll(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialize dayjs
|
// Initialize dayjs
|
||||||
await import("dayjs/locale/en.js");
|
await setDayjsLocale("en");
|
||||||
dayjs.locale("en");
|
|
||||||
});
|
});
|
||||||
|
|||||||
2
apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
generated
vendored
2
apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
generated
vendored
File diff suppressed because one or more lines are too long
@ -4,28 +4,29 @@ class="image image-style-align-center">
|
|||||||
<img style="aspect-ratio:1398/1015;" src="Split View_2_Split View_im.png"
|
<img style="aspect-ratio:1398/1015;" src="Split View_2_Split View_im.png"
|
||||||
width="1398" height="1015">
|
width="1398" height="1015">
|
||||||
</figure>
|
</figure>
|
||||||
<h2><strong>Interactions</strong></h2>
|
|
||||||
|
<h2><strong>Interactions</strong></h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="eb22263532280510ca0efeb2c2e757629">Press the
|
<li>Press the
|
||||||
<img src="Split View_Split View_imag.png">button to the right of a note's title to open a new split to the right
|
<img src="Split View_Split View_imag.png">button to the right of a note's title to open a new split to the right
|
||||||
of it.
|
of it.
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="eda17492ea2d8da7c4bf2fb3e2f7bfbe9">It is possible to have as many splits as desired, simply press again the
|
<li>It is possible to have as many splits as desired, simply press again the
|
||||||
button.</li>
|
button.</li>
|
||||||
<li data-list-item-id="ea0223c947ea17534d577c9cfef4d5c6e">Only horizontal splits are possible, vertical or drag & dropping is
|
<li>Only horizontal splits are possible, vertical or drag & dropping is
|
||||||
not supported.</li>
|
not supported.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li data-list-item-id="e77d99fdc9a0846903de57d1b710fdd56">When at least one split is open, press the
|
<li>When at least one split is open, press the
|
||||||
<img src="Split View_3_Split View_im.png">button next to it to close it.</li>
|
<img src="Split View_3_Split View_im.png">button next to it to close it.</li>
|
||||||
<li data-list-item-id="ec9d11f5bcfd10795f282e275938b2f4a">Use the
|
<li>Use the
|
||||||
<img src="Split View_4_Split View_im.png">or the
|
<img src="Split View_4_Split View_im.png">or the
|
||||||
<img src="Split View_1_Split View_im.png">button to move around the splits.</li>
|
<img src="Split View_1_Split View_im.png">button to move around the splits.</li>
|
||||||
<li data-list-item-id="e8384a579c3d6ee8df4d7dbf9b07c3436">Each <a href="#root/_help_3seOhtN8uLIY">tab</a> has its own split view configuration
|
<li>Each <a href="#root/_help_3seOhtN8uLIY">tab</a> has its own split view configuration
|
||||||
(e.g. one tab can have two notes in a split view, whereas the others are
|
(e.g. one tab can have two notes in a split view, whereas the others are
|
||||||
one-note views).
|
one-note views).
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="e298299f6b2f1b9d8b5f26a5f8a0c9092">The tab will indicate only the title of the main note (the first one in
|
<li>The tab will indicate only the title of the main note (the first one in
|
||||||
the list).</li>
|
the list).</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@ -48,35 +49,34 @@ class="image image-style-align-center">
|
|||||||
as well, with the following differences from the desktop version of the
|
as well, with the following differences from the desktop version of the
|
||||||
split:</p>
|
split:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="efc0bb8eb81ea1617b73188613f2ede5d">On smartphones, the split views are laid out vertically (one on the top
|
<li>On smartphones, the split views are laid out vertically (one on the top
|
||||||
and one on the bottom), instead of horizontally as on the desktop.</li>
|
and one on the bottom), instead of horizontally as on the desktop.</li>
|
||||||
<li
|
<li>There can be only one split open per tab.</li>
|
||||||
data-list-item-id="e7659da22c36db39ae8e3dc5424afba1e">There can be only one split open per tab.</li>
|
<li>It's not possible to resize the two split panes.</li>
|
||||||
<li data-list-item-id="eb848af9837a484a9de9117922c6d7186">It's not possible to resize the two split panes.</li>
|
<li>When the keyboard is opened, the active note will be “maximized”, thus
|
||||||
<li data-list-item-id="e869c240066f602fbc1c0e55259ba62e5">When the keyboard is opened, the active note will be “maximized”, thus
|
|
||||||
allowing for more space even when a split is open. When the keyboard is
|
allowing for more space even when a split is open. When the keyboard is
|
||||||
closed, the splits become equal in size again.</li>
|
closed, the splits become equal in size again.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Interaction:</p>
|
<p>Interaction:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="edbf5c644758db5aca9867c516f97542b">To create a new split, click the three dots button on the right of the
|
<li>To create a new split, click the three dots button on the right of the
|
||||||
note title and select <em>Create new split</em>.
|
note title and select <em>Create new split</em>.
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="eed272873b629f70418c3e7074a829369">This option will only be available if there is no split already open in
|
<li>This option will only be available if there is no split already open in
|
||||||
the current tab.</li>
|
the current tab.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li data-list-item-id="e733863e6058336ebfcf27042f56be312">To close a split, click the three dots button on the right of the note
|
<li>To close a split, click the three dots button on the right of the note
|
||||||
title and select <em>Close this pane</em>.
|
title and select <em>Close this pane</em>.
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="e9e6c191873dcd658242c64553343e0c7">Note that this option will only be available on the second note in the
|
<li>Note that this option will only be available on the second note in the
|
||||||
split (the one at the bottom on smartphones, the one on the right on tablets).</li>
|
split (the one at the bottom on smartphones, the one on the right on tablets).</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li data-list-item-id="e780e5e7736b4d26705102545c5626a6a">When long-pressing a link, a contextual menu will show up with an option
|
<li>When long-pressing a link, a contextual menu will show up with an option
|
||||||
to <em>Open note in a new split</em>.
|
to <em>Open note in a new split</em>.
|
||||||
<ul>
|
<ul>
|
||||||
<li data-list-item-id="e264277ee51f6d266a4088e2545f6648d">If there's already a split, the option will replace the existing split
|
<li>If there's already a split, the option will replace the existing split
|
||||||
instead.</li>
|
instead.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
37
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Script API/Day.js.html
generated
vendored
Normal file
37
apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Script API/Day.js.html
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<p>Day.js is a date manipulation library that's used by Trilium, but it's
|
||||||
|
also shared with both front-end and back-end scripts. For more information
|
||||||
|
about the library itself, consult the <a href="https://day.js.org/en/">official documentation</a>.</p>
|
||||||
|
<h2>How to use</h2>
|
||||||
|
<p>The <code>dayjs</code> method is provided directly in the <code>api</code> global:</p><pre><code class="language-application-javascript-env-backend">const date = api.dayjs();
|
||||||
|
api.log(date.format("YYYY-MM-DD"));</code></pre>
|
||||||
|
<h2>Plugins</h2>
|
||||||
|
<p>Day.js uses a modular, plugin-based architecture. Generally these plugins
|
||||||
|
must be imported, but this process doesn't work inside Trilium scripts
|
||||||
|
due to the use of a bundler.</p>
|
||||||
|
<p>Since v0.100.0, the same set of plugins is available for both front-end
|
||||||
|
and back-end scripts.</p>
|
||||||
|
<p>The following Day.js plugins are directly integrated into Trilium:</p>
|
||||||
|
<ul>
|
||||||
|
<li data-list-item-id="ee48062bdf09fc0c616bb530989292b21"><a href="https://day.js.org/docs/en/plugin/advanced-format">AdvancedFormat</a>
|
||||||
|
</li>
|
||||||
|
<li data-list-item-id="ed6e0cbd3a519a8720d1c9e9cc400bb04"><a href="https://day.js.org/docs/en/plugin/duration">Duration</a>, since
|
||||||
|
v0.100.0.</li>
|
||||||
|
<li data-list-item-id="e94f06e705bf337dd83b07f86d3f7adb7"><a href="https://day.js.org/docs/en/plugin/is-between">IsBetween</a>
|
||||||
|
</li>
|
||||||
|
<li data-list-item-id="e15e52376df80e3452567a47df67f41bd"><a href="https://day.js.org/docs/en/plugin/iso-week">IsoWeek</a>
|
||||||
|
</li>
|
||||||
|
<li data-list-item-id="e5c807c71a6b02320901809cc94dcca25"><a href="https://day.js.org/docs/en/plugin/is-same-or-after">IsSameOrAfter</a>
|
||||||
|
</li>
|
||||||
|
<li data-list-item-id="e1536f1bb4d9b9a40789ebe18a183e5d4"><a href="https://day.js.org/docs/en/plugin/is-same-or-before">IsSameOrBefore</a>
|
||||||
|
</li>
|
||||||
|
<li data-list-item-id="ec1f021ff7cf2edec5f8548843a1d5a0c"><a href="https://day.js.org/docs/en/plugin/quarter-of-year">QuarterOfYear</a>
|
||||||
|
</li>
|
||||||
|
<li data-list-item-id="e7cf8af04f2a71d8e380627cd6d3f6d04"><a href="https://day.js.org/docs/en/plugin/utc">UTC</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<aside class="admonition note">
|
||||||
|
<p>If another Day.js plugin might be needed for scripting purposes, feel
|
||||||
|
free to open a feature request for it. Depending on the size of the plugin
|
||||||
|
and the potential use of it inside the Trilium code base, it has a chance
|
||||||
|
of being integrated.</p>
|
||||||
|
</aside>
|
||||||
@ -11,8 +11,7 @@ import AbstractBeccaEntity from "./abstract_becca_entity.js";
|
|||||||
import BRevision from "./brevision.js";
|
import BRevision from "./brevision.js";
|
||||||
import BAttachment from "./battachment.js";
|
import BAttachment from "./battachment.js";
|
||||||
import TaskContext from "../../services/task_context.js";
|
import TaskContext from "../../services/task_context.js";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import utc from "dayjs/plugin/utc.js";
|
|
||||||
import eventService from "../../services/events.js";
|
import eventService from "../../services/events.js";
|
||||||
import type { AttachmentRow, AttributeType, CloneResponse, NoteRow, NoteType, RevisionRow } from "@triliumnext/commons";
|
import type { AttachmentRow, AttributeType, CloneResponse, NoteRow, NoteType, RevisionRow } from "@triliumnext/commons";
|
||||||
import type BBranch from "./bbranch.js";
|
import type BBranch from "./bbranch.js";
|
||||||
@ -22,7 +21,6 @@ import searchService from "../../services/search/services/search.js";
|
|||||||
import cloningService from "../../services/cloning.js";
|
import cloningService from "../../services/cloning.js";
|
||||||
import noteService from "../../services/notes.js";
|
import noteService from "../../services/notes.js";
|
||||||
import handlers from "../../services/handlers.js";
|
import handlers from "../../services/handlers.js";
|
||||||
dayjs.extend(utc);
|
|
||||||
|
|
||||||
const LABEL = "label";
|
const LABEL = "label";
|
||||||
const RELATION = "relation";
|
const RELATION = "relation";
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { beforeAll, describe, expect, it } from "vitest";
|
import { beforeAll, describe, expect, it } from "vitest";
|
||||||
import supertest, { type Response } from "supertest";
|
import supertest, { type Response } from "supertest";
|
||||||
import type { Application } from "express";
|
import type { Application } from "express";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import { type SQLiteSessionStore } from "./session_parser.js";
|
import { type SQLiteSessionStore } from "./session_parser.js";
|
||||||
import { SessionData } from "express-session";
|
import { SessionData } from "express-session";
|
||||||
import cls from "../services/cls.js";
|
import cls from "../services/cls.js";
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import dateNoteService from "./date_notes.js";
|
|||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import config from "./config.js";
|
import config from "./config.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import xml2js from "xml2js";
|
import xml2js from "xml2js";
|
||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
import cloningService from "./cloning.js";
|
import cloningService from "./cloning.js";
|
||||||
@ -37,17 +37,6 @@ import type Becca from "../becca/becca-interface.js";
|
|||||||
import type { NoteParams } from "./note-interface.js";
|
import type { NoteParams } from "./note-interface.js";
|
||||||
import type { ApiParams } from "./backend_script_api_interface.js";
|
import type { ApiParams } from "./backend_script_api_interface.js";
|
||||||
|
|
||||||
// Dayjs plugins
|
|
||||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
|
||||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
|
||||||
import isBetween from "dayjs/plugin/isBetween";
|
|
||||||
import advancedFormat from "dayjs/plugin/advancedFormat.js";
|
|
||||||
|
|
||||||
dayjs.extend(isSameOrBefore);
|
|
||||||
dayjs.extend(isSameOrAfter);
|
|
||||||
dayjs.extend(isBetween);
|
|
||||||
dayjs.extend(advancedFormat);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A whole number
|
* A whole number
|
||||||
* @typedef {number} int
|
* @typedef {number} int
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
import type BNote from "../becca/entities/bnote.js";
|
import type BNote from "../becca/entities/bnote.js";
|
||||||
|
|||||||
@ -1,27 +1,17 @@
|
|||||||
import type BNote from "../becca/entities/bnote.js";
|
import type BNote from "../becca/entities/bnote.js";
|
||||||
import type { Dayjs } from "dayjs";
|
|
||||||
|
|
||||||
import advancedFormat from "dayjs/plugin/advancedFormat.js";
|
|
||||||
import attributeService from "./attributes.js";
|
import attributeService from "./attributes.js";
|
||||||
import cloningService from "./cloning.js";
|
import cloningService from "./cloning.js";
|
||||||
import dayjs from "dayjs";
|
import { dayjs, Dayjs } from "@triliumnext/commons";
|
||||||
import hoistedNoteService from "./hoisted_note.js";
|
import hoistedNoteService from "./hoisted_note.js";
|
||||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
|
|
||||||
import noteService from "./notes.js";
|
import noteService from "./notes.js";
|
||||||
import optionService from "./options.js";
|
import optionService from "./options.js";
|
||||||
import protectedSessionService from "./protected_session.js";
|
import protectedSessionService from "./protected_session.js";
|
||||||
import quarterOfYear from "dayjs/plugin/quarterOfYear.js";
|
|
||||||
import searchContext from "../services/search/search_context.js";
|
import searchContext from "../services/search/search_context.js";
|
||||||
import searchService from "../services/search/services/search.js";
|
import searchService from "../services/search/services/search.js";
|
||||||
import sql from "./sql.js";
|
import sql from "./sql.js";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { ordinal } from "./i18n.js";
|
import { ordinal } from "./i18n.js";
|
||||||
import isoWeek from "dayjs/plugin/isoWeek.js";
|
|
||||||
|
|
||||||
dayjs.extend(isSameOrAfter);
|
|
||||||
dayjs.extend(quarterOfYear);
|
|
||||||
dayjs.extend(advancedFormat);
|
|
||||||
dayjs.extend(isoWeek);
|
|
||||||
|
|
||||||
const CALENDAR_ROOT_LABEL = "calendarRoot";
|
const CALENDAR_ROOT_LABEL = "calendarRoot";
|
||||||
const YEAR_LABEL = "yearNote";
|
const YEAR_LABEL = "yearNote";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import cls from "./cls.js";
|
import cls from "./cls.js";
|
||||||
|
|
||||||
const LOCAL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss.SSSZZ";
|
const LOCAL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss.SSSZZ";
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { LOCALES } from "@triliumnext/commons";
|
import { LOCALES } from "@triliumnext/commons";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { DAYJS_LOADER } from "./i18n";
|
|
||||||
|
|
||||||
describe("i18n", () => {
|
describe("i18n", () => {
|
||||||
it("translations are valid JSON", () => {
|
it("translations are valid JSON", () => {
|
||||||
@ -16,13 +15,4 @@ describe("i18n", () => {
|
|||||||
.not.toThrow();
|
.not.toThrow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("all dayjs locales are valid", async () => {
|
|
||||||
for (const locale of LOCALES) {
|
|
||||||
const dayjsLoader = DAYJS_LOADER[locale.id];
|
|
||||||
expect(dayjsLoader, `Locale ${locale.id} missing.`).toBeDefined();
|
|
||||||
|
|
||||||
await dayjsLoader();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,31 +4,7 @@ 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, type LOCALE_IDS } from "@triliumnext/commons";
|
import { dayjs, LOCALES, setDayjsLocale, type Dayjs, type Locale, type LOCALE_IDS } from "@triliumnext/commons";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
|
||||||
|
|
||||||
// When adding a new locale, prefer the version with hyphen instead of underscore.
|
|
||||||
export const DAYJS_LOADER: Record<LOCALE_IDS, () => Promise<typeof import("dayjs/locale/en.js")>> = {
|
|
||||||
"ar": () => import("dayjs/locale/ar.js"),
|
|
||||||
"cn": () => import("dayjs/locale/zh-cn.js"),
|
|
||||||
"de": () => import("dayjs/locale/de.js"),
|
|
||||||
"en": () => import("dayjs/locale/en.js"),
|
|
||||||
"en-GB": () => import("dayjs/locale/en-gb.js"),
|
|
||||||
"en_rtl": () => import("dayjs/locale/en.js"),
|
|
||||||
"es": () => import("dayjs/locale/es.js"),
|
|
||||||
"fa": () => import("dayjs/locale/fa.js"),
|
|
||||||
"fr": () => import("dayjs/locale/fr.js"),
|
|
||||||
"it": () => import("dayjs/locale/it.js"),
|
|
||||||
"he": () => import("dayjs/locale/he.js"),
|
|
||||||
"ja": () => import("dayjs/locale/ja.js"),
|
|
||||||
"ku": () => import("dayjs/locale/ku.js"),
|
|
||||||
"pt_br": () => import("dayjs/locale/pt-br.js"),
|
|
||||||
"pt": () => import("dayjs/locale/pt.js"),
|
|
||||||
"ro": () => import("dayjs/locale/ro.js"),
|
|
||||||
"ru": () => import("dayjs/locale/ru.js"),
|
|
||||||
"tw": () => import("dayjs/locale/zh-tw.js"),
|
|
||||||
"uk": () => import("dayjs/locale/uk.js"),
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function initializeTranslations() {
|
export async function initializeTranslations() {
|
||||||
const resourceDir = getResourceDir();
|
const resourceDir = getResourceDir();
|
||||||
@ -46,10 +22,7 @@ export async function initializeTranslations() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialize dayjs locale.
|
// Initialize dayjs locale.
|
||||||
const dayjsLocale = DAYJS_LOADER[locale];
|
await setDayjsLocale(locale);
|
||||||
if (dayjsLocale) {
|
|
||||||
dayjs.locale(await dayjsLocale());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ordinal(date: Dayjs) {
|
export function ordinal(date: Dayjs) {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import sax from "sax";
|
import sax from "sax";
|
||||||
import stream from "stream";
|
import stream from "stream";
|
||||||
import { Throttle } from "stream-throttle";
|
import { Throttle } from "stream-throttle";
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import BBranch from "../becca/entities/bbranch.js";
|
|||||||
import BNote from "../becca/entities/bnote.js";
|
import BNote from "../becca/entities/bnote.js";
|
||||||
import BAttribute from "../becca/entities/battribute.js";
|
import BAttribute from "../becca/entities/battribute.js";
|
||||||
import BAttachment from "../becca/entities/battachment.js";
|
import BAttachment from "../becca/entities/battachment.js";
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import htmlSanitizer from "./html_sanitizer.js";
|
import htmlSanitizer from "./html_sanitizer.js";
|
||||||
import ValidationError from "../errors/validation_error.js";
|
import ValidationError from "../errors/validation_error.js";
|
||||||
import noteTypesService from "./note_types.js";
|
import noteTypesService from "./note_types.js";
|
||||||
|
|||||||
@ -59,7 +59,7 @@ describe("Script", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("dayjs", () => {
|
describe("dayjs in backend scripts", () => {
|
||||||
const scriptNote = note("dayjs", {
|
const scriptNote = note("dayjs", {
|
||||||
type: "code",
|
type: "code",
|
||||||
mime: "application/javascript;env=backend",
|
mime: "application/javascript;env=backend",
|
||||||
@ -74,7 +74,7 @@ describe("Script", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("dayjs is-same-or-before", () => {
|
it("dayjs is-same-or-before plugin exists", () => {
|
||||||
cls.init(() => {
|
cls.init(() => {
|
||||||
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-01").isSameOrBefore(api.dayjs("2023-10-02"));`);
|
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-01").isSameOrBefore(api.dayjs("2023-10-02"));`);
|
||||||
expect(bundle).toBeDefined();
|
expect(bundle).toBeDefined();
|
||||||
@ -82,33 +82,5 @@ describe("Script", () => {
|
|||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("dayjs is-same-or-after", () => {
|
|
||||||
cls.init(() => {
|
|
||||||
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-02").isSameOrAfter(api.dayjs("2023-10-01"));`);
|
|
||||||
expect(bundle).toBeDefined();
|
|
||||||
const result = executeBundle(bundle!);
|
|
||||||
expect(result).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("dayjs is-between", () => {
|
|
||||||
cls.init(() => {
|
|
||||||
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-02").isBetween(api.dayjs("2023-10-01"), api.dayjs("2023-10-03"));`);
|
|
||||||
expect(bundle).toBeDefined();
|
|
||||||
const result = executeBundle(bundle!);
|
|
||||||
expect(result).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// advanced format
|
|
||||||
it("dayjs advanced format", () => {
|
|
||||||
cls.init(() => {
|
|
||||||
const bundle = getScriptBundle(scriptNote.note, true, "backend", [], `return api.dayjs("2023-10-01").format("Q");`);
|
|
||||||
expect(bundle).toBeDefined();
|
|
||||||
const result = executeBundle(bundle!);
|
|
||||||
expect(result).not.toBe("Q");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import dayjs from "dayjs";
|
import { dayjs } from "@triliumnext/commons";
|
||||||
import AndExp from "../expressions/and.js";
|
import AndExp from "../expressions/and.js";
|
||||||
import OrExp from "../expressions/or.js";
|
import OrExp from "../expressions/or.js";
|
||||||
import NotExp from "../expressions/not.js";
|
import NotExp from "../expressions/not.js";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Documentation
|
# Documentation
|
||||||
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/KJUp1g3csedB/Documentation_image.png" width="205" height="162">
|
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/rE2kRecw5n8A/Documentation_image.png" width="205" height="162">
|
||||||
|
|
||||||
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
|
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
|
||||||
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
|
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
|
||||||
|
|||||||
35
docs/User Guide/!!!meta.json
vendored
35
docs/User Guide/!!!meta.json
vendored
@ -15928,6 +15928,41 @@
|
|||||||
],
|
],
|
||||||
"dataFileName": "Backend API.dat",
|
"dataFileName": "Backend API.dat",
|
||||||
"attachments": []
|
"attachments": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"isClone": false,
|
||||||
|
"noteId": "ApVHZ8JY5ofC",
|
||||||
|
"notePath": [
|
||||||
|
"pOsGYCXsbNQG",
|
||||||
|
"CdNpE2pqjmI6",
|
||||||
|
"GLks18SNjxmC",
|
||||||
|
"ApVHZ8JY5ofC"
|
||||||
|
],
|
||||||
|
"title": "Day.js",
|
||||||
|
"notePosition": 30,
|
||||||
|
"prefix": null,
|
||||||
|
"isExpanded": false,
|
||||||
|
"type": "text",
|
||||||
|
"mime": "text/html",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"type": "label",
|
||||||
|
"name": "shareAlias",
|
||||||
|
"value": "day.js",
|
||||||
|
"isInheritable": false,
|
||||||
|
"position": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "label",
|
||||||
|
"name": "iconClass",
|
||||||
|
"value": "bx bx-calendar",
|
||||||
|
"isInheritable": false,
|
||||||
|
"position": 40
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"format": "markdown",
|
||||||
|
"dataFileName": "Day.js.md",
|
||||||
|
"attachments": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
31
docs/User Guide/User Guide/Scripting/Script API/Day.js.md
vendored
Normal file
31
docs/User Guide/User Guide/Scripting/Script API/Day.js.md
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Day.js
|
||||||
|
Day.js is a date manipulation library that's used by Trilium, but it's also shared with both front-end and back-end scripts. For more information about the library itself, consult the [official documentation](https://day.js.org/en/).
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
The `dayjs` method is provided directly in the `api` global:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const date = api.dayjs();
|
||||||
|
api.log(date.format("YYYY-MM-DD"));
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
Day.js uses a modular, plugin-based architecture. Generally these plugins must be imported, but this process doesn't work inside Trilium scripts due to the use of a bundler.
|
||||||
|
|
||||||
|
Since v0.100.0, the same set of plugins is available for both front-end and back-end scripts.
|
||||||
|
|
||||||
|
The following Day.js plugins are directly integrated into Trilium:
|
||||||
|
|
||||||
|
* [AdvancedFormat](https://day.js.org/docs/en/plugin/advanced-format)
|
||||||
|
* [Duration](https://day.js.org/docs/en/plugin/duration), since v0.100.0.
|
||||||
|
* [IsBetween](https://day.js.org/docs/en/plugin/is-between)
|
||||||
|
* [IsoWeek](https://day.js.org/docs/en/plugin/iso-week)
|
||||||
|
* [IsSameOrAfter](https://day.js.org/docs/en/plugin/is-same-or-after)
|
||||||
|
* [IsSameOrBefore](https://day.js.org/docs/en/plugin/is-same-or-before)
|
||||||
|
* [QuarterOfYear](https://day.js.org/docs/en/plugin/quarter-of-year)
|
||||||
|
* [UTC](https://day.js.org/docs/en/plugin/utc)
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If another Day.js plugin might be needed for scripting purposes, feel free to open a feature request for it. Depending on the size of the plugin and the potential use of it inside the Trilium code base, it has a chance of being integrated.
|
||||||
@ -10,5 +10,12 @@
|
|||||||
"name": "Trilium Notes Team",
|
"name": "Trilium Notes Team",
|
||||||
"email": "contact@eliandoran.me",
|
"email": "contact@eliandoran.me",
|
||||||
"url": "https://triliumnotes.org"
|
"url": "https://triliumnotes.org"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "vitest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dayjs": "1.11.19",
|
||||||
|
"dayjs-plugin-utc": "0.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,3 +11,4 @@ export * from "./lib/shared_constants.js";
|
|||||||
export * from "./lib/ws_api.js";
|
export * from "./lib/ws_api.js";
|
||||||
export * from "./lib/attribute_names.js";
|
export * from "./lib/attribute_names.js";
|
||||||
export * from "./lib/utils.js";
|
export * from "./lib/utils.js";
|
||||||
|
export * from "./lib/dayjs.js";
|
||||||
|
|||||||
59
packages/commons/src/lib/dayjs.spec.ts
Normal file
59
packages/commons/src/lib/dayjs.spec.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/advancedFormat.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/duration.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/isBetween.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/isoWeek.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/isSameOrAfter.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/isSameOrBefore.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/quarterOfYear.d.ts" />
|
||||||
|
/// <reference types="../../../../node_modules/dayjs/plugin/utc.d.ts" />
|
||||||
|
|
||||||
|
import { LOCALES } from "./i18n.js";
|
||||||
|
import { DAYJS_LOADER, dayjs } from "./dayjs.js";
|
||||||
|
|
||||||
|
describe("dayjs", () => {
|
||||||
|
it("all dayjs locales are valid", async () => {
|
||||||
|
for (const locale of LOCALES) {
|
||||||
|
const dayjsLoader = DAYJS_LOADER[locale.id];
|
||||||
|
expect(dayjsLoader, `Locale ${locale.id} missing.`).toBeDefined();
|
||||||
|
|
||||||
|
await dayjsLoader();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Plugins", () => {
|
||||||
|
it("advanced format is available", () => {
|
||||||
|
expect(dayjs("2023-10-01").format("Q")).not.toBe("Q");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("duration plugin is available", () => {
|
||||||
|
const d = dayjs.duration({ hours: 2, minutes: 30 });
|
||||||
|
expect(d.asMinutes()).toBe(150);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is-between is available", () => {
|
||||||
|
expect(dayjs("2023-10-02").isBetween(dayjs("2023-10-01"), dayjs("2023-10-03"))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("iso-week is available", () => {
|
||||||
|
// ISO week number: 2023-01-01 is ISO week 52 of previous year
|
||||||
|
expect(dayjs("2023-01-01").isoWeek()).toBe(52);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is-same-or-before is available", () => {
|
||||||
|
expect(dayjs("2023-10-01").isSameOrBefore(dayjs("2023-10-02"))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is-same-or-after is available", () => {
|
||||||
|
expect(dayjs("2023-10-02").isSameOrAfter(dayjs("2023-10-01"))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("quarter-year is available", () => {
|
||||||
|
expect(dayjs("2023-05-15").quarter()).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("utc is available", () => {
|
||||||
|
const utcDate = dayjs("2023-10-01T12:00:00").utc();
|
||||||
|
expect(utcDate.utcOffset()).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
68
packages/commons/src/lib/dayjs.ts
Normal file
68
packages/commons/src/lib/dayjs.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { default as dayjs, type Dayjs } from "dayjs";
|
||||||
|
|
||||||
|
import "dayjs/plugin/advancedFormat";
|
||||||
|
import "dayjs/plugin/duration";
|
||||||
|
import "dayjs/plugin/isBetween";
|
||||||
|
import "dayjs/plugin/isoWeek";
|
||||||
|
import "dayjs/plugin/isSameOrAfter";
|
||||||
|
import "dayjs/plugin/isSameOrBefore";
|
||||||
|
import "dayjs/plugin/quarterOfYear";
|
||||||
|
import "dayjs/plugin/utc";
|
||||||
|
|
||||||
|
//#region Plugins
|
||||||
|
import advancedFormat from "dayjs/plugin/advancedFormat.js";
|
||||||
|
import duration from "dayjs/plugin/duration.js";
|
||||||
|
import isBetween from "dayjs/plugin/isBetween.js";
|
||||||
|
import isoWeek from "dayjs/plugin/isoWeek.js";
|
||||||
|
import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js";
|
||||||
|
import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js";
|
||||||
|
import quarterOfYear from "dayjs/plugin/quarterOfYear.js";
|
||||||
|
import utc from "dayjs/plugin/utc.js";
|
||||||
|
import { LOCALE_IDS } from "./i18n.js";
|
||||||
|
|
||||||
|
dayjs.extend(advancedFormat);
|
||||||
|
dayjs.extend(duration);
|
||||||
|
dayjs.extend(isBetween);
|
||||||
|
dayjs.extend(isoWeek);
|
||||||
|
dayjs.extend(isSameOrAfter);
|
||||||
|
dayjs.extend(isSameOrBefore);
|
||||||
|
dayjs.extend(quarterOfYear);
|
||||||
|
dayjs.extend(utc);
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Locales
|
||||||
|
export const DAYJS_LOADER: Record<LOCALE_IDS, () => Promise<typeof import("dayjs/locale/en.js")>> = {
|
||||||
|
"ar": () => import("dayjs/locale/ar.js"),
|
||||||
|
"cn": () => import("dayjs/locale/zh-cn.js"),
|
||||||
|
"de": () => import("dayjs/locale/de.js"),
|
||||||
|
"en": () => import("dayjs/locale/en.js"),
|
||||||
|
"en-GB": () => import("dayjs/locale/en-gb.js"),
|
||||||
|
"en_rtl": () => import("dayjs/locale/en.js"),
|
||||||
|
"es": () => import("dayjs/locale/es.js"),
|
||||||
|
"fa": () => import("dayjs/locale/fa.js"),
|
||||||
|
"fr": () => import("dayjs/locale/fr.js"),
|
||||||
|
"it": () => import("dayjs/locale/it.js"),
|
||||||
|
"he": () => import("dayjs/locale/he.js"),
|
||||||
|
"ja": () => import("dayjs/locale/ja.js"),
|
||||||
|
"ku": () => import("dayjs/locale/ku.js"),
|
||||||
|
"pt_br": () => import("dayjs/locale/pt-br.js"),
|
||||||
|
"pt": () => import("dayjs/locale/pt.js"),
|
||||||
|
"ro": () => import("dayjs/locale/ro.js"),
|
||||||
|
"ru": () => import("dayjs/locale/ru.js"),
|
||||||
|
"tw": () => import("dayjs/locale/zh-tw.js"),
|
||||||
|
"uk": () => import("dayjs/locale/uk.js"),
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setDayjsLocale(locale: LOCALE_IDS) {
|
||||||
|
const dayjsLocale = DAYJS_LOADER[locale];
|
||||||
|
if (dayjsLocale) {
|
||||||
|
dayjs.locale(await dayjsLocale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
export {
|
||||||
|
dayjs,
|
||||||
|
Dayjs,
|
||||||
|
setDayjsLocale
|
||||||
|
};
|
||||||
@ -11,6 +11,7 @@ export interface Locale {
|
|||||||
electronLocale?: "en" | "de" | "es" | "fr" | "zh_CN" | "zh_TW" | "ro" | "af" | "am" | "ar" | "bg" | "bn" | "ca" | "cs" | "da" | "el" | "en_GB" | "es_419" | "et" | "fa" | "fi" | "fil" | "gu" | "he" | "hi" | "hr" | "hu" | "id" | "it" | "ja" | "kn" | "ko" | "lt" | "lv" | "ml" | "mr" | "ms" | "nb" | "nl" | "pl" | "pt_BR" | "pt_PT" | "ru" | "sk" | "sl" | "sr" | "sv" | "sw" | "ta" | "te" | "th" | "tr" | "uk" | "ur" | "vi";
|
electronLocale?: "en" | "de" | "es" | "fr" | "zh_CN" | "zh_TW" | "ro" | "af" | "am" | "ar" | "bg" | "bn" | "ca" | "cs" | "da" | "el" | "en_GB" | "es_419" | "et" | "fa" | "fi" | "fil" | "gu" | "he" | "hi" | "hr" | "hu" | "id" | "it" | "ja" | "kn" | "ko" | "lt" | "lv" | "ml" | "mr" | "ms" | "nb" | "nl" | "pl" | "pt_BR" | "pt_PT" | "ru" | "sk" | "sl" | "sr" | "sv" | "sw" | "ta" | "te" | "th" | "tr" | "uk" | "ur" | "vi";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When adding a new locale, prefer the version with hyphen instead of underscore.
|
||||||
const UNSORTED_LOCALES = [
|
const UNSORTED_LOCALES = [
|
||||||
{ id: "cn", name: "简体中文", electronLocale: "zh_CN" },
|
{ id: "cn", name: "简体中文", electronLocale: "zh_CN" },
|
||||||
{ id: "de", name: "Deutsch", electronLocale: "de" },
|
{ id: "de", name: "Deutsch", electronLocale: "de" },
|
||||||
|
|||||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
@ -226,12 +226,6 @@ importers:
|
|||||||
color:
|
color:
|
||||||
specifier: 5.0.3
|
specifier: 5.0.3
|
||||||
version: 5.0.3
|
version: 5.0.3
|
||||||
dayjs:
|
|
||||||
specifier: 1.11.19
|
|
||||||
version: 1.11.19
|
|
||||||
dayjs-plugin-utc:
|
|
||||||
specifier: 0.1.2
|
|
||||||
version: 0.1.2
|
|
||||||
debounce:
|
debounce:
|
||||||
specifier: 3.0.0
|
specifier: 3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
@ -636,9 +630,6 @@ importers:
|
|||||||
csrf-csrf:
|
csrf-csrf:
|
||||||
specifier: 3.2.2
|
specifier: 3.2.2
|
||||||
version: 3.2.2
|
version: 3.2.2
|
||||||
dayjs:
|
|
||||||
specifier: 1.11.19
|
|
||||||
version: 1.11.19
|
|
||||||
debounce:
|
debounce:
|
||||||
specifier: 3.0.0
|
specifier: 3.0.0
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
@ -1328,7 +1319,14 @@ importers:
|
|||||||
specifier: 9.39.1
|
specifier: 9.39.1
|
||||||
version: 9.39.1
|
version: 9.39.1
|
||||||
|
|
||||||
packages/commons: {}
|
packages/commons:
|
||||||
|
dependencies:
|
||||||
|
dayjs:
|
||||||
|
specifier: 1.11.19
|
||||||
|
version: 1.11.19
|
||||||
|
dayjs-plugin-utc:
|
||||||
|
specifier: 0.1.2
|
||||||
|
version: 0.1.2
|
||||||
|
|
||||||
packages/express-partial-content:
|
packages/express-partial-content:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15150,6 +15148,8 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-core': 47.2.0
|
'@ckeditor/ckeditor5-core': 47.2.0
|
||||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-code-block@47.2.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
'@ckeditor/ckeditor5-code-block@47.2.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15214,8 +15214,6 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||||
'@ckeditor/ckeditor5-watchdog': 47.2.0
|
'@ckeditor/ckeditor5-watchdog': 47.2.0
|
||||||
es-toolkit: 1.39.5
|
es-toolkit: 1.39.5
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)':
|
'@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15719,6 +15717,8 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||||
'@ckeditor/ckeditor5-widget': 47.2.0
|
'@ckeditor/ckeditor5-widget': 47.2.0
|
||||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-mention@47.2.0(patch_hash=5981fb59ba35829e4dff1d39cf771000f8a8fdfa7a34b51d8af9549541f2d62d)':
|
'@ckeditor/ckeditor5-mention@47.2.0(patch_hash=5981fb59ba35829e4dff1d39cf771000f8a8fdfa7a34b51d8af9549541f2d62d)':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user