diff --git a/Makefile b/Makefile index 926e24cc..12f63a7f 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,8 @@ clean: check: # Frontend type check npm run check + # Frontend lint + npx biome lint . # Backend lint go vet diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..caf4c0b1 --- /dev/null +++ b/biome.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.6/schema.json", + "formatter": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off", + "noAssignInExpressions": "off", + "noImplicitAnyLet": "off", + "noSelfCompare": "off", + "noShadowRestrictedNames": "off", + "noTemplateCurlyInString": "off", + "noThenProperty": "off", + "useIterableCallbackReturn": "off" + }, + "style": { + "noNonNullAssertion": "off" + }, + "complexity": { + "noBannedTypes": "off" + }, + "a11y": { + "recommended": false + }, + "correctness": { + "noInnerDeclarations": "off" + } + } + }, + "files": { + "includes": [ + "**", + "!**/node_modules", + "!**/dist", + "!**/client_bundle", + "!**/website_build", + "!**/*.html" + ] + } +} diff --git a/build_client.ts b/build_client.ts index 723d6891..a08bd714 100644 --- a/build_client.ts +++ b/build_client.ts @@ -1,6 +1,6 @@ import { cp, mkdir, readFile, writeFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; -import { dirname, resolve } from "node:path"; +import { dirname, } from "node:path"; import * as sass from "sass"; import * as esbuild from "esbuild"; diff --git a/build_plugs_libraries.ts b/build_plugs_libraries.ts index 76cf62bf..f41f7354 100644 --- a/build_plugs_libraries.ts +++ b/build_plugs_libraries.ts @@ -26,8 +26,8 @@ if (isMain) { strict: false, }); - const manifests = builtinPlugNames.map((name) => - `./plugs/${name}/${name}.plug.yaml` + const manifests = builtinPlugNames.map( + (name) => `./plugs/${name}/${name}.plug.yaml`, ); const plugBundlePath = "client_bundle/base_fs"; @@ -44,14 +44,10 @@ if (isMain) { }); // Build the plugs - await compileManifests( - manifests, - targetDir, - { - debug: args.debug as boolean | undefined, - info: args.info as boolean | undefined, - }, - ); + await compileManifests(manifests, targetDir, { + debug: args.debug as boolean | undefined, + info: args.info as boolean | undefined, + }); esbuild.stop(); } @@ -62,16 +58,11 @@ export async function updateVersionFile() { }); let stdout = ""; - let stderr = ""; gitProcess.stdout?.on("data", (data) => { stdout += data.toString(); }); - gitProcess.stderr?.on("data", (data) => { - stderr += data.toString(); - }); - gitProcess.on("close", async (code) => { let commitVersion = stdout.trim(); @@ -82,9 +73,11 @@ export async function updateVersionFile() { const versionFilePath = "./public_version.ts"; // Write version to file with date in YYYY-MM-DDTHH-MM-SSZ format attached to the version - const versionContent = `export const publicVersion = "${commitVersion}-${ - new Date().toISOString().split(".")[0].replaceAll(":", "-").concat("Z") - }";`; + const versionContent = `export const publicVersion = "${commitVersion}-${new Date() + .toISOString() + .split(".")[0] + .replaceAll(":", "-") + .concat("Z")}";`; try { await writeFile(versionFilePath, versionContent, "utf-8"); diff --git a/client/boot.ts b/client/boot.ts index eb97fdf0..29082fc1 100644 --- a/client/boot.ts +++ b/client/boot.ts @@ -235,7 +235,6 @@ safeRun(async () => { logger.postToServer(".logs", "client"); }, 1000); } - // @ts-ignore: on purpose globalThis.client = client; clientProxy.setTarget(client); await client.init(encryptionKey); diff --git a/client/client.ts b/client/client.ts index 203c7a30..0dd02f8c 100644 --- a/client/client.ts +++ b/client/client.ts @@ -990,7 +990,7 @@ export class Client { } catch (e: any) { // If there is no document editor we will open the file raw if (e.message.includes("Couldn't find")) { - this.openUrl(fsEndpoint + "/" + path, !previousPath); + this.openUrl(`${fsEndpoint}/${path}`, !previousPath); } throw e; @@ -1211,7 +1211,7 @@ export class Client { // Deliberately not awaiting this function as destroying & last-save can be handled in the background this.documentEditor.destroy(); - // @ts-ignore: This is there the hacked type-guard from isDocumentEditor fails + // @ts-expect-error: This is there the hacked type-guard from isDocumentEditor fails this.documentEditor = null; this.rebuildEditorState(); @@ -1309,7 +1309,7 @@ export class Client { // Prepare separate " + `` ).join("\n\n"); this.ui.viewDispatch({ type: "set-ui-option", @@ -1430,7 +1430,7 @@ export class Client { if (!isMarkdownPath(pageState.path)) return; // We can't use getOffsetFromRef here, because it is asyncronous. - let pos: number | undefined = undefined; + let pos: number | undefined ; // Don't use getOffsetFromRef, so we can show error messages if (pageState.details?.type === "header") { @@ -1576,7 +1576,7 @@ export class Client { await new Promise((resolve) => { navigator.serviceWorker.addEventListener("message", async (e: any) => { const message: ServiceWorkerSourceMessage = e.data; - if (message.type == "dataWiped") { + if (message.type === "dataWiped") { console.log( "Got data wipe confirm, uninstalling service worker now", ); diff --git a/client/codemirror/code_copy.ts b/client/codemirror/code_copy.ts index 588a835a..ca41ca13 100644 --- a/client/codemirror/code_copy.ts +++ b/client/codemirror/code_copy.ts @@ -21,7 +21,7 @@ class CodeCopyWidget extends WidgetType { } override eq(other: CodeCopyWidget) { - return other.value == this.value; + return other.value === this.value; } toDOM() { @@ -68,7 +68,7 @@ function codeCopyDecoration( from, to, enter: (node) => { - if (node.name == "FencedCode") { + if (node.name === "FencedCode") { const textNodes = node.node.getChildren("CodeText"); const infoNode = node.node.getChild("CodeInfo"); @@ -116,7 +116,7 @@ export const codeCopyPlugin = (client: Client) => { update(update: ViewUpdate) { if ( update.docChanged || update.viewportChanged || - syntaxTree(update.startState) != syntaxTree(update.state) + syntaxTree(update.startState) !== syntaxTree(update.state) ) { this.decorations = codeCopyDecoration(update.view, client); } diff --git a/client/codemirror/editor_paste.ts b/client/codemirror/editor_paste.ts index 11bdf69c..1c2519f9 100644 --- a/client/codemirror/editor_paste.ts +++ b/client/codemirror/editor_paste.ts @@ -6,7 +6,7 @@ import type { Client } from "../client.ts"; import TurndownService from "turndown"; // With tables and task notation as well -// @ts-ignore - No type definitions available for this package +// @ts-expect-error - No type definitions available for this package import { tables, taskListItems } from "@joplin/turndown-plugin-gfm"; import { lezerToParseTree } from "../markdown_parser/parse_tree.ts"; import { @@ -58,7 +58,7 @@ async function doesFileExist( } const urlRegexp = - /^https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; + /^https?:\/\/[-a-zA-Z0-9@:%._+~#=]{1,256}([-a-zA-Z0-9()@:%_+.~#?&//=]*)/; // Known iOS Safari paste issue (unrelated to this implementation): https://voxpelli.com/2015/03/ios-safari-url-copy-paste-bug/ export const pasteLinkExtension = ViewPlugin.fromClass( @@ -84,12 +84,10 @@ export const pasteLinkExtension = ViewPlugin.fromClass( { from: from, to: to, - insert: `[${ - update.startState.sliceDoc( - selection.from, - selection.to, - ) - }](${pastedString})`, + insert: `[${update.startState.sliceDoc( + selection.from, + selection.to, + )}](${pastedString})`, }, ], }); @@ -151,9 +149,8 @@ export function documentExtension(editor: Client) { editor.editorView.state.selection.main.from, ); if (currentNode) { - const fencedParentNode = findParentMatching( - currentNode, - (t) => ["FrontMatter", "FencedCode"].includes(t.type!), + const fencedParentNode = findParentMatching(currentNode, (t) => + ["FrontMatter", "FencedCode"].includes(t.type!), ); if ( fencedParentNode || @@ -164,8 +161,9 @@ export function documentExtension(editor: Client) { } } - const markdown = striptHtmlComments(turndownService.turndown(richText)) - .trim(); + const markdown = striptHtmlComments( + turndownService.turndown(richText), + ).trim(); const view = editor.editorView; const selection = view.state.selection.main; view.dispatch({ @@ -298,7 +296,7 @@ export function documentExtension(editor: Client) { await editor.space.writeDocument(finalFilePath, file.content); let documentMarkdown = `[[${finalFilePath}]]`; if (file.contentType.startsWith("image/")) { - documentMarkdown = "!" + documentMarkdown; + documentMarkdown = `!${documentMarkdown}`; } editor.editorView.dispatch({ changes: [ diff --git a/client/codemirror/editor_state.ts b/client/codemirror/editor_state.ts index 5a42802c..75ce5f32 100644 --- a/client/codemirror/editor_state.ts +++ b/client/codemirror/editor_state.ts @@ -145,7 +145,7 @@ export function createEditorState( ], optionClass(completion: any) { if (completion.cssClass) { - return "sb-decorated-object " + completion.cssClass; + return `sb-decorated-object ${completion.cssClass}`; } else { return ""; } diff --git a/client/codemirror/line_wrapper.ts b/client/codemirror/line_wrapper.ts index da475fa7..4d854a3c 100644 --- a/client/codemirror/line_wrapper.ts +++ b/client/codemirror/line_wrapper.ts @@ -17,7 +17,7 @@ export function lineWrapper(wrapElements: WrapElement[]) { syntaxTree(state).iterate({ enter: ({ type, from, to }) => { for (const wrapElement of wrapElements) { - if (type.name == wrapElement.selector) { + if (type.name === wrapElement.selector) { if (wrapElement.nesting) { elementStack.push(type.name); } @@ -40,7 +40,7 @@ export function lineWrapper(wrapElements: WrapElement[]) { }, leave({ type }) { for (const wrapElement of wrapElements) { - if (type.name == wrapElement.selector && wrapElement.nesting) { + if (type.name === wrapElement.selector && wrapElement.nesting) { elementStack.pop(); } } diff --git a/client/codemirror/lua_widget.ts b/client/codemirror/lua_widget.ts index eff36158..9e97ceb2 100644 --- a/client/codemirror/lua_widget.ts +++ b/client/codemirror/lua_widget.ts @@ -119,7 +119,7 @@ export class LuaWidget extends WidgetType { let html: HTMLElement | undefined; let block = false; - let copyContent: string | undefined = undefined; + let copyContent: string | undefined ; // Normalization if (typeof widgetContent === "string" || !widgetContent._isWidget) { @@ -375,7 +375,7 @@ export function renderExpressionResult(result: any): Promise { // Not-object array, let's render it as a Markdown list return Promise.resolve(result.map((item) => `- ${item}`).join("\n")); } else { - return Promise.resolve("" + result); + return Promise.resolve(`${result}`); } } diff --git a/client/codemirror/smart_quotes.ts b/client/codemirror/smart_quotes.ts index 06199950..e031fc8e 100644 --- a/client/codemirror/smart_quotes.ts +++ b/client/codemirror/smart_quotes.ts @@ -62,7 +62,7 @@ function keyBindingForQuote( ), }; } else { - const quote = (/\W/.exec(chBefore) && !/[!\?,\.\-=“]/.exec(chBefore)) + const quote = (/\W/.exec(chBefore) && !/[!?,.\-=“]/.exec(chBefore)) ? left : right; diff --git a/client/codemirror/wiki_link_processor.ts b/client/codemirror/wiki_link_processor.ts index 8c9f5b44..fccd3ea5 100644 --- a/client/codemirror/wiki_link_processor.ts +++ b/client/codemirror/wiki_link_processor.ts @@ -121,7 +121,7 @@ export function processWikiLink(options: WikiLinkProcessorOptions): any[] { text: linkText, title: helpText, href: ref ? encodePageURI(encodeRef(ref)) : undefined, - cssClass: "sb-wiki-link " + css, + cssClass: `sb-wiki-link ${css}`, from: matchFrom, callback: (e) => callback(e, ref), }), diff --git a/client/components/anything_picker.tsx b/client/components/anything_picker.tsx index 2966ea46..7b182798 100644 --- a/client/components/anything_picker.tsx +++ b/client/components/anything_picker.tsx @@ -106,7 +106,7 @@ export function AnythingPicker({ aliases = aliases.concat(pageMeta.aliases); } if (aliases.length > 0) { - description = "(a.k.a. " + aliases.join(", ") + ") "; + description = `(a.k.a. ${aliases.join(", ")}) `; } if (pageMeta.tags) { description = (description || "") + @@ -158,7 +158,7 @@ export function AnythingPicker({ } const completePrefix = - (folderName(currentPath) || getNameFromPath(currentPath)) + "/"; + `${folderName(currentPath) || getNameFromPath(currentPath)}/`; const allowNew = mode !== "document"; const creatablePageNoun = mode !== "all" ? mode : "page"; diff --git a/client/components/filter.tsx b/client/components/filter.tsx index e9628a23..0fc98291 100644 --- a/client/components/filter.tsx +++ b/client/components/filter.tsx @@ -219,7 +219,7 @@ export function FilterList({ ? "sb-option sb-selected-option" : "sb-option") + (option.cssClass - ? " sb-decorated-object " + option.cssClass + ? ` sb-decorated-object ${option.cssClass}` : "")} onMouseMove={() => { if (selectedOption !== currentOptionIndex) { diff --git a/client/components/mini_editor.tsx b/client/components/mini_editor.tsx index 3b554a4d..e2ac3891 100644 --- a/client/components/mini_editor.tsx +++ b/client/components/mini_editor.tsx @@ -20,31 +20,29 @@ type MiniEditorEvents = { onKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean; }; -export function MiniEditor( - { - text, - placeholderText, - vimMode, - darkMode, - vimStartInInsertMode, - onBlur, - onEscape, - onKeyUp, - onKeyDown, - onEnter, - onChange, - focus, - editable, - }: { - text: string; - placeholderText?: string; - vimMode: boolean; - darkMode?: boolean; - vimStartInInsertMode?: boolean; - focus?: boolean; - editable: boolean; - } & MiniEditorEvents, -) { +export function MiniEditor({ + text, + placeholderText, + vimMode, + darkMode, + vimStartInInsertMode, + onBlur, + onEscape, + onKeyUp, + onKeyDown, + onEnter, + onChange, + focus, + editable, +}: { + text: string; + placeholderText?: string; + vimMode: boolean; + darkMode?: boolean; + vimStartInInsertMode?: boolean; + focus?: boolean; + editable: boolean; +} & MiniEditorEvents) { const editorDiv = useRef(null); const editorViewRef = useRef(); const vimModeRef = useRef("normal"); @@ -106,8 +104,6 @@ export function MiniEditor( } }, [text, vimMode]); - let onBlurred = false, onEntered = false; - return (
{ - onEnter(view, false); + onEnter(view.state.sliceDoc(), false); return true; }, }, { key: "Shift-Enter", run: (view) => { - onEnter(view, true); + onEnter(view.state.sliceDoc(), true); return true; }, }, { key: "Escape", run: (view) => { - callbacksRef.current!.onEscape && - callbacksRef.current!.onEscape(view.state.sliceDoc()); + callbacksRef.current!.onEscape?.(view.state.sliceDoc()); return true; }, }, @@ -199,7 +194,7 @@ export function MiniEditor( // Enter should be handled by the keymap, except when in Vim normal mode // because then it's disabled if (vimMode && vimModeRef.current === "normal") { - onEnter(view, event.shiftKey); + onEnter(view.state.sliceDoc(), event.shiftKey); return true; } return false; @@ -210,7 +205,7 @@ export function MiniEditor( return false; }, blur: (_e, view) => { - onBlur(view); + onBlur?.(view.state.sliceDoc()); }, }), @@ -218,44 +213,12 @@ export function MiniEditor( class { update(update: ViewUpdate): void { if (update.docChanged) { - callbacksRef.current!.onChange && - callbacksRef.current!.onChange(update.state.sliceDoc()); + callbacksRef.current!.onChange?.(update.state.sliceDoc()); } } }, ), ], }); - - // Avoid double triggering these events (may happen due to onkeypress vs onkeyup delay) - function onEnter(view: EditorView, shiftDown: boolean) { - if (onEntered) { - return; - } - onEntered = true; - callbacksRef.current!.onEnter(view.state.sliceDoc(), shiftDown); - // Event may occur again in 500ms - setTimeout(() => { - onEntered = false; - }, 500); - } - - function onBlur(view: EditorView) { - if (onBlurred || onEntered) { - return; - } - onBlurred = true; - if (callbacksRef.current!.onBlur) { - Promise.resolve(callbacksRef.current!.onBlur(view.state.sliceDoc())) - .catch(() => { - // Reset the state - view.setState(buildEditorState()); - }); - } - // Event may occur again in 500ms - setTimeout(() => { - onBlurred = false; - }, 500); - } } } diff --git a/client/components/top_bar.tsx b/client/components/top_bar.tsx index 392b2636..cf3d5769 100644 --- a/client/components/top_bar.tsx +++ b/client/components/top_bar.tsx @@ -66,7 +66,7 @@ export function TopBar({ : unsavedChanges ? "sb-unsaved" : "sb-saved") + - (cssClass ? " sb-decorated-object " + cssClass : "")} + (cssClass ? ` sb-decorated-object ${cssClass}` : "")} > { - const tempPath = tempFilePath() + "_nonexistent"; + const tempPath = `${tempFilePath()}_nonexistent`; // Disable throttling for tests const store = new MemoryKvPrimitives(tempPath, { throttleMs: 0 }); await store.init(); diff --git a/client/data/object_index.ts b/client/data/object_index.ts index a3b89a1f..00398a30 100644 --- a/client/data/object_index.ts +++ b/client/data/object_index.ts @@ -130,7 +130,7 @@ export class ObjectIndex { }; } - getObjectByRef(page: string, tag: string, ref: string) { + getObjectByRef<_T>(page: string, tag: string, ref: string) { return this.ds.get([indexKey, tag, this.cleanKey(ref, page), page]); } diff --git a/client/html/auth.html b/client/html/auth.html index 0010f4bc..e6f009f9 100644 --- a/client/html/auth.html +++ b/client/html/auth.html @@ -152,7 +152,6 @@ autocapitalize="off" autocomplete="off" autocorrect="off" - autofocus id="username" name="username" type="text" diff --git a/client/html/index.html b/client/html/index.html index 52f857b0..bbfe8a98 100644 --- a/client/html/index.html +++ b/client/html/index.html @@ -41,7 +41,7 @@ }, }, env: { - get(key) { + get(_key) { // return undefined; }, }, diff --git a/client/lib/logger.ts b/client/lib/logger.ts index 187178fc..3cf5ae31 100644 --- a/client/lib/logger.ts +++ b/client/lib/logger.ts @@ -109,7 +109,7 @@ export class Logger { } // Global logger instance -let globalLogger: Logger | undefined = undefined; +let globalLogger: Logger | undefined ; export function initLogger(prefix: string = ""): Logger { globalLogger = new Logger(prefix); diff --git a/client/lib/polyfills.ts b/client/lib/polyfills.ts index 25d679dc..9650dccf 100644 --- a/client/lib/polyfills.ts +++ b/client/lib/polyfills.ts @@ -1,8 +1,8 @@ // Source: https://github.com/ungap/with-resolvers/blob/main/index.js -// @ts-ignore This is fine +// @ts-expect-error This is fine Promise.withResolvers || (Promise.withResolvers = function withResolvers() { let a, b; - const c = new this(function (resolve, reject) { + const c = new this((resolve, reject) => { a = resolve; b = reject; }); diff --git a/client/lib/util.ts b/client/lib/util.ts index 2f829361..7c091b48 100644 --- a/client/lib/util.ts +++ b/client/lib/util.ts @@ -11,11 +11,11 @@ export function authCookieName(host: string) { export function fileMetaToHeaders(fileMeta: FileMeta) { return { "Content-Type": fileMeta.contentType, - "X-Last-Modified": "" + fileMeta.lastModified, - "X-Created": "" + fileMeta.created, + "X-Last-Modified": `${fileMeta.lastModified}`, + "X-Created": `${fileMeta.created}`, "Cache-Control": "no-cache", "X-Permission": fileMeta.perm, - "X-Content-Length": "" + fileMeta.size, + "X-Content-Length": `${fileMeta.size}`, }; } diff --git a/client/markdown_parser/constants.ts b/client/markdown_parser/constants.ts index 250e70ae..69c85d5a 100644 --- a/client/markdown_parser/constants.ts +++ b/client/markdown_parser/constants.ts @@ -4,10 +4,10 @@ export const mdLinkRegex = /!?\[(?[^\]]*)\]\((?<url>.+)\)/g; export const tagRegex = /#(?:(?:\d*[^\d\s!@#$%^&*(),.?":{}|<>\\][^\s!@#$%^&*(),.?":{}|<>\\]*)|(?:<[^>\n]+>))/; export const nakedUrlRegex = - /(^https?:\/\/([-a-zA-Z0-9@:%_\+~#=]|(?:[.](?!(\s|$)))){1,256})(([-a-zA-Z0-9(@:%_\+~#?&=\/]|(?:[.,:;)](?!(\s|$))))*)/; + /(^https?:\/\/([-a-zA-Z0-9@:%_+~#=]|(?:[.](?!(\s|$)))){1,256})(([-a-zA-Z0-9(@:%_+~#?&=/]|(?:[.,:;)](?!(\s|$))))*)/; export const frontmatterQuotesRegex = /["'].*["']/g; export const frontmatterUrlRegex = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s"']+)/g; export const frontmatterWikiLinkRegex = /(?<leadingTrivia>!?\[\[)(?<stringRef>.*?)(?:\|(?<alias>.*?))?(?<trailingTrivia>\]\])/g; export const frontmatterMailtoRegex = /(mailto:[^@\s]+@[^@\s"']+)/ig; -export const pWikiLinkRegex = new RegExp("^" + wikiLinkRegex.source); // Modified regex used only in parser +export const pWikiLinkRegex = new RegExp(`^${wikiLinkRegex.source}`); // Modified regex used only in parser diff --git a/client/markdown_parser/extended_task.ts b/client/markdown_parser/extended_task.ts index 67e2f7be..ed39a26d 100644 --- a/client/markdown_parser/extended_task.ts +++ b/client/markdown_parser/extended_task.ts @@ -52,7 +52,7 @@ export const TaskList: MarkdownConfig = { // Note: task states cannot contain : to avoid ambiguity with attribute syntax const match = /^\[([^\]:]+)\][ \t]/.exec(leaf.content); return match && - cx.parentType().name == "ListItem" + cx.parentType().name === "ListItem" ? new MultiStatusTaskParser(match[1]) : null; }, diff --git a/client/markdown_parser/parser.ts b/client/markdown_parser/parser.ts index 130a1ca3..c3220e93 100644 --- a/client/markdown_parser/parser.ts +++ b/client/markdown_parser/parser.ts @@ -30,7 +30,7 @@ const WikiLink: MarkdownConfig = { name: "WikiLink", parse(cx, next, pos) { // Do a preliminary check for performance - if (next != 91 /* '[' */ && next != 33 /* '!' */) { + if (next !== 91 /* '[' */ && next !== 33 /* '!' */) { return -1; } @@ -68,7 +68,7 @@ const WikiLink: MarkdownConfig = { ]); // If inline image - if (next == 33) { + if (next === 33) { allElts = cx.elt("Image", pos, endPos, [allElts]); } @@ -171,7 +171,7 @@ export const Highlight: MarkdownConfig = { { name: "Highlight", parse(cx, next, pos) { - if (next != 61 /* '=' */ || cx.char(pos + 1) != 61) return -1; + if (next !== 61 /* '=' */ || cx.char(pos + 1) !== 61) return -1; return cx.addDelimiter(HighlightDelim, pos, pos + 2, true, true); }, after: "Emphasis", @@ -179,7 +179,7 @@ export const Highlight: MarkdownConfig = { ], }; -export const attributeStartRegex = /^\[([\w\$]+)(::?\s*)/; +export const attributeStartRegex = /^\[([\w$]+)(::?\s*)/; export const Attribute: MarkdownConfig = { defineNodes: [ @@ -196,7 +196,7 @@ export const Attribute: MarkdownConfig = { let match: RegExpMatchArray | null; const textFromPos = cx.slice(pos, cx.end); if ( - next != 91 /* '[' */ || + next !== 91 /* '[' */ || // and match the whole thing !(match = attributeStartRegex.exec(textFromPos)) ) { @@ -332,7 +332,7 @@ export const FrontMatter: MarkdownConfig = { let text = ""; let lastPos = cx.parsedPos; do { - text += line.text + "\n"; + text += `${line.text}\n`; endPos += line.text.length + 1; cx.nextLine(); if (cx.parsedPos === lastPos) { diff --git a/client/markdown_parser/table_parser.ts b/client/markdown_parser/table_parser.ts index 61b22b97..99d33ae1 100644 --- a/client/markdown_parser/table_parser.ts +++ b/client/markdown_parser/table_parser.ts @@ -59,7 +59,7 @@ function parseRow( } } - if (next == 124 /* '|' */ && !esc && !inWikilink && !inCommandButton) { + if (next === 124 /* '|' */ && !esc && !inWikilink && !inCommandButton) { if (!first || cellStart > -1) count++; first = false; if (elts) { @@ -67,11 +67,11 @@ function parseRow( elts.push(cx.elt("TableDelimiter", i + offset, i + offset + 1)); } cellStart = cellEnd = -1; - } else if (esc || next != 32 && next != 9) { + } else if (esc || next !== 32 && next !== 9) { if (cellStart < 0) cellStart = i; cellEnd = i + 1; } - esc = !esc && next == 92; + esc = !esc && next === 92; } if (cellStart > -1) { count++; @@ -83,8 +83,8 @@ function parseRow( function hasPipe(str: string, start: number) { for (let i = start; i < str.length; i++) { const next = str.charCodeAt(i); - if (next == 124 /* '|' */) return true; - if (next == 92 /* '\\' */) i++; + if (next === 124 /* '|' */) return true; + if (next === 92 /* '\\' */) i++; } return false; } @@ -102,12 +102,12 @@ class TableParser implements LeafBlockParser { this.rows = false; let lineText; if ( - (line.next == 45 || line.next == 58 || line.next == 124 /* '-:|' */) && + (line.next === 45 || line.next === 58 || line.next === 124 /* '-:|' */) && delimiterLine.test(lineText = line.text.slice(line.pos)) ) { const firstRow: Element[] = [], firstCount = parseRow(cx, leaf.content, 0, firstRow, leaf.start); - if (firstCount == parseRow(cx, lineText, line.pos)) { + if (firstCount === parseRow(cx, lineText, line.pos)) { this.rows = [ cx.elt( "TableHeader", @@ -180,10 +180,10 @@ export const Table: MarkdownConfig = { leaf.parsers.some((p) => p instanceof TableParser) || !hasPipe(line.text, line.basePos) ) return false; - // @ts-ignore: internal + // @ts-expect-error: internal const next = cx.scanLine(cx.absoluteLineEnd + 1).text; return delimiterLine.test(next) && - parseRow(cx, line.text, line.basePos) == + parseRow(cx, line.text, line.basePos) === parseRow(cx, next, line.basePos); }, before: "SetextHeading", diff --git a/client/markdown_renderer/html_render.ts b/client/markdown_renderer/html_render.ts index b50d7a27..ad00af46 100644 --- a/client/markdown_renderer/html_render.ts +++ b/client/markdown_renderer/html_render.ts @@ -33,11 +33,11 @@ export function renderHtml(t: Tag | null): string { return htmlEscape(t); } const attrs = t.attrs - ? " " + Object.entries(t.attrs) + ? ` ${Object.entries(t.attrs) .filter(([, value]) => value !== undefined) .map(([k, v]) => `${k}="${htmlEscape(v!)}"`).join( " ", - ) + )}` : ""; const body = typeof t.body === "string" ? htmlEscape(t.body) diff --git a/client/markdown_renderer/inline.ts b/client/markdown_renderer/inline.ts index b3e22771..407d9242 100644 --- a/client/markdown_renderer/inline.ts +++ b/client/markdown_renderer/inline.ts @@ -208,7 +208,7 @@ export function inlineContentFromURL( // If the URL is a local, prefix it with /.fs and encode the : so that it's not interpreted as a protocol const sanitizedFsUrl = isLocalURL(transclusion.url) - ? fsEndpoint.slice(1) + "/" + transclusion.url.replace(":", "%3A") + ? `${fsEndpoint.slice(1)}/${transclusion.url.replace(":", "%3A")}` : transclusion.url; if (mimeType.startsWith("image/")) { diff --git a/client/markdown_renderer/markdown_render.ts b/client/markdown_renderer/markdown_render.ts index 4a47c368..170bf02b 100644 --- a/client/markdown_renderer/markdown_render.ts +++ b/client/markdown_renderer/markdown_render.ts @@ -84,7 +84,7 @@ function posPreservingRender( if (!tag.attrs) { tag.attrs = {}; } - tag.attrs["data-pos"] = "" + t.from; + tag.attrs["data-pos"] = `${t.from}`; } return tag; } diff --git a/client/markdown_renderer/result_render.ts b/client/markdown_renderer/result_render.ts index 36496f86..d830ca3a 100644 --- a/client/markdown_renderer/result_render.ts +++ b/client/markdown_renderer/result_render.ts @@ -19,7 +19,7 @@ export function defaultTransformer(v: any, _k: string): Promise<string> { if (typeof v === "number") { return Promise.resolve(luaFormatNumber(v)); } - return Promise.resolve("" + v); + return Promise.resolve(`${v}`); } export function refCellTransformer(v: any, k: string) { @@ -43,18 +43,18 @@ function escapeRegularPipes(s: string) { let isInCommandButton = false; for (let i = 0; i < s.length; i++) { - if (s[i] == "[" && s[i + 1] == "[") { + if (s[i] === "[" && s[i + 1] === "[") { isInWikiLink = true; - } else if (s[i] == "]" && s[i + 1] == "]" && isInWikiLink) { + } else if (s[i] === "]" && s[i + 1] === "]" && isInWikiLink) { isInWikiLink = false; } - if (s[i] == "{" && s[i + 1] == "[") { + if (s[i] === "{" && s[i + 1] === "[") { isInCommandButton = true; } else if ( - (s[i] == "]" || s[i] == ")") && s[i + 1] == "}" && isInCommandButton + (s[i] === "]" || s[i] === ")") && s[i + 1] === "}" && isInCommandButton ) { isInCommandButton = false; - } else if (s[i] == "|" && !isInWikiLink && !isInCommandButton) { + } else if (s[i] === "|" && !isInWikiLink && !isInCommandButton) { result += "\\"; } @@ -77,7 +77,7 @@ export async function jsonToMDTable( } // Handle empty case manually, instead of three lines of || - if (headers.size == 0) { + if (headers.size === 0) { return "*(empty table)*"; } @@ -105,7 +105,7 @@ export async function jsonToMDTable( const s = await valueTransformer(val[prop], prop); el.push(s); } - lines.push("|" + el.join("|") + "|"); + lines.push(`|${el.join("|")}|`); } return lines.join("\n"); } @@ -145,7 +145,7 @@ export function renderExpressionResult(result: any): Promise<string> { // Not-object array, let's render it as a Markdown list return Promise.resolve(result.map((item) => `- ${item}`).join("\n")); } else { - return Promise.resolve("" + result); + return Promise.resolve(`${result}`); } } @@ -158,5 +158,5 @@ export function isBlockMarkdown(s: string) { return true; } // If it contains something resembling a list - return !!s.match(/[\-\*]\s+/); + return !!s.match(/[-*]\s+/); } diff --git a/client/plugos/hooks/code_widget.ts b/client/plugos/hooks/code_widget.ts index c921cabb..1ed3752e 100644 --- a/client/plugos/hooks/code_widget.ts +++ b/client/plugos/hooks/code_widget.ts @@ -7,9 +7,6 @@ export class CodeWidgetHook implements Hook<CodeWidgetT> { codeWidgetCallbacks = new Map<string, CodeWidgetCallback>(); codeWidgetModes = new Map<string, "markdown" | "iframe">(); - constructor() { - } - collectAllCodeWidgets(system: System<CodeWidgetT>) { this.codeWidgetCallbacks.clear(); for (const plug of system.loadedPlugs.values()) { diff --git a/client/plugos/hooks/document_editor.ts b/client/plugos/hooks/document_editor.ts index ac78aa19..82492fd4 100644 --- a/client/plugos/hooks/document_editor.ts +++ b/client/plugos/hooks/document_editor.ts @@ -9,9 +9,6 @@ export class DocumentEditorHook implements Hook<DocumentEditorT> { { extensions: string[]; callback: DocumentEditorCallback } >(); - constructor() { - } - collectAllDocumentEditors(system: System<DocumentEditorT>) { this.documentEditors.clear(); for (const plug of system.loadedPlugs.values()) { diff --git a/client/plugos/hooks/plug_namespace.ts b/client/plugos/hooks/plug_namespace.ts index ee4f9338..a73969a1 100644 --- a/client/plugos/hooks/plug_namespace.ts +++ b/client/plugos/hooks/plug_namespace.ts @@ -15,9 +15,6 @@ type SpaceFunction = { export class PlugNamespaceHook implements Hook<PlugNamespaceHookT> { spaceFunctions: SpaceFunction[] = []; - constructor() { - } - apply(system: System<PlugNamespaceHookT>): void { system.on({ plugLoaded: () => { diff --git a/client/plugos/hooks/slash_command.ts b/client/plugos/hooks/slash_command.ts index 0d90db9e..9713cdcf 100644 --- a/client/plugos/hooks/slash_command.ts +++ b/client/plugos/hooks/slash_command.ts @@ -15,7 +15,7 @@ import type { SlashCompletions, } from "@silverbulletmd/silverbullet/type/client"; -const slashCommandRegexp = /([^\w:]|^)\/[\w#\-]*/; +const slashCommandRegexp = /([^\w:]|^)\/[\w#-]*/; export class SlashCommandHook implements Hook<SlashCommandHookT> { slashCommands: SlashCommand[] = []; @@ -94,7 +94,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> { continue; } if ( - def.exceptContexts && def.exceptContexts.some( + def.exceptContexts?.some( (context) => parentNodes.some((node) => node.startsWith(context)), ) ) { diff --git a/client/plugos/plug_compile.ts b/client/plugos/plug_compile.ts index e03bf196..fb7a0aa3 100644 --- a/client/plugos/plug_compile.ts +++ b/client/plugos/plug_compile.ts @@ -6,7 +6,6 @@ import * as YAML from "js-yaml"; import * as esbuild from "esbuild"; import { bundleAssets } from "../asset_bundle/builder.ts"; import type { Manifest } from "./types.ts"; -import { version } from "../../version.ts"; import { existsSync } from "node:fs"; diff --git a/client/plugos/sandboxes/web_worker_sandbox.ts b/client/plugos/sandboxes/web_worker_sandbox.ts index b3c5f3f1..24df2989 100644 --- a/client/plugos/sandboxes/web_worker_sandbox.ts +++ b/client/plugos/sandboxes/web_worker_sandbox.ts @@ -11,7 +11,7 @@ export function createWorkerSandboxFromLocalPath<HookT>( plug, new URL( name, - document.baseURI.slice(0, -1) + fsEndpoint + "/", // We're NOT striping trailing '/', this used to be `location.origin` + `${document.baseURI.slice(0, -1) + fsEndpoint}/`, // We're NOT striping trailing '/', this used to be `location.origin` ), ); } diff --git a/client/plugos/sandboxes/worker_sandbox.ts b/client/plugos/sandboxes/worker_sandbox.ts index 7487a1c4..a5ad5b41 100644 --- a/client/plugos/sandboxes/worker_sandbox.ts +++ b/client/plugos/sandboxes/worker_sandbox.ts @@ -94,10 +94,9 @@ export class WorkerSandbox<HookT> implements Sandbox<HookT> { const resultCbs = this.outstandingInvocations.get(data.id!); this.outstandingInvocations.delete(data.id!); if (data.error) { - resultCbs && - resultCbs.reject(new Error(data.error)); + resultCbs?.reject(new Error(data.error)); } else { - resultCbs && resultCbs.resolve(data.result); + resultCbs?.resolve(data.result); } break; } diff --git a/client/plugos/syscalls/editor.ts b/client/plugos/syscalls/editor.ts index f53fcbc0..bb58f46e 100644 --- a/client/plugos/syscalls/editor.ts +++ b/client/plugos/syscalls/editor.ts @@ -110,7 +110,7 @@ export function editorSyscalls(client: Client): SysCallMapping { } if ( - // @ts-ignore: Legacy support + // @ts-expect-error: Legacy support ref.page !== undefined ) { console.warn( @@ -127,7 +127,7 @@ export function editorSyscalls(client: Client): SysCallMapping { legacyRef.kind ??= "page"; - let details: Ref["details"] = undefined; + let details: Ref["details"] ; if (typeof legacyRef.pos === "number") { details = { @@ -232,7 +232,7 @@ export function editorSyscalls(client: Client): SysCallMapping { "editor.newWindow": () => { globalThis.open( location.href, - "rnd" + Math.random(), + `rnd${Math.random()}`, `width=${globalThis.innerWidth},heigh=${globalThis.innerHeight}`, ); }, @@ -268,7 +268,7 @@ export function editorSyscalls(client: Client): SysCallMapping { const reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onloadend = async (evt) => { - if (evt.target?.readyState == FileReader.DONE) { + if (evt.target?.readyState === FileReader.DONE) { resolve({ name: file.name, contentType: file.type, @@ -506,7 +506,7 @@ export function editorSyscalls(client: Client): SysCallMapping { }, "editor.vimEx": (_ctx, exCommand: string) => { const cm = vimGetCm(client.editorView); - if (cm && cm.state.vim) { + if (cm?.state.vim) { return Vim.handleEx(cm as any, exCommand); } else { throw new Error("Vim mode not active or not initialized."); @@ -555,30 +555,30 @@ export function editorSyscalls(client: Client): SysCallMapping { if (config) { config.unmap?.forEach((binding) => { if (typeof binding === "string") { - console.log("Unmapping " + binding); - // @ts-ignore: unmap expects a string for the mode, this is problematic with Ex mappings which requires undefined or false + console.log(`Unmapping ${binding}`); + // @ts-expect-error: unmap expects a string for the mode, this is problematic with Ex mappings which requires undefined or false Vim.unmap(binding, undefined); } else if (binding.key) { console.log( - "Unmapping " + binding.key + " in " + (binding.mode ?? "normal"), + `Unmapping ${binding.key} in ${binding.mode ?? "normal"}`, ); Vim.unmap(binding.key, binding.mode ?? "normal"); } }); config.map?.forEach(({ map, to, mode }) => { console.log( - "Mapping " + map + " to " + to + " for " + (mode ?? "normal"), + `Mapping ${map} to ${to} for ${mode ?? "normal"}`, ); Vim.map(map, to, mode ?? "normal"); }); config.noremap?.forEach(({ map, to, mode }) => { console.log( - "Noremapping " + map + " to " + to + " for " + (mode ?? "normal"), + `Noremapping ${map} to ${to} for ${mode ?? "normal"}`, ); Vim.noremap(map, to, mode ?? "normal"); }); config.commands?.forEach(({ ex, command }) => { - console.log("Mapping command '" + command + "' to Ex " + ex); + console.log(`Mapping command '${command}' to Ex ${ex}`); Vim.defineEx(ex, "", () => client.runCommandByName(command)); }); } else { diff --git a/client/plugos/syscalls/jsonschema.ts b/client/plugos/syscalls/jsonschema.ts index 4e05957f..ab205970 100644 --- a/client/plugos/syscalls/jsonschema.ts +++ b/client/plugos/syscalls/jsonschema.ts @@ -33,7 +33,7 @@ export function validateObject(schema: any, object: any): undefined | string { } else { let text = ajv.errorsText(validate.errors); text = text.replaceAll("/", "."); - text = text.replace(/^data[\.\s]/, ""); + text = text.replace(/^data[.\s]/, ""); return text; } } catch (e: any) { diff --git a/client/plugos/syscalls/mq.ts b/client/plugos/syscalls/mq.ts index 18a7f418..a6ae2cef 100644 --- a/client/plugos/syscalls/mq.ts +++ b/client/plugos/syscalls/mq.ts @@ -17,7 +17,7 @@ export function mqSyscalls( _ctx, def: MQListenerSpec, ) => { - def.autoAck = def.autoAck != false; + def.autoAck = def.autoAck !== false; // console.log("Registering Lua event listener: ", def.name); client.config.insert([ "mqSubscriptions", diff --git a/client/plugos/syscalls/space.ts b/client/plugos/syscalls/space.ts index b4332a8b..39cc956d 100644 --- a/client/plugos/syscalls/space.ts +++ b/client/plugos/syscalls/space.ts @@ -32,7 +32,7 @@ export function spaceReadSyscalls(client: Client): SysCallMapping { return (await client.space.readRef(ref)).text; }, "space.pageExists": (_ctx, name: string): boolean => { - return client.clientSystem.allKnownFiles.has(name + ".md"); + return client.clientSystem.allKnownFiles.has(`${name}.md`); }, "space.getPageMeta": (_ctx, name: string): Promise<PageMeta> => { return client.space.getPageMeta(name); diff --git a/client/plugos/system.ts b/client/plugos/system.ts index 457ed011..334407b4 100644 --- a/client/plugos/system.ts +++ b/client/plugos/system.ts @@ -193,8 +193,8 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> { this.plugs.delete(name); } - unloadAll(): Promise<void[]> { - return Promise.all( + async unloadAll(): Promise<void> { + await Promise.all( Array.from(this.plugs.keys()).map(this.unload.bind(this)), ); } diff --git a/client/plugos/worker_runtime.ts b/client/plugos/worker_runtime.ts index 9216ea36..b44c72a8 100644 --- a/client/plugos/worker_runtime.ts +++ b/client/plugos/worker_runtime.ts @@ -20,7 +20,7 @@ let workerPostMessage = (_msg: ControllerMessage): void => { // - in a browser's worker threads, typeof window === "undefined" // - in Cloudflare workers typeof window === "undefined", but typeof globalThis.WebSocketPair is defined const runningAsWebWorker = typeof window === "undefined" && - // @ts-ignore: globalThis + // @ts-expect-error: globalThis typeof globalThis.WebSocketPair === "undefined"; const pendingRequests = new Map< @@ -136,17 +136,16 @@ export async function sandboxFetch( return syscall("sandboxFetch.fetch", reqInfo, options); } -// @ts-ignore: monkey patching fetch globalThis.nativeFetch = globalThis.fetch; // Monkey patch fetch() export function monkeyPatchFetch() { - // @ts-ignore: monkey patching fetch - globalThis.fetch = async function ( + // @ts-expect-error: monkey patching fetch + globalThis.fetch = async ( reqInfo: RequestInfo, init?: RequestInit, - ): Promise<Response> { - const encodedBody = init && init.body + ): Promise<Response> => { + const encodedBody = init?.body ? base64Encode( new Uint8Array(await (new Response(init.body)).arrayBuffer()), ) diff --git a/client/reducer.ts b/client/reducer.ts index f0f81f0f..0217cf30 100644 --- a/client/reducer.ts +++ b/client/reducer.ts @@ -76,7 +76,7 @@ export default function reducer( let currPageMeta: PageMeta | undefined; for (const pageMeta of action.allPages) { const oldPageMetaItem = oldPageMeta.get(pageMeta.name); - if (oldPageMetaItem && oldPageMetaItem.lastOpened) { + if (oldPageMetaItem?.lastOpened) { pageMeta.lastOpened = oldPageMetaItem.lastOpened; } if (parseToRef(pageMeta.name)?.path === state.current?.path) { diff --git a/client/service_worker.ts b/client/service_worker.ts index d0957396..7882b25b 100644 --- a/client/service_worker.ts +++ b/client/service_worker.ts @@ -64,7 +64,7 @@ const proxyRouter = new ProxyRouter( // Configuration mutex let configuring = false; -// @ts-ignore: debugging +// @ts-expect-error: debugging globalThis.proxyRouter = proxyRouter; // This is the in-memory store of an encryption key that SB clients and the index engine can share without asking for it constantly @@ -72,7 +72,7 @@ let encryptionKeyMemoryStore: CryptoKey | undefined; // Let's clean this encryptionKey if there's no more clients left for a little while, asking to re-enter setInterval(() => { - // @ts-ignore: service worker API + // @ts-expect-error: service worker API globalThis.clients.matchAll().then((clients) => { if (clients.length === 0 && encryptionKeyMemoryStore) { console.info("No more clients, flushing encryption key"); @@ -86,7 +86,7 @@ self.addEventListener("message", async (event: any) => { const message: ServiceWorkerTargetMessage = event.data; switch (message.type) { case "skip-waiting": { - // @ts-ignore: Skip waiting to activate this service worker immediately + // @ts-expect-error: Skip waiting to activate this service worker immediately self.skipWaiting(); break; } @@ -317,7 +317,7 @@ self.addEventListener("message", async (event: any) => { }); function broadcastMessage(message: ServiceWorkerSourceMessage) { - // @ts-ignore: service worker API + // @ts-expect-error: service worker API const clients: any = self.clients; // Find all windows attached to this service worker clients.matchAll({ @@ -364,7 +364,7 @@ self.addEventListener("install", (event: any) => { Object.keys(precacheFiles).length, "client files cached", ); - // @ts-ignore: Force the waiting service worker to become the active service worker + // @ts-expect-error: Force the waiting service worker to become the active service worker await self.skipWaiting(); })(), ); @@ -389,7 +389,7 @@ self.addEventListener("activate", (event: any) => { } }), ); - // @ts-ignore: Take control of all clients as soon as the service worker activates + // @ts-expect-error: Take control of all clients as soon as the service worker activates await clients.claim(); })(), ); diff --git a/client/space.ts b/client/space.ts index 91327c31..0efb0771 100644 --- a/client/space.ts +++ b/client/space.ts @@ -106,7 +106,7 @@ export class Space { let text = `**Error:** Header not found: ${desiredHeaderText}`; let offset = 0; traverseTree(tree, (n) => { - if (n.type && n.type.startsWith("ATXHeading")) { + if (n.type?.startsWith("ATXHeading")) { const level = +n.type!.substring("ATXHeading".length); const headerText = renderToText(n).slice(level + 1); let endPos = pageText.length; diff --git a/client/space_lua/aggregates.test.ts b/client/space_lua/aggregates.test.ts index 4a19388e..da9cad79 100644 --- a/client/space_lua/aggregates.test.ts +++ b/client/space_lua/aggregates.test.ts @@ -742,7 +742,7 @@ test("aggregate: custom concat with finish", async () => { state.rawSet("s", String(value)); state.rawSet("first", false); } else { - state.rawSet("s", state.rawGet("s") + ", " + String(value)); + state.rawSet("s", `${state.rawGet("s")}, ${String(value)}`); } return state; }), diff --git a/client/space_lua/aggregates.ts b/client/space_lua/aggregates.ts index 96b47198..6046fbd3 100644 --- a/client/space_lua/aggregates.ts +++ b/client/space_lua/aggregates.ts @@ -119,7 +119,6 @@ const noCtx = {}; function buildAggCtx(name: string): LuaTable { const ctx = new LuaTable(); ctx.rawSet("name", name); - // @ts-ignore: Hack to access client via the browser const clientConfig = globalThis.client?.config; const aggConfig = clientConfig ? clientConfig.get(`aggregateConfig.${name}`, {}) @@ -129,7 +128,6 @@ function buildAggCtx(name: string): LuaTable { } export function getAggregateSpec(name: string): AggregateSpec | null { - // @ts-ignore: Hack to access client via the browser const clientConfig = globalThis.client?.config; if (clientConfig) { const spec: any = clientConfig.get(`aggregates.${name}`, null); diff --git a/client/space_lua/eval.ts b/client/space_lua/eval.ts index 94cd1741..fb933482 100644 --- a/client/space_lua/eval.ts +++ b/client/space_lua/eval.ts @@ -176,14 +176,6 @@ function blockMetaOrThrow( } } -// Queryable guard to avoid `(collection as any).query` usage -type Queryable = { - query: ( - q: LuaCollectionQuery, - env: LuaEnv, - sf: LuaStackFrame, - ) => Promise<any>; -}; function arithVerbFromOperator(op: string): string | null { switch (op) { @@ -1427,9 +1419,9 @@ function evalExpressions( } type EvalBlockResult = - | void + | undefined | ControlSignal - | Promise<void | ControlSignal>; + | Promise<undefined | ControlSignal>; function runStatementsNoGoto( stmts: LuaStatement[], @@ -1437,10 +1429,10 @@ function runStatementsNoGoto( sf: LuaStackFrame, returnOnReturn: boolean, startIdx: number, -): void | ControlSignal | Promise<void | ControlSignal> { +): undefined | ControlSignal | Promise<undefined | ControlSignal> { const processFrom = ( idx: number, - ): void | ControlSignal | Promise<void | ControlSignal> => { + ): undefined | ControlSignal | Promise<undefined | ControlSignal> => { for (let i = idx; i < stmts.length; i++) { const result = evalStatement( stmts[i], @@ -1638,7 +1630,7 @@ export function evalStatement( env: LuaEnv, sf: LuaStackFrame, returnOnReturn = false, -): void | ControlSignal | Promise<void | ControlSignal> { +): undefined | ControlSignal | Promise<undefined | ControlSignal> { switch (s.type) { case "Assignment": { const a = asAssignment(s); @@ -1790,7 +1782,7 @@ export function evalStatement( return rpThen(rp, onValue) as any; }; - return runFrom(0); + return runFrom(0) as undefined | Promise<undefined>; } case "Semicolon": { return; @@ -1843,9 +1835,9 @@ export function evalStatement( const runFrom = ( i: number, ): - | void + | undefined | ControlSignal - | Promise<void | ControlSignal> => { + | Promise<undefined | ControlSignal> => { if (i >= conds.length) { if (iff.elseBlock) { return evalStatement(iff.elseBlock, env, sf, returnOnReturn); @@ -1872,7 +1864,7 @@ export function evalStatement( case "While": { const w = asWhile(s); - const runAsync = async (): Promise<void | ControlSignal> => { + const runAsync = async (): Promise<undefined | ControlSignal> => { while (true) { const c = await evalExpression(w.condition, env, sf); if (!luaTruthy(c)) { @@ -1897,7 +1889,6 @@ export function evalStatement( if (!luaTruthy(cv)) { return; } - try { const r = evalStatement(w.block, env, sf, returnOnReturn); if (isPromise(r)) { return (r as Promise<any>).then((res) => { @@ -1917,9 +1908,6 @@ export function evalStatement( return r; } return runAsync(); - } catch (e: any) { - throw e; - } }); } if (!luaTruthy(c)) { @@ -1949,7 +1937,7 @@ export function evalStatement( case "Repeat": { const r = asRepeat(s); - const runAsync = async (): Promise<void | ControlSignal> => { + const runAsync = async (): Promise<undefined | ControlSignal> => { while (true) { const rr = evalStatement(r.block, env, sf, returnOnReturn); const res = isPromise(rr) ? await rr : rr; @@ -2118,7 +2106,7 @@ export function evalStatement( loopEnv: LuaEnv, i: number, loopType: NumericType, - ): void | ControlSignal | Promise<void | ControlSignal> => { + ): undefined | ControlSignal | Promise<undefined | ControlSignal> => { loopEnv.setLocal(fr.name, wrapLoopVar(i, loopType)); return evalStatement(fr.block, loopEnv, sf, returnOnReturn); } @@ -2126,7 +2114,7 @@ export function evalStatement( _loopEnv: LuaEnv, i: number, loopType: NumericType, - ): void | ControlSignal | Promise<void | ControlSignal> => { + ): undefined | ControlSignal | Promise<undefined | ControlSignal> => { const localEnv = new LuaEnv(env); localEnv.setLocal(fr.name, wrapLoopVar(i, loopType)); return evalStatement(fr.block, localEnv, sf, returnOnReturn); @@ -2165,9 +2153,9 @@ export function evalStatement( step: number, loopType: NumericType, ): - | void + | undefined | ControlSignal - | Promise<void | ControlSignal> => { + | Promise<undefined | ControlSignal> => { if (step === 0) { throw new LuaRuntimeError("'for' step is zero", sf.withCtx(fr.ctx)); } diff --git a/client/space_lua/labels.ts b/client/space_lua/labels.ts index 18671f34..d4368b2e 100644 --- a/client/space_lua/labels.ts +++ b/client/space_lua/labels.ts @@ -122,7 +122,7 @@ function resolveFunction(root: LuaBlock): FunctionMeta { while (searchBlock) { const meta = blockMeta.get(searchBlock); - if (meta && meta.labels.has(target)) { + if (meta?.labels.has(target)) { labelIndex = meta.labels.get(target); labelDefBlock = searchBlock; break; diff --git a/client/space_lua/lua.test.ts b/client/space_lua/lua.test.ts index a225c479..2f6e511f 100644 --- a/client/space_lua/lua.test.ts +++ b/client/space_lua/lua.test.ts @@ -3,9 +3,9 @@ import { parse } from "./parse.ts"; import { luaBuildStandardEnv } from "./stdlib.ts"; import { LuaEnv, LuaRuntimeError, LuaStackFrame } from "./runtime.ts"; import { evalStatement } from "./eval.ts"; -import { readFile, readdir } from "node:fs/promises"; +import { readFile, } from "node:fs/promises"; import { fileURLToPath } from "node:url"; -import { dirname, join } from "node:path"; +import { dirname, } from "node:path"; import { readdirSync } from "node:fs"; const __dirname = dirname(fileURLToPath(import.meta.url)); diff --git a/client/space_lua/parse.test.ts b/client/space_lua/parse.test.ts index 403d4d33..f6279b25 100644 --- a/client/space_lua/parse.test.ts +++ b/client/space_lua/parse.test.ts @@ -16,21 +16,15 @@ test("Test Lua parser", () => { parse(`e([[hel]lo]], "Grinny face\\u{1F600}")`); parse(`e([=[Hello page [[index]] end scene]=], [[yo]])`); - // console.log( - // JSON.stringify( - parse(`e([==[Hello page [[index]] end scene]==], [==[yo]==])`), - // null, - // 2, - // ), - // ); - console.log( - JSON.stringify( - parse(`e([==[Hello page [[bla]] + parse(`e([==[Hello page [[index]] end scene]==], [==[yo]==])`); + console.log( + JSON.stringify( + parse(`e([==[Hello page [[bla]] ]==])`), - null, - 2, - ), - ); + null, + 2, + ), + ); parse(`e(10 << 10, 10 >> 10, 10 & 10, 10 | 10, 10 ~ 10)`); @@ -162,7 +156,8 @@ test("Test numeric constant parsing", () => { expect((parseExpressionString(`0xBEBADA`) as LuaNumberLiteral).value).toEqual(0xBEBADA, ); expect((parseExpressionString(`3.0`) as LuaNumberLiteral).value).toEqual(3.0); - expect((parseExpressionString(`3.1416`) as LuaNumberLiteral).value).toEqual(3.1416, + expect((parseExpressionString(`3.1416`) as LuaNumberLiteral).value)// biome-ignore lint/suspicious/noApproximativeNumericConstant: testing exact value 3.1416, not Math.PI + .toEqual(3.1416, ); expect((parseExpressionString(`314.16e-2`) as LuaNumberLiteral).value).toEqual(314.16e-2, ); diff --git a/client/space_lua/parse.ts b/client/space_lua/parse.ts index ac60d417..17133111 100644 --- a/client/space_lua/parse.ts +++ b/client/space_lua/parse.ts @@ -4,7 +4,7 @@ import { cleanTree, type ParseTree, } from "@silverbulletmd/silverbullet/lib/tree"; -// @ts-ignore - Local generated JavaScript file without type definitions +// @ts-expect-error - Local generated JavaScript file without type definitions import { parser } from "./parse-lua.js"; import { styleTags, tags as t } from "@lezer/highlight"; import { indentNodeProp, LRLanguage } from "@codemirror/language"; @@ -586,7 +586,7 @@ function parseBlock(t: ParseTree, ctx: ASTCtx): LuaBlock { if (t.type !== "Block") { throw new Error(`Expected Block, got ${t.type}`); } - const stmtNodes = t.children!.filter((c) => c && c.type); + const stmtNodes = t.children!.filter((c) => c?.type); const statements = stmtNodes.map((s) => parseStatement(s, ctx)); const block: LuaBlock = { type: "Block", statements, ctx: context(t, ctx) }; @@ -814,7 +814,7 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement { from?: number; to?: number; }[] = []; - let elseBlock: LuaBlock | undefined = undefined; + let elseBlock: LuaBlock | undefined ; for (let i = 0; i < t.children!.length; i += 4) { const child = t.children![i]; if (!child || !child.children || !child.children[0]) { @@ -949,7 +949,7 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement { console.error(t); throw new Error( `Unknown statement type: ${ - t.children![0] && t.children![0].text + t.children![0]?.text ? t.children![0].text : String(t.type) }`, @@ -1044,7 +1044,7 @@ function parseFunctionName(t: ParseTree, ctx: ASTCtx): LuaFunctionName { throw new Error(`Expected FunctionName, got ${t.type}`); } const propNames: string[] = []; - let colonName: string | undefined = undefined; + let colonName: string | undefined ; for (let i = 0; i < t.children!.length; i += 2) { const prop = t.children![i]; propNames.push(prop.children![0].text!); @@ -1152,8 +1152,9 @@ function parseExpression(t: ParseTree, ctx: ASTCtx): LuaExpression { return { type: "Number", // Use the integer parser fox 0x literals + // biome-ignore lint/correctness/useParseIntRadix: hex strings need auto-detect radix value: text.includes("x") ? parseInt(text) : parseFloat(text), - numericType: /[\.eEpP]/.test(text) ? "float" : "int", + numericType: /[.eEpP]/.test(text) ? "float" : "int", ctx: context(t, ctx), }; } @@ -1475,7 +1476,7 @@ export function stripLuaComments(s: string): string { if (s[j] === "[") { // Found long string start const openBracket = s.substring(i, j + 1); - const closeBracket = "]" + "=".repeat(equalsCount) + "]"; + const closeBracket = `]${"=".repeat(equalsCount)}]`; result += openBracket; i = j + 1; @@ -1528,7 +1529,7 @@ export function stripLuaComments(s: string): string { } if (s[j] === "[") { // Found long comment start - const closeBracket = "]" + "=".repeat(equalsCount) + "]"; + const closeBracket = `]${"=".repeat(equalsCount)}]`; // Replace opening bracket with spaces result += " ".repeat(j - i + 1); i = j + 1; diff --git a/client/space_lua/query_collection.test.ts b/client/space_lua/query_collection.test.ts index 30888b3d..68c086bb 100644 --- a/client/space_lua/query_collection.test.ts +++ b/client/space_lua/query_collection.test.ts @@ -4,7 +4,6 @@ import { ArrayQueryCollection } from "./query_collection.ts"; import { LuaEnv, LuaNativeJSFunction, - LuaRuntimeError, LuaStackFrame, } from "./runtime.ts"; @@ -13,7 +12,7 @@ test("ArrayQueryCollection", async () => { rootEnv.setLocal( "build_name", new LuaNativeJSFunction((a, b) => { - return Promise.resolve(a + " " + b); + return Promise.resolve(`${a} ${b}`); }), ); diff --git a/client/space_lua/query_collection.ts b/client/space_lua/query_collection.ts index 9713fc19..a823c059 100644 --- a/client/space_lua/query_collection.ts +++ b/client/space_lua/query_collection.ts @@ -815,7 +815,6 @@ export async function applyQuery( if (query.orderBy) { if (collation === undefined) { - // @ts-ignore: Hack to access client via the browser const config = globalThis.client.config; collation = config.get("queryCollation", {}); } diff --git a/client/space_lua/runtime.ts b/client/space_lua/runtime.ts index c621860c..098dcc19 100644 --- a/client/space_lua/runtime.ts +++ b/client/space_lua/runtime.ts @@ -128,7 +128,7 @@ export function luaIsCallable( } if (v instanceof LuaTable) { const mt = getMetatable(v, sf); - if (mt && mt.has("__call")) { + if (mt?.has("__call")) { const mm = mt.get("__call", sf); return !!mm && (typeof mm === "function" || isILuaFunction(mm)); } @@ -614,7 +614,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable { if (init && !Array.isArray(init)) { for (const k in init) { - if (Object.prototype.hasOwnProperty.call(init, k)) { + if (Object.hasOwn(init, k)) { this.stringKeys[k] = (init as any)[k]; } } @@ -689,7 +689,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable { keys(): any[] { const keys: any[] = []; for (const k in this.stringKeys) { - if (Object.prototype.hasOwnProperty.call(this.stringKeys, k)) { + if (Object.hasOwn(this.stringKeys, k)) { keys.push(k); } } @@ -706,7 +706,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable { empty(): boolean { for (const k in this.stringKeys) { - if (Object.prototype.hasOwnProperty.call(this.stringKeys, k)) { + if (Object.hasOwn(this.stringKeys, k)) { return false; } } @@ -934,7 +934,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable { ); } - if (typeof key === "number" && isNaN(key)) { + if (typeof key === "number" && Number.isNaN(key)) { throw new LuaRuntimeError( "table index is NaN", errSf, @@ -1135,9 +1135,9 @@ export class LuaTable implements ILuaSettable, ILuaGettable { if (typeof key === "string") { result += key; } else { - result += "[" + key + "]"; + result += `[${key}]`; } - result += " = " + await luaToString(this.get(key)); + result += ` = ${await luaToString(this.get(key))}`; } result += "}"; return result; @@ -1492,7 +1492,7 @@ export class LuaRuntimeError extends Error { ); // Add position indicator - const pointer = " ".repeat(column) + "^"; + const pointer = `${" ".repeat(column)}^`; traceStr += `* ${ctx.ref || "(unknown source)"} @ ${line}:${column}:\n` + ` ${codeLine}\n` + @@ -1579,7 +1579,7 @@ export function luaToString( const strVal = await luaToString(val, visited); result += strVal; } - return result + "}"; + return `${result}}`; } // Handle objects @@ -1606,7 +1606,7 @@ export function luaToString( } export function luaFormatNumber(n: number, kind?: "int" | "float"): string { - if (kind !== "float" && Number.isInteger(n) && isFinite(n)) { + if (kind !== "float" && Number.isInteger(n) && Number.isFinite(n)) { return String(n); } if (n !== n) return "-nan"; @@ -1619,7 +1619,7 @@ export function luaFormatNumber(n: number, kind?: "int" | "float"): string { const s = luaFormat("%.14g", n); // Guarantee `.01 suffix for integer-valued floats if (s.indexOf(".") === -1 && s.indexOf("e") === -1) { - return s + ".0"; + return `${s}.0`; } return s; } diff --git a/client/space_lua/stdlib.ts b/client/space_lua/stdlib.ts index 0d7ec5ab..3837106c 100644 --- a/client/space_lua/stdlib.ts +++ b/client/space_lua/stdlib.ts @@ -450,7 +450,7 @@ const nextFunction = new LuaBuiltinFunction( const someFunction = new LuaBuiltinFunction(async (_sf, value: any) => { switch (await luaTypeOf(value)) { case "number": - if (!isFinite(value)) return null; + if (!Number.isFinite(value)) return null; break; case "string": if (value.trim() === "") return null; diff --git a/client/space_lua/stdlib/format.ts b/client/space_lua/stdlib/format.ts index a4cb3489..7b7f73ac 100644 --- a/client/space_lua/stdlib/format.ts +++ b/client/space_lua/stdlib/format.ts @@ -128,8 +128,8 @@ function pad(s: string, width: number, flags: number, numPad: boolean): string { } function addSign(s: string, flags: number): string { - if (flags & FLAG_PLUS) return "+" + s; - if (flags & FLAG_SPACE) return " " + s; + if (flags & FLAG_PLUS) return `+${s}`; + if (flags & FLAG_SPACE) return ` ${s}`; return s; } @@ -200,7 +200,7 @@ function formatInt(n: number, spec: FormatSpec): string { let result: string; if (neg) { - result = "-" + prefix + digits; + result = `-${prefix}${digits}`; } else { result = addSign(prefix + digits, spec.flags); } @@ -216,7 +216,7 @@ function formatFloat(n: number, spec: FormatSpec): string { const lower = code | 32; // to lowercase // Lua convention - if (!isFinite(n)) { + if (!Number.isFinite(n)) { let s: string; if (n !== n) { s = upper ? "-NAN" : "-nan"; @@ -275,9 +275,9 @@ function formatFloat(n: number, spec: FormatSpec): string { const EIdx = body.indexOf("E"); const expIdx = eIdx !== -1 ? eIdx : EIdx; if (expIdx !== -1) { - body = body.slice(0, expIdx) + "." + body.slice(expIdx); + body = `${body.slice(0, expIdx)}.${body.slice(expIdx)}`; } else { - body = body + "."; + body = `${body}.`; } } } @@ -287,16 +287,16 @@ function formatFloat(n: number, spec: FormatSpec): string { if (body.indexOf(".") === -1) { const expIdx = findExpIndex(body); if (expIdx !== -1) { - body = body.slice(0, expIdx) + "." + body.slice(expIdx); + body = `${body.slice(0, expIdx)}.${body.slice(expIdx)}`; } else { - body = body + "."; + body = `${body}.`; } } } let result: string; if (neg) { - result = "-" + body; + result = `-${body}`; } else { result = addSign(body, spec.flags); } @@ -322,7 +322,7 @@ function ensureExpTwoDigits(s: string): string { const digitStart = signIdx + 1; const expLen = s.length - digitStart; if (expLen < 2) { - return s.slice(0, digitStart) + "0" + s.slice(digitStart); + return `${s.slice(0, digitStart)}0${s.slice(digitStart)}`; } return s; } @@ -353,7 +353,7 @@ function formatHexFloat(n: number, spec: FormatSpec): string { const code = spec.spec; const upper = code === 65; // 'A' - if (!isFinite(n)) { + if (!Number.isFinite(n)) { let s: string; if (n !== n) { s = upper ? "-NAN" : "-nan"; @@ -373,7 +373,7 @@ function formatHexFloat(n: number, spec: FormatSpec): string { if (abs === 0) { const prec = spec.hasPrec ? spec.prec : 0; if (prec > 0) { - body = "0x0." + "0".repeat(prec) + "p+0"; + body = `0x0.${"0".repeat(prec)}p+0`; } else { body = "0x0p+0"; } @@ -395,14 +395,14 @@ function formatHexFloat(n: number, spec: FormatSpec): string { } } if (!hasDot) { - body = body.slice(0, pIdx) + "." + body.slice(pIdx); + body = `${body.slice(0, pIdx)}.${body.slice(pIdx)}`; } } } let result: string; if (neg) { - result = "-" + body; + result = `-${body}`; } else { result = addSign(body, spec.flags); } @@ -474,9 +474,9 @@ function hexFloatBody(abs: number, spec: FormatSpec): string { const expSign = exponent >= 0 ? "+" : ""; if (fracHex.length > 0) { - return "0x" + firstDigit + "." + fracHex + "p" + expSign + exponent; + return `0x${firstDigit}.${fracHex}p${expSign}${exponent}`; } - return "0x" + firstDigit + "p" + expSign + exponent; + return `0x${firstDigit}p${expSign}${exponent}`; } // Convert 52-bit value to 13 hex digits, zero-padded @@ -581,7 +581,7 @@ function quoteString(s: string): string { if (ds.length < 3) out += "0".repeat(3 - ds.length); out += ds; } else { - out += "\\" + c.toString(); + out += `\\${c.toString()}`; } } else { out += String.fromCharCode(c); @@ -643,7 +643,7 @@ function toPointer(v: unknown): string { id = nextId++; stringIds.set(key, id); } - return "0x" + id.toString(16).padStart(14, "0"); + return `0x${id.toString(16).padStart(14, "0")}`; } const obj = v as object; @@ -652,7 +652,7 @@ function toPointer(v: unknown): string { id = nextId++; objectIds.set(obj, id); } - return "0x" + id.toString(16).padStart(14, "0"); + return `0x${id.toString(16).padStart(14, "0")}`; } function formatPointer(v: unknown, spec: FormatSpec): string { diff --git a/client/space_lua/stdlib/math.ts b/client/space_lua/stdlib/math.ts index 90c8f801..df113a9b 100644 --- a/client/space_lua/stdlib/math.ts +++ b/client/space_lua/stdlib/math.ts @@ -52,15 +52,15 @@ export const mathApi = new LuaTable({ */ tointeger: new LuaBuiltinFunction((_sf, x?: any) => { if (typeof x === "number") { - return Number.isInteger(x) && isFinite(x) ? x : null; + return Number.isInteger(x) && Number.isFinite(x) ? x : null; } if (isTaggedFloat(x)) { const n = x.value; - return Number.isInteger(n) && isFinite(n) ? n : null; + return Number.isInteger(n) && Number.isFinite(n) ? n : null; } if (typeof x === "string") { const n = untagNumber(x); // Number(x) coerces the string - if (isNaN(n) || !isFinite(n) || !Number.isInteger(n)) return null; + if (Number.isNaN(n) || !Number.isFinite(n) || !Number.isInteger(n)) return null; return n; } return null; @@ -123,12 +123,12 @@ export const mathApi = new LuaTable({ // Special cases: frexp(0) = (0, 0); frexp(+-inf/nan) = (x, 0). frexp: new LuaBuiltinFunction((_sf, x: number) => { const xn = untagNumber(x); - if (xn === 0 || !isFinite(xn) || isNaN(xn)) { + if (xn === 0 || !Number.isFinite(xn) || Number.isNaN(xn)) { return new LuaMultiRes([xn, 0]); } const abs = Math.abs(xn); let e = Math.floor(Math.log2(abs)) + 1; - let m = xn / Math.pow(2, e); + let m = xn / 2 ** e; if (Math.abs(m) >= 1.0) { e += 1; m /= 2; @@ -142,7 +142,7 @@ export const mathApi = new LuaTable({ // Returns m * 2^e (the inverse of frexp). Mirrors C99/Lua. ldexp: new LuaBuiltinFunction((_sf, m: number, e: number) => - untagNumber(m) * Math.pow(2, untagNumber(e)) + untagNumber(m) * 2 ** untagNumber(e) ), // Power and logarithms @@ -155,7 +155,7 @@ export const mathApi = new LuaTable({ }), // Power function (deprecated in Lua 5.4 but retained for compatibility) pow: new LuaBuiltinFunction((_sf, x: number, y: number) => - Math.pow(untagNumber(x), untagNumber(y)) + untagNumber(x) ** untagNumber(y) ), sqrt: new LuaBuiltinFunction((_sf, x: number) => Math.sqrt(untagNumber(x))), diff --git a/client/space_lua/stdlib/net.ts b/client/space_lua/stdlib/net.ts index d79de4e8..8927168f 100644 --- a/client/space_lua/stdlib/net.ts +++ b/client/space_lua/stdlib/net.ts @@ -66,7 +66,7 @@ export const netApi = new LuaTable({ (uri: string, options: { uri?: string; encoding?: string } = {}) => { options.uri = uri; return client.clientSystem.serviceRegistry.invokeBestMatch( - "net.readURI:" + uri, + `net.readURI:${uri}`, options, ); }, @@ -74,7 +74,7 @@ export const netApi = new LuaTable({ writeURI: new LuaNativeJSFunction( (uri: string, content: string | Uint8Array) => { return client.clientSystem.serviceRegistry.invokeBestMatch( - "net.writeURI:" + uri, + `net.writeURI:${uri}`, { uri, content }, ); }, diff --git a/client/space_lua/stdlib/pattern.ts b/client/space_lua/stdlib/pattern.ts index 795ddfc6..c4c5fce7 100644 --- a/client/space_lua/stdlib/pattern.ts +++ b/client/space_lua/stdlib/pattern.ts @@ -409,7 +409,6 @@ function match(ms: MatchState, si: number, pi: number): number { } si++; pi = ep; - continue; } } diff --git a/client/space_lua/stdlib/prng.ts b/client/space_lua/stdlib/prng.ts index ecb9d51e..82301866 100644 --- a/client/space_lua/stdlib/prng.ts +++ b/client/space_lua/stdlib/prng.ts @@ -92,7 +92,7 @@ export class LuaPRNG { return Number(rv >> 11n) * (1.0 / 9007199254740992.0); } - if (!isFinite(arg1) || !Number.isInteger(arg1)) { + if (!Number.isFinite(arg1) || !Number.isInteger(arg1)) { throw new Error( "bad argument #1 to 'random' (number has no integer representation)", ); @@ -112,7 +112,7 @@ export class LuaPRNG { return Number(this.project(rv, BigInt(arg1) - 1n) + 1n); } - if (!isFinite(arg2) || !Number.isInteger(arg2)) { + if (!Number.isFinite(arg2) || !Number.isInteger(arg2)) { throw new Error( "bad argument #2 to 'random' (number has no integer representation)", ); @@ -128,12 +128,12 @@ export class LuaPRNG { if (arg1 === undefined) { return this.autoSeed(); } - if (!isFinite(arg1) || !Number.isInteger(arg1)) { + if (!Number.isFinite(arg1) || !Number.isInteger(arg1)) { throw new Error( "bad argument #1 to 'randomseed' (number has no integer representation)", ); } - if (arg2 !== undefined && (!isFinite(arg2) || !Number.isInteger(arg2))) { + if (arg2 !== undefined && (!Number.isFinite(arg2) || !Number.isInteger(arg2))) { throw new Error( "bad argument #2 to 'randomseed' (number has no integer representation)", ); diff --git a/client/space_lua/tonumber.ts b/client/space_lua/tonumber.ts index 3f790d18..4e3e421f 100644 --- a/client/space_lua/tonumber.ts +++ b/client/space_lua/tonumber.ts @@ -283,7 +283,7 @@ function parseDecFloat(s: string): { ok: boolean; value: number } { return { ok: false, value: 0 }; } - const result = sign * (hasExp ? val * Math.pow(10, exp) : val); + const result = sign * (hasExp ? val * 10 ** exp : val); return { ok: true, value: result }; } @@ -434,7 +434,7 @@ function parseHexFloat(s: string): { ok: boolean; value: number } { } const frac = fracVal === 0 ? 0 : (fracVal / fracScale); - const result = sign * (intVal + frac) * Math.pow(2, expSign * exp); + const result = sign * (intVal + frac) * 2 ** (expSign * exp); return { ok: true, value: result }; } @@ -460,6 +460,7 @@ export function luaToNumberDetailed( } { + // biome-ignore lint/correctness/useParseIntRadix: local parseInt function, not global const parsed = parseInt(s); if (parsed.ok) { const v = parsed.value; diff --git a/client/space_lua_api.ts b/client/space_lua_api.ts index d3cc0d96..3d1323fe 100644 --- a/client/space_lua_api.ts +++ b/client/space_lua_api.ts @@ -75,7 +75,7 @@ export async function handleLuaError(e: LuaRuntimeError, system: System<any>) { e.message, e.sf?.astCtx, ); - if (e.sf?.astCtx && e.sf.astCtx.ref) { + if (e.sf?.astCtx?.ref) { // We got an error and actually know where it came from, let's navigate there to help debugging await system.localSyscall( "editor.flashNotification", diff --git a/client/spaces/datastore_space_primitives.test.ts b/client/spaces/datastore_space_primitives.test.ts index 41706d37..ab7c675b 100644 --- a/client/spaces/datastore_space_primitives.test.ts +++ b/client/spaces/datastore_space_primitives.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from "vitest"; +import { test } from "vitest"; import "fake-indexeddb/auto"; import { IndexedDBKvPrimitives } from "../data/indexeddb_kv_primitives.ts"; import { DataStoreSpacePrimitives } from "./datastore_space_primitives.ts"; diff --git a/client/spaces/http_space_primitives.ts b/client/spaces/http_space_primitives.ts index ca562cdb..f77235d8 100644 --- a/client/spaces/http_space_primitives.ts +++ b/client/spaces/http_space_primitives.ts @@ -189,9 +189,9 @@ export class HttpSpacePrimitives implements SpacePrimitives { "Content-Type": "application/octet-stream", }; if (meta) { - headers["X-Created"] = "" + meta.created; - headers["X-Last-Modified"] = "" + meta.lastModified; - headers["X-Perm"] = "" + meta.perm; + headers["X-Created"] = `${meta.created}`; + headers["X-Last-Modified"] = `${meta.lastModified}`; + headers["X-Perm"] = `${meta.perm}`; } const res = await this.authenticatedFetch( diff --git a/client/spaces/sync.test.ts b/client/spaces/sync.test.ts index d17bafd8..991e1848 100644 --- a/client/spaces/sync.test.ts +++ b/client/spaces/sync.test.ts @@ -1,7 +1,6 @@ import { expect, test } from "vitest"; import { SpaceSync, SyncSnapshot } from "./sync.ts"; import { sleep } from "@silverbulletmd/silverbullet/lib/async"; -import { assert } from "node:console"; import { MemoryKvPrimitives } from "../data/memory_kv_primitives.ts"; import { DataStoreSpacePrimitives } from "./datastore_space_primitives.ts"; diff --git a/client/spaces/sync.ts b/client/spaces/sync.ts index 743191ea..66d7b0c9 100644 --- a/client/spaces/sync.ts +++ b/client/spaces/sync.ts @@ -55,8 +55,6 @@ export type SyncOptions = { isSyncCandidate: (path: string) => boolean; }; -type SyncDirection = "primary->secondary" | "secondary->primary"; - export type SyncEvents = { syncProgress: ( syncStatus: SyncStatus, @@ -150,7 +148,7 @@ export class SpaceSync extends EventEmitter<SyncEvents> { "Completed:", operations, "operations in", - ((Date.now() - startTime) / 1000) + "s", + `${(Date.now() - startTime) / 1000}s`, ); } finally { this.isSyncing = false; diff --git a/package-lock.json b/package-lock.json index 722d4a92..3276198f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "plug-compile": "dist/plug-compile.js" }, "devDependencies": { + "@biomejs/biome": "2.4.6", "@preact/preset-vite": "^2.10.3", "@types/gitignore-parser": "^0.0.3", "@types/js-yaml": "^4.0.9", @@ -67,7 +68,7 @@ "vitest": "^4.0.18" }, "engines": { - "node": ">=24.0.0", + "node": ">=24.13.0", "npm": ">=10.0.0" } }, @@ -386,6 +387,169 @@ "node": ">=6.9.0" } }, + "node_modules/@biomejs/biome": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.6.tgz", + "integrity": "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.6", + "@biomejs/cli-darwin-x64": "2.4.6", + "@biomejs/cli-linux-arm64": "2.4.6", + "@biomejs/cli-linux-arm64-musl": "2.4.6", + "@biomejs/cli-linux-x64": "2.4.6", + "@biomejs/cli-linux-x64-musl": "2.4.6", + "@biomejs/cli-win32-arm64": "2.4.6", + "@biomejs/cli-win32-x64": "2.4.6" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.6.tgz", + "integrity": "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.6.tgz", + "integrity": "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.6.tgz", + "integrity": "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.6.tgz", + "integrity": "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.6.tgz", + "integrity": "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.6.tgz", + "integrity": "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.6.tgz", + "integrity": "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.6.tgz", + "integrity": "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@codemirror/autocomplete": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", diff --git a/package.json b/package.json index 1475e7ca..8b5eb0d9 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "prepublishOnly": "npm run build:plug-compile", "test": "vitest run", "check": "tsc --noEmit", + "lint": "biome lint .", "bench": "vitest bench" }, "dependencies": { @@ -104,6 +105,7 @@ "turndown": "7.2.2" }, "devDependencies": { + "@biomejs/biome": "2.4.6", "@preact/preset-vite": "^2.10.3", "@types/gitignore-parser": "^0.0.3", "@types/js-yaml": "^4.0.9", diff --git a/plug-api/lib/async.ts b/plug-api/lib/async.ts index 380b44e0..42a7b9c2 100644 --- a/plug-api/lib/async.ts +++ b/plug-api/lib/async.ts @@ -1,6 +1,6 @@ export function throttle(func: () => void, limit: number): () => void { let timer: any = null; - return function () { + return () => { if (!timer) { timer = setTimeout(() => { func(); @@ -15,7 +15,7 @@ export function throttleImmediately( limit: number, ): () => void { let timer: any = null; - return function () { + return () => { if (!timer) { func(); timer = setTimeout(() => { diff --git a/plug-api/lib/dates.test.ts b/plug-api/lib/dates.test.ts index f945ed1e..63958a4a 100644 --- a/plug-api/lib/dates.test.ts +++ b/plug-api/lib/dates.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from "vitest"; +import { test } from "vitest"; import { localDateString } from "./dates.ts"; test("Dates", () => { diff --git a/plug-api/lib/json.ts b/plug-api/lib/json.ts index 7c4a6564..1fadfc1c 100644 --- a/plug-api/lib/json.ts +++ b/plug-api/lib/json.ts @@ -125,7 +125,7 @@ export function deepClone<T>(obj: T, ignoreKeys: string[] = []): T { for (const key in obj) { if (ignoreKeys.includes(key)) { objClone[key] = obj[key]; - } else if (Object.prototype.hasOwnProperty.call(obj, key)) { + } else if (Object.hasOwn(obj, key)) { objClone[key] = deepClone(obj[key], ignoreKeys); } } diff --git a/plug-api/lib/ref.ts b/plug-api/lib/ref.ts index bd8ca5fa..800f0b17 100644 --- a/plug-api/lib/ref.ts +++ b/plug-api/lib/ref.ts @@ -95,7 +95,7 @@ export function isValidPath(path: string): path is Path { * TO THE INNER WORKINGS OF SILVERBULLET AND CHANGES COULD INTRODUCE MAJOR BUGS */ const refRegex = - /^(?<meta>\^)?(?<path>(?!.*\.[a-zA-Z0-9]+\.md$)(?!\/?(\.|\^))(?!.*(?:\/|^)\.{1,2}(?:\/|$)|.*\/{2})(?!.*(?:\]\]|\[\[))[^@#\|<>]*)(@(?<pos>\d+)|@[Ll](?<line>\d+)(?:[Cc](?<col>\d+))?|#\s*(?<header>.*))?$/; + /^(?<meta>\^)?(?<path>(?!.*\.[a-zA-Z0-9]+\.md$)(?!\/?(\.|\^))(?!.*(?:\/|^)\.{1,2}(?:\/|$)|.*\/{2})(?!.*(?:\]\]|\[\[))[^@#|<>]*)(@(?<pos>\d+)|@[Ll](?<line>\d+)(?:[Cc](?<col>\d+))?|#\s*(?<header>.*))?$/; /** * Parses a reference string into a ref object. @@ -118,13 +118,13 @@ export function parseToRef(stringRef: string): Ref | null { if (groups.pos !== undefined) { ref.details = { type: "position", - pos: parseInt(groups.pos), + pos: parseInt(groups.pos, 10), }; } else if (groups.line !== undefined) { ref.details = { type: "linecolumn", - line: parseInt(groups.line), - column: groups.col !== undefined ? parseInt(groups.col) : 1, + line: parseInt(groups.line, 10), + column: groups.col !== undefined ? parseInt(groups.col, 10) : 1, }; } else if (groups.header !== undefined) { ref.details = { diff --git a/plug-api/lib/transclusion.ts b/plug-api/lib/transclusion.ts index 78a2e226..b7914681 100644 --- a/plug-api/lib/transclusion.ts +++ b/plug-api/lib/transclusion.ts @@ -45,19 +45,19 @@ export function parseDimensionFromAlias( const [width, height] = dimPart.split("x"); dim = {}; if (width) { - dim.width = parseInt(width); + dim.width = parseInt(width, 10); } if (height) { - dim.height = parseInt(height); + dim.height = parseInt(height, 10); } } else if (/^[x\d]/.test(text)) { const [width, height] = text.split("x"); dim = {}; if (width) { - dim.width = parseInt(width); + dim.width = parseInt(width, 10); } if (height) { - dim.height = parseInt(height); + dim.height = parseInt(height, 10); } alias = ""; } else { @@ -74,7 +74,7 @@ export function parseDimensionFromAlias( export function parseTransclusion( text: string, ): Transclusion | null { - let url, alias = undefined; + let url, alias ; let linktype: LinkType = "markdownlink"; // TODO: Take in the tree and use tree nodes to get url and alias (Applies to all regex uses) mdLinkRegex.lastIndex = 0; diff --git a/plug-api/lib/tree.ts b/plug-api/lib/tree.ts index 12f2f1cd..99d19da9 100644 --- a/plug-api/lib/tree.ts +++ b/plug-api/lib/tree.ts @@ -91,7 +91,7 @@ export function replaceNodesMatching( tree: ParseTree, substituteFn: (tree: ParseTree) => ParseTree | null | undefined, ) { - if (tree && tree.children) { + if (tree?.children) { const children = tree.children.slice(); for (const child of children) { const subst = substituteFn(child); diff --git a/plug-api/lib/yaml.ts b/plug-api/lib/yaml.ts index e96f1b24..ee1aecf1 100644 --- a/plug-api/lib/yaml.ts +++ b/plug-api/lib/yaml.ts @@ -44,7 +44,7 @@ function serializeToYamlValue( return "[]"; // Use flow style for empty arrays for simplicity } // Determine indentation for list items (base + 2 spaces) - const itemIndentation = baseIndentation + " "; + const itemIndentation = `${baseIndentation} `; // Format each item recursively, preceded by '- ' marker return "\n" + value.map((item) => @@ -55,7 +55,7 @@ function serializeToYamlValue( } else if (typeof value === "object" && value !== null) { // Basic object serialization (not requested, but good to consider) // This is highly simplified and doesn't handle nesting well without more context - const itemIndentation = baseIndentation + " "; + const itemIndentation = `${baseIndentation} `; const entries = Object.entries(value); if (entries.length === 0) return "{}"; // Flow style empty objects return "\n" + @@ -89,7 +89,7 @@ export function applyPatches( for (let i = 0; i < lines.length; i++) { const line = lines[i]; const trimmedLine = line.trim(); - if (trimmedLine.startsWith(key + ":")) { + if (trimmedLine.startsWith(`${key}:`)) { keyLineIndex = i; startDeleteIndex = i; endDeleteIndex = i; @@ -203,7 +203,7 @@ export function applyPatches( for (let i = 0; i < lines.length; i++) { const line = lines[i]; const trimmedLine = line.trim(); - if (trimmedLine.startsWith(key + ":")) { + if (trimmedLine.startsWith(`${key}:`)) { keyLineIndex = i; // Extract inline comment if present const commentMatch = line.match(/#.*$/); @@ -214,7 +214,7 @@ export function applyPatches( for (let j = i - 1; j >= 0; j--) { const prevLine = lines[j].trim(); if (prevLine.startsWith("#")) { - commentBlock = lines[j] + "\n" + commentBlock; + commentBlock = `${lines[j]}\n${commentBlock}`; } else if (prevLine !== "") { break; } @@ -223,7 +223,7 @@ export function applyPatches( for (let j = i + 1; j < lines.length; j++) { const nextLine = lines[j].trim(); if (nextLine.startsWith("#")) { - trailingComments += lines[j] + "\n"; + trailingComments += `${lines[j]}\n`; } else if (nextLine !== "") { break; } @@ -243,7 +243,7 @@ export function applyPatches( } else { // For scalars, format as key: value replacementLine = `${key}: ${serializedNewValue}${ - inlineComment ? " " + inlineComment : "" + inlineComment ? ` ${inlineComment}` : "" }`; } @@ -267,14 +267,14 @@ export function applyPatches( ]; // Join lines and ensure proper newlines - currentYaml = newContent.join("\n").replace(/\n*$/, "\n") + "\n"; + currentYaml = `${newContent.join("\n").replace(/\n*$/, "\n")}\n`; } else { // Key not found: Add the new key-value pair to the end const newLineBlock = replacementLine; if (currentYaml.trim() === "") { - currentYaml = newLineBlock + "\n"; + currentYaml = `${newLineBlock}\n`; } else { - currentYaml = currentYaml.replace(/\n*$/, "\n") + newLineBlock + "\n"; + currentYaml = `${currentYaml.replace(/\n*$/, "\n") + newLineBlock}\n`; } } } diff --git a/plug-api/system_mock.ts b/plug-api/system_mock.ts index 9d7276b7..a3cae529 100644 --- a/plug-api/system_mock.ts +++ b/plug-api/system_mock.ts @@ -71,7 +71,6 @@ export function createMockSystem() { systemSyscalls(clientMock, false), ); - // @ts-ignore: global globalThis.syscall = (name: string, ...args: any): Promise<any> => { return system.localSyscall(name, args); }; diff --git a/plugs/editor/complete.ts b/plugs/editor/complete.ts index ed31d4c1..29c63c84 100644 --- a/plugs/editor/complete.ts +++ b/plugs/editor/complete.ts @@ -30,10 +30,10 @@ export async function pageComplete(completeEvent: CompleteEvent) { let isWikilink = true; // This negative lookbehind is to prevent matching query[[. This requires negative lookbehind, which generally supported now (it seems), in versions of iOS Safari 13.1 and later // https://caniuse.com/js-regexp-lookbehind - let match = /(?<!query)\[\[([^\]@$#:\{}]*)$/.exec(completeEvent.linePrefix); + let match = /(?<!query)\[\[([^\]@$#:{}]*)$/.exec(completeEvent.linePrefix); if (!match) { // Try to match [markdown link]() - match = /\[.*\]\(([^\]\)@$#:\{}]*)$/.exec(completeEvent.linePrefix); + match = /\[.*\]\(([^\])@$#:{}]*)$/.exec(completeEvent.linePrefix); isWikilink = false; } if (!match) { @@ -50,7 +50,7 @@ export async function pageComplete(completeEvent: CompleteEvent) { // Let's prefix the names with a caret to make them match allPages = allPages.map((page) => ({ ...page, - name: "^" + page.name, + name: `^${page.name}`, })); } else { // This is the most common case, we're combining three types of completions here: @@ -88,7 +88,7 @@ export async function pageComplete(completeEvent: CompleteEvent) { return { from: completeEvent.pos - prefix.length, - options: allPages.map((pageMeta) => { + options: allPages.flatMap((pageMeta) => { const completions: any[] = []; const namePrefix = (pageMeta as PageMeta).pageDecoration?.prefix || ""; const cssClass = ((pageMeta as PageMeta).pageDecoration?.cssClasses || []) @@ -121,7 +121,7 @@ export async function pageComplete(completeEvent: CompleteEvent) { for (const alias of pageMeta.aliases) { const decoratedName = namePrefix + alias; completions.push({ - label: "" + alias, + label: `${alias}`, displayLabel: decoratedName, boost: new Date(pageMeta.lastModified).getTime(), apply: pageMeta.tag === "template" @@ -154,19 +154,19 @@ export async function pageComplete(completeEvent: CompleteEvent) { boost = boost * 1.1; } else { // Absolute path otherwise - labelText = "/" + labelText; + labelText = `/${labelText}`; } completions.push({ label: labelText, displayLabel: namePrefix + labelText, boost: boost, - apply: labelText.includes(" ") ? "<" + labelText + ">" : labelText, + apply: labelText.includes(" ") ? `<${labelText}>` : labelText, type: "page", cssClass, }); } return completions; - }).flat(), + }), }; } diff --git a/plugs/editor/editor.ts b/plugs/editor/editor.ts index 26e8faf3..3b1a5e76 100644 --- a/plugs/editor/editor.ts +++ b/plugs/editor/editor.ts @@ -159,9 +159,9 @@ export async function moveToLineCommand() { return; } let column = 1; - const line = parseInt(match[1]); + const line = parseInt(match[1], 10); if (match[2]) { - column = parseInt(match[2]); + column = parseInt(match[2], 10); } await editor.moveCursorToLine(line, column, true); // showing the movement for better UX } diff --git a/plugs/editor/outline.ts b/plugs/editor/outline.ts index 0d902de6..5b9c8587 100644 --- a/plugs/editor/outline.ts +++ b/plugs/editor/outline.ts @@ -143,7 +143,7 @@ export async function indentItem() { const currentItemBounds = determineItemBounds(text, cursorPos); const itemText = text.slice(currentItemBounds.from, currentItemBounds.to); const newText = itemText.split("\n").map((line) => - line ? " " + line : line + line ? ` ${line}` : line ).join("\n"); const preText = text.slice(currentItemBounds.from, cursorPos); const newCursorPos = cursorPos + preText.split("\n").length * 2; @@ -198,7 +198,7 @@ export async function outdentItem() { export function ensureNewLine(s: string) { if (!s.endsWith("\n")) { - return s + "\n"; + return `${s}\n`; } else { return s; } diff --git a/plugs/editor/stats.ts b/plugs/editor/stats.ts index 726a57fa..d5461908 100644 --- a/plugs/editor/stats.ts +++ b/plugs/editor/stats.ts @@ -1,7 +1,7 @@ import { editor, space } from "@silverbulletmd/silverbullet/syscalls"; function countWords(str: string): number { - const matches = str.match(/[\w\d\'-]+/gi); + const matches = str.match(/[\w\d'-]+/gi); return matches ? matches.length : 0; } diff --git a/plugs/editor/text.ts b/plugs/editor/text.ts index d75cd40d..b241f355 100644 --- a/plugs/editor/text.ts +++ b/plugs/editor/text.ts @@ -24,13 +24,13 @@ export async function listifySelection() { const selection = await editor.getSelection(); //if very first of doc, just add a bullet and end - if (selection.to == 0 && selection.from == 0) { + if (selection.to === 0 && selection.from === 0) { await editor.insertAtCursor("* "); return; } let from = selection.from; - if (text[from] == "\n") { + if (text[from] === "\n") { //end of line, need to find previous line break from--; } diff --git a/plugs/editor/upload.ts b/plugs/editor/upload.ts index 33abb86b..11fadd0a 100644 --- a/plugs/editor/upload.ts +++ b/plugs/editor/upload.ts @@ -110,7 +110,7 @@ export async function saveFile(file: UploadFile) { documentMarkdown = `[${finalFilePath}](${encodePageURI(finalFilePath)})`; } if (file.contentType.startsWith("image/")) { - documentMarkdown = "!" + documentMarkdown; + documentMarkdown = `!${documentMarkdown}`; } editor.insertAtCursor(documentMarkdown); } diff --git a/plugs/emoji/emoji.ts b/plugs/emoji/emoji.ts index 6c68b957..7f7630d8 100644 --- a/plugs/emoji/emoji.ts +++ b/plugs/emoji/emoji.ts @@ -85,7 +85,7 @@ async function updateConfig() { } if (badAliases.length > 0) { await editor.flashNotification( - errorMsg + `, need to fix: ${badAliases.join(",")}`, + `${errorMsg}, need to fix: ${badAliases.join(",")}`, "error", ); } diff --git a/plugs/image-viewer/viewer.ts b/plugs/image-viewer/viewer.ts index edac863d..120b8a4e 100644 --- a/plugs/image-viewer/viewer.ts +++ b/plugs/image-viewer/viewer.ts @@ -61,7 +61,7 @@ html, body { * Copyright Timmy Willison and other contributors * https://github.com/timmywil/panzoom/blob/main/MIT-License.txt */ -((t,e)=>{"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Panzoom=e()})(this,function(){var a,X=function(){return(X=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)},i=("undefined"!=typeof window&&(window.NodeList&&!NodeList.prototype.forEach&&(NodeList.prototype.forEach=Array.prototype.forEach),"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(t,e){e=e||{bubbles:!1,cancelable:!1,detail:null};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}),"undefined"!=typeof document&&!!document.documentMode);var c=["webkit","moz","ms"],l={};function Y(t){if(l[t])return l[t];var e=a=a||document.createElement("div").style;if(t in e)return l[t]=t;for(var n=t[0].toUpperCase()+t.slice(1),o=c.length;o--;){var r="".concat(c[o]).concat(n);if(r in e)return l[t]=r}}function o(t,e){return parseFloat(e[Y(t)])||0}function s(t,e,n){void 0===n&&(n=window.getComputedStyle(t));t="border"===e?"Width":"";return{left:o("".concat(e,"Left").concat(t),n),right:o("".concat(e,"Right").concat(t),n),top:o("".concat(e,"Top").concat(t),n),bottom:o("".concat(e,"Bottom").concat(t),n)}}function C(t,e,n){t.style[Y(e)]=n}function N(t){var e=t.parentNode,n=window.getComputedStyle(t),o=window.getComputedStyle(e),r=t.getBoundingClientRect(),a=e.getBoundingClientRect();return{elem:{style:n,width:r.width,height:r.height,top:r.top,bottom:r.bottom,left:r.left,right:r.right,margin:s(t,"margin",n),border:s(t,"border",n)},parent:{style:o,width:a.width,height:a.height,top:a.top,bottom:a.bottom,left:a.left,right:a.right,padding:s(e,"padding",o),border:s(e,"border",o)}}}var T={down:"mousedown",move:"mousemove",up:"mouseup mouseleave"};function L(t,e,n,o){T[t].split(" ").forEach(function(t){e.addEventListener(t,n,o)})}function V(t,e,n){T[t].split(" ").forEach(function(t){e.removeEventListener(t,n)})}function G(t,e){for(var n=t.length;n--;)if(t[n].pointerId===e.pointerId)return n;return-1}function I(t,e){if(e.touches)for(var n=0,o=0,r=e.touches;o<r.length;o++){var a=r[o];a.pointerId=n++,I(t,a)}else-1<(n=G(t,e))&&t.splice(n,1),t.push(e)}function R(t){for(var e,n=(t=t.slice(0)).pop();e=t.pop();)n={clientX:(e.clientX-n.clientX)/2+n.clientX,clientY:(e.clientY-n.clientY)/2+n.clientY};return n}function W(t){var e;return t.length<2?0:(e=t[0],t=t[1],Math.sqrt(Math.pow(Math.abs(t.clientX-e.clientX),2)+Math.pow(Math.abs(t.clientY-e.clientY),2)))}"undefined"!=typeof window&&("function"==typeof window.PointerEvent?T={down:"pointerdown",move:"pointermove",up:"pointerup pointerleave pointercancel"}:"function"==typeof window.TouchEvent&&(T={down:"touchstart",move:"touchmove",up:"touchend touchcancel"}));var Z=/^http:[\w\.\/]+svg$/;var q={animate:!1,canvas:!1,cursor:"move",disablePan:!1,disableZoom:!1,disableXAxis:!1,disableYAxis:!1,duration:200,easing:"ease-in-out",exclude:[],excludeClass:"panzoom-exclude",handleStartEvent:function(t){t.preventDefault(),t.stopPropagation()},maxScale:4,minScale:.125,overflow:"hidden",panOnlyWhenZoomed:!1,pinchAndPan:!1,relative:!1,setTransform:function(t,e,n){var o=e.x,r=e.y,a=e.isSVG;C(t,"transform","scale(".concat(e.scale,") translate(").concat(o,"px, ").concat(r,"px)")),a&&i&&(e=window.getComputedStyle(t).getPropertyValue("transform"),t.setAttribute("transform",e))},startX:0,startY:0,startScale:1,step:.3,touchAction:"none"};function t(u,f){if(!u)throw new Error("Panzoom requires an element as an argument");if(1!==u.nodeType)throw new Error("Panzoom requires an element with a nodeType of 1");if(!(t=>{for(var e=t;e&&e.parentNode;){if(e.parentNode===document)return 1;e=e.parentNode instanceof ShadowRoot?e.parentNode.host:e.parentNode}})(u))throw new Error("Panzoom should be called on elements that have been attached to the DOM");f=X(X({},q),f);t=u;var t,l=Z.test(t.namespaceURI)&&"svg"!==t.nodeName.toLowerCase(),n=u.parentNode;n.style.overflow=f.overflow,n.style.userSelect="none",n.style.touchAction=f.touchAction,(f.canvas?n:u).style.cursor=f.cursor,u.style.userSelect="none",u.style.touchAction=f.touchAction,C(u,"transformOrigin","string"==typeof f.origin?f.origin:l?"0 0":"50% 50%");var r,a,i,c,s,d,m=0,h=0,v=1,p=!1;function g(t,e,n){n.silent||(n=new CustomEvent(t,{detail:e}),u.dispatchEvent(n))}function y(o,r,t){var a={x:m,y:h,scale:v,isSVG:l,originalEvent:t};return requestAnimationFrame(function(){var t,e,n;"boolean"==typeof r.animate&&(r.animate?(t=u,e=r,n=Y("transform"),C(t,"transition","".concat(n," ").concat(e.duration,"ms ").concat(e.easing))):C(u,"transition","none")),r.setTransform(u,a,r),g(o,a,r),g("panzoomchange",a,r)}),a}function w(t,e,n,o){var r,a,i,c,l,s,d,o=X(X({},f),o),p={x:m,y:h,opts:o};return!o.force&&(o.disablePan||o.panOnlyWhenZoomed&&v===o.startScale)||(t=parseFloat(t),e=parseFloat(e),o.disableXAxis||(p.x=(o.relative?m:0)+t),o.disableYAxis||(p.y=(o.relative?h:0)+e),o.contain&&(e=((r=(e=(t=N(u)).elem.width/v)*n)-e)/2,i=((a=(i=t.elem.height/v)*n)-i)/2,"inside"===o.contain?(c=(-t.elem.margin.left-t.parent.padding.left+e)/n,l=(t.parent.width-r-t.parent.padding.left-t.elem.margin.left-t.parent.border.left-t.parent.border.right+e)/n,p.x=Math.max(Math.min(p.x,l),c),s=(-t.elem.margin.top-t.parent.padding.top+i)/n,d=(t.parent.height-a-t.parent.padding.top-t.elem.margin.top-t.parent.border.top-t.parent.border.bottom+i)/n,p.y=Math.max(Math.min(p.y,d),s)):"outside"===o.contain&&(c=(-(r-t.parent.width)-t.parent.padding.left-t.parent.border.left-t.parent.border.right+e)/n,l=(e-t.parent.padding.left)/n,p.x=Math.max(Math.min(p.x,l),c),s=(-(a-t.parent.height)-t.parent.padding.top-t.parent.border.top-t.parent.border.bottom+i)/n,d=(i-t.parent.padding.top)/n,p.y=Math.max(Math.min(p.y,d),s))),o.roundPixels&&(p.x=Math.round(p.x),p.y=Math.round(p.y))),p}function b(t,e){var n,o,r,a,e=X(X({},f),e),i={scale:v,opts:e};return!e.force&&e.disableZoom||(n=f.minScale,o=f.maxScale,e.contain&&(a=(e=N(u)).elem.width/v,r=e.elem.height/v,1<a)&&1<r&&(a=(e.parent.width-e.parent.border.left-e.parent.border.right)/a,e=(e.parent.height-e.parent.border.top-e.parent.border.bottom)/r,"inside"===f.contain?o=Math.min(o,a,e):"outside"===f.contain&&(n=Math.max(n,a,e))),i.scale=Math.min(Math.max(t,n),o)),i}function x(t,e,n,o){t=w(t,e,v,n);return m!==t.x||h!==t.y?(m=t.x,h=t.y,y("panzoompan",t.opts,o)):{x:m,y:h,scale:v,isSVG:l,originalEvent:o}}function S(t,e,n){var o,r,e=b(t,e),a=e.opts;if(a.force||!a.disableZoom)return t=e.scale,e=m,o=h,a.focal&&(e=((r=a.focal).x/t-r.x/v+m*t)/t,o=(r.y/t-r.y/v+h*t)/t),r=w(e,o,t,{relative:!1,force:!0}),m=r.x,h=r.y,v=t,y("panzoomzoom",a,n)}function e(t,e){e=X(X(X({},f),{animate:!0}),e);return S(v*Math.exp((t?1:-1)*e.step),e)}function E(t,e,n,o){var r=N(u),a=r.parent.width-r.parent.padding.left-r.parent.padding.right-r.parent.border.left-r.parent.border.right,i=r.parent.height-r.parent.padding.top-r.parent.padding.bottom-r.parent.border.top-r.parent.border.bottom,c=e.clientX-r.parent.left-r.parent.padding.left-r.parent.border.left-r.elem.margin.left,e=e.clientY-r.parent.top-r.parent.padding.top-r.parent.border.top-r.elem.margin.top,r=(l||(c-=r.elem.width/v/2,e-=r.elem.height/v/2),{x:c/a*(a*t),y:e/i*(i*t)});return S(t,X(X({},n),{animate:!1,focal:r}),o)}S(f.startScale,{animate:!1,force:!0}),setTimeout(function(){x(f.startX,f.startY,{animate:!1,force:!0})});var M=[];function o(t){((t,e)=>{for(var n,o,r=t;null!=r;r=r.parentNode)if(n=r,o=e.excludeClass,1===n.nodeType&&-1<" ".concat((n.getAttribute("class")||"").trim()," ").indexOf(" ".concat(o," "))||-1<e.exclude.indexOf(r))return 1})(t.target,f)||(I(M,t),p=!0,f.handleStartEvent(t),g("panzoomstart",{x:r=m,y:a=h,scale:v,isSVG:l,originalEvent:t},f),t=R(M),i=t.clientX,c=t.clientY,s=v,d=W(M))}function A(t){var e,n,o;p&&void 0!==r&&void 0!==a&&void 0!==i&&void 0!==c&&(I(M,t),e=R(M),n=1<M.length,o=v,n&&(0===d&&(d=W(M)),E(o=b((W(M)-d)*f.step/80+s).scale,e,{animate:!1},t)),n&&!f.pinchAndPan||x(r+(e.clientX-i)/o,a+(e.clientY-c)/o,{animate:!1},t))}function P(t){1===M.length&&g("panzoomend",{x:m,y:h,scale:v,isSVG:l,originalEvent:t},f);var e=M;if(t.touches)for(;e.length;)e.pop();else{t=G(e,t);-1<t&&e.splice(t,1)}p&&(p=!1,r=a=i=c=void 0)}var O=!1;function z(){O||(O=!0,L("down",f.canvas?n:u,o),L("move",document,A,{passive:!0}),L("up",document,P,{passive:!0}))}return f.noBind||z(),{bind:z,destroy:function(){O=!1,V("down",f.canvas?n:u,o),V("move",document,A),V("up",document,P)},eventNames:T,getPan:function(){return{x:m,y:h}},getScale:function(){return v},getOptions:function(){var t,e=f,n={};for(t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);return n},handleDown:o,handleMove:A,handleUp:P,pan:x,reset:function(t){var t=X(X(X({},f),{animate:!0,force:!0}),t),e=(v=b(t.startScale,t).scale,w(t.startX,t.startY,v,t));return m=e.x,h=e.y,y("panzoomreset",t)},resetStyle:function(){n.style.overflow="",n.style.userSelect="",n.style.touchAction="",n.style.cursor="",u.style.cursor="",u.style.userSelect="",u.style.touchAction="",C(u,"transformOrigin","")},setOptions:function(t){for(var e in t=void 0===t?{}:t)t.hasOwnProperty(e)&&(f[e]=t[e]);(t.hasOwnProperty("cursor")||t.hasOwnProperty("canvas"))&&(n.style.cursor=u.style.cursor="",(f.canvas?n:u).style.cursor=f.cursor),t.hasOwnProperty("overflow")&&(n.style.overflow=t.overflow),t.hasOwnProperty("touchAction")&&(n.style.touchAction=t.touchAction,u.style.touchAction=t.touchAction)},setStyle:function(t,e){return C(u,t,e)},zoom:S,zoomIn:function(t){return e(!0,t)},zoomOut:function(t){return e(!1,t)},zoomToPoint:E,zoomWithWheel:function(t,e){t.preventDefault();var e=X(X(X({},f),e),{animate:!1}),n=0===t.deltaY&&t.deltaX?t.deltaX:t.deltaY;return E(b(v*Math.exp((n<0?1:-1)*e.step/3),e).scale,t,e,t)}}}return t.defaultOptions=q,t}); +((t,e)=>{"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Panzoom=e()})(this,function(){var a,X=function(){return(X=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)},i=("undefined"!=typeof window&&(window.NodeList&&!NodeList.prototype.forEach&&(NodeList.prototype.forEach=Array.prototype.forEach),"function"!=typeof window.CustomEvent)&&(window.CustomEvent=function(t,e){e=e||{bubbles:!1,cancelable:!1,detail:null};var n=document.createEvent("CustomEvent");return n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}),"undefined"!=typeof document&&!!document.documentMode);var c=["webkit","moz","ms"],l={};function Y(t){if(l[t])return l[t];var e=a=a||document.createElement("div").style;if(t in e)return l[t]=t;for(var n=t[0].toUpperCase()+t.slice(1),o=c.length;o--;){var r="".concat(c[o]).concat(n);if(r in e)return l[t]=r}}function o(t,e){return parseFloat(e[Y(t)])||0}function s(t,e,n){void 0===n&&(n=window.getComputedStyle(t));t="border"===e?"Width":"";return{left:o("".concat(e,"Left").concat(t),n),right:o("".concat(e,"Right").concat(t),n),top:o("".concat(e,"Top").concat(t),n),bottom:o("".concat(e,"Bottom").concat(t),n)}}function C(t,e,n){t.style[Y(e)]=n}function N(t){var e=t.parentNode,n=window.getComputedStyle(t),o=window.getComputedStyle(e),r=t.getBoundingClientRect(),a=e.getBoundingClientRect();return{elem:{style:n,width:r.width,height:r.height,top:r.top,bottom:r.bottom,left:r.left,right:r.right,margin:s(t,"margin",n),border:s(t,"border",n)},parent:{style:o,width:a.width,height:a.height,top:a.top,bottom:a.bottom,left:a.left,right:a.right,padding:s(e,"padding",o),border:s(e,"border",o)}}}var T={down:"mousedown",move:"mousemove",up:"mouseup mouseleave"};function L(t,e,n,o){T[t].split(" ").forEach(function(t){e.addEventListener(t,n,o)})}function V(t,e,n){T[t].split(" ").forEach(function(t){e.removeEventListener(t,n)})}function G(t,e){for(var n=t.length;n--;)if(t[n].pointerId===e.pointerId)return n;return-1}function I(t,e){if(e.touches)for(var n=0,o=0,r=e.touches;o<r.length;o++){var a=r[o];a.pointerId=n++,I(t,a)}else-1<(n=G(t,e))&&t.splice(n,1),t.push(e)}function R(t){for(var e,n=(t=t.slice(0)).pop();e=t.pop();)n={clientX:(e.clientX-n.clientX)/2+n.clientX,clientY:(e.clientY-n.clientY)/2+n.clientY};return n}function W(t){var e;return t.length<2?0:(e=t[0],t=t[1],Math.sqrt(Math.pow(Math.abs(t.clientX-e.clientX),2)+Math.pow(Math.abs(t.clientY-e.clientY),2)))}"undefined"!=typeof window&&("function"==typeof window.PointerEvent?T={down:"pointerdown",move:"pointermove",up:"pointerup pointerleave pointercancel"}:"function"==typeof window.TouchEvent&&(T={down:"touchstart",move:"touchmove",up:"touchend touchcancel"}));var Z=/^http:[w./]+svg$/;var q={animate:!1,canvas:!1,cursor:"move",disablePan:!1,disableZoom:!1,disableXAxis:!1,disableYAxis:!1,duration:200,easing:"ease-in-out",exclude:[],excludeClass:"panzoom-exclude",handleStartEvent:function(t){t.preventDefault(),t.stopPropagation()},maxScale:4,minScale:.125,overflow:"hidden",panOnlyWhenZoomed:!1,pinchAndPan:!1,relative:!1,setTransform:function(t,e,n){var o=e.x,r=e.y,a=e.isSVG;C(t,"transform","scale(".concat(e.scale,") translate(").concat(o,"px, ").concat(r,"px)")),a&&i&&(e=window.getComputedStyle(t).getPropertyValue("transform"),t.setAttribute("transform",e))},startX:0,startY:0,startScale:1,step:.3,touchAction:"none"};function t(u,f){if(!u)throw new Error("Panzoom requires an element as an argument");if(1!==u.nodeType)throw new Error("Panzoom requires an element with a nodeType of 1");if(!(t=>{for(var e=t;e&&e.parentNode;){if(e.parentNode===document)return 1;e=e.parentNode instanceof ShadowRoot?e.parentNode.host:e.parentNode}})(u))throw new Error("Panzoom should be called on elements that have been attached to the DOM");f=X(X({},q),f);t=u;var t,l=Z.test(t.namespaceURI)&&"svg"!==t.nodeName.toLowerCase(),n=u.parentNode;n.style.overflow=f.overflow,n.style.userSelect="none",n.style.touchAction=f.touchAction,(f.canvas?n:u).style.cursor=f.cursor,u.style.userSelect="none",u.style.touchAction=f.touchAction,C(u,"transformOrigin","string"==typeof f.origin?f.origin:l?"0 0":"50% 50%");var r,a,i,c,s,d,m=0,h=0,v=1,p=!1;function g(t,e,n){n.silent||(n=new CustomEvent(t,{detail:e}),u.dispatchEvent(n))}function y(o,r,t){var a={x:m,y:h,scale:v,isSVG:l,originalEvent:t};return requestAnimationFrame(function(){var t,e,n;"boolean"==typeof r.animate&&(r.animate?(t=u,e=r,n=Y("transform"),C(t,"transition","".concat(n," ").concat(e.duration,"ms ").concat(e.easing))):C(u,"transition","none")),r.setTransform(u,a,r),g(o,a,r),g("panzoomchange",a,r)}),a}function w(t,e,n,o){var r,a,i,c,l,s,d,o=X(X({},f),o),p={x:m,y:h,opts:o};return!o.force&&(o.disablePan||o.panOnlyWhenZoomed&&v===o.startScale)||(t=parseFloat(t),e=parseFloat(e),o.disableXAxis||(p.x=(o.relative?m:0)+t),o.disableYAxis||(p.y=(o.relative?h:0)+e),o.contain&&(e=((r=(e=(t=N(u)).elem.width/v)*n)-e)/2,i=((a=(i=t.elem.height/v)*n)-i)/2,"inside"===o.contain?(c=(-t.elem.margin.left-t.parent.padding.left+e)/n,l=(t.parent.width-r-t.parent.padding.left-t.elem.margin.left-t.parent.border.left-t.parent.border.right+e)/n,p.x=Math.max(Math.min(p.x,l),c),s=(-t.elem.margin.top-t.parent.padding.top+i)/n,d=(t.parent.height-a-t.parent.padding.top-t.elem.margin.top-t.parent.border.top-t.parent.border.bottom+i)/n,p.y=Math.max(Math.min(p.y,d),s)):"outside"===o.contain&&(c=(-(r-t.parent.width)-t.parent.padding.left-t.parent.border.left-t.parent.border.right+e)/n,l=(e-t.parent.padding.left)/n,p.x=Math.max(Math.min(p.x,l),c),s=(-(a-t.parent.height)-t.parent.padding.top-t.parent.border.top-t.parent.border.bottom+i)/n,d=(i-t.parent.padding.top)/n,p.y=Math.max(Math.min(p.y,d),s))),o.roundPixels&&(p.x=Math.round(p.x),p.y=Math.round(p.y))),p}function b(t,e){var n,o,r,a,e=X(X({},f),e),i={scale:v,opts:e};return!e.force&&e.disableZoom||(n=f.minScale,o=f.maxScale,e.contain&&(a=(e=N(u)).elem.width/v,r=e.elem.height/v,1<a)&&1<r&&(a=(e.parent.width-e.parent.border.left-e.parent.border.right)/a,e=(e.parent.height-e.parent.border.top-e.parent.border.bottom)/r,"inside"===f.contain?o=Math.min(o,a,e):"outside"===f.contain&&(n=Math.max(n,a,e))),i.scale=Math.min(Math.max(t,n),o)),i}function x(t,e,n,o){t=w(t,e,v,n);return m!==t.x||h!==t.y?(m=t.x,h=t.y,y("panzoompan",t.opts,o)):{x:m,y:h,scale:v,isSVG:l,originalEvent:o}}function S(t,e,n){var o,r,e=b(t,e),a=e.opts;if(a.force||!a.disableZoom)return t=e.scale,e=m,o=h,a.focal&&(e=((r=a.focal).x/t-r.x/v+m*t)/t,o=(r.y/t-r.y/v+h*t)/t),r=w(e,o,t,{relative:!1,force:!0}),m=r.x,h=r.y,v=t,y("panzoomzoom",a,n)}function e(t,e){e=X(X(X({},f),{animate:!0}),e);return S(v*Math.exp((t?1:-1)*e.step),e)}function E(t,e,n,o){var r=N(u),a=r.parent.width-r.parent.padding.left-r.parent.padding.right-r.parent.border.left-r.parent.border.right,i=r.parent.height-r.parent.padding.top-r.parent.padding.bottom-r.parent.border.top-r.parent.border.bottom,c=e.clientX-r.parent.left-r.parent.padding.left-r.parent.border.left-r.elem.margin.left,e=e.clientY-r.parent.top-r.parent.padding.top-r.parent.border.top-r.elem.margin.top,r=(l||(c-=r.elem.width/v/2,e-=r.elem.height/v/2),{x:c/a*(a*t),y:e/i*(i*t)});return S(t,X(X({},n),{animate:!1,focal:r}),o)}S(f.startScale,{animate:!1,force:!0}),setTimeout(function(){x(f.startX,f.startY,{animate:!1,force:!0})});var M=[];function o(t){((t,e)=>{for(var n,o,r=t;null!=r;r=r.parentNode)if(n=r,o=e.excludeClass,1===n.nodeType&&-1<" ".concat((n.getAttribute("class")||"").trim()," ").indexOf(" ".concat(o," "))||-1<e.exclude.indexOf(r))return 1})(t.target,f)||(I(M,t),p=!0,f.handleStartEvent(t),g("panzoomstart",{x:r=m,y:a=h,scale:v,isSVG:l,originalEvent:t},f),t=R(M),i=t.clientX,c=t.clientY,s=v,d=W(M))}function A(t){var e,n,o;p&&void 0!==r&&void 0!==a&&void 0!==i&&void 0!==c&&(I(M,t),e=R(M),n=1<M.length,o=v,n&&(0===d&&(d=W(M)),E(o=b((W(M)-d)*f.step/80+s).scale,e,{animate:!1},t)),n&&!f.pinchAndPan||x(r+(e.clientX-i)/o,a+(e.clientY-c)/o,{animate:!1},t))}function P(t){1===M.length&&g("panzoomend",{x:m,y:h,scale:v,isSVG:l,originalEvent:t},f);var e=M;if(t.touches)for(;e.length;)e.pop();else{t=G(e,t);-1<t&&e.splice(t,1)}p&&(p=!1,r=a=i=c=void 0)}var O=!1;function z(){O||(O=!0,L("down",f.canvas?n:u,o),L("move",document,A,{passive:!0}),L("up",document,P,{passive:!0}))}return f.noBind||z(),{bind:z,destroy:function(){O=!1,V("down",f.canvas?n:u,o),V("move",document,A),V("up",document,P)},eventNames:T,getPan:function(){return{x:m,y:h}},getScale:function(){return v},getOptions:function(){var t,e=f,n={};for(t in e)e.hasOwnProperty(t)&&(n[t]=e[t]);return n},handleDown:o,handleMove:A,handleUp:P,pan:x,reset:function(t){var t=X(X(X({},f),{animate:!0,force:!0}),t),e=(v=b(t.startScale,t).scale,w(t.startX,t.startY,v,t));return m=e.x,h=e.y,y("panzoomreset",t)},resetStyle:function(){n.style.overflow="",n.style.userSelect="",n.style.touchAction="",n.style.cursor="",u.style.cursor="",u.style.userSelect="",u.style.touchAction="",C(u,"transformOrigin","")},setOptions:function(t){for(var e in t=void 0===t?{}:t)t.hasOwnProperty(e)&&(f[e]=t[e]);(t.hasOwnProperty("cursor")||t.hasOwnProperty("canvas"))&&(n.style.cursor=u.style.cursor="",(f.canvas?n:u).style.cursor=f.cursor),t.hasOwnProperty("overflow")&&(n.style.overflow=t.overflow),t.hasOwnProperty("touchAction")&&(n.style.touchAction=t.touchAction,u.style.touchAction=t.touchAction)},setStyle:function(t,e){return C(u,t,e)},zoom:S,zoomIn:function(t){return e(!0,t)},zoomOut:function(t){return e(!1,t)},zoomToPoint:E,zoomWithWheel:function(t,e){t.preventDefault();var e=X(X(X({},f),e),{animate:!1}),n=0===t.deltaY&&t.deltaX?t.deltaX:t.deltaY;return E(b(v*Math.exp((n<0?1:-1)*e.step/3),e).scale,t,e,t)}}}return t.defaultOptions=q,t}); let imageRotation = 0; let info = undefined; diff --git a/plugs/index/api.ts b/plugs/index/api.ts index 1a26dab5..4bb155f0 100644 --- a/plugs/index/api.ts +++ b/plugs/index/api.ts @@ -38,7 +38,7 @@ export async function patchFrontmatter( // No frontmatter found, create from patches const patchedFrontmatter = applyPatches("", patches).trim(); if (patchedFrontmatter) { - return "---\n" + patchedFrontmatter + "\n---\n" + text; + return `---\n${patchedFrontmatter}\n---\n${text}`; } else { return text; } diff --git a/plugs/index/complete.ts b/plugs/index/complete.ts index ee243f54..1102c4ef 100644 --- a/plugs/index/complete.ts +++ b/plugs/index/complete.ts @@ -23,7 +23,7 @@ export async function attributeCompletion( label: attr, type: `attribute`, detail: `for #${tag} (type: ${humanReadableSchemaType(def)})${ - def.description ? ": " + def.description : "" + def.description ? `: ${def.description}` : "" }`, apply: `${attr}: `, }); @@ -50,7 +50,7 @@ function humanReadableSchemaType(type: any): string { * Task state completion */ export async function completeTaskState(completeEvent: CompleteEvent) { - const taskMatch = /([\-\*]\s+\[)([^\[\]]+)$/.exec( + const taskMatch = /([-*]\s+\[)([^[\]]+)$/.exec( completeEvent.linePrefix, ); if (!taskMatch) { diff --git a/plugs/index/header.ts b/plugs/index/header.ts index 06c8ddbf..7fc2bcb1 100644 --- a/plugs/index/header.ts +++ b/plugs/index/header.ts @@ -68,7 +68,7 @@ export function indexHeaders( } export async function headerComplete(completeEvent: CompleteEvent) { - const match = /(?:\[\[|\[.*?\]\()(?<path>[^\[]*)$/.exec( + const match = /(?:\[\[|\[.*?\]\()(?<path>[^[]*)$/.exec( completeEvent.linePrefix, ); if (!match || !match.groups?.path) { diff --git a/plugs/index/link.ts b/plugs/index/link.ts index 241ba05d..88f6fa1d 100644 --- a/plugs/index/link.ts +++ b/plugs/index/link.ts @@ -173,7 +173,7 @@ export async function indexLinks( wikiLinkRegex.lastIndex = 0; const match = wikiLinkRegex.exec(text); // Search in entire node text to get correct position, but check for full match against trimmed - if (match && match.groups && match[0] === trimmed) { + if (match?.groups && match[0] === trimmed) { const { leadingTrivia, stringRef, alias } = match.groups; const pos = textNode.from! + match.index! + leadingTrivia.length; const link: LinkObject = { diff --git a/plugs/index/lint.ts b/plugs/index/lint.ts index ae1baa1f..f8526420 100644 --- a/plugs/index/lint.ts +++ b/plugs/index/lint.ts @@ -81,7 +81,7 @@ function lintYamlBlock( ): LintDiagnostic | undefined { try { const parsed = YAML.load(yamlText) as any; - if (pageName && parsed.name && parsed.name != pageName) { + if (pageName && parsed.name && parsed.name !== pageName) { return { from: startPos, to: startPos + yamlText.length, diff --git a/plugs/index/refactor.ts b/plugs/index/refactor.ts index 4bc8411d..7ff65fd5 100644 --- a/plugs/index/refactor.ts +++ b/plugs/index/refactor.ts @@ -43,7 +43,7 @@ export async function renamePageCommand(cmdDef: any) { editor.flashNotification("Must provide a non-empty page title.", "error"); return false; } - const pageList: [string, string][] = [[oldName + ".md", newName + ".md"]]; + const pageList: [string, string][] = [[`${oldName}.md`, `${newName}.md`]]; await batchRenameFiles(pageList); return true; } @@ -80,7 +80,7 @@ export async function renamePageLinkCommand() { editor.flashNotification("Must provide a non-empty page title.", "error"); return false; } - const pageList: [string, string][] = [[oldName + ".md", newName + ".md"]]; + const pageList: [string, string][] = [[`${oldName}.md`, `${newName}.md`]]; await batchRenameFiles(pageList); } @@ -225,7 +225,7 @@ async function renamePage(oldName: string, newName: string) { newTail = newTail.replace(/^.*?(?=@\d*|#|\$|\))/, newLink); // Wrap in <> if link has spaces if (newLink.includes(" ")) { - newTail = "<" + newTail.replace(")", ">)"); + newTail = `<${newTail.replace(")", ">)")}`; } text = text.substring(0, link.pos) + newTail; } @@ -239,7 +239,7 @@ async function renamePage(oldName: string, newName: string) { const batchRenameDocuments: [string, string][] = []; for (const document of documentsToMove) { const newAttName = oldFolder.length === 0 - ? newFolder + "/" + document + ? `${newFolder}/${document}` : document.replace(oldFolder, newFolder).replace(/^\//, ""); batchRenameDocuments.push([document, newAttName]); } @@ -340,7 +340,7 @@ export async function renamePrefixCommand(cmdDef: any) { file, ) => file.startsWith(oldPrefix)); allAffectedFiles = allAffectedFiles.concat( - allPages.map((page) => page.name + ".md").filter((page) => + allPages.map((page) => `${page.name}.md`).filter((page) => page.startsWith(oldPrefix) ), ); @@ -452,7 +452,7 @@ async function updateBacklinks( // Is [Markdown link]() if (newTail.startsWith("/") || newTail.startsWith("</")) { // Is absolute mdlink, update with full path with leading / - newLink = "/" + newLink; + newLink = `/${newLink}`; } else { // Is relative mdlink newLink = absoluteToRelativePath(pageToEdit, newLink); @@ -461,7 +461,7 @@ async function updateBacklinks( // Wrap in <> if link has spaces if (newLink.includes(" ")) { - newTail = "<" + newTail.replace(")", ">)"); + newTail = `<${newTail.replace(")", ">)")}`; } } else { // Is wikilink, replace with full path diff --git a/plugs/index/snippet.ts b/plugs/index/snippet.ts index 98dc7b9a..54212e2d 100644 --- a/plugs/index/snippet.ts +++ b/plugs/index/snippet.ts @@ -51,7 +51,7 @@ export function extractSnippet( // Find tasks that don't have a page reference, and add one const taskMatch = line.match( - /^(\s*)([\*\-]\s+\[[^\]]+\]\s+)([^\[][^\[].+)$/, + /^(\s*)([*-]\s+\[[^\]]+\]\s+)([^[][^[].+)$/, ); if (taskMatch) { const pos = lineLengths.slice(0, i).reduce((acc, len) => diff --git a/plugs/index/tags.ts b/plugs/index/tags.ts index b5c8cfc0..ea146216 100644 --- a/plugs/index/tags.ts +++ b/plugs/index/tags.ts @@ -67,7 +67,7 @@ export async function tagComplete(completeEvent: CompleteEvent) { return null; } - const match = /#[^#\s\[\]]+\w*$/.exec(completeEvent.linePrefix); + const match = /#[^#\s[\]]+\w*$/.exec(completeEvent.linePrefix); if (!match) { return null; } @@ -104,7 +104,7 @@ export function updateITags<T>(obj: ObjectValue<T>, frontmatter: FrontMatter) { /** Get markup for a hashtag name with # prefix and angle brackets if necessary */ export function renderHashtag(name: string): string { // detect with the same regex as the parser - const simple: string = "#" + name; + const simple: string = `#${name}`; const match = simple.match(tagRegex); if (!match || match[0].length !== simple.length) { return `#<${name}>`; diff --git a/plugs/index/task.ts b/plugs/index/task.ts index e29ff700..f44f84a9 100644 --- a/plugs/index/task.ts +++ b/plugs/index/task.ts @@ -44,7 +44,7 @@ async function convertListItemToTask(node: ParseTree) { let taskMarker: string; if (originalMark.match(/^\d+\./)) { // Numbered list: preserve the number - taskMarker = originalMark + " [ ]"; + taskMarker = `${originalMark} [ ]`; } else { // Bullet list: use standard bullet taskMarker = "* [ ]"; diff --git a/plugs/plug-manager/plugmanager.ts b/plugs/plug-manager/plugmanager.ts index fa97fd04..f0fcc38a 100644 --- a/plugs/plug-manager/plugmanager.ts +++ b/plugs/plug-manager/plugmanager.ts @@ -91,7 +91,7 @@ export async function updatePlugsCommand() { typeof manifest.name !== "string" ) { // Try taking a good guess at a name if it isn't provided - const match = /\/([^\/]+)\.plug\.js$/.exec(plugUri); + const match = /\/([^/]+)\.plug\.js$/.exec(plugUri); if (!match) { console.error( `No plug name provided and could not extract name from ${plugUri} ignoring...`, @@ -140,7 +140,7 @@ export async function updatePlugsCommand() { await editor.flashNotification("All done!"); system.reloadPlugs(); } catch (e: any) { - editor.flashNotification("Error updating plugs: " + e.message, "error"); + editor.flashNotification(`Error updating plugs: ${e.message}`, "error"); } }