diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index afa511f4..b26e20c9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -33,7 +33,7 @@ jobs: - name: Setup Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.44 + deno-version: v1.45 - name: Run bundle build run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index abdd8ae4..06432852 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: - name: Setup Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.44 + deno-version: v1.45 - name: Run build run: deno task build - name: Bundle diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index 03e6ee69..83b9d09c 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -16,7 +16,7 @@ jobs: - name: Setup Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.44 + deno-version: v1.45 - name: Build bundles run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6022355a..b80cf18a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Deno uses: denoland/setup-deno@v1 with: - deno-version: v1.44 + deno-version: v1.45 - name: Run build run: deno task build @@ -28,5 +28,11 @@ jobs: - name: Run type check run: deno task check + - name: Run lint + run: deno task lint + + - name: Run benchmarks + run: deno task bench + - name: Run tests run: deno task test --trace-ops diff --git a/cmd/test/plug_run.test.ts b/cmd/test/plug_run.test.ts index 0c69d375..6c213846 100644 --- a/cmd/test/plug_run.test.ts +++ b/cmd/test/plug_run.test.ts @@ -1,7 +1,7 @@ import { AssetBundle } from "$lib/asset_bundle/bundle.ts"; import { compileManifest } from "../compile.ts"; import * as esbuild from "esbuild"; -import assets from "../../dist/plug_asset_bundle.json" assert { type: "json" }; +import assets from "../../dist/plug_asset_bundle.json" with { type: "json" }; import { assertEquals } from "$std/testing/asserts.ts"; import { dirname, join } from "$std/path/mod.ts"; import { MemoryKvPrimitives } from "$lib/data/memory_kv_primitives.ts"; diff --git a/common/markdown_parser/parser.test.ts b/common/markdown_parser/parser.test.ts index 213564d8..8dcac270 100644 --- a/common/markdown_parser/parser.test.ts +++ b/common/markdown_parser/parser.test.ts @@ -35,11 +35,11 @@ Deno.test("Test parser", () => { const links = collectNodesOfType(tree, "WikiLink"); assertEquals(links.length, 2); const nameNode = findNodeOfType(links[0], "WikiLinkPage"); - assertEquals(nameNode?.children![0].text, "wiki link"); + assertEquals(nameNode!.children![0].text, "wiki link"); // Check if alias is parsed properly const aliasNode = findNodeOfType(links[1], "WikiLinkAlias"); - assertEquals(aliasNode?.children![0].text, "alias"); + assertEquals(aliasNode!.children![0].text, "alias"); // Find frontmatter let node = findNodeOfType(tree, "FrontMatter"); @@ -65,19 +65,19 @@ Deno.test("Test inline attribute syntax", () => { // console.log("Attribute parsed", JSON.stringify(tree, null, 2)); const attributes = collectNodesOfType(tree, "Attribute"); let nameNode = findNodeOfType(attributes[0], "AttributeName"); - assertEquals(nameNode?.children![0].text, "age"); + assertEquals(nameNode!.children![0].text, "age"); let valueNode = findNodeOfType(attributes[0], "AttributeValue"); - assertEquals(valueNode?.children![0].text, "100"); + assertEquals(valueNode!.children![0].text, "100"); nameNode = findNodeOfType(attributes[1], "AttributeName"); - assertEquals(nameNode?.children![0].text, "age"); + assertEquals(nameNode!.children![0].text, "age"); valueNode = findNodeOfType(attributes[1], "AttributeValue"); - assertEquals(valueNode?.children![0].text, "200"); + assertEquals(valueNode!.children![0].text, "200"); nameNode = findNodeOfType(attributes[2], "AttributeName"); - assertEquals(nameNode?.children![0].text, "array"); + assertEquals(nameNode!.children![0].text, "array"); valueNode = findNodeOfType(attributes[2], "AttributeValue"); - assertEquals(valueNode?.children![0].text, "[1, 2, 3]"); + assertEquals(valueNode!.children![0].text, "[1, 2, 3]"); }); Deno.test("Test template directive parsing", () => { diff --git a/common/markdown_parser/table_parser.ts b/common/markdown_parser/table_parser.ts index e26db3fb..4d055bcd 100644 --- a/common/markdown_parser/table_parser.ts +++ b/common/markdown_parser/table_parser.ts @@ -21,7 +21,7 @@ function parseRow( offset = 0, ) { let count = 0, first = true, cellStart = -1, cellEnd = -1, esc = false; - let parseCell = () => { + const parseCell = () => { elts!.push( cx.elt( "TableCell", @@ -37,7 +37,7 @@ function parseRow( let inWikilink = false; for (let i = startI; i < line.length; i++) { - let next = line.charCodeAt(i); + const next = line.charCodeAt(i); if (next === 91 /* '[' */ && line.charAt(i + 1) === "[") { inWikilink = true; } else if ( @@ -68,7 +68,7 @@ function parseRow( function hasPipe(str: string, start: number) { for (let i = start; i < str.length; i++) { - let next = str.charCodeAt(i); + const next = str.charCodeAt(i); if (next == 124 /* '|' */) return true; if (next == 92 /* '\\' */) i++; } @@ -91,7 +91,7 @@ class TableParser implements LeafBlockParser { (line.next == 45 || line.next == 58 || line.next == 124 /* '-:|' */) && delimiterLine.test(lineText = line.text.slice(line.pos)) ) { - let firstRow: Element[] = [], + const firstRow: Element[] = [], firstCount = parseRow(cx, leaf.content, 0, firstRow, leaf.start); if (firstCount == parseRow(cx, lineText, line.pos)) { this.rows = [ @@ -110,7 +110,7 @@ class TableParser implements LeafBlockParser { } } } else if (this.rows) { // Line after the second - let content: Element[] = []; + const content: Element[] = []; parseRow(cx, line.text, line.pos, content, cx.lineStart); this.rows.push( cx.elt( @@ -167,7 +167,7 @@ export const Table: MarkdownConfig = { !hasPipe(line.text, line.basePos) ) return false; // @ts-ignore: internal - let next = cx.scanLine(cx.absoluteLineEnd + 1).text; + const next = cx.scanLine(cx.absoluteLineEnd + 1).text; return delimiterLine.test(next) && parseRow(cx, line.text, line.basePos) == parseRow(cx, next, line.basePos); diff --git a/common/space_script.test.ts b/common/space_script.test.ts index ec506701..8a2021bc 100644 --- a/common/space_script.test.ts +++ b/common/space_script.test.ts @@ -1,6 +1,6 @@ import { ScriptEnvironment } from "./space_script.ts"; -Deno.test("Space script", async () => { +Deno.test("Space script", () => { const env = new ScriptEnvironment(); env.evalScript( ` diff --git a/common/spaces/s3_space_primitives.test.ts b/common/spaces/s3_space_primitives.test.ts index 6e58f217..6f29280b 100644 --- a/common/spaces/s3_space_primitives.test.ts +++ b/common/spaces/s3_space_primitives.test.ts @@ -1,9 +1,11 @@ +// deno-lint-ignore-file no-unreachable import { S3SpacePrimitives } from "./s3_space_primitives.ts"; import { MemoryKvPrimitives } from "$lib/data/memory_kv_primitives.ts"; import { testSpacePrimitives } from "$common/spaces/space_primitives.test.ts"; Deno.test("s3_space_primitives", async () => { return; + const options = { accessKey: Deno.env.get("AWS_ACCESS_KEY_ID")!, secretKey: Deno.env.get("AWS_SECRET_ACCESS_KEY")!, diff --git a/deno.json b/deno.json index 0e7bfbba..7ca69968 100644 --- a/deno.json +++ b/deno.json @@ -6,6 +6,7 @@ "install": "deno install -g -f --unstable-kv --unstable-worker-options -A --import-map deno.json silverbullet.ts", "check": "find . -name '*.ts*' | xargs deno check", + "lint": "deno lint", "test": "deno test -A --unstable-kv --unstable-worker-options", "bench": "deno bench", @@ -30,7 +31,10 @@ "lint": { "exclude": [ "dist", - "dist_bundle" + "dist_client_bundle", + "dist_plug_bundle", + "cmd/test_space", + "cmd/test/test_space" ], "rules": { "exclude": ["no-explicit-any"] diff --git a/lib/async.test.ts b/lib/async.test.ts index 4bf78922..71c4a2bb 100644 --- a/lib/async.test.ts +++ b/lib/async.test.ts @@ -35,8 +35,8 @@ Deno.test("Batch test", async () => { return batch.map((e) => e * 2); }, 9); assertEquals(multiplied, elements.map((e) => e * 2)); - const multiplied2 = await batchRequests(elements, async (batch) => { - return batch.map((e) => e * 2); + const multiplied2 = await batchRequests(elements, (batch) => { + return Promise.resolve(batch.map((e) => e * 2)); }, 10000); assertEquals(multiplied2, elements.map((e) => e * 2)); }); diff --git a/lib/data/dynamodb_kv_primitives.ts b/lib/data/dynamodb_kv_primitives.ts deleted file mode 100644 index 732c3cb6..00000000 --- a/lib/data/dynamodb_kv_primitives.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { KV, KvKey } from "../../plug-api/types.ts"; -import { KvPrimitives, KvQueryOptions } from "./kv_primitives.ts"; -import { createClient, DynamoDBClient } from "../deps_server.ts"; - -export type AwsOptions = { - accessKey: string; - secretKey: string; - region: string; -}; - -const keySeparator = "\0"; - -const batchReadSize = 100; - -/** - * Start of an implementation, to be continued at some point - */ - -export class DynamoDBKvPrimitives implements KvPrimitives { - client: DynamoDBClient; - partitionKey: string; - tableName: string; - - constructor(tableName: string, partitionKey: string, options: AwsOptions) { - this.tableName = tableName; - this.partitionKey = partitionKey; - this.client = createClient({ - credentials: { - accessKeyId: options.accessKey, - secretAccessKey: options.secretKey, - }, - region: options.region, - }); - } - - batchGet(keys: KvKey[]): Promise { - const allResults: any[] = []; - const promises: Promise[] = []; - for (let i = 0; i < keys.length; i += batchReadSize) { - const batch = keys.slice(i, i + batchReadSize); - promises.push( - this.client.batchGetItem( - { - RequestItems: { - [this.tableName]: { - Keys: batch.map((key) => ({ - pk: this.partitionKey, - sk: key.join(keySeparator), - })), - }, - }, - }, - ), - ); - } - throw new Error("Method not implemented."); - } - batchSet(entries: KV[]): Promise { - throw new Error("Method not implemented."); - } - batchDelete(keys: KvKey[]): Promise { - throw new Error("Method not implemented."); - } - query(options: KvQueryOptions): AsyncIterableIterator { - throw new Error("Method not implemented."); - } - close(): void { - throw new Error("Method not implemented."); - } -} diff --git a/plug-api/lib/tree.test.ts b/plug-api/lib/tree.test.ts index 5ae2c426..3ed289f4 100644 --- a/plug-api/lib/tree.test.ts +++ b/plug-api/lib/tree.test.ts @@ -34,14 +34,6 @@ http://zef.plus - And a _third_ one [[Wiki Page]] yo `; -const mdTest2 = ` -Hello - -* Item 1 -* - -Sup`; - const mdTest3 = ` \`\`\`yaml name: something @@ -74,7 +66,7 @@ Deno.test("Test parsing", () => { } }); // console.log(JSON.stringify(mdTree, null, 2)); - let mdTree3 = parse(extendedMarkdownLanguage, mdTest3); + parse(extendedMarkdownLanguage, mdTest3); // console.log(JSON.stringify(mdTree3, null, 2)); }); diff --git a/plugs/editor/navigate.ts b/plugs/editor/navigate.ts index 9db73b8b..4e8e342f 100644 --- a/plugs/editor/navigate.ts +++ b/plugs/editor/navigate.ts @@ -99,7 +99,7 @@ async function actionClickOrActionEnter( case "CommandLink": { const commandName = mdTree.children![1]!.children![0].text!; const argsNode = findNodeOfType(mdTree, "CommandLinkArgs"); - const argsText = argsNode?.children![0]?.text; + const argsText = argsNode?.children?.[0]?.text; // Assume the arguments are can be parsed as the innards of a valid JSON list try { const args = argsText ? JSON.parse(`[${argsText}]`) : []; diff --git a/plugs/emoji/build.ts b/plugs/emoji/build.ts index 272b207c..77911e22 100644 --- a/plugs/emoji/build.ts +++ b/plugs/emoji/build.ts @@ -1,12 +1,12 @@ // Generates emoji.json from emoji-data.txt const emojiRe = /#\s([^\s]+)\s+E[^\s]+\s+(.+)$/; -let text = Deno.readTextFileSync("emoji-data.txt"); +const text = Deno.readTextFileSync("emoji-data.txt"); const lines = text.split("\n").filter((line) => !line.startsWith("#")); const emojis: string[] = []; for (const line of lines) { - let match = emojiRe.exec(line); + const match = emojiRe.exec(line); if (match) { const emoji = match[1]; const name = match[2].toLowerCase().replaceAll(/\W+/g, "_"); diff --git a/plugs/federation/federation.ts b/plugs/federation/federation.ts index 7c157e07..0e5d606d 100644 --- a/plugs/federation/federation.ts +++ b/plugs/federation/federation.ts @@ -149,37 +149,17 @@ function errorResult( }; } -export async function writeFile( - name: string, - data: Uint8Array, +export function writeFile( + _name: string, + _data: Uint8Array, ): Promise { throw new Error("Writing federation file, not yet supported"); - // const url = resolveFederated(name); - // console.log("Writing federation file", url); - - // const r = await nativeFetch(url, { - // method: "PUT", - // body: data, - // }); - // const fileMeta = await responseToFileMeta(r, name); - // if (!r.ok) { - // throw new Error("Could not write file"); - // } - - // return fileMeta; } -export async function deleteFile( - name: string, +export function deleteFile( + _name: string, ): Promise { throw new Error("Writing federation file, not yet supported"); - - // console.log("Deleting federation file", name); - // const url = resolveFederated(name); - // const r = await nativeFetch(url, { method: "DELETE" }); - // if (!r.ok) { - // throw Error("Failed to delete file"); - // } } export async function getFileMeta(name: string): Promise { diff --git a/plugs/index/refactor.ts b/plugs/index/refactor.ts index f5d6bbb1..08bf2926 100644 --- a/plugs/index/refactor.ts +++ b/plugs/index/refactor.ts @@ -1,4 +1,4 @@ -import { editor, space, system } from "$sb/syscalls.ts"; +import { editor, space } from "$sb/syscalls.ts"; import { validatePageName } from "$sb/lib/page_ref.ts"; import { getBackLinks, LinkObject } from "./page_links.ts"; import { queryObjects } from "./api.ts"; diff --git a/plugs/markdown/html_render.ts b/plugs/markdown/html_render.ts index b95d64ca..11e7aa28 100644 --- a/plugs/markdown/html_render.ts +++ b/plugs/markdown/html_render.ts @@ -20,7 +20,7 @@ function htmlEscape(s: string): string { let oldS = s; do { oldS = s; - s = s.replace(/ /g, "  "); + s = s.replace(/ {2}/g, "  "); } while (s !== oldS); return s; } diff --git a/plugs/markdown/markdown_render.test.ts b/plugs/markdown/markdown_render.test.ts index db0f8f1a..9d04ebaf 100644 --- a/plugs/markdown/markdown_render.test.ts +++ b/plugs/markdown/markdown_render.test.ts @@ -4,6 +4,7 @@ import { System } from "../../lib/plugos/system.ts"; import { createSandbox } from "../../lib/plugos/sandboxes/deno_worker_sandbox.ts"; import { renderMarkdownToHtml } from "./markdown_render.ts"; import { extendedMarkdownLanguage } from "$common/markdown_parser/parser.ts"; +import { assertEquals } from "$std/testing/asserts.ts"; Deno.test("Markdown render", async () => { const system = new System("server"); @@ -38,10 +39,10 @@ Deno.test("Smart hard break test", () => { failOnUnknown: true, smartHardBreak: true, }); - // assertEquals( - // html, - // `Hello
world!
`, - // ); + assertEquals( + html, + `Hello
world!
`, + ); const example2 = `This is going to be a text. With a new line. diff --git a/server/http_server.ts b/server/http_server.ts index 69afb758..67d76d32 100644 --- a/server/http_server.ts +++ b/server/http_server.ts @@ -343,14 +343,14 @@ export class HttpServer { ]; // TODO: This should probably be a POST request - this.app.get("/.logout", async (c) => { + this.app.get("/.logout", (c) => { const url = new URL(c.req.url); deleteCookie(c, authCookieName(url.host)); return c.redirect("/.auth"); }); - this.app.get("/.auth", async (c) => { + this.app.get("/.auth", (c) => { const html = this.clientAssetBundle.readTextFileSync(".client/auth.html"); return c.html(html); @@ -402,7 +402,7 @@ export class HttpServer { return c.redirect("/.auth?error=1"); } }, - ).all(async (c) => { + ).all((c) => { return c.redirect("/.auth"); }); diff --git a/web/boot.ts b/web/boot.ts index f4f0cbfe..0489bb88 100644 --- a/web/boot.ts +++ b/web/boot.ts @@ -1,7 +1,7 @@ import { safeRun } from "../lib/async.ts"; import { Client } from "./client.ts"; -const syncMode = window.silverBulletConfig.syncOnly || +const syncMode = globalThis.silverBulletConfig.syncOnly || !!localStorage.getItem("syncMode"); safeRun(async () => { @@ -19,7 +19,8 @@ safeRun(async () => { syncMode, window.silverBulletConfig.readOnly, ); - window.client = client; + // @ts-ignore: on purpose + globalThis.client = client; await client.init(); }); @@ -35,7 +36,7 @@ if (navigator.serviceWorker) { navigator.serviceWorker.ready.then((registration) => { registration.active!.postMessage({ type: "config", - config: window.silverBulletConfig, + config: globalThis.silverBulletConfig, }); }); } diff --git a/web/client.ts b/web/client.ts index 3b4b0b3f..ce43178a 100644 --- a/web/client.ts +++ b/web/client.ts @@ -70,16 +70,15 @@ const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/; const autoSaveInterval = 1000; declare global { - interface Window { - // Injected via index.html - silverBulletConfig: { - spaceFolderPath: string; - syncOnly: boolean; - readOnly: boolean; - enableSpaceScript: boolean; - }; - client: Client; - } + // deno-lint-ignore no-var + var silverBulletConfig: { + spaceFolderPath: string; + syncOnly: boolean; + readOnly: boolean; + enableSpaceScript: boolean; + }; + // deno-lint-ignore no-var + var client: Client; } type WidgetCacheItem = { diff --git a/web/cm_plugins/admonition.ts b/web/cm_plugins/admonition.ts index 6242b0e0..0d81e065 100644 --- a/web/cm_plugins/admonition.ts +++ b/web/cm_plugins/admonition.ts @@ -2,7 +2,6 @@ import { EditorState } from "@codemirror/state"; import { syntaxTree } from "@codemirror/language"; import { Decoration } from "@codemirror/view"; import { SyntaxNodeRef } from "@lezer/common"; -import { Client } from "../client.ts"; import { decoratorStateField, isCursorInRange } from "./util.ts"; const ADMONITION_REGEX = @@ -57,7 +56,7 @@ function extractAdmonitionFields(rawText: string): AdmonitionFields | null { return null; } -export function admonitionPlugin(editor: Client) { +export function admonitionPlugin() { return decoratorStateField((state: EditorState) => { const widgets: any[] = []; @@ -77,8 +76,7 @@ export function admonitionPlugin(editor: Client) { return; } - const { preSpaces, admonitionType, postSyntax, postSpaces } = - extractedFields; + const { preSpaces, admonitionType, postSyntax } = extractedFields; // A blockquote is actually rendered as many divs, one per line. // We need to keep track of the `from` offsets here, so we can attach css diff --git a/web/cm_plugins/clean.ts b/web/cm_plugins/clean.ts index e8eb5e16..e1e458ae 100644 --- a/web/cm_plugins/clean.ts +++ b/web/cm_plugins/clean.ts @@ -19,7 +19,7 @@ export function cleanModePlugins(client: Client) { return [ linkPlugin(client), blockquotePlugin(), - admonitionPlugin(client), + admonitionPlugin(), hideMarksPlugin(), hideHeaderMarkPlugin(), cleanBlockPlugin(), diff --git a/web/components/basic_modals.tsx b/web/components/basic_modals.tsx index 79db6ca2..211c7857 100644 --- a/web/components/basic_modals.tsx +++ b/web/components/basic_modals.tsx @@ -162,7 +162,6 @@ export function AlwaysShownModal({ return ( { e.preventDefault(); onCancel?.(); diff --git a/web/components/filter.tsx b/web/components/filter.tsx index be18fbd4..5a9261e8 100644 --- a/web/components/filter.tsx +++ b/web/components/filter.tsx @@ -164,7 +164,10 @@ export function FilterList({ // Operate on the highlighted option, ignoring prompt const option = matchingOptions[selectedOption].name; // Get the folder it's nested in, keeping the trailing / - const folderPath = option.slice(0, option.lastIndexOf("/") + 1); + const folderPath = option.slice( + 0, + option.lastIndexOf("/") + 1, + ); // If the option wasn't in a folder, make it a folder setText(folderPath !== "" ? folderPath : option + "/"); return true; @@ -194,7 +197,7 @@ export function FilterList({ className={selectedOption === idx ? "sb-selected-option" : "sb-option"} - onMouseMove={(e) => { + onMouseMove={() => { if (selectedOption !== idx) { setSelectionOption(idx); } diff --git a/web/components/mini_editor.tsx b/web/components/mini_editor.tsx index 7a5e2b45..6ae8996f 100644 --- a/web/components/mini_editor.tsx +++ b/web/components/mini_editor.tsx @@ -190,7 +190,7 @@ export function MiniEditor( ...standardKeymap, ...historyKeymap, ...completionKeymap, - ...createCommandKeyBindings(window.client), + ...createCommandKeyBindings(globalThis.client), ]), EditorView.domEventHandlers({ click: (e) => { @@ -253,7 +253,7 @@ export function MiniEditor( onBlurred = true; if (callbacksRef.current!.onBlur) { Promise.resolve(callbacksRef.current!.onBlur(view.state.sliceDoc())) - .catch((e) => { + .catch(() => { // Reset the state view.setState(buildEditorState()); }); diff --git a/web/components/top_bar.tsx b/web/components/top_bar.tsx index e827c03b..233029f4 100644 --- a/web/components/top_bar.tsx +++ b/web/components/top_bar.tsx @@ -19,7 +19,6 @@ export function TopBar({ unsavedChanges, syncFailures, isLoading, - isMobile, notifications, onRename, actionButtons, @@ -36,7 +35,6 @@ export function TopBar({ unsavedChanges: boolean; syncFailures: number; isLoading: boolean; - isMobile: boolean; notifications: Notification[]; darkMode: boolean; vimMode: boolean; @@ -59,7 +57,7 @@ export function TopBar({
-
{pageNamePrefix}
+
{pageNamePrefix}
{ if (!existingWindow) { - const win = window.open(url, "_blank"); + const win = globalThis.open(url, "_blank"); if (win) { win.focus(); }