Sfoglia il codice sorgente

refactor(editor): fix TypeScript errors, path alias setup, and CI deploy

- Fix 30+ TypeScript errors across 6 files (noUncheckedIndexedAccess,
  missing imports, non-null assertions)
- Fix declaration generation (tsconfig.app.json: noEmit, declaration,
  rootDir) for clean vue-tsc builds
- Remove priority/date from parser; simplify block-classifier to 5 kinds
- Switch 43 relative imports to @/ path aliases in editor package
- Configure @/ resolution in tsconfig, vite.config, vitest.config
- Dev app: add @/ regex alias for editor HMR, remove unused resolver plugin
- Remove dead @/* alias from editor tsconfig
- Fix CI workflow (build order: editor → dev → deploy)
Zander Hawke 1 settimana fa
parent
commit
fff9911f7c
32 ha cambiato i file con 167 aggiunte e 105 eliminazioni
  1. 1 0
      .forgejo/workflows/deploy.yml
  2. 1 2
      apps/dev/tsconfig.app.json
  3. 57 28
      apps/dev/vite.config.ts
  4. 2 1
      biome.json
  5. 1 1
      packages/editor/package.json
  6. 16 15
      packages/editor/src/components/Block.vue
  7. 1 1
      packages/editor/src/components/__tests__/block-expose.test.ts
  8. 1 1
      packages/editor/src/components/__tests__/block-focus.test.ts
  9. 2 2
      packages/editor/src/components/__tests__/block-keyboard.test.ts
  10. 2 2
      packages/editor/src/composables/__tests__/markdown-decorations.test.ts
  11. 2 2
      packages/editor/src/composables/__tests__/pattern-plugin.test.ts
  12. 2 2
      packages/editor/src/composables/useBlockKeyboardHandlers.ts
  13. 2 2
      packages/editor/src/composables/useMarkdownDecorations.ts
  14. 3 3
      packages/editor/src/index.ts
  15. 2 2
      packages/editor/src/lib/__tests__/content-model.test.ts
  16. 1 1
      packages/editor/src/lib/__tests__/debug-tag.test.ts
  17. 1 1
      packages/editor/src/lib/__tests__/lezer-debug.test.ts
  18. 1 1
      packages/editor/src/lib/__tests__/lezer-structure.test.ts
  19. 1 1
      packages/editor/src/lib/__tests__/markdown-parser.test.ts
  20. 1 1
      packages/editor/src/lib/__tests__/paragraph-parsing.test.ts
  21. 2 2
      packages/editor/src/lib/__tests__/schema.test.ts
  22. 1 1
      packages/editor/src/lib/content-model.ts
  23. 4 2
      packages/editor/src/lib/markdown-extensions.ts
  24. 2 2
      packages/editor/src/lib/markdown-parser.ts
  25. 6 3
      packages/editor/src/lib/markdown-rules/block-classifier.ts
  26. 6 5
      packages/editor/src/lib/markdown-rules/block-rules.ts
  27. 7 5
      packages/editor/src/lib/markdown-rules/engine.ts
  28. 4 4
      packages/editor/src/lib/markdown-rules/index.ts
  29. 11 6
      packages/editor/src/lib/markdown-rules/inline-rules.ts
  30. 13 6
      packages/editor/tsconfig.app.json
  31. 5 0
      packages/editor/vite.config.ts
  32. 6 0
      packages/editor/vitest.config.ts

+ 1 - 0
.forgejo/workflows/deploy.yml

@@ -13,6 +13,7 @@ jobs:
           node-version: 22
       - run: corepack enable && corepack prepare pnpm@latest --activate
       - run: pnpm install
+      - run: pnpm --filter @enesis/editor build
       - run: pnpm --filter @enesis/dev build
       - if: ${{ forge.event_name == 'push' && forge.event.ref == 'refs/heads/master' }}
         uses: actions/git-pages@v2

+ 1 - 2
apps/dev/tsconfig.app.json

@@ -12,8 +12,7 @@
 
     "paths": {
       "#build/ui/*": ["./node_modules/.nuxt-ui/ui/*"],
-      "~/*": ["./src/*"],
-      "@enesis/editor": ["../../packages/editor/src/index.ts"]
+      "~/*": ["./src/*"]
     }
   },
   "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]

+ 57 - 28
apps/dev/vite.config.ts

@@ -27,34 +27,63 @@ export default defineConfig({
     exclude: ["node"],
   },
   resolve: {
-    alias: {
-      "@enesis/editor": resolve(__dirname, `${PKGS_EDITOR}/src/index.ts`),
-      "prosemirror-state": resolve(
-        __dirname,
-        `${PKGS_EDITOR}/node_modules/prosemirror-state`,
-      ),
-      "prosemirror-view": resolve(
-        __dirname,
-        `${PKGS_EDITOR}/node_modules/prosemirror-view`,
-      ),
-      "prosemirror-model": resolve(
-        __dirname,
-        `${PKGS_EDITOR}/node_modules/prosemirror-model`,
-      ),
-      "prosemirror-transform": resolve(
-        __dirname,
-        `${PKGS_EDITOR}/node_modules/prosemirror-transform`,
-      ),
-      "prosemirror-gapcursor": resolve(
-        __dirname,
-        `${PKGS_EDITOR}/node_modules/prosemirror-gapcursor`,
-      ),
-      "prosemirror-keymap": resolve(
-        __dirname,
-        `${PKGS_EDITOR}/node_modules/prosemirror-keymap`,
-      ),
-      "~": resolve(__dirname, "src"),
-    },
+    alias: [
+      // Editor source uses @/ prefix for src/ imports
+      {
+        find: /^@\//,
+        replacement: `${resolve(__dirname, PKGS_EDITOR, "src")}/`,
+      },
+      {
+        find: "@enesis/editor",
+        replacement: resolve(__dirname, `${PKGS_EDITOR}/src/index.ts`),
+      },
+      {
+        find: "prosemirror-state",
+        replacement: resolve(
+          __dirname,
+          `${PKGS_EDITOR}/node_modules/prosemirror-state`,
+        ),
+      },
+      {
+        find: "prosemirror-view",
+        replacement: resolve(
+          __dirname,
+          `${PKGS_EDITOR}/node_modules/prosemirror-view`,
+        ),
+      },
+      {
+        find: "prosemirror-model",
+        replacement: resolve(
+          __dirname,
+          `${PKGS_EDITOR}/node_modules/prosemirror-model`,
+        ),
+      },
+      {
+        find: "prosemirror-transform",
+        replacement: resolve(
+          __dirname,
+          `${PKGS_EDITOR}/node_modules/prosemirror-transform`,
+        ),
+      },
+      {
+        find: "prosemirror-gapcursor",
+        replacement: resolve(
+          __dirname,
+          `${PKGS_EDITOR}/node_modules/prosemirror-gapcursor`,
+        ),
+      },
+      {
+        find: "prosemirror-keymap",
+        replacement: resolve(
+          __dirname,
+          `${PKGS_EDITOR}/node_modules/prosemirror-keymap`,
+        ),
+      },
+      {
+        find: "~",
+        replacement: resolve(__dirname, "src"),
+      },
+    ],
   },
   server: {
     sourcemapIgnoreList: (sourcePath) => sourcePath.includes("node_modules"),

+ 2 - 1
biome.json

@@ -49,7 +49,8 @@
         "rules": {
           "style": {
             "useConst": "off",
-            "useImportType": "off"
+            "useImportType": "off",
+            "noNonNullAssertion": "off"
           },
           "correctness": {
             "noUnusedVariables": "off",

+ 1 - 1
packages/editor/package.json

@@ -14,7 +14,7 @@
     "dist"
   ],
   "scripts": {
-    "build": "vite build && vue-tsc --declaration --emitDeclarationOnly",
+    "build": "vite build && vue-tsc -b --declaration --emitDeclarationOnly",
     "dev": "vite build --watch",
     "test": "vitest run",
     "test:watch": "vitest"

+ 16 - 15
packages/editor/src/components/Block.vue

@@ -1,22 +1,22 @@
 <script setup lang="ts">
 import { watchDebounced } from "@vueuse/core"
 import { EditorState, Selection, type Transaction } from "prosemirror-state"
-import { schema } from "../lib/schema"
+import { schema } from "@/lib/schema"
 import { EditorView } from "prosemirror-view"
 import { onMounted, onUnmounted, useTemplateRef, watch } from "vue"
-import { createBlockKeyboardHandler } from "../composables/useBlockKeyboardHandlers"
+import { createBlockKeyboardHandler } from "@/composables/useBlockKeyboardHandlers"
 import {
   createMarkdownDecorationsPlugin,
   type MarkerVisibilityMode,
-} from "../composables/useMarkdownDecorations"
-import { createPatternPlugin } from "../composables/usePatternPlugin"
+} from "@/composables/useMarkdownDecorations"
+import { createPatternPlugin } from "@/composables/usePatternPlugin"
 import type {
   PatternClosePayload,
   PatternOpenPayload,
   PatternUpdatePayload,
-} from "../composables/usePatternPlugin.ts"
-import { contentToDoc, docToContent } from "../lib/content-model"
-import { classifyBlock } from "../lib/markdown-rules/block-classifier"
+} from "@/composables/usePatternPlugin"
+import { contentToDoc, docToContent } from "@/lib/content-model"
+import { classifyBlock } from "@/lib/markdown-rules/block-classifier"
 
 const content = defineModel<string>("content")
 
@@ -182,7 +182,7 @@ onMounted(() => {
       if (lines.length <= 1) return false
 
       const classifications = lines.map((line) => classifyBlock(line))
-      const firstType = classifications[0].kind
+      const firstType = classifications[0]!.kind
 
       const isContinuationBlock = (kind: string) =>
         kind === "blockquote" &&
@@ -213,20 +213,21 @@ onMounted(() => {
         schema.nodes.doc.create(null, afterDoc.content),
       )
 
-      const firstGroupLines: string[] = [lines[0]]
+      const firstGroupLines: string[] = [lines[0]!]
       const restLines: string[] = []
 
       let foundBreak = false
       for (let i = 1; i < lines.length; i++) {
+        const c = classifications[i]!
+        const line = lines[i]!
         if (
           !foundBreak &&
-          (classifications[i].kind === firstType ||
-            classifications[i].kind === "paragraph")
+          (c.kind === firstType || c.kind === "paragraph")
         ) {
-          firstGroupLines.push(lines[i])
+          firstGroupLines.push(line)
         } else {
           foundBreak = true
-          restLines.push(lines[i])
+          restLines.push(line)
         }
       }
 
@@ -264,12 +265,12 @@ onMounted(() => {
         // different block type, auto-promote it by firing a split.
         const lines = newContent.split("\n")
         if (lines.length > 1) {
-          const firstBlock = classifyBlock(lines[0])
+          const firstBlock = classifyBlock(lines[0]!)
           const isContinuationBlock = (kind: string) =>
             kind === "blockquote" &&
             (firstBlock.kind === "callout" || firstBlock.kind === "blockquote")
           for (let i = 1; i < lines.length; i++) {
-            const block = classifyBlock(lines[i])
+            const block = classifyBlock(lines[i]!)
             if (
               block.kind !== "paragraph" &&
               !isContinuationBlock(block.kind) &&

+ 1 - 1
packages/editor/src/components/__tests__/block-expose.test.ts

@@ -1,5 +1,5 @@
 import { describe, expect, it } from "vitest"
-import { contentToDoc, docToContent } from "../../lib/content-model"
+import { contentToDoc, docToContent } from "@/lib/content-model"
 
 // Unit tests for expose logic without mounting
 describe("Block expose API", () => {

+ 1 - 1
packages/editor/src/components/__tests__/block-focus.test.ts

@@ -1,6 +1,6 @@
 import { TextSelection } from "prosemirror-state"
 import { describe, expect, it, vi } from "vitest"
-import { contentToDoc } from "../../lib/content-model"
+import { contentToDoc } from "@/lib/content-model"
 
 describe("focusAt logic", () => {
   // Test the focusAt function behavior directly

+ 2 - 2
packages/editor/src/components/__tests__/block-keyboard.test.ts

@@ -4,8 +4,8 @@ import { describe, expect, it, vi } from "vitest"
 import {
   type BlockKeyboardHandlers,
   createBlockKeyboardHandler,
-} from "../../composables/useBlockKeyboardHandlers"
-import { contentToDoc } from "../../lib/content-model"
+} from "@/composables/useBlockKeyboardHandlers"
+import { contentToDoc } from "@/lib/content-model"
 
 function createMockKeyEvent(
   key: string,

+ 2 - 2
packages/editor/src/composables/__tests__/markdown-decorations.test.ts

@@ -1,7 +1,7 @@
 import { EditorState } from "prosemirror-state"
 import { describe, expect, it } from "vitest"
-import { contentToDoc } from "../../lib/content-model"
-import { createMarkdownDecorationsPlugin } from "../useMarkdownDecorations"
+import { contentToDoc } from "@/lib/content-model"
+import { createMarkdownDecorationsPlugin } from "@/composables/useMarkdownDecorations"
 
 describe("Markdown decorations plugin", () => {
   it("builds decorations on init with content when blurred", () => {

+ 2 - 2
packages/editor/src/composables/__tests__/pattern-plugin.test.ts

@@ -1,11 +1,11 @@
 import { EditorState, TextSelection } from "prosemirror-state"
 import { describe, expect, it, vi } from "vitest"
-import { contentToDoc } from "../../lib/content-model"
+import { contentToDoc } from "@/lib/content-model"
 import {
   createPatternPlugin,
   getPatternSession,
   type PatternSession,
-} from "../usePatternPlugin"
+} from "@/composables/usePatternPlugin"
 
 describe("Pattern plugin", () => {
   function createTestState(content: string, cursorPos?: number) {

+ 2 - 2
packages/editor/src/composables/useBlockKeyboardHandlers.ts

@@ -20,8 +20,8 @@ import {
 } from "prosemirror-commands"
 import type { EditorState } from "prosemirror-state"
 import type { EditorView } from "prosemirror-view"
-import { docToContent } from "../lib/content-model"
-import { schema } from "../lib/schema"
+import { docToContent } from "@/lib/content-model"
+import { schema } from "@/lib/schema"
 
 export interface BlockKeyboardHandlers {
   split: (before: string, after: string) => void

+ 2 - 2
packages/editor/src/composables/useMarkdownDecorations.ts

@@ -21,8 +21,8 @@ import {
   type DecorationRange,
   MarkdownRuleEngine,
   parseMarkdown,
-} from "../lib/markdown-parser"
-import type { BlockDecoration } from "../lib/markdown-rules"
+} from "@/lib/markdown-parser"
+import type { BlockDecoration } from "@/lib/markdown-rules"
 
 export type MarkerVisibilityMode = "live-preview" | "always-visible"
 

+ 3 - 3
packages/editor/src/index.ts

@@ -1,9 +1,9 @@
 import type { App } from "vue"
-import Block from "./components/Block.vue"
+import Block from "@/components/Block.vue"
 
-import "./assets/style.css"
+import "@/assets/style.css"
 
-export type { MarkdownPattern } from "./lib/markdown-extensions"
+export type { MarkdownPattern } from "@/lib/markdown-extensions"
 export { Block }
 
 export default {

+ 2 - 2
packages/editor/src/lib/__tests__/content-model.test.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from "vitest"
-import { contentToDoc, docToContent } from "../content-model"
-import { schema } from "../schema"
+import { contentToDoc, docToContent } from "@/lib/content-model"
+import { schema } from "@/lib/schema"
 
 describe("contentToDoc", () => {
   it("handles plain text", () => {

+ 1 - 1
packages/editor/src/lib/__tests__/debug-tag.test.ts

@@ -1,7 +1,7 @@
 import { describe, it, expect } from "vitest"
 import { parser } from "@lezer/markdown"
 import { GFM } from "@lezer/markdown"
-import { createMarkdownExtensions } from "../../lib/markdown-extensions"
+import { createMarkdownExtensions } from "@/lib/markdown-extensions"
 
 const markdownParser = parser.configure([GFM, ...createMarkdownExtensions()])
 

+ 1 - 1
packages/editor/src/lib/__tests__/lezer-debug.test.ts

@@ -1,7 +1,7 @@
 import { describe, it } from "vitest"
 import { parser } from "@lezer/markdown"
 import { GFM } from "@lezer/markdown"
-import { createMarkdownExtensions } from "../../lib/markdown-extensions"
+import { createMarkdownExtensions } from "@/lib/markdown-extensions"
 
 const markdownParser = parser.configure([GFM, ...createMarkdownExtensions()])
 

+ 1 - 1
packages/editor/src/lib/__tests__/lezer-structure.test.ts

@@ -1,7 +1,7 @@
 import { describe, it } from "vitest"
 import { parser } from "@lezer/markdown"
 import { GFM } from "@lezer/markdown"
-import { createMarkdownExtensions } from "../markdown-extensions"
+import { createMarkdownExtensions } from "@/lib/markdown-extensions"
 
 const markdownParser = parser.configure([GFM, ...createMarkdownExtensions()])
 

+ 1 - 1
packages/editor/src/lib/__tests__/markdown-parser.test.ts

@@ -4,7 +4,7 @@ import {
   type PageRefDecoration,
   parseMarkdown,
   type TagDecoration,
-} from "../markdown-parser"
+} from "@/lib/markdown-parser"
 
 describe("parseMarkdown", () => {
   it("parses bold", () => {

+ 1 - 1
packages/editor/src/lib/__tests__/paragraph-parsing.test.ts

@@ -1,5 +1,5 @@
 import { describe, expect, it } from "vitest"
-import { parseMarkdown } from "../markdown-parser"
+import { parseMarkdown } from "@/lib/markdown-parser"
 
 describe("paragraph-based parsing", () => {
   it("parses single-line blockquote", () => {

+ 2 - 2
packages/editor/src/lib/__tests__/schema.test.ts

@@ -1,6 +1,6 @@
 import { describe, expect, it } from "vitest"
-import { contentToDoc, docToContent } from "../content-model"
-import { schema } from "../schema"
+import { contentToDoc, docToContent } from "@/lib/content-model"
+import { schema } from "@/lib/schema"
 
 describe("schema", () => {
   describe("nodes", () => {

+ 1 - 1
packages/editor/src/lib/content-model.ts

@@ -6,7 +6,7 @@
  */
 
 import type { Node as ProsemirrorNode } from "prosemirror-model"
