diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 0b75758..ed6f93f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,31 +1,40 @@ -/** @type { import("eslint").Linter.Config } */ +/** + * @type {import('eslint').Linter.Config} + */ module.exports = { root: true, extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:svelte/recommended', - 'prettier' + 'prettier', ], parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], parserOptions: { sourceType: 'module', ecmaVersion: 2020, - extraFileExtensions: ['.svelte'] + extraFileExtensions: ['.svelte'], }, env: { browser: true, es2017: true, - node: true + node: true, }, overrides: [ { files: ['*.svelte'], parser: 'svelte-eslint-parser', parserOptions: { - parser: '@typescript-eslint/parser' - } - } - ] + parser: '@typescript-eslint/parser', + }, + }, + ], + rules: { + 'curly': ['error', 'all'], + '@typescript-eslint/no-unused-vars': [ + 'error', + { ignoreRestSiblings: true, destructuredArrayIgnorePattern: '^_' }, + ], + }, }; diff --git a/.prettierrc b/.prettierrc index 9573023..b266063 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,36 @@ { "useTabs": true, + "tabWidth": 2, + "semi": true, "singleQuote": true, - "trailingComma": "none", + "trailingComma": "es5", "printWidth": 100, - "plugins": ["prettier-plugin-svelte"], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] -} + "proseWrap": "always", + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always", + "svelteIndentScriptAndStyle": true, + "svelteAllowShorthand": true, + "jsdocDescriptionWithDot": true, + "jsdocPreferCodeFences": true, + "jsdocCommentLineStrategy": "multiline", + "tsdoc": true, + "quoteProps": "consistent", + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "ignore", + "plugins": [ + "prettier-plugin-svelte", + "./node_modules/prettier-plugin-jsdoc/dist/index.js" + ], + "pluginSearchDirs": [ + "." + ], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 407243a..d0d5903 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@inlang/paraglide-js": "1.1.0", "@inlang/paraglide-js-adapter-vite": "^1.2.0", "@sveltejs/adapter-static": "^3.0.1", - "@sveltejs/kit": "^2.0.6", + "@sveltejs/kit": "^2.0.7", "@sveltejs/vite-plugin-svelte": "^3.0.1", "@types/eslint": "8.56.0", "@typescript-eslint/eslint-plugin": "^6.18.0", @@ -25,6 +25,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.35.1", "prettier": "^3.1.1", + "prettier-plugin-jsdoc": "^1.3.0", "prettier-plugin-svelte": "^3.1.2", "svelte": "5.0.0-next.29", "svelte-check": "^3.6.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9aa9c87..5e80e5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,10 +13,10 @@ devDependencies: version: 1.2.0(@sinclair/typebox@0.31.28) '@sveltejs/adapter-static': specifier: ^3.0.1 - version: 3.0.1(@sveltejs/kit@2.0.6) + version: 3.0.1(@sveltejs/kit@2.0.7) '@sveltejs/kit': - specifier: ^2.0.6 - version: 2.0.6(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@5.0.0-next.29)(vite@5.0.11) + specifier: ^2.0.7 + version: 2.0.7(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@5.0.0-next.29)(vite@5.0.11) '@sveltejs/vite-plugin-svelte': specifier: ^3.0.1 version: 3.0.1(svelte@5.0.0-next.29)(vite@5.0.11) @@ -41,6 +41,9 @@ devDependencies: prettier: specifier: ^3.1.1 version: 3.1.1 + prettier-plugin-jsdoc: + specifier: ^1.3.0 + version: 1.3.0(prettier@3.1.1) prettier-plugin-svelte: specifier: ^3.1.2 version: 3.1.2(prettier@3.1.1)(svelte@5.0.0-next.29) @@ -907,16 +910,16 @@ packages: resolution: {integrity: sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==} dev: true - /@sveltejs/adapter-static@3.0.1(@sveltejs/kit@2.0.6): + /@sveltejs/adapter-static@3.0.1(@sveltejs/kit@2.0.7): resolution: {integrity: sha512-6lMvf7xYEJ+oGeR5L8DFJJrowkefTK6ZgA4JiMqoClMkKq0s6yvsd3FZfCFvX1fQ0tpCD7fkuRVHsnUVgsHyNg==} peerDependencies: '@sveltejs/kit': ^2.0.0 dependencies: - '@sveltejs/kit': 2.0.6(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@5.0.0-next.29)(vite@5.0.11) + '@sveltejs/kit': 2.0.7(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@5.0.0-next.29)(vite@5.0.11) dev: true - /@sveltejs/kit@2.0.6(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@5.0.0-next.29)(vite@5.0.11): - resolution: {integrity: sha512-dnHtyjBLGXx+hrZQ9GuqLlSfTBixewJaByUVWai7LmB4dgV3FwkK155OltEgONDQW6KW64hLNS/uojdx3uC2/g==} + /@sveltejs/kit@2.0.7(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@5.0.0-next.29)(vite@5.0.11): + resolution: {integrity: sha512-qLoPLqdyMKASxAMFuMrv86l81YKhdioXLsa87+QFv2Rmh0GroFXfSIJMpxI4bHfVt8oXRa71G0/lXhJ/2wTPxQ==} engines: {node: '>=18.13'} hasBin: true requiresBuild: true @@ -989,6 +992,12 @@ packages: resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} dev: true + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + dependencies: + '@types/ms': 0.7.34 + dev: true + /@types/eslint@8.56.0: resolution: {integrity: sha512-FlsN0p4FhuYRjIxpbdXovvHQhtlG05O1GG/RNWvdAxTboR438IOTwmrY/vLA+Xfgg06BTkP045M3vpFwTMv1dg==} dependencies: @@ -1010,6 +1019,16 @@ packages: '@types/node': 20.10.7 dev: true + /@types/mdast@4.0.3: + resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + dependencies: + '@types/unist': 3.0.2 + dev: true + + /@types/ms@0.7.34: + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + dev: true + /@types/node@20.10.7: resolution: {integrity: sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==} dependencies: @@ -1024,6 +1043,10 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true + /@types/unist@3.0.2: + resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} + dev: true + /@typescript-eslint/eslint-plugin@6.18.0(@typescript-eslint/parser@6.18.0)(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-3lqEvQUdCozi6d1mddWqd+kf8KxmGq2Plzx36BlkjuQe3rSTm/O98cLf0A4uDO+a5N1KD2SeEEl6fW97YHY+6w==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1271,6 +1294,10 @@ packages: engines: {node: '>=8'} dev: true + /binary-searching@2.0.5: + resolution: {integrity: sha512-v4N2l3RxL+m4zDxyxz3Ne2aTmiPn8ZUpKFpdPtO+ItW1NcTCXA7JeHG5GMBSvoKSkQZ9ycS+EouDVxYB9ufKWA==} + dev: true + /bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} dev: true @@ -1320,6 +1347,10 @@ packages: supports-color: 7.2.0 dev: true + /character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + dev: true + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -1367,6 +1398,11 @@ packages: engines: {node: '>=16'} dev: true + /comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + dev: true + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true @@ -1418,6 +1454,12 @@ packages: ms: 2.1.2 dev: true + /decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + dependencies: + character-entities: 2.0.2 + dev: true + /decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -1471,6 +1513,12 @@ packages: resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} dev: true + /devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dependencies: + dequal: 2.0.3 + dev: true + /diff3@0.0.3: resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==} dev: true @@ -2088,11 +2136,211 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /mdast-util-from-markdown@2.0.0: + resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} + dependencies: + '@types/mdast': 4.0.3 + '@types/unist': 3.0.2 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + 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 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + dependencies: + '@types/mdast': 4.0.3 + dev: true + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} dev: true + /micromark-core-commonmark@2.0.0: + resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==} + dependencies: + decode-named-character-reference: 1.0.2 + devlop: 1.1.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.1 + 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 + dev: true + + /micromark-factory-destination@2.0.0: + resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-label@2.0.0: + resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-space@2.0.0: + resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-title@2.0.0: + resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-factory-whitespace@2.0.0: + resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} + dependencies: + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-character@2.0.1: + resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==} + dependencies: + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-chunked@2.0.0: + resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-classify-character@2.0.0: + resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-combine-extensions@2.0.0: + resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==} + dependencies: + micromark-util-chunked: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-decode-numeric-character-reference@2.0.1: + resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-decode-string@2.0.0: + resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==} + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-encode@2.0.0: + resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + dev: true + + /micromark-util-html-tag-name@2.0.0: + resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} + dev: true + + /micromark-util-normalize-identifier@2.0.0: + resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} + dependencies: + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-resolve-all@2.0.0: + resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} + dependencies: + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-sanitize-uri@2.0.0: + resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + dependencies: + micromark-util-character: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-symbol: 2.0.0 + dev: true + + /micromark-util-subtokenize@2.0.0: + resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==} + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + dev: true + + /micromark-util-symbol@2.0.0: + resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + dev: true + + /micromark-util-types@2.0.0: + resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + dev: true + + /micromark@4.0.0: + resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.4 + decode-named-character-reference: 1.0.2 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.0 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.0.1 + micromark-util-chunked: 2.0.0 + micromark-util-combine-extensions: 2.0.0 + micromark-util-decode-numeric-character-reference: 2.0.1 + micromark-util-encode: 2.0.0 + micromark-util-normalize-identifier: 2.0.0 + micromark-util-resolve-all: 2.0.0 + micromark-util-sanitize-uri: 2.0.0 + micromark-util-subtokenize: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -2346,6 +2594,20 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier-plugin-jsdoc@1.3.0(prettier@3.1.1): + resolution: {integrity: sha512-cQm8xIa0fN9ieJFMXACQd6JPycl+8ouOijAqUqu44EF/s4fXL3Wi9sKXuEaodsEWgCN42Xby/bNhqgM1iWx4uw==} + engines: {node: '>=14.13.1 || >=16.0.0'} + peerDependencies: + prettier: ^3.0.0 + dependencies: + binary-searching: 2.0.5 + comment-parser: 1.4.1 + mdast-util-from-markdown: 2.0.0 + prettier: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + /prettier-plugin-svelte@3.1.2(prettier@3.1.1)(svelte@5.0.0-next.29): resolution: {integrity: sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==} peerDependencies: @@ -2773,6 +3035,12 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true + /unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + dependencies: + '@types/unist': 3.0.2 + dev: true + /universal-github-app-jwt@1.1.2: resolution: {integrity: sha512-t1iB2FmLFE+yyJY9+3wMx0ejB+MQpEVkH0gQv7dR6FZyltyq+ZZO0uDpbopxhrZ3SLEO4dCEkIujOMldEQ2iOA==} dependencies: diff --git a/src/app.d.ts b/src/app.d.ts index 743f07b..650582d 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,10 +1,17 @@ // See https://kit.svelte.dev/docs/types#app + +import type { AvailableLanguageTag } from '$translations/runtime'; + // for information about these interfaces declare global { namespace App { // interface Error {} - // interface Locals {} - // interface PageData {} + interface Locals { + lang: AvailableLanguageTag; + } + interface PageData { + lang: AvailableLanguageTag; + } // interface PageState {} // interface Platform {} } diff --git a/src/app.html b/src/app.html index 77a5ff5..817dac3 100644 --- a/src/app.html +++ b/src/app.html @@ -1,5 +1,5 @@ - + diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..75ec060 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,4 @@ +import handleLang from '$lib/i18n/handle.server'; +import { sequence } from '@sveltejs/kit/hooks'; + +export const handle = sequence(handleLang); diff --git a/src/lib/i18n/event.ts b/src/lib/i18n/event.ts new file mode 100644 index 0000000..fb04cb0 --- /dev/null +++ b/src/lib/i18n/event.ts @@ -0,0 +1,12 @@ +import { isAvailableLanguageTag, sourceLanguageTag } from '$translations/runtime'; +import type { LoadEvent, RequestEvent, ServerLoadEvent } from '@sveltejs/kit'; + +/** + * Consistently retrieve an event's lang to ensure symmetrical percolation across SSR and client. + */ +export function getEventLang(event: E) { + if (isAvailableLanguageTag(event.params.lang)) { + return event.params.lang; + } + return sourceLanguageTag; +} diff --git a/src/lib/i18n/handle.server.ts b/src/lib/i18n/handle.server.ts new file mode 100644 index 0000000..215788d --- /dev/null +++ b/src/lib/i18n/handle.server.ts @@ -0,0 +1,31 @@ +import { setLanguageTag } from '$translations/runtime'; +import { isRedirect, type Handle } from '@sveltejs/kit'; +import { getEventLang } from './event'; +import { link } from './link'; + +/** + * Handle hook for: + * - setting SSR lang + * - transforming html lang attribute placeholder + * - rewriting unlocalized SSR redirects lang segment in location + */ +const handle = (async ({ event, resolve }) => { + event.locals.lang = getEventLang(event); + setLanguageTag(() => event.locals.lang); + const response = await resolve(event, { + transformPageChunk(input) { + return input.html.replace('%lang%', event.locals.lang); + } + }); + if (!isRedirect(response)) { + return response; + } + const location = response.headers.get('location'); + if (!location || !location.startsWith('/')) { + return response; + } + response.headers.set('location', link(location, event.locals.lang)); + return response; +}) satisfies Handle; + +export default handle; diff --git a/src/lib/i18n/link.ts b/src/lib/i18n/link.ts index d6eff3e..6827df6 100644 --- a/src/lib/i18n/link.ts +++ b/src/lib/i18n/link.ts @@ -1,3 +1,38 @@ -import type { AvailableLanguageTag } from '$messages/runtime'; +import { + availableLanguageTags, + languageTag, + sourceLanguageTag, + type AvailableLanguageTag, +} from '$translations/runtime'; +import type { HTMLAnchorAttributes } from 'svelte/elements'; -export function link

(path: P, lang: AvailableLanguageTag = 'fr') {} +export function delang(location: L) { + const [_, maybeLang, ...rest] = location.split('/'); + if (availableLanguageTags.includes(maybeLang as AvailableLanguageTag)) { + return `/${rest.join('/')}`; + } + return location; +} + +export function link(location: L, lang: AvailableLanguageTag = languageTag()) { + const tail = delang(location); + if (lang === sourceLanguageTag) { + return tail; + } + return `/${lang}${location}`; +} + +export function linkAttributes

( + path: P, + { + lang = languageTag(), + }: { + lang?: AvailableLanguageTag; + } = {} +) { + const href = link(path, lang); + return { + href, + hreflang: lang, + } satisfies Partial; +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index e69de29..a54cfdc 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -0,0 +1,5 @@ + + +{@render children()} diff --git a/src/routes/[[lang=lang]]/+layout.svelte b/src/routes/[[lang=lang]]/+layout.svelte new file mode 100644 index 0000000..1a2a3d8 --- /dev/null +++ b/src/routes/[[lang=lang]]/+layout.svelte @@ -0,0 +1,31 @@ + + + + {#each availableLanguageTags as lang} + + {/each} + + +

+ +
+
+ {languageTag()} + {@render children()} +
+ + + diff --git a/src/routes/[[lang]]/+page.svelte b/src/routes/[[lang=lang]]/+page.svelte similarity index 80% rename from src/routes/[[lang]]/+page.svelte rename to src/routes/[[lang=lang]]/+page.svelte index 5982b0a..00a6809 100644 --- a/src/routes/[[lang]]/+page.svelte +++ b/src/routes/[[lang=lang]]/+page.svelte @@ -1,2 +1,5 @@ + +

Welcome to SvelteKit

Visit kit.svelte.dev to read the documentation

diff --git a/src/routes/[[lang]]/+layout.svelte b/src/routes/[[lang]]/+layout.svelte deleted file mode 100644 index e69de29..0000000