Compare commits
3 Commits
b4c6915021
...
2e692f7264
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e692f7264 | |||
| 803da33f37 | |||
| b5635597a8 |
53
app/bun.lock
53
app/bun.lock
@ -12,7 +12,11 @@
|
||||
"@universal-middleware/fastify": "^0.5.16",
|
||||
"clsx": "^2.1.1",
|
||||
"fastify": "^5.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"reading-time-estimator": "^1.14.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-heading-id": "^1.0.1",
|
||||
"solid-heroicons": "^3.2.4",
|
||||
"solid-js": "^1.9.5",
|
||||
"solid-jsx": "^1.1.4",
|
||||
@ -20,6 +24,7 @@
|
||||
"solid-toast": "^0.5.0",
|
||||
"telefunc": "^0.2.3",
|
||||
"terracotta": "^1.0.6",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vike": "^0.4.228",
|
||||
"vike-solid": "^0.7.9",
|
||||
"vite-plugin-prismjs": "^0.0.11",
|
||||
@ -28,8 +33,10 @@
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@eslint/js": "^9.24.0",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^18.19.86",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/remark-heading-id": "^1.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
@ -329,6 +336,8 @@
|
||||
|
||||
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||
|
||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||
@ -341,6 +350,8 @@
|
||||
|
||||
"@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="],
|
||||
|
||||
"@types/remark-heading-id": ["@types/remark-heading-id@1.0.0", "", { "dependencies": { "unified": "^11.0.0" } }, "sha512-V6OgBN2Uv3kaYHOrBI2+j9xIo6N56bMpIFoKVkGltoJtzHr7Vo8pFxDZxNqUXC5NScV991Iq3BYD52BkCFMY+w=="],
|
||||
|
||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.30.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.30.1", "@typescript-eslint/type-utils": "8.30.1", "@typescript-eslint/utils": "8.30.1", "@typescript-eslint/visitor-keys": "8.30.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q=="],
|
||||
@ -461,6 +472,8 @@
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
@ -469,6 +482,14 @@
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
|
||||
"domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="],
|
||||
|
||||
"domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.139", "", {}, "sha512-GGnRYOTdN5LYpwbIr0rwP/ZHOQSvAF6TG0LSzp28uCBb9JiXHJGmaaKw29qjNJc5bGnnp6kXJqRnGMQoELwi5w=="],
|
||||
@ -559,6 +580,8 @@
|
||||
|
||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||
|
||||
"fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="],
|
||||
|
||||
"fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
@ -575,6 +598,8 @@
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
@ -601,6 +626,8 @@
|
||||
|
||||
"html-tags": ["html-tags@3.3.1", "", {}, "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ=="],
|
||||
|
||||
"htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
@ -637,6 +664,8 @@
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
|
||||
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
|
||||
|
||||
"is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="],
|
||||
|
||||
"isbot-fast": ["isbot-fast@1.2.0", "", {}, "sha512-twjuQzy2gKMDVfKGQyQqrx6Uy4opu/fiVUTTpdqtFsd7OQijIp5oXvb27n5EemYXaijh5fomndJt/SPRLsEdSg=="],
|
||||
@ -697,6 +726,8 @@
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="],
|
||||
|
||||
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
|
||||
@ -713,6 +744,8 @@
|
||||
|
||||
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="],
|
||||
|
||||
"mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="],
|
||||
|
||||
"mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="],
|
||||
|
||||
"mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="],
|
||||
@ -737,6 +770,8 @@
|
||||
|
||||
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
|
||||
|
||||
"micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="],
|
||||
|
||||
"micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="],
|
||||
|
||||
"micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="],
|
||||
@ -823,6 +858,8 @@
|
||||
|
||||
"parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
|
||||
|
||||
"parse-srcset": ["parse-srcset@1.0.2", "", {}, "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="],
|
||||
|
||||
"parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="],
|
||||
|
||||
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
|
||||
@ -869,6 +906,8 @@
|
||||
|
||||
"raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="],
|
||||
|
||||
"reading-time-estimator": ["reading-time-estimator@1.14.0", "", { "dependencies": { "sanitize-html": "^2.15.0" } }, "sha512-EWLj21Ou07uUvZWE0suAGPvEebhp91ZABl8jhTzZXY/ziBOPXfQ4tZ1eHiUV7moQ1NJ1KJj9krWuFlnoMx0upA=="],
|
||||
|
||||
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
|
||||
|
||||
"recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="],
|
||||
@ -883,6 +922,10 @@
|
||||
|
||||
"rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="],
|
||||
|
||||
"remark-frontmatter": ["remark-frontmatter@5.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-frontmatter": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0", "unified": "^11.0.0" } }, "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ=="],
|
||||
|
||||
"remark-heading-id": ["remark-heading-id@1.0.1", "", { "dependencies": { "lodash": "^4.17.21", "unist-util-visit": "^1.4.0" } }, "sha512-GmJjuCeEkYvwFlvn/Skjc/1Qafj71412gbQnrwUmP/tKskmAf1cMRlZRNoovV+aIvsSRkTb2rCmGv2b9RdoJbQ=="],
|
||||
|
||||
"remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="],
|
||||
|
||||
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
|
||||
@ -911,6 +954,8 @@
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"sanitize-html": ["sanitize-html@2.16.0", "", { "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", "htmlparser2": "^8.0.0", "is-plain-object": "^5.0.0", "parse-srcset": "^1.0.2", "postcss": "^8.3.11" } }, "sha512-0s4caLuHHaZFVxFTG74oW91+j6vW7gKbGD6CD2+miP73CE6z6YtOBN0ArtLd2UGyi4IC7K47v3ENUbQX4jV3Mg=="],
|
||||
|
||||
"secure-json-parse": ["secure-json-parse@4.0.0", "", {}, "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA=="],
|
||||
|
||||
"semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
@ -1129,12 +1174,16 @@
|
||||
|
||||
"light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
|
||||
|
||||
"mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||
|
||||
"pino/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
|
||||
|
||||
"remark-heading-id/unist-util-visit": ["unist-util-visit@1.4.1", "", { "dependencies": { "unist-util-visit-parents": "^2.0.0" } }, "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw=="],
|
||||
|
||||
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
@ -1163,6 +1212,8 @@
|
||||
|
||||
"glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
"remark-heading-id/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@2.1.2", "", { "dependencies": { "unist-util-is": "^3.0.0" } }, "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"vike/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
|
||||
@ -1218,5 +1269,7 @@
|
||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"remark-heading-id/unist-util-visit/unist-util-visit-parents/unist-util-is": ["unist-util-is@3.0.0", "", {}, "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A=="],
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import type { readingTime } from "reading-time-estimator";
|
||||
import type { TableOfContents } from "./remarkHeadingId";
|
||||
|
||||
import { createHandler } from "@universal-middleware/fastify";
|
||||
import { telefuncHandler } from "./server/telefunc-handler";
|
||||
import { vikeHandler } from "./server/vike-handler";
|
||||
@ -11,6 +14,23 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const root = __dirname;
|
||||
|
||||
declare global {
|
||||
namespace Vike {
|
||||
interface PageContext {
|
||||
exports: {
|
||||
frontmatter?: Partial<{
|
||||
title: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
}>;
|
||||
readingTime?: ReturnType<typeof readingTime>;
|
||||
tableOfContents?: TableOfContents;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function startServer() {
|
||||
const app = Fastify();
|
||||
|
||||
|
||||
@ -1,38 +1,42 @@
|
||||
import type { JSXElement } from "solid-js";
|
||||
|
||||
import { TableOfContents } from "@/partials/TableOfContents";
|
||||
import { PrevNextLinks } from "@/components/PrevNextLinks";
|
||||
import { usePageContext } from "vike-solid/usePageContext";
|
||||
import { collectSections } from "@/libs/sections";
|
||||
import { clientOnly } from "vike-solid/clientOnly";
|
||||
import { clock } from "solid-heroicons/outline";
|
||||
import { navigation } from "@/libs/navigation";
|
||||
import { Prose } from "@/components/Prose";
|
||||
import { MDXProvider } from "solid-jsx";
|
||||
import { Icon } from "solid-heroicons";
|
||||
|
||||
type DocsLayoutProps = {
|
||||
children: JSXElement;
|
||||
title?: string;
|
||||
// frontmatter: { title?: string };
|
||||
estimatedReadingTime?: string;
|
||||
// nodes: Array<Node>;
|
||||
};
|
||||
|
||||
const TableOfContents = clientOnly(() =>
|
||||
import("@/partials/TableOfContents").then((m) => m.TableOfContents),
|
||||
);
|
||||
|
||||
export function DocsLayout(props: DocsLayoutProps) {
|
||||
// const tableOfContents = collectSections(nodes);
|
||||
const {
|
||||
exports: { frontmatter, readingTime },
|
||||
} = usePageContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<MDXProvider components={{}}>
|
||||
<div class="max-w-2xl min-w-0 flex-auto px-4 py-16 lg:max-w-none lg:pr-0 lg:pl-8 xl:px-16 grow">
|
||||
<article>
|
||||
<DocsHeader
|
||||
title={props.title}
|
||||
estimatedReadingTime={props.estimatedReadingTime}
|
||||
title={frontmatter?.title}
|
||||
estimatedReadingTime={readingTime?.text}
|
||||
/>
|
||||
<Prose>{props.children}</Prose>
|
||||
</article>
|
||||
<PrevNextLinks />
|
||||
</div>
|
||||
|
||||
{/* <TableOfContents tableOfContents={tableOfContents} /> */}
|
||||
</>
|
||||
<TableOfContents fallback={null} />
|
||||
</MDXProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -64,11 +68,11 @@ export function DocsHeader(props: DocsHeaderProps) {
|
||||
{props.title}
|
||||
</h1>
|
||||
)}
|
||||
{/* {props.estimatedReadingTime && (
|
||||
<p class="text-sm text-slate-500 inline-flex items-center gap-1">
|
||||
<ClockIcon class="w-4" /> {props.estimatedReadingTime}
|
||||
</p>
|
||||
)} */}
|
||||
{props.estimatedReadingTime && (
|
||||
<p class="text-sm text-slate-500 inline-flex items-center gap-1">
|
||||
<Icon path={clock} class="w-4" /> {props.estimatedReadingTime}
|
||||
</p>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,21 +3,17 @@ import type { JSX, JSXElement } from "solid-js";
|
||||
// import { CookiesContainer } from "@/components/common/Cookies";
|
||||
import { MobileNavigation } from "@/partials/MobileNavigation";
|
||||
import { usePageContext } from "vike-solid/usePageContext";
|
||||
// import { ThemeProvider } from "@/providers/ThemeProvider";
|
||||
// import { clientOnly } from "vike-react/clientOnly";
|
||||
import { createEffect, createSignal } from "solid-js";
|
||||
import { HeroSection } from "@/partials/HeroSection";
|
||||
import { Navigation } from "@/partials/Navigation";
|
||||
import { DocsLayout } from "./DocsLayout";
|
||||
import { Link } from "@/components/Link";
|
||||
import { Logo } from "@/components/Logo";
|
||||
import { Toaster } from "solid-toast";
|
||||
import clsx from "clsx";
|
||||
|
||||
// import "./style.css";
|
||||
import "./tailwind.css";
|
||||
import { DocsLayout } from "./DocsLayout";
|
||||
// import "./prism.css";
|
||||
// import "unfonts.css";
|
||||
|
||||
// const Search = clientOnly(() => import("@/components/Search").then((module) => module.Search));
|
||||
|
||||
|
||||
@ -1,100 +1,6 @@
|
||||
import type { Node } from "@markdoc/markdoc";
|
||||
|
||||
import { slugifyWithCounter } from "@sindresorhus/slugify";
|
||||
|
||||
interface HeadingNode extends Node {
|
||||
type: "heading";
|
||||
attributes: {
|
||||
level: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
id?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
type H2Node = HeadingNode & {
|
||||
attributes: {
|
||||
level: 2;
|
||||
};
|
||||
};
|
||||
|
||||
type H3Node = HeadingNode & {
|
||||
attributes: {
|
||||
level: 3;
|
||||
};
|
||||
};
|
||||
|
||||
function isHeadingNode(node: Node): node is HeadingNode {
|
||||
return (
|
||||
node.type === "heading" &&
|
||||
[1, 2, 3, 4, 5, 6].includes(node.attributes.level) &&
|
||||
(typeof node.attributes.id === "string" ||
|
||||
typeof node.attributes.id === "undefined")
|
||||
);
|
||||
}
|
||||
|
||||
function isH2Node(node: Node): node is H2Node {
|
||||
return isHeadingNode(node) && node.attributes.level === 2;
|
||||
}
|
||||
|
||||
function isH3Node(node: Node): node is H3Node {
|
||||
return isHeadingNode(node) && node.attributes.level === 3;
|
||||
}
|
||||
|
||||
function getNodeText(node: Node) {
|
||||
let text = "";
|
||||
|
||||
for (const child of node.children ?? []) {
|
||||
if (child.type === "text") {
|
||||
text += child.attributes.content;
|
||||
}
|
||||
text += getNodeText(child);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
export type Subsection = H3Node["attributes"] & {
|
||||
export type Section = {
|
||||
id: string;
|
||||
title: string;
|
||||
children?: undefined;
|
||||
level: 2 | 3;
|
||||
path: string;
|
||||
};
|
||||
|
||||
export type Section = H2Node["attributes"] & {
|
||||
id: string;
|
||||
title: string;
|
||||
children: Array<Subsection>;
|
||||
};
|
||||
|
||||
export function collectSections(
|
||||
nodes: Array<Node>,
|
||||
slugify = slugifyWithCounter(),
|
||||
) {
|
||||
const sections: Array<Section> = [];
|
||||
|
||||
for (const node of nodes) {
|
||||
if (isH2Node(node) || isH3Node(node)) {
|
||||
const title = getNodeText(node);
|
||||
if (title) {
|
||||
const id = slugify(title);
|
||||
|
||||
if (isH3Node(node)) {
|
||||
if (!sections[sections.length - 1]) {
|
||||
throw new Error(
|
||||
"Cannot add `h3` to table of contents without a preceding `h2`",
|
||||
);
|
||||
}
|
||||
sections[sections.length - 1].children.push({
|
||||
...node.attributes,
|
||||
id,
|
||||
title,
|
||||
});
|
||||
} else {
|
||||
sections.push({ ...node.attributes, id, title, children: [] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sections.push(...collectSections(node.children ?? [], slugify));
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
@ -17,7 +17,11 @@
|
||||
"@universal-middleware/fastify": "^0.5.16",
|
||||
"clsx": "^2.1.1",
|
||||
"fastify": "^5.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"reading-time-estimator": "^1.14.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-heading-id": "^1.0.1",
|
||||
"solid-heroicons": "^3.2.4",
|
||||
"solid-js": "^1.9.5",
|
||||
"solid-jsx": "^1.1.4",
|
||||
@ -25,6 +29,7 @@
|
||||
"solid-toast": "^0.5.0",
|
||||
"telefunc": "^0.2.3",
|
||||
"terracotta": "^1.0.6",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vike": "^0.4.228",
|
||||
"vike-solid": "^0.7.9",
|
||||
"vite-plugin-prismjs": "^0.0.11"
|
||||
@ -33,8 +38,10 @@
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@eslint/js": "^9.24.0",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^18.19.86",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/remark-heading-id": "^1.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
|
||||
9
app/pages/+data.ts
Normal file
9
app/pages/+data.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { PageContext } from "vike/types";
|
||||
|
||||
export type Data = Awaited<ReturnType<typeof data>>;
|
||||
|
||||
export async function data(pageContext: PageContext) {
|
||||
return {
|
||||
tableOfContents: pageContext.exports.tableOfContents,
|
||||
};
|
||||
}
|
||||
25
app/pages/index/+Page.mdx
Executable file
25
app/pages/index/+Page.mdx
Executable file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Synthèses et ressources pour développeurs
|
||||
description: Plonge toi dans une documentation synthétique et concise, conçue pour les développeurs ou passionnés de l'information en quête de savoir !
|
||||
tags: []
|
||||
---
|
||||
|
||||
Toi qui vient d'arriver sur cette plateforme, sache que si tu cherches un coup de main pour mieux comprendre certaines notions dans le développement, tu es tombé au bon endroit !
|
||||
|
||||
En prime, tu trouveras également des synthèses de certains référentiels de titres professionnels ! 🎉
|
||||
|
||||
## Pourquoi cette plateforme ?
|
||||
|
||||
À l'instar du [MDN Web Docs](https://developer.mozilla.org/fr/) et feu **Le Site du Zéro**, cette plateforme a pour objectif de te fournir des ressources de qualité pour t'aider à te perfectionner dans le développement.
|
||||
|
||||
## Contenu du Memento
|
||||
|
||||
Le contenu de cette plateforme est divisé en plusieurs sections :
|
||||
|
||||
- [**Certifications**](/certifications) : Des synthèses de référentiels des certifications de titres professionnels pour t'aider à te préparer aux examens.
|
||||
- [**Documentations**](/docs) : Une documentation synthétique _(mais complète et détaillée)_ sur les différentes technologies du développement web.
|
||||
|
||||
Les différents contenus ne sont pas uniquement destinées aux développeurs, mais également aux passionnés de l'information en quête de savoir !
|
||||
Il est donc possible que tu trouves des articles qui ne te concernent pas directement, mais qui pourraient t'intéresser tout de même ! 😊
|
||||
|
||||
Par contre je te préviens : certains concepts peuvent être un peu techniques et nécessiter un peu de temps pour être compris.
|
||||
@ -1,11 +0,0 @@
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<h1>My Vike app</h1>
|
||||
This page is:
|
||||
<ul>
|
||||
<li>Rendered to HTMLs.</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import { createSignal } from "solid-js";
|
||||
|
||||
export { Counter };
|
||||
|
||||
function Counter() {
|
||||
const [count, setCount] = createSignal(0);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
class={
|
||||
"inline-block border border-black rounded bg-gray-200 px-2 py-1 text-xs font-medium uppercase leading-normal"
|
||||
}
|
||||
onClick={() => setCount((count) => count + 1)}
|
||||
>
|
||||
Counter {count()}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<h1 class={"font-bold text-3xl pb-4"}>Temp</h1>
|
||||
This page is:
|
||||
<ul>
|
||||
<li>Rendered to HTML.</li>
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,37 +1,39 @@
|
||||
import type { Section, Subsection } from "@/libs/sections";
|
||||
import type { TableOfContents as TableOfContentsType } from "@/remarkHeadingId";
|
||||
import type { Section } from "@/libs/sections";
|
||||
import type { Data } from "@/pages/+data";
|
||||
|
||||
import { createSignal, createEffect, For } from "solid-js";
|
||||
import { useData } from "vike-solid/useData";
|
||||
import { Link } from "@/components/Link";
|
||||
import clsx from "clsx";
|
||||
|
||||
type TableOfContentsProps = {
|
||||
tableOfContents: Array<Section>;
|
||||
};
|
||||
export function TableOfContents() {
|
||||
const { tableOfContents } = useData<Data>();
|
||||
|
||||
if (!tableOfContents) return null;
|
||||
|
||||
export function TableOfContents(props: TableOfContentsProps) {
|
||||
const [currentSection, setCurrentSection] = createSignal(
|
||||
props.tableOfContents[0]?.id,
|
||||
tableOfContents[0]?.id,
|
||||
);
|
||||
|
||||
const getHeadings = (tableOfContents: Array<Section>) => {
|
||||
const getHeadings = () => {
|
||||
return tableOfContents
|
||||
.flatMap((node) => [node.id, ...node.children.map((child) => child.id)])
|
||||
.map((id) => {
|
||||
const el = document.getElementById(id);
|
||||
.map((section) => {
|
||||
const el = document.getElementById(section.id);
|
||||
if (!el) return null;
|
||||
|
||||
const style = window.getComputedStyle(el);
|
||||
const scrollMt = Number.parseFloat(style.scrollMarginTop);
|
||||
|
||||
const top = window.scrollY + el.getBoundingClientRect().top - scrollMt;
|
||||
return { id, top };
|
||||
return { id: section.id, top };
|
||||
})
|
||||
.filter((x): x is { id: string; top: number } => x !== null);
|
||||
};
|
||||
|
||||
createEffect(() => {
|
||||
if (props.tableOfContents.length === 0) return;
|
||||
const headings = getHeadings(props.tableOfContents);
|
||||
if (tableOfContents.length === 0) return;
|
||||
const headings = getHeadings();
|
||||
|
||||
function onScroll() {
|
||||
const top = window.scrollY;
|
||||
@ -49,69 +51,46 @@ export function TableOfContents(props: TableOfContentsProps) {
|
||||
return () => {
|
||||
window.removeEventListener("scroll", onScroll);
|
||||
};
|
||||
}, [getHeadings, props.tableOfContents]);
|
||||
}, [getHeadings, tableOfContents]);
|
||||
|
||||
function isActive(section: Section | Subsection) {
|
||||
function isActive(section: Section) {
|
||||
if (section.id === currentSection()) return true;
|
||||
if (!section.children) return false;
|
||||
return false;
|
||||
// if (!section.children) return false;
|
||||
|
||||
return section.children.findIndex(isActive) > -1;
|
||||
// return section.children.findIndex(isActive) > -1;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="hidden xl:sticky xl:top-[4.75rem] xl:-mr-6 xl:block xl:h-[calc(100vh-4.75rem)] xl:flex-none xl:overflow-y-auto xl:py-16 xl:pr-6">
|
||||
<nav aria-labelledby="on-this-page-title" class="w-56">
|
||||
{props.tableOfContents.length > 0 && (
|
||||
<>
|
||||
<h2
|
||||
id="on-this-page-title"
|
||||
class="font-display text-sm font-medium text-slate-900"
|
||||
>
|
||||
Table des matières
|
||||
</h2>
|
||||
<h2
|
||||
id="on-this-page-title"
|
||||
class="font-display text-sm font-medium text-slate-900"
|
||||
>
|
||||
Table des matières
|
||||
</h2>
|
||||
|
||||
<ol class="mt-4 space-y-3 text-sm">
|
||||
<For each={props.tableOfContents}>
|
||||
{(section) => (
|
||||
<li>
|
||||
<h3>
|
||||
<Link
|
||||
href={`#${section.id}`}
|
||||
class={clsx(
|
||||
isActive(section)
|
||||
? "text-violet-500"
|
||||
: "font-normal text-slate-500 hover:text-slate-700",
|
||||
)}
|
||||
>
|
||||
{section.title}
|
||||
</Link>
|
||||
</h3>
|
||||
{section.children.length > 0 && (
|
||||
<ol class="mt-2 space-y-3 pl-5 text-slate-500">
|
||||
<For each={section.children}>
|
||||
{(subSection) => (
|
||||
<li>
|
||||
<Link
|
||||
href={`#${subSection.id}`}
|
||||
class={
|
||||
isActive(subSection)
|
||||
? "text-violet-500"
|
||||
: "hover:text-slate-600"
|
||||
}
|
||||
>
|
||||
{subSection.title}
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ol>
|
||||
<ol class="mt-4 space-y-3 text-sm">
|
||||
<For each={tableOfContents}>
|
||||
{(section) => (
|
||||
<li>
|
||||
<h3>
|
||||
<Link
|
||||
href={`#${section.id}`}
|
||||
class={clsx(
|
||||
isActive(section)
|
||||
? "text-violet-500"
|
||||
: "font-normal text-slate-500 hover:text-slate-700",
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ol>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
{section.title}
|
||||
</Link>
|
||||
</h3>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
|
||||
133
app/remarkExtractFrontmatter.ts
Normal file
133
app/remarkExtractFrontmatter.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import type { Program } from "estree-jsx";
|
||||
import type { Plugin } from "unified";
|
||||
import type { VFile } from "vfile";
|
||||
import type { Root } from "mdast";
|
||||
|
||||
import { readingTime } from "reading-time-estimator";
|
||||
import { visit } from "unist-util-visit";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
export interface Frontmatter {
|
||||
title: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface MDXJSEsm {
|
||||
type: "mdxjsEsm";
|
||||
value: string;
|
||||
data?: {
|
||||
estree?: Program;
|
||||
};
|
||||
}
|
||||
|
||||
interface CustomVFile extends VFile {
|
||||
data: {
|
||||
frontmatter?: Frontmatter;
|
||||
readingTime?: ReturnType<typeof readingTime>;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
const remarkExtractFrontmatter: Plugin<[], Root> =
|
||||
() => (tree: Root, file: CustomVFile) => {
|
||||
visit(tree, "yaml", (node) => {
|
||||
try {
|
||||
const data = (yaml.load(node.value) as Frontmatter) || {};
|
||||
|
||||
const fileContent = file.toString();
|
||||
const estimatedReadingTime = readingTime(fileContent, 300, "fr");
|
||||
|
||||
// Ajout du frontmatter au fichier virtual de remark
|
||||
file.data.frontmatter = data;
|
||||
|
||||
// Ajout du temps de lecture au fichier virtual de remark
|
||||
file.data.readingTime = estimatedReadingTime;
|
||||
|
||||
// Créer un noeud export pour le frontmatter
|
||||
const exportNode: MDXJSEsm = {
|
||||
type: "mdxjsEsm",
|
||||
value: `export const frontmatter = ${JSON.stringify(data)}; export const readingTime = ${JSON.stringify(estimatedReadingTime)};`,
|
||||
data: {
|
||||
estree: {
|
||||
type: "Program",
|
||||
body: [
|
||||
{
|
||||
type: "ExportNamedDeclaration",
|
||||
declaration: {
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [
|
||||
{
|
||||
type: "VariableDeclarator",
|
||||
id: {
|
||||
type: "Identifier",
|
||||
name: "frontmatter",
|
||||
},
|
||||
init: {
|
||||
type: "ObjectExpression",
|
||||
properties: Object.entries(data).map(
|
||||
([key, value]) => ({
|
||||
type: "Property",
|
||||
key: {
|
||||
type: "Identifier",
|
||||
name: key,
|
||||
},
|
||||
value: {
|
||||
type: "Literal",
|
||||
value: value,
|
||||
},
|
||||
kind: "init",
|
||||
computed: false,
|
||||
method: false,
|
||||
shorthand: false,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "VariableDeclarator",
|
||||
id: {
|
||||
type: "Identifier",
|
||||
name: "readingTime",
|
||||
},
|
||||
init: {
|
||||
type: "ObjectExpression",
|
||||
properties: Object.entries(estimatedReadingTime).map(
|
||||
([key, value]) => ({
|
||||
type: "Property",
|
||||
key: {
|
||||
type: "Identifier",
|
||||
name: key,
|
||||
},
|
||||
value: {
|
||||
type: "Literal",
|
||||
value: value,
|
||||
},
|
||||
kind: "init",
|
||||
computed: false,
|
||||
method: false,
|
||||
shorthand: false,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
specifiers: [],
|
||||
source: null,
|
||||
},
|
||||
],
|
||||
sourceType: "module",
|
||||
} as unknown as Program,
|
||||
},
|
||||
};
|
||||
|
||||
tree.children.push(exportNode);
|
||||
} catch (e) {
|
||||
console.error("Error parsing frontmatter:", e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default remarkExtractFrontmatter;
|
||||
192
app/remarkHeadingId.ts
Normal file
192
app/remarkHeadingId.ts
Normal file
@ -0,0 +1,192 @@
|
||||
import type { Heading, PhrasingContent } from "mdast";
|
||||
import type { Section } from "./libs/sections";
|
||||
import type { Program } from "estree-jsx";
|
||||
import type { Plugin } from "unified";
|
||||
import type { Root } from "mdast";
|
||||
|
||||
import { slugifyWithCounter } from "@sindresorhus/slugify";
|
||||
import { visit } from "unist-util-visit";
|
||||
|
||||
type PhrasingContentWithParent = PhrasingContent & {
|
||||
children: PhrasingContent[];
|
||||
};
|
||||
|
||||
export type TableOfContents = Array<Section>;
|
||||
|
||||
interface MDXJSEsm {
|
||||
type: "mdxjsEsm";
|
||||
value: string;
|
||||
data?: {
|
||||
estree?: Program;
|
||||
};
|
||||
}
|
||||
|
||||
const tableOfContents: TableOfContents = [];
|
||||
|
||||
const doesHaveChildren = (child: PhrasingContent): boolean => {
|
||||
return ["delete", "emphasis", "strong", "link", "linkReference"].includes(
|
||||
child.type,
|
||||
);
|
||||
};
|
||||
|
||||
const setNodeId = (node: Heading, id: string) => {
|
||||
if (!node.data) node.data = {};
|
||||
if (!node.data.hProperties) node.data.hProperties = {};
|
||||
node.data.hProperties.id = id;
|
||||
};
|
||||
|
||||
const extractText = (children: PhrasingContent[]): string => {
|
||||
return children
|
||||
.map((child) => {
|
||||
if (child.type === "text" && child.value.length > 0) {
|
||||
return child.value;
|
||||
}
|
||||
|
||||
if (
|
||||
doesHaveChildren(child) &&
|
||||
(child as PhrasingContentWithParent).children.length > 0
|
||||
) {
|
||||
return extractText((child as PhrasingContentWithParent).children);
|
||||
}
|
||||
|
||||
return "";
|
||||
})
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
const formatExportNode = (): MDXJSEsm => {
|
||||
return {
|
||||
type: "mdxjsEsm",
|
||||
value: `export const tableOfContents = ${JSON.stringify(tableOfContents)};`,
|
||||
data: {
|
||||
estree: {
|
||||
type: "Program",
|
||||
body: [
|
||||
{
|
||||
type: "ExportNamedDeclaration",
|
||||
declaration: {
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [
|
||||
{
|
||||
type: "VariableDeclarator",
|
||||
id: {
|
||||
type: "Identifier",
|
||||
name: "tableOfContents",
|
||||
},
|
||||
init: {
|
||||
type: "ArrayExpression",
|
||||
elements: tableOfContents.map((section) => ({
|
||||
type: "ObjectExpression",
|
||||
properties: [
|
||||
{
|
||||
type: "Property",
|
||||
key: {
|
||||
type: "Identifier",
|
||||
name: "id",
|
||||
},
|
||||
value: {
|
||||
type: "Literal",
|
||||
value: section.id,
|
||||
},
|
||||
kind: "init",
|
||||
computed: false,
|
||||
method: false,
|
||||
shorthand: false,
|
||||
},
|
||||
{
|
||||
type: "Property",
|
||||
key: {
|
||||
type: "Identifier",
|
||||
name: "title",
|
||||
},
|
||||
value: {
|
||||
type: "Literal",
|
||||
value: section.title,
|
||||
},
|
||||
kind: "init",
|
||||
computed: false,
|
||||
method: false,
|
||||
shorthand: false,
|
||||
},
|
||||
{
|
||||
type: "Property",
|
||||
key: {
|
||||
type: "Identifier",
|
||||
name: "level",
|
||||
},
|
||||
value: {
|
||||
type: "Literal",
|
||||
value: section.level,
|
||||
},
|
||||
kind: "init",
|
||||
computed: false,
|
||||
method: false,
|
||||
shorthand: false,
|
||||
},
|
||||
{
|
||||
type: "Property",
|
||||
key: {
|
||||
type: "Identifier",
|
||||
name: "path",
|
||||
},
|
||||
value: {
|
||||
type: "Literal",
|
||||
value: section.path,
|
||||
},
|
||||
kind: "init",
|
||||
computed: false,
|
||||
method: false,
|
||||
shorthand: false,
|
||||
},
|
||||
],
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
specifiers: [],
|
||||
source: null,
|
||||
},
|
||||
],
|
||||
sourceType: "module",
|
||||
} as unknown as Program,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const remarkHeadingId: Plugin<[], Root> = () => (tree: Root, file) => {
|
||||
const slugify = slugifyWithCounter();
|
||||
|
||||
visit(tree, "heading", (node) => {
|
||||
const lastChild = node.children[node.children.length - 1];
|
||||
|
||||
const filePath = file.path;
|
||||
console.log(`File path: ${filePath}`);
|
||||
|
||||
if (lastChild && lastChild.type === "text") {
|
||||
const string = lastChild.value.replace(/ +$/, "");
|
||||
const matched = string.match(/ {#(.*?)}$/);
|
||||
|
||||
if (matched) return;
|
||||
}
|
||||
|
||||
const slug = slugify(extractText(node.children));
|
||||
setNodeId(node, slug);
|
||||
|
||||
const depth = node.depth as 2 | 3;
|
||||
if (depth > 3) return;
|
||||
|
||||
tableOfContents.push({
|
||||
id: slug,
|
||||
title: extractText(node.children),
|
||||
level: depth,
|
||||
path: filePath,
|
||||
});
|
||||
});
|
||||
|
||||
const exportNode = formatExportNode();
|
||||
tree.children.push(exportNode);
|
||||
};
|
||||
|
||||
export default remarkHeadingId;
|
||||
@ -1,4 +1,7 @@
|
||||
import remarkExtractFrontmatter from "./remarkExtractFrontmatter";
|
||||
import prismjsVitePlugin from "vite-plugin-prismjs";
|
||||
import remarkFrontmatter from "remark-frontmatter";
|
||||
import remarkHeadingId from "./remarkHeadingId";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { telefunc } from "telefunc/vite";
|
||||
import vikeSolid from "vike-solid/vite";
|
||||
@ -25,7 +28,15 @@ export default defineConfig({
|
||||
}),
|
||||
vike(),
|
||||
vikeSolid(),
|
||||
mdx({ jsxImportSource: "solid-jsx", providerImportSource: "solid-mdx" }),
|
||||
mdx({
|
||||
jsxImportSource: "solid-jsx",
|
||||
providerImportSource: "solid-mdx",
|
||||
remarkPlugins: [
|
||||
remarkFrontmatter,
|
||||
remarkHeadingId,
|
||||
remarkExtractFrontmatter,
|
||||
],
|
||||
}),
|
||||
tailwindcss(),
|
||||
telefunc(),
|
||||
],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user