diff --git a/docs/index.md b/docs/index.md index a632fd3..485a68c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -45,7 +45,7 @@ docs/ New to docs-engine? Start here: - **[Getting Started](./getting-started.md)** - 5-minute setup guide -- **[Installation](../README.md#installation)** - Package installation +- **[Quick Start](../README.md#quick-start)** - Package installation ## Architecture Overview diff --git a/eslint.config.js b/eslint.config.js index 839c856..49ad22c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -47,10 +47,19 @@ export default [ 'no-implied-eval': 'error', 'no-new-func': 'error', - // Code quality - '@typescript-eslint/explicit-function-return-type': 'warn', + // Code quality - STRICT mode + '@typescript-eslint/explicit-function-return-type': 'error', '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], - '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-non-null-assertion': 'warn', + + // Security rules - adjust for intentional patterns + // Object injection is safe when using typed objects with validated keys + 'security/detect-object-injection': 'off', + // Non-literal regexp is intentional for search functionality + 'security/detect-non-literal-regexp': 'off', + // Unsafe regex - we'll fix specific cases manually + 'security/detect-unsafe-regex': 'warn', }, }, { diff --git a/package.json b/package.json index adba27e..da09b38 100644 --- a/package.json +++ b/package.json @@ -93,9 +93,9 @@ } }, "optionalDependencies": { + "@rollup/rollup-linux-arm64-gnu": "4.57.0", "chokidar": "^5.0.0", - "playwright": "^1.58.0", - "@rollup/rollup-linux-arm64-gnu": "4.53.2" + "playwright": "^1.58.0" }, "keywords": [ "sveltekit", @@ -132,9 +132,8 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@testing-library/svelte": "^5.3.1", - "@types/dompurify": "^3.2.0", "@types/mdast": "^4.0.4", - "@types/node": "^25.0.10", + "@types/node": "^25.1.0", "@types/unist": "^3.0.3", "@typescript-eslint/eslint-plugin": "^8.54.0", "@typescript-eslint/parser": "^8.54.0", @@ -144,14 +143,14 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-security": "^3.0.1", "eslint-plugin-svelte": "^3.14.0", - "happy-dom": "^20.3.9", + "happy-dom": "^20.4.0", "husky": "^9.1.7", "jscpd": "^4.0.7", "lint-staged": "^16.2.7", "madge": "^8.0.0", "prettier": "^3.8.1", "prettier-plugin-svelte": "^3.4.1", - "svelte": "^5.48.2", + "svelte": "^5.48.5", "svelte-eslint-parser": "^1.4.1", "ts-morph": "^27.0.2", "tsup": "^8.5.1", diff --git a/packages/docs-engine-cli/package.json b/packages/docs-engine-cli/package.json index b5c738e..ab16713 100644 --- a/packages/docs-engine-cli/package.json +++ b/packages/docs-engine-cli/package.json @@ -31,7 +31,7 @@ "p-limit": "^7.2.0" }, "devDependencies": { - "@types/node": "^25.0.10", + "@types/node": "^25.1.0", "@types/prompts": "^2.4.9", "tsup": "^8.5.1", "typescript": "^5.9.3" diff --git a/packages/docs-engine-cli/src/link-checker.ts b/packages/docs-engine-cli/src/link-checker.ts index 69430f9..3c673a9 100644 --- a/packages/docs-engine-cli/src/link-checker.ts +++ b/packages/docs-engine-cli/src/link-checker.ts @@ -282,8 +282,8 @@ async function checkExternalLink( cache: Map ): Promise { // Check cache first - if (cache.has(link.url)) { - const cached = cache.get(link.url)!; + const cached = cache.get(link.url); + if (cached) { return { ...cached, link }; } diff --git a/packages/docs-engine-cli/src/link-extractor.ts b/packages/docs-engine-cli/src/link-extractor.ts index e8c3c4d..888fe9b 100644 --- a/packages/docs-engine-cli/src/link-extractor.ts +++ b/packages/docs-engine-cli/src/link-extractor.ts @@ -114,7 +114,8 @@ export function extractLinksFromFile(filePath: string): ExtractedLink[] { }); // Extract HTML links (basic regex for ) - const htmlLinkRegex = /]*?\s+)?href=["']([^"']+)["']/gi; + // Use [^>]*? without nested \s+ to avoid catastrophic backtracking + const htmlLinkRegex = /]*?href=["']([^"']+)["']/gi; const lines = content.split('\n'); lines.forEach((lineContent, index) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e3889b..b1b5851 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,13 +13,13 @@ importers: dependencies: '@goobits/themes': specifier: ^1.3.2 - version: 1.3.2(@lucide/svelte@0.563.1(svelte@5.48.2))(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2) + version: 1.3.2(@lucide/svelte@0.563.1(svelte@5.48.5))(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5) '@lucide/svelte': specifier: ^0.563.1 - version: 0.563.1(svelte@5.48.2) + version: 0.563.1(svelte@5.48.5) '@sveltejs/kit': specifier: ^2.0.0 - version: 2.48.4(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + version: 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) dompurify: specifier: ^3.3.1 version: 3.3.1 @@ -74,32 +74,19 @@ importers: yaml: specifier: ^2.8.2 version: 2.8.2 - optionalDependencies: - '@rollup/rollup-linux-arm64-gnu': - specifier: 4.53.2 - version: 4.53.2 - chokidar: - specifier: ^5.0.0 - version: 5.0.0 - playwright: - specifier: ^1.58.0 - version: 1.58.0 devDependencies: '@eslint/js': specifier: ^9.39.2 version: 9.39.2 '@testing-library/svelte': specifier: ^5.3.1 - version: 5.3.1(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))(vitest@4.0.18) - '@types/dompurify': - specifier: ^3.2.0 - version: 3.2.0 + version: 5.3.1(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))(vitest@4.0.18) '@types/mdast': specifier: ^4.0.4 version: 4.0.4 '@types/node': - specifier: ^25.0.10 - version: 25.0.10 + specifier: ^25.1.0 + version: 25.1.0 '@types/unist': specifier: ^3.0.3 version: 3.0.3 @@ -126,10 +113,10 @@ importers: version: 3.0.1 eslint-plugin-svelte: specifier: ^3.14.0 - version: 3.14.0(eslint@9.39.2)(svelte@5.48.2) + version: 3.14.0(eslint@9.39.2)(svelte@5.48.5) happy-dom: - specifier: ^20.3.9 - version: 20.3.9 + specifier: ^20.4.0 + version: 20.4.0 husky: specifier: ^9.1.7 version: 9.1.7 @@ -147,13 +134,13 @@ importers: version: 3.8.1 prettier-plugin-svelte: specifier: ^3.4.1 - version: 3.4.1(prettier@3.8.1)(svelte@5.48.2) + version: 3.4.1(prettier@3.8.1)(svelte@5.48.5) svelte: - specifier: ^5.48.2 - version: 5.48.2 + specifier: ^5.48.5 + version: 5.48.5 svelte-eslint-parser: specifier: ^1.4.1 - version: 1.4.1(svelte@5.48.2) + version: 1.4.1(svelte@5.48.5) ts-morph: specifier: ^27.0.2 version: 27.0.2 @@ -165,7 +152,17 @@ importers: version: 11.0.5 vitest: specifier: ^4.0.18 - version: 4.0.18(@types/node@25.0.10)(@vitest/ui@4.0.18)(happy-dom@20.3.9)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + version: 4.0.18(@types/node@25.1.0)(@vitest/ui@4.0.18)(happy-dom@20.4.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + optionalDependencies: + '@rollup/rollup-linux-arm64-gnu': + specifier: 4.57.0 + version: 4.57.0 + chokidar: + specifier: ^5.0.0 + version: 5.0.0 + playwright: + specifier: ^1.58.0 + version: 1.58.0 packages/docs-engine-cli: dependencies: @@ -204,8 +201,8 @@ importers: version: 5.1.0 devDependencies: '@types/node': - specifier: ^25.0.10 - version: 25.0.10 + specifier: ^25.1.0 + version: 25.1.0 '@types/prompts': specifier: ^2.4.9 version: 2.4.9 @@ -223,7 +220,7 @@ importers: version: link:.. '@lucide/svelte': specifier: ^0.563.1 - version: 0.563.1(svelte@5.48.2) + version: 0.563.1(svelte@5.48.5) gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -232,7 +229,7 @@ importers: version: 0.16.28 mdsvex: specifier: ^0.12.6 - version: 0.12.6(svelte@5.48.2) + version: 0.12.6(svelte@5.48.5) rehype-stringify: specifier: ^10.0.1 version: 10.0.1 @@ -250,20 +247,20 @@ importers: version: 11.1.2 svelte-preprocess: specifier: ^6.0.3 - version: 6.0.3(postcss-load-config@6.0.1(postcss@8.5.6)(yaml@2.8.2))(postcss@8.5.6)(pug@3.0.3)(sass@1.97.3)(svelte@5.48.2)(typescript@5.9.3) + version: 6.0.3(postcss-load-config@6.0.1(postcss@8.5.6)(yaml@2.8.2))(postcss@8.5.6)(pug@3.0.3)(sass@1.97.3)(svelte@5.48.5)(typescript@5.9.3) unified: specifier: ^11.0.5 version: 11.0.5 devDependencies: '@sveltejs/adapter-auto': specifier: ^7.0.0 - version: 7.0.0(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))) + version: 7.0.0(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))) '@sveltejs/kit': specifier: ^2.50.1 - version: 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + version: 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) '@sveltejs/vite-plugin-svelte': specifier: ^6.2.4 - version: 6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + version: 6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) playwright: specifier: ^1.58.0 version: 1.58.0 @@ -271,11 +268,11 @@ importers: specifier: ^1.97.3 version: 1.97.3 svelte: - specifier: ^5.48.2 - version: 5.48.2 + specifier: ^5.48.5 + version: 5.48.5 vite: specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + version: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) packages: @@ -916,6 +913,11 @@ packages: cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.57.0': + resolution: {integrity: sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.53.2': resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} cpu: [arm64] @@ -1015,19 +1017,6 @@ packages: peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/kit@2.48.4': - resolution: {integrity: sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==} - engines: {node: '>=18.13'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.0.0 - '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@sveltejs/kit@2.50.1': resolution: {integrity: sha512-XRHD2i3zC4ukhz2iCQzO4mbsts081PAZnnMAQ7LNpWeYgeBmwMsalf0FGSwhFXBbtr2XViPKnFJBDCckWqrsLw==} engines: {node: '>=18.13'} @@ -1209,10 +1198,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/dompurify@3.2.0': - resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} - deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. - '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -1237,8 +1222,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.0.10': - resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} + '@types/node@25.1.0': + resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==} '@types/prompts@2.4.9': resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} @@ -1965,9 +1950,6 @@ packages: peerDependencies: typescript: ^5.4.4 - devalue@5.4.2: - resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==} - devalue@5.6.2: resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==} @@ -2320,8 +2302,8 @@ packages: hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - happy-dom@20.3.9: - resolution: {integrity: sha512-OIoj0PcK2JaxQuANHxWkxFRSNXAuSgO1vCzCT66KItE0W/ieZLG+/iW8OetlxB+F9EaPB7DoFYKAubFG1f4Mvw==} + happy-dom@20.4.0: + resolution: {integrity: sha512-RDeQm3dT9n0A5f/TszjUmNCLEuPnMGv3Tv4BmNINebz/h17PA6LMBcxJ5FrcqltNBMh9jA/8ufgDdBYUdBt+eg==} engines: {node: '>=20.0.0'} has-flag@4.0.0: @@ -3823,8 +3805,8 @@ packages: typescript: optional: true - svelte@5.48.2: - resolution: {integrity: sha512-VPWD+UyoSFZ7Nxix5K/F8yWiKWOiROkLlWYXOZReE0TUycw+58YWB3D6lAKT+57xmN99wRX4H3oZmw0NPy7y3Q==} + svelte@5.48.5: + resolution: {integrity: sha512-NB3o70OxfmnE5UPyLr8uH3IV02Q43qJVAuWigYmsSOYsS0s/rHxP0TF81blG0onF/xkhNvZw4G8NfzIX+By5ZQ==} engines: {node: '>=18'} sync-child-process@1.0.2: @@ -4402,13 +4384,13 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@goobits/themes@1.3.2(@lucide/svelte@0.563.1(svelte@5.48.2))(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)': + '@goobits/themes@1.3.2(@lucide/svelte@0.563.1(svelte@5.48.5))(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)': dependencies: - '@sveltejs/kit': 2.48.4(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + '@sveltejs/kit': 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) esm-env: 1.2.2 - svelte: 5.48.2 + svelte: 5.48.5 optionalDependencies: - '@lucide/svelte': 0.563.1(svelte@5.48.2) + '@lucide/svelte': 0.563.1(svelte@5.48.5) '@humanfs/core@0.19.1': {} @@ -4601,9 +4583,9 @@ snapshots: reprism: 0.0.11 spark-md5: 3.0.2 - '@lucide/svelte@0.563.1(svelte@5.48.2)': + '@lucide/svelte@0.563.1(svelte@5.48.5)': dependencies: - svelte: 5.48.2 + svelte: 5.48.5 '@mermaid-js/parser@0.6.3': dependencies: @@ -4716,6 +4698,9 @@ snapshots: '@rollup/rollup-linux-arm64-gnu@4.53.2': optional: true + '@rollup/rollup-linux-arm64-gnu@4.57.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.53.2': optional: true @@ -4794,34 +4779,15 @@ snapshots: dependencies: acorn: 8.15.0 - '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))': - dependencies: - '@sveltejs/kit': 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) - - '@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': + '@sveltejs/adapter-auto@7.0.0(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))': dependencies: - '@standard-schema/spec': 1.0.0 - '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) - '@types/cookie': 0.6.0 - acorn: 8.15.0 - cookie: 0.6.0 - devalue: 5.4.2 - esm-env: 1.2.2 - kleur: 4.1.5 - magic-string: 0.30.21 - mrmime: 2.0.1 - sade: 1.8.1 - set-cookie-parser: 2.7.2 - sirv: 3.0.2 - svelte: 5.48.2 - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + '@sveltejs/kit': 2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) - '@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': + '@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(typescript@5.9.3)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': dependencies: '@standard-schema/spec': 1.0.0 '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 @@ -4833,29 +4799,29 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.2 sirv: 3.0.2 - svelte: 5.48.2 - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + svelte: 5.48.5 + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) optionalDependencies: typescript: 5.9.3 - '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) debug: 4.4.3 - svelte: 5.48.2 - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + svelte: 5.48.5 + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)))(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.1 - svelte: 5.48.2 - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) - vitefu: 1.1.1(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + svelte: 5.48.5 + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vitefu: 1.1.1(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -4870,18 +4836,18 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/svelte-core@1.0.0(svelte@5.48.2)': + '@testing-library/svelte-core@1.0.0(svelte@5.48.5)': dependencies: - svelte: 5.48.2 + svelte: 5.48.5 - '@testing-library/svelte@5.3.1(svelte@5.48.2)(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))(vitest@4.0.18)': + '@testing-library/svelte@5.3.1(svelte@5.48.5)(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))(vitest@4.0.18)': dependencies: '@testing-library/dom': 10.4.1 - '@testing-library/svelte-core': 1.0.0(svelte@5.48.2) - svelte: 5.48.2 + '@testing-library/svelte-core': 1.0.0(svelte@5.48.5) + svelte: 5.48.5 optionalDependencies: - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) - vitest: 4.0.18(@types/node@25.0.10)(@vitest/ui@4.0.18)(happy-dom@20.3.9)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.1.0)(@vitest/ui@4.0.18)(happy-dom@20.4.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) '@ts-graphviz/adapter@2.0.6': dependencies: @@ -5036,10 +5002,6 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/dompurify@3.2.0': - dependencies: - dompurify: 3.3.1 - '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -5062,13 +5024,13 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@25.0.10': + '@types/node@25.1.0': dependencies: undici-types: 7.16.0 '@types/prompts@2.4.9': dependencies: - '@types/node': 25.0.10 + '@types/node': 25.1.0 kleur: 3.0.3 '@types/sarif@2.1.7': {} @@ -5084,7 +5046,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.0.10 + '@types/node': 25.1.0 '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: @@ -5227,7 +5189,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.0.10)(@vitest/ui@4.0.18)(happy-dom@20.3.9)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.1.0)(@vitest/ui@4.0.18)(happy-dom@20.4.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) '@vitest/expect@4.0.18': dependencies: @@ -5238,13 +5200,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': dependencies: @@ -5272,7 +5234,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.0.10)(@vitest/ui@4.0.18)(happy-dom@20.3.9)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.1.0)(@vitest/ui@4.0.18)(happy-dom@20.4.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) '@vitest/utils@4.0.18': dependencies: @@ -5851,8 +5813,6 @@ snapshots: transitivePeerDependencies: - supports-color - devalue@5.4.2: {} - devalue@5.6.2: {} devlop@1.1.0: @@ -5955,7 +5915,7 @@ snapshots: dependencies: safe-regex: 2.1.1 - eslint-plugin-svelte@3.14.0(eslint@9.39.2)(svelte@5.48.2): + eslint-plugin-svelte@3.14.0(eslint@9.39.2)(svelte@5.48.5): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@jridgewell/sourcemap-codec': 1.5.5 @@ -5967,9 +5927,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.6) postcss-safe-parser: 7.0.1(postcss@8.5.6) semver: 7.7.3 - svelte-eslint-parser: 1.4.1(svelte@5.48.2) + svelte-eslint-parser: 1.4.1(svelte@5.48.5) optionalDependencies: - svelte: 5.48.2 + svelte: 5.48.5 transitivePeerDependencies: - ts-node @@ -6261,9 +6221,9 @@ snapshots: hachure-fill@0.5.2: {} - happy-dom@20.3.9: + happy-dom@20.4.0: dependencies: - '@types/node': 25.0.10 + '@types/node': 25.1.0 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 entities: 4.5.0 @@ -6886,13 +6846,13 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdsvex@0.12.6(svelte@5.48.2): + mdsvex@0.12.6(svelte@5.48.5): dependencies: '@types/mdast': 4.0.4 '@types/unist': 2.0.11 prism-svelte: 0.4.7 prismjs: 1.30.0 - svelte: 5.48.2 + svelte: 5.48.5 unist-util-visit: 5.1.0 vfile-message: 2.0.4 @@ -7548,10 +7508,10 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.4.1(prettier@3.8.1)(svelte@5.48.2): + prettier-plugin-svelte@3.4.1(prettier@3.8.1)(svelte@5.48.5): dependencies: prettier: 3.8.1 - svelte: 5.48.2 + svelte: 5.48.5 prettier@3.8.1: {} @@ -8165,7 +8125,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-eslint-parser@1.4.1(svelte@5.48.2): + svelte-eslint-parser@1.4.1(svelte@5.48.5): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -8174,11 +8134,11 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.48.2 + svelte: 5.48.5 - svelte-preprocess@6.0.3(postcss-load-config@6.0.1(postcss@8.5.6)(yaml@2.8.2))(postcss@8.5.6)(pug@3.0.3)(sass@1.97.3)(svelte@5.48.2)(typescript@5.9.3): + svelte-preprocess@6.0.3(postcss-load-config@6.0.1(postcss@8.5.6)(yaml@2.8.2))(postcss@8.5.6)(pug@3.0.3)(sass@1.97.3)(svelte@5.48.5)(typescript@5.9.3): dependencies: - svelte: 5.48.2 + svelte: 5.48.5 optionalDependencies: postcss: 8.5.6 postcss-load-config: 6.0.1(postcss@8.5.6)(yaml@2.8.2) @@ -8186,7 +8146,7 @@ snapshots: sass: 1.97.3 typescript: 5.9.3 - svelte@5.48.2: + svelte@5.48.5: dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 @@ -8404,7 +8364,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2): + vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -8413,20 +8373,20 @@ snapshots: rollup: 4.53.2 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.0.10 + '@types/node': 25.1.0 fsevents: 2.3.3 sass: 1.97.3 sass-embedded: 1.97.3 yaml: 2.8.2 - vitefu@1.1.1(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)): + vitefu@1.1.1(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)): optionalDependencies: - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) - vitest@4.0.18(@types/node@25.0.10)(@vitest/ui@4.0.18)(happy-dom@20.3.9)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2): + vitest@4.0.18(@types/node@25.1.0)(@vitest/ui@4.0.18)(happy-dom@20.4.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -8443,12 +8403,12 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.0.10)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.1.0)(sass-embedded@1.97.3)(sass@1.97.3)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.0.10 + '@types/node': 25.1.0 '@vitest/ui': 4.0.18(vitest@4.0.18) - happy-dom: 20.3.9 + happy-dom: 20.4.0 transitivePeerDependencies: - jiti - less diff --git a/site/package.json b/site/package.json index dbe3ff5..f9ef330 100644 --- a/site/package.json +++ b/site/package.json @@ -16,7 +16,7 @@ "@sveltejs/vite-plugin-svelte": "^6.2.4", "playwright": "^1.58.0", "sass-embedded": "^1.97.3", - "svelte": "^5.48.2", + "svelte": "^5.48.5", "vite": "^7.3.1" }, "dependencies": { diff --git a/site/src/routes/docs/+layout.server.ts b/site/src/routes/docs/+layout.server.ts index ba5f20c..abb25f1 100644 --- a/site/src/routes/docs/+layout.server.ts +++ b/site/src/routes/docs/+layout.server.ts @@ -66,7 +66,7 @@ export const load: LayoutServerLoad = async () => { 'Failed to generate documentation navigation', err instanceof Error ? err.message : String(err), 'Check that the docs folder exists and contains valid markdown files with frontmatter.' - ) as any + ) as { message: string } ); } diff --git a/site/src/routes/docs/[...slug]/+page.server.ts b/site/src/routes/docs/[...slug]/+page.server.ts index 91d0d27..b426f23 100644 --- a/site/src/routes/docs/[...slug]/+page.server.ts +++ b/site/src/routes/docs/[...slug]/+page.server.ts @@ -75,7 +75,7 @@ export const load: PageServerLoad = async ({ params }) => { 'Invalid frontmatter in markdown file', err instanceof Error ? err.message : String(err), 'Check that your YAML frontmatter is properly formatted with --- delimiters.' - ) as any + ) as { message: string } ); } throw error(404, 'Documentation page not found'); @@ -136,7 +136,7 @@ export const load: PageServerLoad = async ({ params }) => { 'Markdown processing failed', err instanceof Error ? err.message : String(err), 'Check for syntax errors in your markdown or plugin configuration.' - ) as any + ) as { message: string } ); } throw error(404, 'Documentation page not found'); @@ -152,7 +152,7 @@ export const load: PageServerLoad = async ({ params }) => { const errorMessage = err instanceof Error ? err.message : String(err); // Re-throw SvelteKit errors (they're already formatted) - if ('status' in (err as any)) { + if (typeof err === 'object' && err !== null && 'status' in err) { throw err; } @@ -168,7 +168,7 @@ export const load: PageServerLoad = async ({ params }) => { `Failed to render documentation page: ${slug}`, errorMessage, 'Check the server logs above for the full stack trace.' - ) as any + ) as { message: string } ); } diff --git a/src/lib/components/DocsLayout.svelte b/src/lib/components/DocsLayout.svelte index 5d8e7cc..bd859ee 100644 --- a/src/lib/components/DocsLayout.svelte +++ b/src/lib/components/DocsLayout.svelte @@ -25,7 +25,7 @@ import ScreenshotHydrator from './ScreenshotHydrator.svelte'; import OpenAPIHydrator from './OpenAPIHydrator.svelte'; import CollapseHydrator from './CollapseHydrator.svelte'; - import type { DocsSection } from './types'; + import type { DocsSection } from '../utils/navigation'; import { getAdjacentLinks } from '../utils'; import type { Contributor } from '../utils'; diff --git a/src/lib/components/DocsSidebar.svelte b/src/lib/components/DocsSidebar.svelte index db1d49d..b16a7e0 100644 --- a/src/lib/components/DocsSidebar.svelte +++ b/src/lib/components/DocsSidebar.svelte @@ -9,7 +9,7 @@ import { Search, ChevronDown, X } from '@lucide/svelte'; import { getAllDocsLinks, type DocsLink } from '$lib/config/docs-navigation'; import { SvelteSet } from 'svelte/reactivity'; - import type { DocsSection } from './types'; + import type { DocsSection } from '../utils/navigation'; // Audience filter state const AUDIENCE_TYPES = [ diff --git a/src/lib/components/ScreenshotImage.svelte b/src/lib/components/ScreenshotImage.svelte index 2ed64a7..f180eae 100644 --- a/src/lib/components/ScreenshotImage.svelte +++ b/src/lib/components/ScreenshotImage.svelte @@ -7,7 +7,14 @@ */ import { onMount } from 'svelte'; - import type { ScreenshotImageProps } from './types.js'; + + export interface ScreenshotImageProps { + name: string; + url: string; + path: string; + version: string; + config?: unknown; + } interface Props extends ScreenshotImageProps {} diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 9517c7e..d6ce2af 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -37,5 +37,5 @@ export { default as ThemeToggle } from './ThemeToggle.svelte'; export { default as OptimizedImage } from './OptimizedImage.svelte'; export { default as OptimizedImageHydrator } from './OptimizedImageHydrator.svelte'; -// Types -export type * from './types'; +// Types - re-exported from navigation utilities +export type { DocsLink, DocsSection } from '../utils/navigation'; diff --git a/src/lib/components/types.ts b/src/lib/components/types.ts deleted file mode 100644 index c843ace..0000000 --- a/src/lib/components/types.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { DocsLink, DocsSection } from '../utils/navigation'; - -// Re-export navigation types for convenience -export type { DocsLink, DocsSection }; - -/** - * Component prop types - */ - -export interface DocsSidebarProps { - navigation: DocsSection[]; - currentPath?: string; -} - -export interface DocsHubProps { - navigation: DocsSection[]; - title?: string; - description?: string; - githubUrl?: string; -} - -export interface SurfaceProps { - variant?: 'base' | 'raised' | 'elevated'; - radius?: 'sm' | 'md' | 'lg' | 'xl'; - padding?: 'sm' | 'md' | 'lg' | 'xl'; - class?: string; -} - -export interface ContentProps { - variant?: 'title' | 'subtitle' | 'body' | 'caption'; - weight?: 'normal' | 'medium' | 'semibold' | 'bold'; - color?: 'primary' | 'secondary' | 'tertiary'; - class?: string; -} - -export interface ScreenshotImageProps { - name: string; - url: string; - path: string; - version: string; - config?: unknown; -} diff --git a/src/lib/generators/api-docs.test.ts b/src/lib/generators/api-docs.test.ts index a15ec68..ea3ef4f 100644 --- a/src/lib/generators/api-docs.test.ts +++ b/src/lib/generators/api-docs.test.ts @@ -1167,7 +1167,7 @@ describe('groupByCategory', () => { ]; const groups = groupByCategory(items); - const utilsGroup = groups.get('Utils')!; + const utilsGroup = groups.get('Utils') ?? []; expect(utilsGroup[0].name).toBe('apple'); expect(utilsGroup[1].name).toBe('mango'); diff --git a/src/lib/generators/api-docs.ts b/src/lib/generators/api-docs.ts index e55b537..2460f34 100644 --- a/src/lib/generators/api-docs.ts +++ b/src/lib/generators/api-docs.ts @@ -483,7 +483,7 @@ export function groupByCategory(items: ApiItem[]): Map { if (!groups.has(category)) { groups.set(category, []); } - groups.get(category)!.push(item); + groups.get(category)?.push(item); } // Sort items within each group alphabetically diff --git a/src/lib/generators/generic-generator.ts b/src/lib/generators/generic-generator.ts index 254e9cd..1f45f66 100644 --- a/src/lib/generators/generic-generator.ts +++ b/src/lib/generators/generic-generator.ts @@ -7,9 +7,131 @@ import { readFile } from 'fs'; import { promisify } from 'util'; -import type { GeneratorConfig, GeneratorResult, GeneratorStats, CategoryRule } from './types'; import { parseJSON, parseEnv, parseSQL, parseGrep, type ParsedItem } from './parsers/index'; +/** + * Category matching rule + */ +export interface CategoryRule { + /** Category name */ + name: string; + /** Display order priority (lower = first) */ + order?: number; + /** Match condition */ + match: + | string // Regex pattern as string + | ((item: unknown) => boolean); // Custom function + /** Optional category description */ + description?: string; +} + +/** + * Enrichment rule for adding metadata + */ +export interface EnrichmentRule { + /** Field name to add */ + field: string; + /** Value mapping or function */ + value: + | Record // Static mapping + | ((item: unknown) => unknown); // Dynamic function +} + +/** + * Markdown template configuration + */ +export interface MarkdownTemplate { + /** Document title */ + title: string; + /** Source file description */ + source?: string; + /** Overview section content */ + overview?: string | ((stats: unknown) => string); + /** Table columns configuration */ + columns: Array<{ + header: string; + field: string | ((item: unknown) => string); + format?: (value: unknown) => string; + }>; + /** Additional sections to append */ + footer?: string | string[]; + /** Show table of contents */ + showTOC?: boolean; + /** Show statistics */ + showStats?: boolean; +} + +/** + * Parser configuration + */ +export type ParserConfig = + | { + type: 'json'; + /** JSONPath to extract items (e.g., "scripts" for package.json) */ + path?: string; + } + | { + type: 'env'; + /** Comment prefix for categories */ + categoryPrefix?: string; + } + | { + type: 'sql'; + /** Table pattern to match */ + tablePattern?: RegExp; + } + | { + type: 'grep'; + /** Grep command to execute */ + command: string; + /** Pattern to extract from matches */ + extractPattern?: RegExp; + } + | { + type: 'custom'; + /** Custom parser function */ + parse: (content: string, config: unknown) => ParsedItem[]; + }; + +/** + * Complete generator configuration + */ +export interface GeneratorConfig { + /** Input file path */ + input: string; + /** Output file path */ + output: string; + /** Parser configuration */ + parser: ParserConfig; + /** Categorization rules (applied in order) */ + categories: CategoryRule[]; + /** Enrichment rules (add metadata to items) */ + enrichments?: EnrichmentRule[]; + /** Description mappings */ + descriptions?: Record; + /** Markdown template */ + template: MarkdownTemplate; +} + +/** + * Generator statistics + */ +export interface GeneratorStats { + totalItems: number; + categoryCount: number; + itemsByCategory: Record; + uncategorized: number; +} + +/** + * Generator result + */ +export interface GeneratorResult { + markdown: string; + stats: GeneratorStats; + lineCount: number; +} + const readFileAsync = promisify(readFile); /** @@ -90,14 +212,14 @@ export class GenericGenerator { for (const rule of this.config.categories) { if (this.matchesRule(item, rule)) { - categorized.get(rule.name)!.push({ ...item, category: rule.name }); + categorized.get(rule.name)?.push({ ...item, category: rule.name }); assigned = true; break; // First match wins } } if (!assigned) { - categorized.get('__uncategorized__')!.push({ ...item, category: 'Other' }); + categorized.get('__uncategorized__')?.push({ ...item, category: 'Other' }); } } @@ -121,7 +243,7 @@ export class GenericGenerator { // Regex pattern const pattern = new RegExp(rule.match); - const testValue = item.name || item.value || item.key || String(item); + const testValue = String(item.name ?? item.value ?? item.key ?? item); return pattern.test(testValue); } @@ -157,15 +279,15 @@ export class GenericGenerator { if (typeof rule.value === 'function') { enriched[rule.field] = rule.value(item); } else { - const key = item.name || item.key; - enriched[rule.field] = rule.value[key] || enriched[rule.field]; + const key = String(item.name ?? item.key ?? ''); + enriched[rule.field] = rule.value[key] ?? enriched[rule.field]; } } } // Apply descriptions if (this.config.descriptions) { - const key = item.name || item.key || item.value; + const key = String(item.name ?? item.key ?? item.value ?? ''); if (key && this.config.descriptions[key]) { enriched.description = this.config.descriptions[key]; } diff --git a/src/lib/generators/index.ts b/src/lib/generators/index.ts index 0a4d342..fff2daf 100644 --- a/src/lib/generators/index.ts +++ b/src/lib/generators/index.ts @@ -5,7 +5,6 @@ * from various source formats (JSON, SQL, ENV, etc.) */ -export * from './types'; export * from './generic-generator'; export * from './api-docs'; export * from './api-parser'; diff --git a/src/lib/generators/parsers/env-parser.ts b/src/lib/generators/parsers/env-parser.ts index 4f90674..55dc3ab 100644 --- a/src/lib/generators/parsers/env-parser.ts +++ b/src/lib/generators/parsers/env-parser.ts @@ -4,7 +4,7 @@ * Parses .env files with support for categories, comments, and variable detection. */ -import type { ParsedItem } from './types'; +import type { ParsedItem } from './index'; /** * Parse .env file content @@ -53,7 +53,8 @@ export function parseEnv(content: string, categoryPrefix = '#'): ParsedItem[] { } // Variable line - const varMatch = line.match(/^\s*(#\s*)?([A-Z][A-Z0-9_]*)\s*=\s*(.*)$/); + // Use single \s instead of \s* inside optional group to avoid nested quantifiers + const varMatch = line.match(/^[ \t]*(#[ \t])?([A-Z][A-Z0-9_]*)[ \t]*=[ \t]*(.*)$/); if (varMatch) { const name = varMatch[2]; let value = varMatch[3].trim(); diff --git a/src/lib/generators/parsers/grep-parser.ts b/src/lib/generators/parsers/grep-parser.ts index 9f08d32..b4aeb4b 100644 --- a/src/lib/generators/parsers/grep-parser.ts +++ b/src/lib/generators/parsers/grep-parser.ts @@ -5,7 +5,7 @@ */ import { execSync } from 'child_process'; -import type { ParsedItem } from './types'; +import type { ParsedItem } from './index'; /** * Execute grep command and parse output diff --git a/src/lib/generators/parsers/index.ts b/src/lib/generators/parsers/index.ts index d51f6a8..e3dcb97 100644 --- a/src/lib/generators/parsers/index.ts +++ b/src/lib/generators/parsers/index.ts @@ -8,4 +8,17 @@ export { parseJSON } from './json-parser'; export { parseEnv } from './env-parser'; export { parseSQL } from './sql-parser'; export { parseGrep } from './grep-parser'; -export type { ParsedItem, Parser } from './types'; + +/** + * Generic parsed item - represents any parsed data structure. + * Uses Record for type safety while allowing + * flexible property access (the actual shape depends on the parser). + */ +export type ParsedItem = Record; + +/** + * Base parser interface that all parsers implement + */ +export interface Parser { + parse(content: string): ParsedItem[]; +} diff --git a/src/lib/generators/parsers/json-parser.ts b/src/lib/generators/parsers/json-parser.ts index 1be80a2..9cbc6cd 100644 --- a/src/lib/generators/parsers/json-parser.ts +++ b/src/lib/generators/parsers/json-parser.ts @@ -4,7 +4,7 @@ * Parses JSON files and extracts items from a specified path. */ -import type { ParsedItem } from './types'; +import type { ParsedItem } from './index'; /** * Parse JSON content and extract items from a path diff --git a/src/lib/generators/parsers/parsers.test.ts b/src/lib/generators/parsers/parsers.test.ts index 829881c..f2ef8ee 100644 --- a/src/lib/generators/parsers/parsers.test.ts +++ b/src/lib/generators/parsers/parsers.test.ts @@ -132,9 +132,10 @@ describe('parseSQL', () => { created_at TIMESTAMP );`; const result = parseSQL(content); - expect(result[0].columns[0]).toEqual({ name: 'id', type: 'INTEGER' }); - expect(result[0].columns[1]).toEqual({ name: 'price', type: 'REAL' }); - expect(result[0].columns[2]).toEqual({ name: 'created_at', type: 'TIMESTAMP' }); + const columns = result[0].columns as Array<{ name: string; type: string }>; + expect(columns[0]).toEqual({ name: 'id', type: 'INTEGER' }); + expect(columns[1]).toEqual({ name: 'price', type: 'REAL' }); + expect(columns[2]).toEqual({ name: 'created_at', type: 'TIMESTAMP' }); }); it('should handle CREATE TABLE IF NOT EXISTS', () => { @@ -152,8 +153,9 @@ describe('parseSQL', () => { CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) );`; const result = parseSQL(content); - expect(result[0].constraints).toHaveLength(1); - expect(result[0].constraints[0]).toContain('CONSTRAINT fk_user'); + const constraints = result[0].constraints as string[]; + expect(constraints).toHaveLength(1); + expect(constraints[0]).toContain('CONSTRAINT fk_user'); }); it('should handle PRIMARY KEY constraints', () => { @@ -163,8 +165,9 @@ describe('parseSQL', () => { PRIMARY KEY (a, b) );`; const result = parseSQL(content); - expect(result[0].constraints).toHaveLength(1); - expect(result[0].constraints[0]).toContain('PRIMARY KEY'); + const constraints = result[0].constraints as string[]; + expect(constraints).toHaveLength(1); + expect(constraints[0]).toContain('PRIMARY KEY'); }); it('should parse multiple tables', () => { @@ -187,7 +190,8 @@ CREATE TABLE posts ( id INTEGER );`; const result = parseSQL(content); - expect(result[0].columns).toHaveLength(1); - expect(result[0].columns[0].name).toBe('id'); + const columns = result[0].columns as Array<{ name: string; type: string }>; + expect(columns).toHaveLength(1); + expect(columns[0].name).toBe('id'); }); }); diff --git a/src/lib/generators/parsers/sql-parser.ts b/src/lib/generators/parsers/sql-parser.ts index 47f8231..fc69561 100644 --- a/src/lib/generators/parsers/sql-parser.ts +++ b/src/lib/generators/parsers/sql-parser.ts @@ -4,7 +4,20 @@ * Parses SQL schema files and extracts table definitions. */ -import type { ParsedItem } from './types'; +import type { ParsedItem } from './index'; + +/** Column definition structure */ +interface SqlColumn { + name: string; + type: string; +} + +/** Internal table structure used during parsing */ +interface SqlTable { + name: string; + columns: SqlColumn[]; + constraints: string[]; +} /** * Parse SQL schema content @@ -13,9 +26,9 @@ import type { ParsedItem } from './types'; * @returns Array of parsed table definitions */ export function parseSQL(content: string, _tablePattern?: RegExp): ParsedItem[] { - const tables: ParsedItem[] = []; + const tables: SqlTable[] = []; const lines = content.split('\n'); - let currentTable: ParsedItem | null = null; + let currentTable: SqlTable | null = null; let inTableDef = false; for (const line of lines) { @@ -59,5 +72,6 @@ export function parseSQL(content: string, _tablePattern?: RegExp): ParsedItem[] } } - return tables; + // Convert SqlTable[] to ParsedItem[] (spreading preserves structure) + return tables.map((table) => ({ ...table })); } diff --git a/src/lib/generators/parsers/types.ts b/src/lib/generators/parsers/types.ts deleted file mode 100644 index de5cc34..0000000 --- a/src/lib/generators/parsers/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Parser Types - * - * Shared types for all parsers in the generic generator system. - */ - -/** - * Generic parsed item - represents any parsed data structure - * The actual shape depends on the parser type (JSON, ENV, SQL, etc.) - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ParsedItem = any; - -/** - * Base parser interface that all parsers implement - */ -export interface Parser { - parse(content: string): ParsedItem[]; -} diff --git a/src/lib/generators/types.ts b/src/lib/generators/types.ts deleted file mode 100644 index d541a52..0000000 --- a/src/lib/generators/types.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Generic Documentation Generator Types - * - * Framework for creating configuration-driven documentation generators - * that parse source files, categorize content, and generate markdown. - */ - -/** - * Category matching rule - */ -export interface CategoryRule { - /** Category name */ - name: string; - /** Display order priority (lower = first) */ - order?: number; - /** Match condition */ - match: - | string // Regex pattern as string - | ((item: unknown) => boolean); // Custom function - /** Optional category description */ - description?: string; -} - -/** - * Enrichment rule for adding metadata - */ -export interface EnrichmentRule { - /** Field name to add */ - field: string; - /** Value mapping or function */ - value: - | Record // Static mapping - | ((item: unknown) => unknown); // Dynamic function -} - -/** - * Markdown template configuration - */ -export interface MarkdownTemplate { - /** Document title */ - title: string; - /** Source file description */ - source?: string; - /** Overview section content */ - overview?: string | ((stats: unknown) => string); - /** Table columns configuration */ - columns: Array<{ - header: string; - field: string | ((item: unknown) => string); - format?: (value: unknown) => string; - }>; - /** Additional sections to append */ - footer?: string | string[]; - /** Show table of contents */ - showTOC?: boolean; - /** Show statistics */ - showStats?: boolean; -} - -/** - * Parser configuration - */ -export type ParserConfig = - | { - type: 'json'; - /** JSONPath to extract items (e.g., "scripts" for package.json) */ - path?: string; - } - | { - type: 'env'; - /** Comment prefix for categories */ - categoryPrefix?: string; - } - | { - type: 'sql'; - /** Table pattern to match */ - tablePattern?: RegExp; - } - | { - type: 'grep'; - /** Grep command to execute */ - command: string; - /** Pattern to extract from matches */ - extractPattern?: RegExp; - } - | { - type: 'custom'; - /** Custom parser function */ - parse: (content: string, config: unknown) => unknown[]; - }; - -/** - * Complete generator configuration - */ -export interface GeneratorConfig { - /** Input file path */ - input: string; - /** Output file path */ - output: string; - /** Parser configuration */ - parser: ParserConfig; - /** Categorization rules (applied in order) */ - categories: CategoryRule[]; - /** Enrichment rules (add metadata to items) */ - enrichments?: EnrichmentRule[]; - /** Description mappings */ - descriptions?: Record; - /** Markdown template */ - template: MarkdownTemplate; -} - -/** - * Generator statistics - */ -export interface GeneratorStats { - totalItems: number; - categoryCount: number; - itemsByCategory: Record; - uncategorized: number; -} - -/** - * Generator result - */ -export interface GeneratorResult { - markdown: string; - stats: GeneratorStats; - lineCount: number; -} diff --git a/src/lib/plugins/callouts.ts b/src/lib/plugins/callouts.ts index b859f82..ee1bb4c 100644 --- a/src/lib/plugins/callouts.ts +++ b/src/lib/plugins/callouts.ts @@ -12,6 +12,16 @@ import type { import type { PhrasingContent } from 'mdast'; import { escapeHtml } from '../utils/html.js'; +/** + * Interface for mutating AST nodes during transformation + * Used when transforming one node type (e.g., Blockquote) to another (e.g., Html) + */ +interface MutableAstNode { + type: string; + value?: string; + children?: unknown[]; +} + /** * Callout configuration */ @@ -62,8 +72,9 @@ export function calloutsPlugin(): (tree: Root) => void { if (!firstText || firstText.type !== 'text') return; // Match [!TYPE] or [!TYPE Custom Title] syntax (case insensitive) + // Capture everything after ] and trim in code to avoid nested quantifiers const match = firstText.value.match( - /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|SUCCESS|DANGER|INFO|QUESTION)\](?:\s+(.+?))?$/i + /^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|SUCCESS|DANGER|INFO|QUESTION)\](.*)$/i ); if (!match) return; @@ -81,9 +92,9 @@ export function calloutsPlugin(): (tree: Root) => void { const contentHtml = renderCalloutContent(node.children as BlockContent[]); // Transform node to HTML - type assertions needed for node type transformation - (node as any).type = 'html'; - (node as any).value = - `
+ const mutableNode = node as MutableAstNode; + mutableNode.type = 'html'; + mutableNode.value = `
${displayTitle} @@ -92,7 +103,7 @@ export function calloutsPlugin(): (tree: Root) => void { ${contentHtml}
`; - delete (node as any).children; + delete mutableNode.children; }); }; } diff --git a/src/lib/plugins/code-highlight.test.ts b/src/lib/plugins/code-highlight.test.ts index 0702e17..e5286e6 100644 --- a/src/lib/plugins/code-highlight.test.ts +++ b/src/lib/plugins/code-highlight.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { codeHighlightPlugin, type CodeBlockMetadata } from './code-highlight'; -import type { Root, Html } from 'mdast'; +import type { Root, Html, Code } from 'mdast'; /** * Tests for code-highlight plugin @@ -43,7 +43,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; @@ -67,7 +67,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; @@ -91,14 +91,14 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; const plugin = codeHighlightPlugin(); await plugin(tree); - const node = tree.children[0] as any; + const node = tree.children[0] as Code; // Should not transform filetree blocks expect(node.type).toBe('code'); expect(node.lang).toBe('filetree'); @@ -118,7 +118,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; @@ -142,7 +142,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 4, column: 4, offset: 30 }, }, - } as any, + } as Code, ], }; @@ -166,7 +166,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 4, column: 4, offset: 30 }, }, - } as any, + } as Code, ], }; @@ -192,7 +192,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; @@ -215,7 +215,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; @@ -240,7 +240,7 @@ describe('code-highlight plugin', () => { start: { line: 1, column: 1, offset: 0 }, end: { line: 3, column: 4, offset: 20 }, }, - } as any, + } as Code, ], }; diff --git a/src/lib/plugins/code-highlight.ts b/src/lib/plugins/code-highlight.ts index 7a9efab..ad5f41b 100644 --- a/src/lib/plugins/code-highlight.ts +++ b/src/lib/plugins/code-highlight.ts @@ -1,10 +1,20 @@ import { visit } from 'unist-util-visit'; import type { Root, Code } from 'mdast'; import type { Parent } from 'unist'; -import { createHighlighter, type Highlighter } from 'shiki'; +import { createHighlighter, type Highlighter, type LanguageRegistration } from 'shiki'; import agentflowGrammar from '../utils/agentflow-grammar.json'; import { escapeHtml } from '../utils/html.js'; +/** + * Interface for mutating AST nodes during transformation + * Used when transforming Code nodes to Html nodes + */ +interface MutableCodeNode { + type: string; + value?: string; + lang?: string; +} + /** * Configuration options for code block highlighting * @@ -265,7 +275,7 @@ export function codeHighlightPlugin( // Get or create highlighter with enhanced language support if (!highlighterPromise) { - highlighterPromise = (async () => { + highlighterPromise = (async (): Promise => { const h = await createHighlighter({ themes: [theme], langs: [ @@ -292,7 +302,7 @@ export function codeHighlightPlugin( }); // Register AgentFlow grammar with aliases - const grammar = agentflowGrammar as any; + const grammar = agentflowGrammar as unknown as LanguageRegistration; await h.loadLanguage({ ...grammar, aliases: ['dsl', 'agentflow'], @@ -357,9 +367,10 @@ export function codeHighlightPlugin( // Transform the node to HTML // Escape curly braces for Svelte 5 parser compatibility - (node as any).type = 'html'; - (node as any).value = highlighted.replace(/{/g, '{').replace(/}/g, '}'); - delete node.lang; + const mutableNode = node as MutableCodeNode; + mutableNode.type = 'html'; + mutableNode.value = highlighted.replace(/{/g, '{').replace(/}/g, '}'); + delete mutableNode.lang; } catch (error) { console.error( `Failed to highlight code block with language "${metadata.language}":`, @@ -367,10 +378,11 @@ export function codeHighlightPlugin( ); // Fallback to plain code block // Escape curly braces for Svelte 5 parser compatibility - (node as any).type = 'html'; + const mutableFallbackNode = node as MutableCodeNode; + mutableFallbackNode.type = 'html'; const fallbackHtml = `
${escapeHtml(code)}
`; - (node as any).value = fallbackHtml.replace(/{/g, '{').replace(/}/g, '}'); - delete node.lang; + mutableFallbackNode.value = fallbackHtml.replace(/{/g, '{').replace(/}/g, '}'); + delete mutableFallbackNode.lang; } }) ); diff --git a/src/lib/plugins/collapse.ts b/src/lib/plugins/collapse.ts index 642abde..4e54dc1 100644 --- a/src/lib/plugins/collapse.ts +++ b/src/lib/plugins/collapse.ts @@ -13,6 +13,18 @@ import type { PhrasingContent } from 'mdast'; import { escapeHtml } from '../utils/html.js'; import { sanitizeTree } from '../utils/ast.js'; +/** + * Interface for container directive nodes (from remark-directive) + * Used for collapse directives that get transformed to HTML + */ +interface ContainerDirectiveNode { + type: string; + name: string; + attributes?: Record; + children?: BlockContent[]; + value?: string; +} + /** * Remark plugin to transform :::collapse directives to HTML
elements * @@ -42,7 +54,7 @@ export function collapsePlugin(): (tree: Root) => void { visit(tree, 'containerDirective', (node: unknown) => { // Extra defensive checks if (!node) return; - const n = node as any; + const n = node as ContainerDirectiveNode; if (typeof n.name !== 'string') return; if (n.name !== 'collapse') return; diff --git a/src/lib/plugins/filetree.ts b/src/lib/plugins/filetree.ts index c094415..0f85ccd 100644 --- a/src/lib/plugins/filetree.ts +++ b/src/lib/plugins/filetree.ts @@ -4,6 +4,15 @@ import { parseTree } from '../utils/tree-parser.js'; import { escapeHtml } from '../utils/html.js'; import { encodeJsonBase64 } from '../utils/base64.js'; +/** + * Interface for mutating AST nodes during transformation + * Used when transforming Code nodes to Html nodes + */ +interface MutableCodeNode { + type: string; + value?: string; +} + /** * Remark plugin to transform filetree code blocks into interactive file trees * @@ -36,13 +45,15 @@ export function filetreePlugin(): (tree: Root) => void { const encoded = encodeJsonBase64(treeData); // Transform to HTML div that will be hydrated client-side - (node as any).type = 'html'; - (node as any).value = `
`; + const mutableNode = node as MutableCodeNode; + mutableNode.type = 'html'; + mutableNode.value = `
`; } catch (error) { // If parsing fails, render an error message console.error('Failed to parse filetree:', error); - (node as any).type = 'html'; - (node as any).value = `
+ const mutableErrorNode = node as MutableCodeNode; + mutableErrorNode.type = 'html'; + mutableErrorNode.value = `
⚠️ diff --git a/src/lib/plugins/image-optimization.test.ts b/src/lib/plugins/image-optimization.test.ts index 408ff90..b996c6c 100644 --- a/src/lib/plugins/image-optimization.test.ts +++ b/src/lib/plugins/image-optimization.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { imageOptimizationPlugin } from './image-optimization.js'; -import type { Root, Image, Paragraph } from 'mdast'; +import type { Root, Image, Paragraph, Html } from 'mdast'; describe('imageOptimizationPlugin', () => { it('should transform markdown images to optimized image divs', async () => { @@ -25,7 +25,7 @@ describe('imageOptimizationPlugin', () => { await plugin(tree); const paragraph = tree.children[0] as Paragraph; - const transformed = paragraph.children[0] as any; + const transformed = paragraph.children[0] as Html; expect(transformed.type).toBe('html'); expect(transformed.value).toContain('md-optimized-image'); @@ -53,7 +53,7 @@ describe('imageOptimizationPlugin', () => { await plugin(tree); const paragraph = tree.children[0] as Paragraph; - const transformed = paragraph.children[0] as any; + const transformed = paragraph.children[0] as Html; // Should have removed dimensions from URL expect(transformed.value).not.toContain('width=200'); @@ -111,7 +111,7 @@ describe('imageOptimizationPlugin', () => { await plugin(tree); const paragraph = tree.children[0] as Paragraph; - const transformed = paragraph.children[0] as any; + const transformed = paragraph.children[0] as Html; // Should be transformed expect(transformed.type).toBe('html'); @@ -147,7 +147,7 @@ describe('imageOptimizationPlugin', () => { await plugin(tree); const paragraph = tree.children[0] as Paragraph; - const transformed = paragraph.children[0] as any; + const transformed = paragraph.children[0] as Html; expect(transformed.type).toBe('html'); expect(transformed.value).toContain('md-optimized-image'); @@ -181,7 +181,7 @@ describe('imageOptimizationPlugin', () => { await plugin(tree); const paragraph = tree.children[0] as Paragraph; - const transformed = paragraph.children[0] as any; + const transformed = paragraph.children[0] as Html; // Alt text is encoded in the config expect(transformed.value).toContain('data-config'); @@ -208,7 +208,7 @@ describe('imageOptimizationPlugin', () => { await plugin(tree); const paragraph = tree.children[0] as Paragraph; - const transformed = paragraph.children[0] as any; + const transformed = paragraph.children[0] as Html; expect(transformed.type).toBe('html'); expect(transformed.value).toContain('md-optimized-image'); @@ -244,8 +244,8 @@ describe('imageOptimizationPlugin', () => { const plugin = imageOptimizationPlugin(); await plugin(tree); - const first = (tree.children[0] as any).children[0] as any; - const second = (tree.children[1] as any).children[0] as any; + const first = (tree.children[0] as Paragraph).children[0] as Html; + const second = (tree.children[1] as Paragraph).children[0] as Html; expect(first.type).toBe('html'); expect(first.value).toContain('md-optimized-image'); diff --git a/src/lib/plugins/image-optimization.ts b/src/lib/plugins/image-optimization.ts index 534a48a..fdcb846 100644 --- a/src/lib/plugins/image-optimization.ts +++ b/src/lib/plugins/image-optimization.ts @@ -60,7 +60,8 @@ interface OptimizedImageConfig extends ImageMetadata { * Module-private helper */ function isExternalUrl(url: string): boolean { - return /^(https?:)?\/\//.test(url); + // Use explicit alternation to avoid nested optional quantifiers + return /^(?:https:\/\/|http:\/\/|\/\/)/.test(url); } /** @@ -144,7 +145,7 @@ export function imageOptimizationPlugin(options: ImageOptimizationOptions = {}) const placeholder = options.placeholder || 'blur'; const skipExternal = options.skipExternal !== false; - return (tree: Root) => { + return (tree: Root): void => { visit(tree, 'image', (node: Image) => { const src = node.url; const alt = node.alt || ''; diff --git a/src/lib/plugins/katex.test.ts b/src/lib/plugins/katex.test.ts index dded1aa..ff51779 100644 --- a/src/lib/plugins/katex.test.ts +++ b/src/lib/plugins/katex.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { katexPlugin, type KaTeXOptions } from './katex'; -import type { Root, Paragraph, Html } from 'mdast'; +import type { Root, Paragraph, Html, Text } from 'mdast'; /** * Tests for KaTeX math rendering plugin @@ -11,6 +11,24 @@ import type { Root, Paragraph, Html } from 'mdast'; * - Tests actual user-facing functionality */ +/** + * Inline math node type (custom mdast node from remark-math) + */ +interface InlineMath { + type: 'inlineMath'; + value: string; + data?: Record; +} + +/** + * Display math node type (custom mdast node from remark-math) + */ +interface DisplayMath { + type: 'math'; + value: string; + data?: Record; +} + /** * Helper to create a test tree with inline math */ @@ -25,7 +43,7 @@ function createInlineMathTree(latex: string): Root { type: 'inlineMath', value: latex, data: {}, - } as any, + } as unknown as InlineMath, ], }, ], @@ -43,7 +61,7 @@ function createDisplayMathTree(latex: string): Root { type: 'math', value: latex, data: {}, - } as any, + } as unknown as DisplayMath, ], }; } @@ -212,17 +230,17 @@ describe('katex plugin', () => { type: 'inlineMath', value: 'x^2', data: {}, - } as any, + } as unknown as InlineMath, { type: 'math', value: 'y = mx + b', data: {}, - } as any, + } as unknown as DisplayMath, { type: 'inlineMath', value: 'z^3', data: {}, - } as any, + } as unknown as InlineMath, ], }; @@ -252,7 +270,7 @@ describe('katex plugin', () => { type: 'inlineMath', value: 'x^2', data: {}, - } as any, + } as unknown as InlineMath, { type: 'text', value: ' more text', @@ -266,7 +284,7 @@ describe('katex plugin', () => { plugin(tree); const paragraph = tree.children[0] as Paragraph; - const mathNode = paragraph.children[1] as any; + const mathNode = paragraph.children[1] as Html; expect(mathNode.type).toBe('html'); expect(mathNode.value).toContain('katex'); @@ -313,7 +331,7 @@ describe('katex plugin', () => { type: 'inlineMath', value: 'x^2', data: {}, - } as any, + } as unknown as InlineMath, ], }; @@ -324,7 +342,7 @@ describe('katex plugin', () => { const paragraph = tree.children[0] as Paragraph; expect(paragraph.type).toBe('paragraph'); expect(paragraph.children[0].type).toBe('text'); - expect((paragraph.children[0] as any).value).toBe('Plain text'); + expect((paragraph.children[0] as Text).value).toBe('Plain text'); // Math node should be transformed const mathNode = tree.children[1] as Html; diff --git a/src/lib/plugins/katex.ts b/src/lib/plugins/katex.ts index 93b2abe..ebaa4e4 100644 --- a/src/lib/plugins/katex.ts +++ b/src/lib/plugins/katex.ts @@ -3,6 +3,16 @@ import type { Root } from 'mdast'; import katex from 'katex'; import { escapeHtml } from '../utils/html.js'; +/** + * Interface for math nodes (from remark-math) + * Represents both inline math and display math nodes + */ +interface MathNode { + type: string; + value: string; + data?: unknown; +} + /** * Configuration options for math rendering with KaTeX * @@ -168,7 +178,7 @@ export function katexPlugin(options: KaTeXOptions = {}): (tree: Root) => void { // Transform each math node to HTML mathNodes.forEach(({ node }) => { // Type assertion needed for accessing node properties - const n = node as any; + const n = node as MathNode; const latex = n.value; const displayMode = n.type === 'math'; // 'math' = display (block), 'inlineMath' = inline diff --git a/src/lib/plugins/mermaid.ts b/src/lib/plugins/mermaid.ts index 71f5dcd..3243982 100644 --- a/src/lib/plugins/mermaid.ts +++ b/src/lib/plugins/mermaid.ts @@ -2,6 +2,15 @@ import { visit } from 'unist-util-visit'; import type { Root, Code } from 'mdast'; import { encodeBase64 } from '../utils/base64.js'; +/** + * Interface for mutating AST nodes during transformation + * Used when transforming Code nodes to Html nodes + */ +interface MutableCodeNode { + type: string; + value?: string; +} + /** * MDsveX/Remark plugin to transform Mermaid code blocks into renderable diagrams * @@ -29,8 +38,9 @@ export function mermaidPlugin(): (tree: Root) => void { // Transform to HTML div that will be hydrated client-side // Type assertion needed as we're transforming from Code to HTML node - (node as any).type = 'html'; - (node as any).value = `
`; + const mutableNode = node as MutableCodeNode; + mutableNode.type = 'html'; + mutableNode.value = `
`; }); }; } diff --git a/src/lib/plugins/reference.test.ts b/src/lib/plugins/reference.test.ts index 1d03d8c..d02b912 100644 --- a/src/lib/plugins/reference.test.ts +++ b/src/lib/plugins/reference.test.ts @@ -1,10 +1,19 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { referencePlugin } from './reference'; -import type { Root } from 'mdast'; +import type { Root, Paragraph, Html, Link, Text, RootContent } from 'mdast'; import * as symbolResolver from '../utils/symbol-resolver.js'; import * as symbolRenderer from '../utils/symbol-renderer.js'; +/** + * Container directive node type (from remark-directive) + */ +interface ContainerDirective { + type: 'containerDirective'; + name: string; + attributes?: Record; + children: RootContent[]; +} + describe('reference plugin', () => { // Mock symbol map and related functions beforeEach(() => { @@ -54,16 +63,16 @@ describe('reference plugin', () => { type: 'root', children: [ { - type: 'containerDirective' as any, + type: 'containerDirective', name: 'reference', attributes, children: [ { type: 'paragraph', children: [{ type: 'text', value: symbolName }], - } as any, + } as Paragraph, ], - } as any, + } as unknown as ContainerDirective, ], }); @@ -83,15 +92,15 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const paragraph = tree.children[0] as any; + const paragraph = tree.children[0] as Paragraph; expect(paragraph.children.length).toBe(3); expect(paragraph.children[0].type).toBe('text'); - expect(paragraph.children[0].value).toBe('Check out '); + expect((paragraph.children[0] as Text).value).toBe('Check out '); expect(paragraph.children[1].type).toBe('link'); - expect(paragraph.children[1].url).toContain('github.com'); - expect(paragraph.children[1].children[0].value).toBe('MyFunction'); + expect((paragraph.children[1] as Link).url).toContain('github.com'); + expect(((paragraph.children[1] as Link).children[0] as Text).value).toBe('MyFunction'); expect(paragraph.children[2].type).toBe('text'); - expect(paragraph.children[2].value).toBe(' for more info'); + expect((paragraph.children[2] as Text).value).toBe(' for more info'); }); it('should handle multiple inline references in one text node', () => { @@ -107,13 +116,13 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const paragraph = tree.children[0] as any; + const paragraph = tree.children[0] as Paragraph; expect(paragraph.children.length).toBe(5); - expect(paragraph.children[0].value).toBe('Use '); + expect((paragraph.children[0] as Text).value).toBe('Use '); expect(paragraph.children[1].type).toBe('link'); - expect(paragraph.children[2].value).toBe(' with '); + expect((paragraph.children[2] as Text).value).toBe(' with '); expect(paragraph.children[3].type).toBe('link'); - expect(paragraph.children[4].value).toBe(' for best results'); + expect((paragraph.children[4] as Text).value).toBe(' for best results'); }); it('should create warning node for unresolved inline reference', () => { @@ -128,11 +137,11 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const paragraph = tree.children[0] as any; + const paragraph = tree.children[0] as Paragraph; expect(paragraph.children.length).toBe(2); expect(paragraph.children[1].type).toBe('html'); - expect(paragraph.children[1].value).toContain('symbol-ref-error'); - expect(paragraph.children[1].value).toContain('UnknownSymbol'); + expect((paragraph.children[1] as Html).value).toContain('symbol-ref-error'); + expect((paragraph.children[1] as Html).value).toContain('UnknownSymbol'); expect(consoleWarnSpy).toHaveBeenCalled(); consoleWarnSpy.mockRestore(); @@ -150,9 +159,9 @@ describe('reference plugin', () => { plugin(tree); // Tree should be unchanged - const paragraph = tree.children[0] as any; + const paragraph = tree.children[0] as Paragraph; expect(paragraph.children.length).toBe(1); - expect(paragraph.children[0].value).toBe('Check {@MyFunction}'); + expect((paragraph.children[0] as Text).value).toBe('Check {@MyFunction}'); expect(consoleWarnSpy).toHaveBeenCalledWith( 'Symbol map not loaded:', expect.stringContaining('Symbol map not found') @@ -168,9 +177,9 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const paragraph = tree.children[0] as any; + const paragraph = tree.children[0] as Paragraph; expect(paragraph.children.length).toBe(1); - expect(paragraph.children[0].value).toBe('Regular text without references'); + expect((paragraph.children[0] as Text).value).toBe('Regular text without references'); }); }); @@ -190,12 +199,10 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const htmlNode = tree.children[0] as any; + const htmlNode = tree.children[0] as Html; expect(htmlNode.type).toBe('html'); expect(htmlNode.value).toContain('symbol-block'); expect(htmlNode.value).toContain('MyFunction docs'); - expect(htmlNode.children).toBeUndefined(); - expect(htmlNode.name).toBeUndefined(); }); it('should pass render options from attributes', () => { @@ -234,7 +241,7 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const htmlNode = tree.children[0] as any; + const htmlNode = tree.children[0] as Html; expect(htmlNode.type).toBe('html'); expect(htmlNode.value).toContain('symbol-ref-block-error'); expect(htmlNode.value).toContain('UnknownSymbol'); @@ -258,9 +265,9 @@ describe('reference plugin', () => { const plugin = referencePlugin(); plugin(tree); - const paragraph = tree.children[0] as any; - expect(paragraph.children[1].value).not.toContain('