diff --git a/apps/client/src/services/mime_type_definitions.ts b/apps/client/src/services/mime_type_definitions.ts index a9b30fb97..04b55b456 100644 --- a/apps/client/src/services/mime_type_definitions.ts +++ b/apps/client/src/services/mime_type_definitions.ts @@ -1,21 +1,12 @@ // TODO: deduplicate with /src/services/import/mime_type_definitions.ts +import type { MimeTypeDefinition } from "@triliumnext/commons"; + /** * A pseudo-MIME type which is used in the editor to automatically determine the language used in code blocks via heuristics. */ export const MIME_TYPE_AUTO = "text-x-trilium-auto"; -export interface MimeTypeDefinition { - default?: boolean; - title: string; - mime: string; - /** The name of the language/mime type as defined by highlight.js (or one of the aliases), in order to be used for syntax highlighting such as inside code blocks. */ - highlightJs?: string; - /** If specified, will load the corresponding highlight.js file from the `libraries/highlightjs/${id}.js` instead of `node_modules/@highlightjs/cdn-assets/languages/${id}.min.js`. */ - highlightJsSource?: "libraries"; - /** If specified, will load the corresponding highlight file from the given path instead of `node_modules`. */ - codeMirrorSource?: string; -} /** * For highlight.js-supported languages, see https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md. diff --git a/apps/client/src/services/mime_types.ts b/apps/client/src/services/mime_types.ts index f0f318141..367d5e5ec 100644 --- a/apps/client/src/services/mime_types.ts +++ b/apps/client/src/services/mime_types.ts @@ -1,13 +1,7 @@ -import { MIME_TYPE_AUTO, MIME_TYPES_DICT, normalizeMimeTypeForCKEditor, type MimeTypeDefinition } from "./mime_type_definitions.js"; +import type { MimeType } from "@triliumnext/commons"; +import { MIME_TYPE_AUTO, MIME_TYPES_DICT, normalizeMimeTypeForCKEditor } from "./mime_type_definitions.js"; import options from "./options.js"; -interface MimeType extends MimeTypeDefinition { - /** - * True if this mime type was enabled by the user in the "Available MIME types in the dropdown" option in the Code Notes settings. - */ - enabled: boolean; -} - let mimeTypes: MimeType[] | null = null; function loadMimeTypes() { diff --git a/apps/client/src/services/syntax_highlight.ts b/apps/client/src/services/syntax_highlight.ts index 213518d58..866344a72 100644 --- a/apps/client/src/services/syntax_highlight.ts +++ b/apps/client/src/services/syntax_highlight.ts @@ -1,4 +1,4 @@ -import { highlight, highlightAuto } from "@triliumnext/highlightjs"; +import { ensureMimeTypes, highlight, highlightAuto } from "@triliumnext/highlightjs"; import mime_types from "./mime_types.js"; import options from "./options.js"; @@ -47,12 +47,8 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery await import("@triliumnext/highlightjs"), + loadHighlightJs: async () => { + ensureMimeTypesForHighlighting(); + return await import("@triliumnext/highlightjs"); + }, mapLanguageName: getHighlightJsNameForMime, defaultMimeType: MIME_TYPE_AUTO, enabled: isSyntaxHighlightEnabled diff --git a/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts b/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts index 361a06cd1..d96f9285c 100644 --- a/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts +++ b/apps/client/src/widgets/type_widgets/options/text_notes/code_block.ts @@ -3,6 +3,7 @@ import { t } from "../../../../services/i18n.js"; import library_loader from "../../../../services/library_loader.js"; import server from "../../../../services/server.js"; import OptionsWidget from "../options_widget.js"; +import { ensureMimeTypesForHighlighting } from "../../../../services/syntax_highlight.js"; const SAMPLE_LANGUAGE = "javascript"; const SAMPLE_CODE = `\ @@ -91,11 +92,14 @@ export default class CodeBlockOptions extends OptionsWidget { #setupPreview(shouldEnableSyntaxHighlight: boolean) { const text = SAMPLE_CODE; if (shouldEnableSyntaxHighlight) { - import("@triliumnext/highlightjs").then((hljs) => { + import("@triliumnext/highlightjs").then(async (hljs) => { + await ensureMimeTypesForHighlighting(); const highlightedText = hljs.highlight(text, { language: SAMPLE_LANGUAGE }); - this.$sampleEl.html(highlightedText.value); + if (highlightedText) { + this.$sampleEl.html(highlightedText.value); + } }); } else { this.$sampleEl.text(text); diff --git a/apps/client/tsconfig.app.json b/apps/client/tsconfig.app.json index 11ed3a1b4..e51437ed1 100644 --- a/apps/client/tsconfig.app.json +++ b/apps/client/tsconfig.app.json @@ -34,6 +34,9 @@ "src/**/*.ts" ], "references": [ + { + "path": "../../packages/highlightjs/tsconfig.lib.json" + }, { "path": "../../packages/codemirror/tsconfig.lib.json" }, diff --git a/apps/client/tsconfig.json b/apps/client/tsconfig.json index 05472be69..c51d8fdcd 100644 --- a/apps/client/tsconfig.json +++ b/apps/client/tsconfig.json @@ -3,6 +3,9 @@ "files": [], "include": [], "references": [ + { + "path": "../../packages/highlightjs" + }, { "path": "../../packages/codemirror" }, diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index 325a06740..96ba3325f 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -3,4 +3,5 @@ export * from "./lib/options_interface.js"; export * from "./lib/keyboard_actions_interface.js"; export * from "./lib/hidden_subtree.js"; export * from "./lib/rows.js"; -export * from "./lib/test-utils.js" +export * from "./lib/test-utils.js"; +export * from "./lib/mime_type.js"; diff --git a/packages/commons/src/lib/mime_type.ts b/packages/commons/src/lib/mime_type.ts new file mode 100644 index 000000000..588affc83 --- /dev/null +++ b/packages/commons/src/lib/mime_type.ts @@ -0,0 +1,18 @@ +export interface MimeTypeDefinition { + default?: boolean; + title: string; + mime: string; + /** The name of the language/mime type as defined by highlight.js (or one of the aliases), in order to be used for syntax highlighting such as inside code blocks. */ + highlightJs?: string; + /** If specified, will load the corresponding highlight.js file from the `libraries/highlightjs/${id}.js` instead of `node_modules/@highlightjs/cdn-assets/languages/${id}.min.js`. */ + highlightJsSource?: "libraries"; + /** If specified, will load the corresponding highlight file from the given path instead of `node_modules`. */ + codeMirrorSource?: string; +} + +export interface MimeType extends MimeTypeDefinition { + /** + * True if this mime type was enabled by the user in the "Available MIME types in the dropdown" option in the Code Notes settings. + */ + enabled: boolean; +} diff --git a/packages/highlightjs/package.json b/packages/highlightjs/package.json index ec07642fa..cc52b96a3 100644 --- a/packages/highlightjs/package.json +++ b/packages/highlightjs/package.json @@ -19,6 +19,7 @@ "name": "highlightjs" }, "dependencies": { + "@triliumnext/commons": "workspace:*", "highlight.js": "11.11.1" } } diff --git a/packages/highlightjs/src/index.ts b/packages/highlightjs/src/index.ts index cd59367e1..2bf98c154 100644 --- a/packages/highlightjs/src/index.ts +++ b/packages/highlightjs/src/index.ts @@ -1,3 +1,46 @@ import hljs from "../node_modules/highlight.js/es/core.js"; +import type { MimeType } from "@triliumnext/commons"; +import definitions from "./syntax_highlighting.js"; +import { type HighlightOptions } from "highlight.js"; -export const { highlight, highlightAuto } = hljs; +const registeredMimeTypes = new Set(); +const unsupportedMimeTypes = new Set(); + +export async function ensureMimeTypes(mimeTypes: MimeType[]) { + for (const mimeType of mimeTypes) { + if (!mimeType.enabled) { + continue; + } + + const mime = mimeType.mime; + if (registeredMimeTypes.has(mime)) { + continue; + } + + registeredMimeTypes.add(mime); + const loader = definitions[mime]; + if (!loader) { + unsupportedMimeTypes.add(mime); + continue; + } + + const language = (await loader).default; + console.info(`Registered highlighting for ${mime}.`); + hljs.registerLanguage(mime, language); + } +} + +export function highlight(code: string, options: HighlightOptions) { + if (unsupportedMimeTypes.has(options.language)) { + return null; + } + + if (!registeredMimeTypes.has(options.language)) { + console.warn(`Unable to find highlighting for ${code}.`); + return null; + } + + return hljs.highlight(code, options); +} + +export const { highlightAuto } = hljs; diff --git a/packages/highlightjs/tsconfig.json b/packages/highlightjs/tsconfig.json index 62ebbd946..fe54eef82 100644 --- a/packages/highlightjs/tsconfig.json +++ b/packages/highlightjs/tsconfig.json @@ -3,6 +3,9 @@ "files": [], "include": [], "references": [ + { + "path": "../commons" + }, { "path": "./tsconfig.lib.json" }, diff --git a/packages/highlightjs/tsconfig.lib.json b/packages/highlightjs/tsconfig.lib.json index f0a69a32c..f46b85d22 100644 --- a/packages/highlightjs/tsconfig.lib.json +++ b/packages/highlightjs/tsconfig.lib.json @@ -17,7 +17,11 @@ "include": [ "src/**/*.ts" ], - "references": [], + "references": [ + { + "path": "../commons/tsconfig.lib.json" + } + ], "exclude": [ "vite.config.ts", "vite.config.mts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b857fe13..b9251fd8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1283,6 +1283,9 @@ importers: packages/highlightjs: dependencies: + '@triliumnext/commons': + specifier: workspace:* + version: link:../commons highlight.js: specifier: 11.11.1 version: 11.11.1