-import { schema } from "./schema"
+import { schema } from "@/lib/schema"
 
 /**
  * Convert markdown string to ProseMirror document.

+ 4 - 2
packages/editor/src/lib/markdown-extensions.ts

@@ -187,8 +187,10 @@ class TaskParser implements LeafBlockParser {
     const trimmed = content.slice(leading)
     const match = trimmed.match(TASK_RE)
     if (!match) return false
-    const markerLen = match[1].length
-    const afterMarker = match[0].length
+    // biome-ignore lint/style/noNonNullAssertion: match confirmed by TASK_RE above
+    const markerLen = match[1]!.length
+    // biome-ignore lint/style/noNonNullAssertion: match confirmed by TASK_RE above
+    const afterMarker = match[0]!.length
     const markerStart = leaf.start + leading
     cx.addLeafElement(
       leaf,

+ 2 - 2
packages/editor/src/lib/markdown-parser.ts

@@ -6,7 +6,7 @@
  */
 
 import { GFM, parser } from "@lezer/markdown"
-import { createMarkdownExtensions } from "./markdown-extensions"
+import { createMarkdownExtensions } from "@/lib/markdown-extensions"
 import {
   type BlockDecoration,
   type BlockRefDecoration,
@@ -21,7 +21,7 @@ import {
   type TagDecoration,
   type TaskState,
   type TokenDecoration,
-} from "./markdown-rules"
+} from "@/lib/markdown-rules"
 
 export type {
   BlockDecoration,

+ 6 - 3
packages/editor/src/lib/markdown-rules/block-classifier.ts

@@ -65,7 +65,8 @@ export function classifyBlock(text: string): ClassificationResult {
     if (match) {
       return {
         kind: "heading",
-        level: match[1].length,
+        // biome-ignore lint/style/noNonNullAssertion: match confirmed by HEADING_RE
+        level: match[1]!.length,
       }
     }
   }
@@ -75,7 +76,8 @@ export function classifyBlock(text: string): ClassificationResult {
     if (match) {
       return {
         kind: "callout",
-        calloutType: match[1],
+        // biome-ignore lint/style/noNonNullAssertion: match confirmed by CALLOUT_RE
+        calloutType: match[1]!,
       }
     }
   }
@@ -94,7 +96,8 @@ export function classifyBlock(text: string): ClassificationResult {
     if (match) {
       return {
         kind: "task",
-        taskState: match[1].toUpperCase(),
+        // biome-ignore lint/style/noNonNullAssertion: match confirmed by TASK_RE
+        taskState: match[1]!.toUpperCase(),
       }
     }
   }

+ 6 - 5
packages/editor/src/lib/markdown-rules/block-rules.ts

@@ -10,14 +10,14 @@
  */
 
 import type { SyntaxNode } from "@lezer/common"
-import { findChildByTypeName } from "./inline-rules"
+import { findChildByTypeName } from "@/lib/markdown-rules/inline-rules"
 import type {
   BlockDecoration,
   BlockRule,
   CalloutType,
   ParseContext,
   TaskState,
-} from "./types"
+} from "@/lib/markdown-rules/types"
 
 const CALLOUT_TYPES: ReadonlySet<string> = new Set([
   "NOTE",
@@ -38,13 +38,14 @@ export function createBlockquoteRule(): BlockRule {
     },
     run(node, ctx) {
       const text = ctx.doc.slice(node.from, node.to)
-      const firstLineText = text.split("\n")[0]
+      // biome-ignore lint/style/noNonNullAssertion: split on non-empty string always returns at least one element
+      const firstLineText = text.split("\n")[0]!
       const trimmed = firstLineText.trimStart()
 
-      // Check if this is a callout by inspecting the first line
       const calloutMatch = trimmed.match(/>\s*\[!([A-Z]+)\]\s*(.*)/)
 
-      if (calloutMatch && CALLOUT_TYPES.has(calloutMatch[1])) {
+      // biome-ignore lint/style/noNonNullAssertion: match confirmed by regex above
+      if (calloutMatch && CALLOUT_TYPES.has(calloutMatch[1]!)) {
         const calloutType = calloutMatch[1] as CalloutType
         return createCalloutDecoration(node, ctx, calloutType, firstLineText)
       }

+ 7 - 5
packages/editor/src/lib/markdown-rules/engine.ts

@@ -11,8 +11,8 @@
  */
 
 import type { Parser, Tree } from "@lezer/common"
-import { createBlockRules } from "./block-rules"
-import { createInlineRules } from "./inline-rules"
+import { createBlockRules } from "@/lib/markdown-rules/block-rules"
+import { createInlineRules } from "@/lib/markdown-rules/inline-rules"
 import type {
   BlockDecoration,
   BlockRule,
@@ -20,7 +20,7 @@ import type {
   ParseContext,
   ParsedDecorations,
   PropertyInfo,
-} from "./types"
+} from "@/lib/markdown-rules/types"
 
 // ── Rule index builders ───────────────────────────────────────────────
 
@@ -59,8 +59,10 @@ const PROPERTY_RE = /^(\w[\w-]*)::([^\n]*)/
 function parseProperties(text: string): PropertyInfo[] {
   const match = text.match(PROPERTY_RE)
   if (!match) return []
-  const key = match[1]
-  const rawValue = match[2]
+  // biome-ignore lint/style/noNonNullAssertion: match confirmed by PROPERTY_RE
+  const key = match[1]!
+  // biome-ignore lint/style/noNonNullAssertion: match confirmed by PROPERTY_RE
+  const rawValue = match[2]!
   const keyEnd = key.length
   const delimiterEnd = keyEnd + 2
   return [

+ 4 - 4
packages/editor/src/lib/markdown-rules/index.ts

@@ -10,9 +10,9 @@ export {
   createBlockquoteRule,
   createBlockRules,
   createHeadingRule,
-} from "./block-rules"
+} from "@/lib/markdown-rules/block-rules"
 // Engine
-export { MarkdownRuleEngine } from "./engine"
+export { MarkdownRuleEngine } from "@/lib/markdown-rules/engine"
 // Inline rule factories (consumed by tests and markdown-parser.ts)
 export {
   createBlockRefRule,
@@ -24,7 +24,7 @@ export {
   createTagRule,
   findChildByTypeName,
   findChildrenByTypeName,
-} from "./inline-rules"
+} from "@/lib/markdown-rules/inline-rules"
 // Types
 export type {
   BlockDecoration,
@@ -43,4 +43,4 @@ export type {
   TagDecoration,
   TaskState,
   TokenDecoration,
-} from "./types"
+} from "@/lib/markdown-rules/types"

+ 11 - 6
packages/editor/src/lib/markdown-rules/inline-rules.ts

@@ -18,7 +18,7 @@ import type {
   ParseContext,
   TagDecoration,
   TokenDecoration,
-} from "./types"
+} from "@/lib/markdown-rules/types"
 
 // ── Tree helpers ─────────────────────────────────────────────────────
 
@@ -68,8 +68,10 @@ export function createDelimitedRule(config: {
     run(node) {
       const marks = findChildrenByTypeName(node, config.mark)
       if (marks.length < 2) return null
-      const opener = marks[0]
-      const closer = marks[marks.length - 1]
+      // biome-ignore lint/style/noNonNullAssertion: checked length >= 2 above
+      const opener = marks[0]!
+      // biome-ignore lint/style/noNonNullAssertion: checked length >= 2 above
+      const closer = marks[marks.length - 1]!
       return {
         type: config.type,
         wrapperAttrs: { "data-md-type": config.type },
@@ -100,9 +102,12 @@ export function createLinkRule(): MarkdownRule<TokenDecoration> {
       if (linkMarks.length < 2) return null
       const url = findChildByTypeName(node, "URL")
       if (!url) return null
-      const opener = linkMarks[0]
-      const closer = linkMarks[linkMarks.length - 1]
-      const textEnd = linkMarks[1]
+      // biome-ignore lint/style/noNonNullAssertion: checked length >= 2 above
+      const opener = linkMarks[0]!
+      // biome-ignore lint/style/noNonNullAssertion: checked length >= 2 above
+      const closer = linkMarks[linkMarks.length - 1]!
+      // biome-ignore lint/style/noNonNullAssertion: checked length >= 2 above
+      const textEnd = linkMarks[1]!
       return {
         type: "link",
         wrapperAttrs: { "data-md-type": "link" },

+ 13 - 6
packages/editor/tsconfig.app.json

@@ -6,13 +6,20 @@
     // Extra safety for array and object lookups, but may have false positives.
     "noUncheckedIndexedAccess": true,
 
-    // Path mapping for cleaner imports.
-    "paths": {
-      "@/*": ["./src/*"]
-    },
-
     // `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
     // Specified here to keep it out of the root directory.
-    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
+    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+
+    // Override @vue/tsconfig which sets noEmit: true
+    "noEmit": false,
+    // Declaration generation (used by build script)
+    "declaration": true,
+    "emitDeclarationOnly": true,
+    "declarationDir": "./dist",
+    "rootDir": "./src",
+
+    "paths": {
+      "@/*": ["./src/*"]
+    }
   }
 }

+ 5 - 0
packages/editor/vite.config.ts

@@ -5,6 +5,11 @@ import { defineConfig } from "vite"
 
 export default defineConfig({
   plugins: [vue(), tailwindcss()],
+  resolve: {
+    alias: [
+      { find: /^@\//, replacement: `${resolve(__dirname, "src")}/` },
+    ],
+  },
   build: {
     lib: {
       entry: resolve(__dirname, "src/index.ts"),

+ 6 - 0
packages/editor/vitest.config.ts

@@ -1,6 +1,12 @@
+import { resolve } from "node:path"
 import { defineConfig } from "vitest/config"
 
 export default defineConfig({
+  resolve: {
+    alias: [
+      { find: /^@\//, replacement: `${resolve(__dirname, "src")}/` },
+    ],
+  },
   test: {
     environment: "jsdom",
     setupFiles: ["./vitest.setup.ts"],