diff --git a/biome.json b/biome.json index 07cacdae..9e2bdac2 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.6.1/schema.json", + "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", "organizeImports": { "enabled": true }, @@ -17,11 +17,13 @@ "useImportRestrictions": "off", "useSortedClasses": "off", "noConsole": "off", - "useJsxKeyInIterable": "off" + "useDefaultSwitchClause": "off" }, "style": { "noImplicitBoolean": "off", - "noDefaultExport": "off" + "noDefaultExport": "off", + "useNamingConvention": "off", + "useFilenamingConvention": "off" }, "complexity": { "useLiteralKeys": "off" diff --git a/deno.json b/deno.json index 83863af6..ab98ac66 100644 --- a/deno.json +++ b/deno.json @@ -10,64 +10,65 @@ "test:coverage:genhtml": "genhtml -o cov/html cov/cov.lcov", "manifest": "deno task cli manifest $(pwd)", "start": "deno run --env -A --watch src/dev.ts", - "build": "deno task compile:mdx && deno run -A src/dev.ts build", + "build": "deno task compile:mdx && deno run --env -A src/dev.ts build", "preview": "deno run --env -A src/main.ts", "update": "deno run -A -r https://fresh.deno.dev/update .", "esm": "deno run -A https://esm.sh/v135", "esm:add": "deno task esm add", "esm:update": "deno task esm update", "esm:remove": "deno task esm remove", - "biome": "deno run -A npm:@biomejs/biome@1.6.1", + "biome": "deno run -A npm:@biomejs/biome@1.7.3", "biome:ci": "deno task biome ci . --error-on-warnings", "biome:check": "deno task biome check . --error-on-warnings", "compile:mdx": "deno run -A tool/compile-mdx.ts" }, "imports": { - "$std/": "https://deno.land/std@0.216.0/", - "$fresh/": "https://deno.land/x/fresh@1.6.5/", - "$gfm": "https://deno.land/x/gfm@0.6.0/mod.ts", - "openai": "https://deno.land/x/openai@v4.29.2/mod.ts", - "openai/": "https://deno.land/x/openai@v4.29.2/", + "$fresh/": "https://deno.land/x/fresh@1.6.8/", "$tabler_icons/": "https://deno.land/x/tabler_icons_tsx@0.0.6/tsx/", - "zod": "https://deno.land/x/zod@v3.22.4/mod.ts", + "@deno/gfm": "https://deno.land/x/gfm@0.6.0/mod.ts", "@headlessui/react": "https://esm.sh/v135/*@headlessui/react@1.7.17", - "@preact/signals-core": "https://esm.sh/v135/@preact/signals-core@1.5.1", - "@preact/signals": "https://esm.sh/v135/*@preact/signals@1.2.2", - "preact": "https://esm.sh/v135/preact@10.19.6", - "preact/": "https://esm.sh/v135/preact@10.19.6/", "@headlessui/tailwindcss": "npm:@headlessui/tailwindcss@0.2.0", "@mdx-js/mdx": "npm:@mdx-js/mdx@3.0.1", + "@preact/signals": "https://esm.sh/v135/*@preact/signals@1.2.3", + "@preact/signals-core": "https://esm.sh/v135/*@preact/signals-core@1.6.0", + "@std/assert": "jsr:@std/assert@^0.224.0", + "@std/path": "jsr:@std/path@^0.224.0", "@tailwindcss/forms": "npm:@tailwindcss/forms@0.5.7", - "@tailwindcss/typography": "npm:@tailwindcss/typography@0.5.10", + "@tailwindcss/typography": "npm:@tailwindcss/typography@0.5.13", "idb-keyval": "npm:idb-keyval@6.2.1", + "openai": "https://deno.land/x/openai@v4.43.0/mod.ts", + "openai/": "https://deno.land/x/openai@v4.43.0/", + "preact": "https://esm.sh/v135/preact@10.21.0", + "preact/": "https://esm.sh/v135/preact@10.21.0/", "rehype-mathjax": "npm:rehype-mathjax@6.0.0", "remark-frontmatter": "npm:remark-frontmatter@5.0.0", - "remark-lint-checkbox-content-indent": "npm:remark-lint-checkbox-content-indent@4.1.1", - "remark-lint-no-tabs": "npm:remark-lint-no-tabs@3.1.1", - "remark-lint-definition-spacing": "npm:remark-lint-definition-spacing@3.1.1", - "remark-lint-heading-increment": "npm:remark-lint-heading-increment@3.1.1", - "remark-lint-linebreak-style": "npm:remark-lint-linebreak-style@3.1.1", - "remark-lint-no-missing-blank-lines": "npm:remark-lint-no-missing-blank-lines@3.1.1", - "remark-lint-no-consecutive-blank-lines": "npm:remark-lint-no-consecutive-blank-lines@4.1.2", + "remark-lint-checkbox-content-indent": "npm:remark-lint-checkbox-content-indent@5.0.0", + "remark-lint-definition-spacing": "npm:remark-lint-definition-spacing@4.0.0", + "remark-lint-heading-increment": "npm:remark-lint-heading-increment@4.0.0", + "remark-lint-linebreak-style": "npm:remark-lint-linebreak-style@4.0.0", + "remark-lint-no-consecutive-blank-lines": "npm:remark-lint-no-consecutive-blank-lines@5.0.0", + "remark-lint-no-missing-blank-lines": "npm:remark-lint-no-missing-blank-lines@4.0.0", + "remark-lint-no-tabs": "npm:remark-lint-no-tabs@4.0.0", "remark-math": "npm:remark-math@6.0.0", "remark-mdx-frontmatter": "npm:remark-mdx-frontmatter@4.0.0", - "remark-preset-lint-consistent": "npm:remark-preset-lint-consistent@5.1.2", - "remark-preset-lint-recommended": "npm:remark-preset-lint-recommended@6.1.3", - "tailwindcss": "npm:tailwindcss@3.4.1", - "tailwindcss/plugin": "npm:tailwindcss@3.4.1/plugin.js", + "remark-preset-lint-consistent": "npm:remark-preset-lint-consistent@6.0.0", + "remark-preset-lint-recommended": "npm:remark-preset-lint-recommended@7.0.0", + "tailwindcss": "npm:tailwindcss@3.4.3", + "tailwindcss/plugin": "npm:tailwindcss@3.4.3/plugin.js", "unified": "npm:unified@11.0.4", + "vfile": "npm:vfile@6.0.1", "vfile-matter": "npm:vfile-matter@5.0.0", - "vfile-reporter": "npm:vfile-reporter@8.1.0", - "vfile": "npm:vfile@6.0.1" + "vfile-reporter": "npm:vfile-reporter@8.1.1", + "zod": "https://deno.land/x/zod@v3.23.7/mod.ts" }, "scopes": { "https://esm.sh/v135/": { - "@preact/signals-react": "https://esm.sh/v135/*@preact/signals@1.2.2", + "@preact/signals-react": "https://esm.sh/v135/*@preact/signals@1.2.3", "client-only": "https://esm.sh/v135/client-only@0.0.1", - "react-dom": "https://esm.sh/v135/preact@10.19.6/compat", - "react": "https://esm.sh/v135/preact@10.19.6/compat", - "react/jsx-runtime": "https://esm.sh/v135/preact@10.19.6/jsx-runtime", - "react-dom/test-utils": "https://esm.sh/v135/preact@10.19.6/test-utils" + "react-dom": "https://esm.sh/v135/preact@10.21.0/compat", + "react": "https://esm.sh/v135/preact@10.21.0/compat", + "react/jsx-runtime": "https://esm.sh/v135/preact@10.21.0/jsx-runtime", + "react-dom/test-utils": "https://esm.sh/v135/preact@10.21.0/test-utils" } }, "compilerOptions": { diff --git a/src/content/geothermal/what.mdx b/src/content/geothermal/what.mdx index 55f26936..e1e0d147 100644 --- a/src/content/geothermal/what.mdx +++ b/src/content/geothermal/what.mdx @@ -19,7 +19,7 @@ The switch to geothermal is still beginning, but it will be a worthy investment There are four types of geothermal energy solutions. -1. Horizontal loop: Horizontal loop systems are best for properties with more land, as they are bigger and require deeper trenches. -2. Vertical Loop: Vertical loop systems are very narrow and great for residential areas! -3. Closed Loop: Closed loop systems mix water and antifreeze to accommodate cold climates, making them optimal for northern locations. -4. Open Loop: Open loop systems circulate water from an outside source, meaning that they are optimal for properties with a steady water source. +1. Horizontal loop: Horizontal loop systems are best for properties with more land, as they are bigger and require deeper trenches. +2. Vertical Loop: Vertical loop systems are very narrow and great for residential areas! +3. Closed Loop: Closed loop systems mix water and antifreeze to accommodate cold climates, making them optimal for northern locations. +4. Open Loop: Open loop systems circulate water from an outside source, meaning that they are optimal for properties with a steady water source. diff --git a/src/dev.ts b/src/dev.ts index ae73946d..1fe3e340 100755 --- a/src/dev.ts +++ b/src/dev.ts @@ -3,6 +3,4 @@ import dev from "$fresh/dev.ts"; import config from "./fresh.config.ts"; -import "$std/dotenv/load.ts"; - await dev(import.meta.url, "./main.ts", config); diff --git a/src/islands/Chatbot.tsx b/src/islands/Chatbot.tsx index 54e2f464..90887a6c 100644 --- a/src/islands/Chatbot.tsx +++ b/src/islands/Chatbot.tsx @@ -1,4 +1,4 @@ -import { render } from "$gfm"; +import { render } from "@deno/gfm"; import { Transition } from "@headlessui/react"; import { useSignal, useSignalEffect } from "@preact/signals"; import { set } from "idb-keyval"; @@ -74,13 +74,11 @@ function ChatbotBox(props: JSX.HTMLAttributes): JSX.Element { const isAsking = useSignal(false); const thread = useIndexedDB( "thread", - [], async () => (await getThread())?.id, ); const messages_ = useIndexedDB( "messages", - [], // deno-lint-ignore require-await async () => [], ); @@ -105,10 +103,10 @@ function ChatbotBox(props: JSX.HTMLAttributes): JSX.Element { )} {messages.value.map((msg) => (
))}
diff --git a/src/main.ts b/src/main.ts index 38dcdc24..c1e3c4db 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,8 +4,6 @@ /// /// -import "$std/dotenv/load.ts"; - import { start } from "$fresh/server.ts"; import config from "./fresh.config.ts"; import manifest from "./fresh.gen.ts"; diff --git a/src/routes/_app.tsx b/src/routes/_app.tsx index db79db2c..f2a79fba 100644 --- a/src/routes/_app.tsx +++ b/src/routes/_app.tsx @@ -1,4 +1,4 @@ -import { Head, asset } from "$fresh/runtime.ts"; +import { Head, Partial, asset } from "$fresh/runtime.ts"; import type { PageProps } from "$fresh/server.ts"; import type { JSX } from "preact"; import { @@ -71,7 +71,9 @@ export default function App({ Component }: PageProps): JSX.Element { - + + + ); diff --git a/src/routes/_layout.tsx b/src/routes/_layout.tsx index 05a8f29a..af66e98b 100644 --- a/src/routes/_layout.tsx +++ b/src/routes/_layout.tsx @@ -1,4 +1,3 @@ -import { Partial } from "$fresh/runtime.ts"; import type { PageProps } from "$fresh/server.ts"; import type { JSX } from "preact"; import { Footer } from "../components/Footer.tsx"; @@ -16,10 +15,8 @@ import { Chatbot } from "../islands/Chatbot.tsx"; export default function Layout({ Component, url }: PageProps): JSX.Element { return (
- -
- - +
+
diff --git a/src/routes/about.test.ts b/src/routes/about.test.ts index 2e650e4e..c53fe684 100644 --- a/src/routes/about.test.ts +++ b/src/routes/about.test.ts @@ -1,5 +1,5 @@ import { type ServeHandlerInfo, createHandler } from "$fresh/server.ts"; -import { assertStringIncludes } from "$std/assert/mod.ts"; +import { assertStringIncludes } from "@std/assert"; import config from "../fresh.config.ts"; import manifest from "../fresh.gen.ts"; diff --git a/src/routes/api/chat/index.ts b/src/routes/api/chat/index.ts index dda2992f..ac6b1ef9 100644 --- a/src/routes/api/chat/index.ts +++ b/src/routes/api/chat/index.ts @@ -1,6 +1,9 @@ import type { Handlers } from "$fresh/server.ts"; import { ask } from "../../../utils/openai/assistant.ts"; -import type { TextContentBlock } from "../../../utils/openai/schemas.ts"; +import type { + Message, + TextContentBlock, +} from "../../../utils/openai/schemas.ts"; export const handler: Handlers = { async GET(req, ctx): Promise { @@ -12,7 +15,7 @@ export const handler: Handlers = { } const response = ask(message, thread_id); - const responses = []; + const responses: Message[] = []; for await (const res of response) { responses.push(res); } diff --git a/src/routes/calculator.tsx b/src/routes/calculator.tsx index bf88efab..1c429207 100644 --- a/src/routes/calculator.tsx +++ b/src/routes/calculator.tsx @@ -73,7 +73,7 @@ export const handler: Handlers = { }); } - if (!geoType.success || !squareFootage) { + if (!(geoType.success && squareFootage)) { return ctx.renderNotFound(); } diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 2f66210c..478aa872 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -31,7 +31,7 @@ function Card({ }: RenderableProps): JSX.Element { return (

{ + csp.directives.scriptSrc ??= []; csp.directives.scriptSrcElem ??= []; csp.directives.styleSrc ??= []; csp.directives.styleSrcElem ??= []; csp.directives.imgSrc ??= []; csp.directives.connectSrc ??= []; csp.directives.manifestSrc ??= []; + csp.directives.baseUri ??= []; + csp.directives.scriptSrc.push(STRICT_DYNAMIC, UNSAFE_INLINE); csp.directives.scriptSrcElem.push(SELF, UNSAFE_INLINE); csp.directives.styleSrc.push(SELF); - csp.directives.styleSrcElem.push(SELF, UNSAFE_INLINE); - csp.directives.imgSrc.push(SELF); + csp.directives.styleSrcElem.push(SELF, STRICT_DYNAMIC); + csp.directives.imgSrc.push(SELF, "data:"); csp.directives.connectSrc.push(SELF); csp.directives.manifestSrc.push(SELF); + csp.directives.baseUri.push(NONE); }); } diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index 2a10ace6..4d78852f 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -14,7 +14,7 @@ export function usePromise(promise: Promise): T | undefined { error.value = e; status.value = "rejected"; } - }, [promise]); + }, [promise, status, result, error]); // Preact Signals dislike promises. const [dataPromise] = useState(fetchData); diff --git a/src/utils/indexeddb.ts b/src/utils/indexeddb.ts index d32d0c11..e0d00c91 100644 --- a/src/utils/indexeddb.ts +++ b/src/utils/indexeddb.ts @@ -1,6 +1,6 @@ import { IS_BROWSER } from "$fresh/runtime.ts"; import { get, set } from "idb-keyval"; -import { type Inputs, useCallback } from "preact/hooks"; +import { useCallback } from "preact/hooks"; import { usePromise } from "./hooks.ts"; /** @@ -16,7 +16,6 @@ import { usePromise } from "./hooks.ts"; */ export function useIndexedDB( key: string, - inputs: Inputs, def?: () => Promise, ): T | undefined { if (!IS_BROWSER) { @@ -35,7 +34,7 @@ export function useIndexedDB( } return val; - }, [key, def, inputs]); + }, [key, def]); return usePromise(callback()); } diff --git a/src/utils/openai/assistant.ts b/src/utils/openai/assistant.ts index 4bdb895a..72be6cb5 100644 --- a/src/utils/openai/assistant.ts +++ b/src/utils/openai/assistant.ts @@ -19,25 +19,10 @@ export async function* ask( role: "user", content: q, }); - let run = await client.beta.threads.runs.create(thread_id, { + await client.beta.threads.runs.createAndPoll(thread_id, { assistant_id, }); - // TODO(lishaduck): Poll on the client - while ( - run.status === "in_progress" || - run.status === "queued" || - run.status === "requires_action" - ) { - // Make sure we don't poll too frequently. - // deno-lint-ignore no-await-in-loop - await new Promise((resolve) => setTimeout(resolve, 1000)); - - // Polling is required, as streaming is not yet supported. - // deno-lint-ignore no-await-in-loop - run = await client.beta.threads.runs.retrieve(thread_id, run.id); - } - for await (const message of client.beta.threads.messages.list(thread_id)) { yield message; } diff --git a/src/utils/openai/references.ts b/src/utils/openai/references.ts index d5fe9206..50a5e784 100644 --- a/src/utils/openai/references.ts +++ b/src/utils/openai/references.ts @@ -1,9 +1,5 @@ -import type { - Annotation, - Message, - TextContentBlock, -} from "openai/resources/beta/threads/messages/messages.ts"; import { getFileData } from "../../sdk/chat/references.ts"; +import type { Annotation, Message, TextContentBlock } from "./schemas.ts"; type AnnotationReplacement = { regex: RegExp; citation: string }; diff --git a/src/utils/openai/schemas.ts b/src/utils/openai/schemas.ts index 778931da..db0fff82 100644 --- a/src/utils/openai/schemas.ts +++ b/src/utils/openai/schemas.ts @@ -1,4 +1,4 @@ -import type { Message } from "openai/resources/beta/threads/messages/messages.ts"; +import type { Message } from "openai/resources/beta/threads/messages.ts"; import type { Thread as OThread } from "openai/resources/beta/threads/threads.ts"; import type { FileObject as OFileObject } from "openai/resources/mod.ts"; import { z } from "zod"; @@ -35,5 +35,8 @@ export const chatThreadSchema = z.custom( (val) => z.object({}).array().safeParse(val).success, ); -export type { Message } from "openai/resources/beta/threads/messages/messages.ts"; -export type { TextContentBlock } from "openai/resources/beta/threads/messages/messages.ts"; +export type { + Message, + Annotation, + TextContentBlock, +} from "openai/resources/beta/threads/messages.ts"; diff --git a/src/utils/solutions.test.ts b/src/utils/solutions.test.ts index 357e688e..700738b1 100644 --- a/src/utils/solutions.test.ts +++ b/src/utils/solutions.test.ts @@ -1,50 +1,47 @@ -import { assertEquals, assertThrows } from "$std/assert/mod.ts"; +import { assertEquals, assertThrows } from "@std/assert"; import { ZodError } from "zod"; import { solutionPagesSchema } from "./solutions.ts"; /** * Test the solution pages schema. */ -Deno.test( - "Solution pages schema.", - async (t: Deno.TestContext): Promise => { - await t.step("Valid data", (): void => { - const value = [ - { - slug: "slug", - data: { - title: "", - description: "", - category: "", - sectionHeader: "", - }, +Deno.test("Solution pages schema.", async (t: Deno.TestContext): Promise => { + await t.step("Valid data", (): void => { + const value = [ + { + slug: "slug", + data: { + title: "", + description: "", + category: "", + sectionHeader: "", }, - ]; + }, + ]; - const actual = solutionPagesSchema.parse(value); - assertEquals(actual, value); - }); + const actual = solutionPagesSchema.parse(value); + assertEquals(actual, value); + }); - await t.step("Invalid data", async (t): Promise => { - await t.step("Empty", (): void => { - const actual = (): void => { - solutionPagesSchema.parse([{}]); - }; - assertThrows(actual, Error, "Required"); - }); - await t.step("Missing category", (): void => { - const actual = (): void => { - solutionPagesSchema.parse([ - { - data: { - title: "title", - description: "description", - }, + await t.step("Invalid data", async (t): Promise => { + await t.step("Empty", (): void => { + const actual = (): void => { + solutionPagesSchema.parse([{}]); + }; + assertThrows(actual, Error, "Required"); + }); + await t.step("Missing category", (): void => { + const actual = (): void => { + solutionPagesSchema.parse([ + { + data: { + title: "title", + description: "description", }, - ]); - }; - assertThrows(actual, ZodError, "Required"); - }); + }, + ]); + }; + assertThrows(actual, ZodError, "Required"); }); - }, -); + }); +}); diff --git a/src/utils/tailwind.test.ts b/src/utils/tailwind.test.ts index fb05702c..ab9e20ae 100644 --- a/src/utils/tailwind.test.ts +++ b/src/utils/tailwind.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "$std/assert/mod.ts"; +import { assertEquals } from "@std/assert"; import { tw } from "./tailwind.ts"; /** diff --git a/tool/compile-mdx.ts b/tool/compile-mdx.ts index a0bf769c..818bc2d8 100644 --- a/tool/compile-mdx.ts +++ b/tool/compile-mdx.ts @@ -1,11 +1,5 @@ -import { - dirname, - fromFileUrl, - join, - relative, - resolve, -} from "$std/path/mod.ts"; import { type CompileOptions, compile } from "@mdx-js/mdx"; +import { dirname, fromFileUrl, join, resolve } from "@std/path"; import rehypeMathjax from "rehype-mathjax"; import remarkFrontmatter from "remark-frontmatter"; import remarkLintCheckboxContentIndent from "remark-lint-checkbox-content-indent"; @@ -43,9 +37,9 @@ declare module "vfile" { Deno.chdir(dirname(fromFileUrl(Deno.mainModule))); // Directories for resolve. -const srcDir = "../src" as const; -const contentDir = `${srcDir}/content` as const; -const utilsDir = `${srcDir}/utils` as const; +const srcDir = resolve("..", "src"); +const contentDir = join(srcDir, "content"); +const utilsDir = join(srcDir, "utils"); /** * Compile the MDX files into JS. @@ -54,11 +48,10 @@ async function run(): Promise { const initialFiles = getSolutions(contentDir); const compiledFiles = compileSolutions(initialFiles); - const promises = []; + const files: VFile[] = []; for await (const file of compiledFiles) { - promises.push(file); + files.push(file); } - const files = await Promise.all(promises); files.sort(sortFiles); lint(files); @@ -83,15 +76,14 @@ async function run(): Promise { */ async function* getSolutions( basePath: string, - currentPath: string = basePath, + currentPath = "", ): AsyncGenerator { - for await (const entry of Deno.readDir(currentPath)) { - const fullPath = join(currentPath, entry.name); - const relPath = relative(basePath, currentPath); - if (entry.isFile && entry.name.match(/mdx?/) !== null) { - yield getSolution(fullPath, entry.name, relPath); + for await (const entry of Deno.readDir(resolve(basePath, currentPath))) { + const fullPath = resolve(basePath, currentPath, entry.name); + if (entry.isFile && entry.name.match(/\.mdx?$/) !== null) { + yield getSolution(fullPath, currentPath, entry.name); } else if (entry.isDirectory) { - yield* getSolutions(basePath, fullPath); + yield* getSolutions(basePath, join(currentPath, entry.name)); } } } @@ -100,14 +92,14 @@ async function* getSolutions( * Get the contents of a file. * * @param fullPath - The full path to the file. - * @param fileName - The name of the file. * @param relPath - The relative path to the file. + * @param fileName - The name of the file. * @returns The file's contents. */ async function getSolution( fullPath: string, + relPath: string, fileName: string, - relPath = ".", ): Promise { const fileContent = await Deno.readTextFile(fullPath); @@ -115,6 +107,7 @@ async function getSolution( value: fileContent, dirname: relPath, basename: fileName, + cwd: fullPath, }); } @@ -155,25 +148,15 @@ async function* compileSolutions( */ const remarkPlugins = [ remarkFrontmatter, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkMdxFrontmatter, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkPresetLintConsistent, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkPresetLintRecommended, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintCheckboxContentIndent, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintDefinitionSpacing, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintHeadingIncrement, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintLinebreakStyle, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintNoTabs, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintNoConsecutiveBlankLines, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkLintNoMissingBlankLines, [remarkMath, { singleDollarTextMath: false } satisfies MathOptions], ] as const satisfies PluggableList; @@ -184,7 +167,6 @@ const rehypePlugins = [rehypeMathjax] as const satisfies PluggableList; const compileOptions = { jsxImportSource: "preact", rehypePlugins, - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. remarkPlugins, } as const satisfies CompileOptions; @@ -197,7 +179,6 @@ const compileOptions = { async function compileSolution(file: VFile): Promise { matter(file); // Extract the frontmatter into `data.matter`. - // @ts-expect-error: Typescript dislikes current Deno deduping of Unified. const compiled = await compile(file, compileOptions); compiled.extname = ".js"; @@ -209,7 +190,7 @@ async function compileSolution(file: VFile): Promise { } async function writeSolutions(solutions: VFile[]): Promise { - const promises = []; + const promises: Promise[] = []; for (const solution of solutions) { promises.push(writeSolution(solution)); }