diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index ecc799951bec5..1a4aa5d3efe58 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -1,6 +1,7 @@ name: Deploy Preview on: + # zizmor: ignore[dangerous-triggers] workflow_run: workflows: ["Deploy"] types: [completed] @@ -23,6 +24,9 @@ jobs: timeout-minutes: 10 permissions: pull-requests: write + env: + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" steps: - name: Download PR metadata uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -41,17 +45,17 @@ jobs: - name: Comment deployment in progress uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + PR_NUMBER: ${{ steps.metadata.outputs.pr_number }} + PREVIEW_URL: ${{ steps.metadata.outputs.preview_url }} with: script: | - const prNumber = parseInt('${{ steps.metadata.outputs.pr_number }}', 10); - const previewUrl = '${{ steps.metadata.outputs.preview_url }}'; - const commitSHA = '${{ github.event.workflow_run.head_sha }}'; - const runUrl = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'; + const prNumber = parseInt(process.env.PR_NUMBER, 10); const body = `**Preview deployment**\n\n` + `🔄 Deployment in progress...\n\n` - + `- **Latest commit:** ${commitSHA}\n` - + `- **Preview:** ${previewUrl}\n` - + `- **Workflow run:** [View logs](${runUrl})`; + + `- **Latest commit:** ${process.env.HEAD_SHA}\n` + + `- **Preview:** ${process.env.PREVIEW_URL}\n` + + `- **Workflow run:** [View logs](${process.env.RUN_URL})`; const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, @@ -92,17 +96,17 @@ jobs: - name: Comment deployment complete if: success() uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + PR_NUMBER: ${{ steps.metadata.outputs.pr_number }} + PREVIEW_URL: ${{ steps.metadata.outputs.preview_url }} with: script: | - const prNumber = parseInt('${{ steps.metadata.outputs.pr_number }}', 10); - const previewUrl = '${{ steps.metadata.outputs.preview_url }}'; - const commitSHA = '${{ github.event.workflow_run.head_sha }}'; - const runUrl = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'; + const prNumber = parseInt(process.env.PR_NUMBER, 10); const body = `**Preview deployment**\n\n` + `✅ Deployment complete!\n\n` - + `- **Latest commit:** ${commitSHA}\n` - + `- **Preview:** ${previewUrl}\n` - + `- **Workflow run:** [View logs](${runUrl})`; + + `- **Latest commit:** ${process.env.HEAD_SHA}\n` + + `- **Preview:** ${process.env.PREVIEW_URL}\n` + + `- **Workflow run:** [View logs](${process.env.RUN_URL})`; const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, @@ -121,15 +125,15 @@ jobs: - name: Comment deployment failed if: failure() uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + PR_NUMBER: ${{ steps.metadata.outputs.pr_number }} with: script: | - const prNumber = parseInt('${{ steps.metadata.outputs.pr_number }}', 10); - const commitSHA = '${{ github.event.workflow_run.head_sha }}'; - const runUrl = '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'; + const prNumber = parseInt(process.env.PR_NUMBER, 10); const body = `**Preview deployment**\n\n` + `❌ Deployment failed!\n\n` - + `- **Latest commit:** ${commitSHA}\n` - + `- **Workflow run:** [View logs](${runUrl})`; + + `- **Latest commit:** ${process.env.HEAD_SHA}\n` + + `- **Workflow run:** [View logs](${process.env.RUN_URL})`; const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/lunaria.yml b/.github/workflows/lunaria.yml index f951b3afb88d8..643bcbf9c481d 100644 --- a/.github/workflows/lunaria.yml +++ b/.github/workflows/lunaria.yml @@ -33,6 +33,6 @@ jobs: uses: ./.github/actions/install - name: Generate Lunaria Overview - uses: lunariajs/action@4911ad0736d1e3b20af4cb70f5079aea2327ed8e # astro-docs + uses: lunariajs/action@e5ab09c3c8353fa80786d8c2149873ba3978a3a7 # v0.2.0 with: token: ${{ secrets.FREDKBOT_GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a661afd021a08..efcd78b0134e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -160,7 +160,7 @@ git checkout -b add/partial-hydration-typo-fix ``` ### Opening a PR -One you have made your changes using any of the above methods, you’re ready to create a “Pull Request!” +Once you have made your changes using any of the above methods, you’re ready to create a “Pull Request!” This will let the Astro docs team know you have some changes to propose. At this point we can give you feedback and might request changes. For translations, we like to have at least one other person who knows the language you are translating into review the PR. diff --git a/lunaria.config.ts b/lunaria.config.ts index aaddd51d5dfa2..17741d053b718 100644 --- a/lunaria.config.ts +++ b/lunaria.config.ts @@ -1,3 +1,4 @@ +import { html } from '@lunariajs/core'; import { defineConfig } from '@lunariajs/core/config'; export default defineConfig({ @@ -132,4 +133,43 @@ export default defineConfig({ 'i18nIgnore', ], }, + dashboard: { + title: 'Astro Docs Translation Status', + description: + 'Translation progress tracker for the Astro Docs site. See how much has been translated in your language and get involved!', + site: 'https://i18n.docs.astro.build/', + basesToHide: ['src/content/docs/en/', 'src/i18n/en/', 'src/content/docs/', 'src/content/'], + customCss: ['./scripts/lunaria/styles.css'], + favicon: { + external: [ + { link: 'https://docs.astro.build/favicon.ico', type: 'image/x-icon' }, + { link: 'https://docs.astro.build/favicon.svg', type: 'image/svg+xml' }, + ], + }, + ui: { + 'statusByLocale.heading': 'Translation progress by locale', + 'statusByLocale.outdatedLocalizationLink': 'outdated translation', + 'statusByLocale.incompleteLocalizationLink': 'incomplete translation', + 'statusByLocale.completeLocalization': 'This translation is complete, amazing job! 🎉', + 'statusByFile.heading': 'Translation status by file', + }, + }, + renderer: { + slots: { + head: () => html``, + afterTitle: () => html` +

+ If you're interested in helping us translate + docs.astro.build into one of the languages listed + below, you've come to the right place! This auto-updating page always lists all the + content that could use your help right now. +

+

+ Before starting, please read our + i18n Guide + to learn about our translation process and how you can get involved. +

+ `, + }, + }, }); diff --git a/package.json b/package.json index ec2d650f0790f..7cfdc1e610e2e 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "@astrojs/sitemap": "^3.7.3", "@astrojs/starlight": "^0.42.0", "@expressive-code/plugin-collapsible-sections": "^0.44.1", - "@lunariajs/core": "https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@722c34c", + "@lunariajs/core": "^0.2.0", "canvas-confetti": "^1.6.0", "jsdoc-api": "^9.3.5", "satteri": "^0.10.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23891b363ad0f..0e6cac730c021 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,13 +19,13 @@ importers: version: 3.7.3 '@astrojs/starlight': specifier: ^0.42.0 - version: 0.42.0(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3))(typescript@6.0.3) + version: 0.42.0(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1))(typescript@6.0.3) '@expressive-code/plugin-collapsible-sections': specifier: ^0.44.1 version: 0.44.1 '@lunariajs/core': - specifier: https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@722c34c - version: https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@722c34c + specifier: ^0.2.0 + version: 0.2.0 canvas-confetti: specifier: ^1.6.0 version: 1.6.0 @@ -62,16 +62,16 @@ importers: version: 24.12.2 '@typescript-eslint/parser': specifier: ^8.59.3 - version: 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) + version: 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) astro: specifier: ^7.2.10 - version: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3) + version: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1) astro-eslint-parser: specifier: ^1.4.0 version: 1.4.0 astro-og-canvas: specifier: ^0.11.1 - version: 0.11.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3)) + version: 0.11.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1)) canvaskit-wasm: specifier: ^0.41.1 version: 0.41.1 @@ -83,10 +83,10 @@ importers: version: 5.0.3 eslint: specifier: ^10.3.0 - version: 10.3.0(jiti@2.3.3) + version: 10.3.0(jiti@2.7.0) eslint-plugin-astro: specifier: ^1.6.0 - version: 1.7.0(eslint@10.3.0(jiti@2.3.3)) + version: 1.7.0(eslint@10.3.0(jiti@2.7.0)) fast-glob: specifier: ^3.3.3 version: 3.3.3 @@ -122,7 +122,7 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.59.3 - version: 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) + version: 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) wrangler: specifier: ^4.99.0 version: 4.99.0 @@ -1269,10 +1269,10 @@ packages: '@kwsites/promise-deferred@1.1.1': resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} - '@lunariajs/core@https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@722c34c': - resolution: {tarball: https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@722c34c} - version: 0.1.1 + '@lunariajs/core@0.2.0': + resolution: {integrity: sha512-JuQsDStxiznw4Km0ab/FNH2VxVfWm08oJdjevRUOY7duN6nPKvBkk9rvXD6zPDlvauPWTf9RFuJ3lD4l+o9M4g==} engines: {node: '>=18.17.0'} + hasBin: true '@napi-rs/wasm-runtime@1.2.3': resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} @@ -1483,6 +1483,12 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@simple-git/args-pathspec@1.0.3': + resolution: {integrity: sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==} + + '@simple-git/argv-parser@1.1.1': + resolution: {integrity: sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==} + '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} @@ -1962,6 +1968,10 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + cookie-es@1.2.3: resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} @@ -2112,6 +2122,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2386,6 +2399,10 @@ packages: resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} engines: {node: '>=20.20.0'} + gettext-parser@9.1.1: + resolution: {integrity: sha512-ZLeqWPz9OMNrTgMuww0C22kkcNqis+e4059R94t7L7ERlZ2rUNpiDbaAUus+esBC6uBAQWbS9N+R5vJIJg//lw==} + engines: {node: '>=20'} + github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} @@ -2502,6 +2519,10 @@ packages: typescript: optional: true + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2596,8 +2617,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@2.3.3: - resolution: {integrity: sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true js-yaml@3.14.1: @@ -3089,8 +3110,8 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - p-all@5.0.0: - resolution: {integrity: sha512-pofqu/1FhCVa+78xNAptCGc9V45exFz2pvBRyIvgXkNM0Rh18Py7j8pQuSjA+zpabI46v9hRjNWmL9EAFcEbpw==} + p-all@5.0.1: + resolution: {integrity: sha512-LMT7WX9ZSaq3J1zjloApkIVmtz0ZdMFSIqbuiEa3txGYPLjUPOvgOPOx3nFjo+f37ZYL+1aY666I2SG7GVwLOA==} engines: {node: '>=16'} p-limit@1.3.0: @@ -3206,6 +3227,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -3384,6 +3409,9 @@ packages: s.color@0.0.15: resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass-formatter@0.7.6: resolution: {integrity: sha512-hXdxU6PCkiV3XAiSnX+XLqz2ohHoEnVUlrd8LEVMAI80uB1+OTScIkH9n6qQwImZpTye1r1WG1rbGUteHNhoHg==} @@ -3443,8 +3471,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-git@3.27.0: - resolution: {integrity: sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==} + simple-git@3.36.0: + resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==} sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -4046,6 +4074,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} @@ -4068,12 +4101,12 @@ packages: youch@4.1.0-beta.10: resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.6.2: + resolution: {integrity: sha512-lh5RCAGFa1Cm2hjtNwLQhSs/AsqdWnTQaBER9fEwN/88pSh7KOtJavtBx/0VlkN/uFd61SwYmljLMDAsHlvzBQ==} + zwitch@1.0.5: resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==} @@ -4335,11 +4368,11 @@ snapshots: github-slugger: 2.0.0 satteri: 0.10.5 - '@astrojs/mdx@8.0.0(@astrojs/markdown-satteri@0.4.0)(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3))': + '@astrojs/mdx@8.0.0(@astrojs/markdown-satteri@0.4.0)(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1))': dependencies: '@astrojs/internal-helpers': 0.11.0 '@astrojs/markdown-satteri': 0.4.0 - astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3) + astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1) es-module-lexer: 2.0.0 '@astrojs/prism@4.0.2': @@ -4352,17 +4385,17 @@ snapshots: stream-replace-string: 2.0.0 zod: 4.3.6 - '@astrojs/starlight@0.42.0(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3))(typescript@6.0.3)': + '@astrojs/starlight@0.42.0(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1))(typescript@6.0.3)': dependencies: '@astrojs/markdown-satteri': 0.4.0 - '@astrojs/mdx': 8.0.0(@astrojs/markdown-satteri@0.4.0)(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3)) + '@astrojs/mdx': 8.0.0(@astrojs/markdown-satteri@0.4.0)(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1)) '@astrojs/sitemap': 3.7.3 '@pagefind/default-ui': 1.3.0 '@types/hast': 3.0.5 '@types/js-yaml': 4.0.9 '@types/mdast': 4.0.4 - astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3) - astro-expressive-code: 0.44.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3)) + astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1) + astro-expressive-code: 0.44.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1)) bcp-47: 2.1.0 hast-util-format: 1.1.0 hast-util-select: 6.0.4 @@ -4732,9 +4765,9 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0(jiti@2.3.3))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.3.0(jiti@2.7.0))': dependencies: - eslint: 10.3.0(jiti@2.3.3) + eslint: 10.3.0(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -5040,19 +5073,20 @@ snapshots: '@kwsites/promise-deferred@1.1.1': {} - '@lunariajs/core@https://pkg.pr.new/lunariajs/lunaria/@lunariajs/core@722c34c': + '@lunariajs/core@0.2.0': dependencies: consola: 3.4.2 - jiti: 2.3.3 - js-yaml: 4.3.1 - neotraverse: 0.6.18 - p-all: 5.0.0 + gettext-parser: 9.1.1 + jiti: 2.7.0 + neotraverse: 1.0.1 + p-all: 5.0.1 path-to-regexp: 6.3.0 - picomatch: 4.0.4 - simple-git: 3.27.0 + picomatch: 4.0.7 + simple-git: 3.36.0 tinyglobby: 0.2.17 ultramatter: 0.0.4 - zod: 3.25.76 + yaml: 2.9.1 + zod: 4.6.2 transitivePeerDependencies: - supports-color @@ -5224,6 +5258,12 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@simple-git/args-pathspec@1.0.3': {} + + '@simple-git/argv-parser@1.1.1': + dependencies: + '@simple-git/args-pathspec': 1.0.3 + '@sindresorhus/is@7.2.0': {} '@speed-highlight/core@1.2.16': {} @@ -5422,15 +5462,15 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3))(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.59.3 - '@typescript-eslint/type-utils': 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.59.3 - eslint: 10.3.0(jiti@2.3.3) + eslint: 10.3.0(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -5438,14 +5478,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3)': + '@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.59.3 '@typescript-eslint/types': 8.59.3 '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.59.3 debug: 4.4.3 - eslint: 10.3.0(jiti@2.3.3) + eslint: 10.3.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5468,13 +5508,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.59.3 '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 - eslint: 10.3.0(jiti@2.3.3) + eslint: 10.3.0(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -5497,13 +5537,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3)': + '@typescript-eslint/utils@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.3.3)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.59.3 '@typescript-eslint/types': 8.59.3 '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - eslint: 10.3.0(jiti@2.3.3) + eslint: 10.3.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5659,20 +5699,20 @@ snapshots: transitivePeerDependencies: - supports-color - astro-expressive-code@0.44.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3)): + astro-expressive-code@0.44.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1)): dependencies: - astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3) + astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1) rehype-expressive-code: 0.44.1 url-extras: 0.1.0 - astro-og-canvas@0.11.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3)): + astro-og-canvas@0.11.1(astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1)): dependencies: - astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3) + astro: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1) canvaskit-wasm: 0.41.1 deterministic-object-hash: 2.0.2 entities: 8.0.0 - astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.3.3)(yaml@2.8.3): + astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.12.2)(jiti@2.7.0)(yaml@2.9.1): dependencies: '@astrojs/compiler-rs': 0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) '@astrojs/internal-helpers': 0.11.0 @@ -5722,8 +5762,8 @@ snapshots: ultrahtml: 1.6.0 unifont: 0.7.5 unstorage: 1.17.5 - vite: 8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.3.3)(yaml@2.8.3) - vitefu: 1.1.2(vite@8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.3.3)(yaml@2.8.3)) + vite: 8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + vitefu: 1.1.2(vite@8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.3.6 @@ -5903,6 +5943,8 @@ snapshots: consola@3.4.2: {} + content-type@1.0.5: {} + cookie-es@1.2.3: {} cookie@1.1.1: {} @@ -6038,6 +6080,10 @@ snapshots: emoji-regex@9.2.2: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + entities@4.5.0: {} entities@6.0.0: {} @@ -6126,19 +6172,19 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.6.4(eslint@10.3.0(jiti@2.3.3)): + eslint-compat-utils@0.6.4(eslint@10.3.0(jiti@2.7.0)): dependencies: - eslint: 10.3.0(jiti@2.3.3) + eslint: 10.3.0(jiti@2.7.0) semver: 7.8.5 - eslint-plugin-astro@1.7.0(eslint@10.3.0(jiti@2.3.3)): + eslint-plugin-astro@1.7.0(eslint@10.3.0(jiti@2.7.0)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.3.3)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) '@jridgewell/sourcemap-codec': 1.5.5 '@typescript-eslint/types': 8.59.3 astro-eslint-parser: 1.4.0 - eslint: 10.3.0(jiti@2.3.3) - eslint-compat-utils: 0.6.4(eslint@10.3.0(jiti@2.3.3)) + eslint: 10.3.0(jiti@2.7.0) + eslint-compat-utils: 0.6.4(eslint@10.3.0(jiti@2.7.0)) globals: 16.5.0 postcss: 8.5.15 postcss-selector-parser: 7.0.0 @@ -6163,9 +6209,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.3.0(jiti@2.3.3): + eslint@10.3.0(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.3.3)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.3.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.5.5 @@ -6196,7 +6242,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.3.3 + jiti: 2.7.0 transitivePeerDependencies: - supports-color @@ -6368,6 +6414,11 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + gettext-parser@9.1.1: + dependencies: + content-type: 1.0.5 + encoding: 0.1.13 + github-slugger@2.0.0: {} glob-parent@5.1.2: @@ -6542,6 +6593,10 @@ snapshots: optionalDependencies: typescript: 6.0.3 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -6610,7 +6665,7 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jiti@2.3.3: {} + jiti@2.7.0: {} js-yaml@3.14.1: dependencies: @@ -7265,7 +7320,7 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - p-all@5.0.0: + p-all@5.0.1: dependencies: p-map: 6.0.0 @@ -7381,6 +7436,8 @@ snapshots: picomatch@4.0.4: {} + picomatch@4.0.7: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -7584,6 +7641,8 @@ snapshots: s.color@0.0.15: {} + safer-buffer@2.1.2: {} + sass-formatter@0.7.6: dependencies: suf-log: 2.5.3 @@ -7712,10 +7771,12 @@ snapshots: signal-exit@4.1.0: {} - simple-git@3.27.0: + simple-git@3.36.0: dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 + '@simple-git/args-pathspec': 1.0.3 + '@simple-git/argv-parser': 1.1.1 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -7975,13 +8036,13 @@ snapshots: dependencies: semver: 7.8.5 - typescript-eslint@8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3): + typescript-eslint@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3))(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) - '@typescript-eslint/parser': 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.59.3(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.3.3))(typescript@6.0.3) - eslint: 10.3.0(jiti@2.3.3) + '@typescript-eslint/utils': 8.59.3(eslint@10.3.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.3.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -8129,7 +8190,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.3.3)(yaml@2.8.3): + vite@8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -8140,12 +8201,12 @@ snapshots: '@types/node': 24.12.2 esbuild: 0.28.1 fsevents: 2.3.3 - jiti: 2.3.3 - yaml: 2.8.3 + jiti: 2.7.0 + yaml: 2.9.1 - vitefu@1.1.2(vite@8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.3.3)(yaml@2.8.3)): + vitefu@1.1.2(vite@8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)): optionalDependencies: - vite: 8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.3.3)(yaml@2.8.3) + vite: 8.0.16(@types/node@24.12.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) volar-service-css@0.0.70(@volar/language-service@2.4.28): dependencies: @@ -8322,6 +8383,8 @@ snapshots: yaml@2.8.3: {} + yaml@2.9.1: {} + yargs-parser@22.0.0: {} yargs@18.1.0: @@ -8350,10 +8413,10 @@ snapshots: cookie: 1.1.1 youch-core: 0.3.3 - zod@3.25.76: {} - zod@4.3.6: {} + zod@4.6.2: {} + zwitch@1.0.5: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3721b47ebc6a4..4b55438004fed 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,6 +17,7 @@ minimumReleaseAgeExclude: - '@astrojs/mdx@8.0.0' - '@astrojs/starlight@0.42.0' - '@astrojs/internal-helpers@0.11.0' + - '@lunariajs/core@0.2.0' # We don’t need to run these build scripts, so explicitly disallow them as a minor security measure. allowBuilds: diff --git a/public/logos/hackmd.svg b/public/logos/hackmd.svg new file mode 100644 index 0000000000000..7cf83b6e9b8ea --- /dev/null +++ b/public/logos/hackmd.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/logos/ishosting.svg b/public/logos/ishosting.svg new file mode 100644 index 0000000000000..90cf199e085d4 --- /dev/null +++ b/public/logos/ishosting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/lunaria.mts b/scripts/lunaria.mts index 394a3d0d9d1a7..66349446b4cf5 100644 --- a/scripts/lunaria.mts +++ b/scripts/lunaria.mts @@ -1,13 +1,12 @@ -import { createLunaria } from '@lunariajs/core'; +import { createLunaria, generateDashboard } from '@lunariajs/core'; import { mkdirSync, writeFileSync } from 'node:fs'; -import { Page, SvgSummary } from './lunaria/components.ts'; +import { join } from 'node:path'; +import { SvgSummary } from './lunaria/summary.ts'; const lunaria = await createLunaria(); const status = await lunaria.getFullStatus(); -const html = Page(lunaria.config, status, lunaria); -const svg = SvgSummary(lunaria.config, status); - -mkdirSync('dist/lunaria', { recursive: true }); -writeFileSync('dist/lunaria/index.html', html); -writeFileSync('dist/lunaria/summary.svg', svg); +const outDir = lunaria.config.outDir; +mkdirSync(outDir, { recursive: true }); +writeFileSync(join(outDir, 'index.html'), generateDashboard(lunaria.config, status)); +writeFileSync(join(outDir, 'summary.svg'), SvgSummary(lunaria.config, status)); diff --git a/scripts/lunaria/components.ts b/scripts/lunaria/components.ts deleted file mode 100644 index fed41a17bfc3c..0000000000000 --- a/scripts/lunaria/components.ts +++ /dev/null @@ -1,434 +0,0 @@ -import { - createLunaria, - type Locale, - type LunariaConfig, - type LunariaStatus, - type StatusEntry, -} from '@lunariajs/core'; -import { BaseStyles, CustomStyles } from './styles.ts'; - -export function html( - strings: TemplateStringsArray, - ...values: ((string | number) | (string | number)[])[] -) { - const treatedValues = values.map((value) => (Array.isArray(value) ? value.join('') : value)); - - return String.raw({ raw: strings }, ...treatedValues); -} - -type LunariaInstance = Awaited>; - -function collapsePath(path: string) { - const basesToHide = ['src/content/docs/en/', 'src/i18n/en/', 'src/content/docs/', 'src/content/']; - - for (const base of basesToHide) { - const newPath = path.replace(base, ''); - - if (newPath === path) continue; - return newPath; - } - - return path; -} - -export const Page = ( - config: LunariaConfig, - status: LunariaStatus, - lunaria: LunariaInstance -): string => { - return html` - - - - ${Meta} ${BaseStyles} ${CustomStyles} - - - ${Body(config, status, lunaria)} - - - `; -}; - -export const Meta = html` - - - Astro Docs Translation Status - - - - - - - - - -`; - -export const Body = ( - config: LunariaConfig, - status: LunariaStatus, - lunaria: LunariaInstance -): string => { - return html` -
-
-

Astro Docs Translation Status

- ${TitleParagraph} ${StatusByLocale(config, status, lunaria)} -
- ${StatusByFile(config, status, lunaria)} -
- `; -}; - -export const StatusByLocale = ( - config: LunariaConfig, - status: LunariaStatus, - lunaria: LunariaInstance -): string => { - const { locales } = config; - return html` -

- Translation progress by locale -

- ${locales.map((locale) => LocaleDetails(status, locale, lunaria))} - `; -}; - -export const LocaleDetails = ( - status: LunariaStatus, - locale: Locale, - lunaria: LunariaInstance -): string => { - const { label, lang } = locale; - - const missingFiles = status.filter( - (file) => - file.localizations.find((localization) => localization.lang === lang)?.status === 'missing' - ); - const outdatedFiles = status.filter((file) => { - const localization = file.localizations.find((localization) => localization.lang === lang); - - if (!localization || localization.status === 'missing') return false; - if (file.type === 'dictionary') - return 'missingKeys' in localization ? localization.missingKeys.length > 0 : false; - - return ( - localization.status === 'outdated' || - ('missingKeys' in localization && localization.missingKeys.length > 0) - ); - }); - - const doneLength = status.length - outdatedFiles.length - missingFiles.length; - - const links = lunaria.gitHostingLinks(); - - return html` -
- - ${label} (${lang}) -
- - ${doneLength.toString()} done, ${outdatedFiles.length.toString()} outdated, - ${missingFiles.length.toString()} missing - -
- ${ProgressBar(status.length, outdatedFiles.length, missingFiles.length)} -
- ${outdatedFiles.length > 0 ? OutdatedFiles(outdatedFiles, lang, lunaria) : ''} - ${missingFiles.length > 0 - ? html`

Missing

- ` - : ''} - ${missingFiles.length == 0 && outdatedFiles.length == 0 - ? html`

This translation is complete, amazing job! 🎉

` - : ''} -
- `; -}; - -export const OutdatedFiles = ( - outdatedFiles: LunariaStatus, - lang: string, - lunaria: LunariaInstance -): string => { - return html` -

Outdated

- - `; -}; - -export const StatusByFile = ( - config: LunariaConfig, - status: LunariaStatus, - lunaria: LunariaInstance -): string => { - const { locales } = config; - return html` -

- Translation status by file -

- - - - ${['File', ...locales.map(({ lang }) => lang)].map((col) => html``)} - - - ${TableBody(status, locales, lunaria)} -
${col}
- ❌ missing   🔄 outdated   ✔ done - `; -}; - -export const TableBody = ( - status: LunariaStatus, - locales: Locale[], - lunaria: LunariaInstance -): string => { - const links = lunaria.gitHostingLinks(); - - return html` - - ${status.map( - (file) => - html` - - ${Link(links.source(file.source.path), collapsePath(file.source.path))} - ${locales.map(({ lang }) => { - return TableContentStatus(file.localizations, lang, lunaria); - })} - - ` - )} - - `; -}; - -export const TableContentStatus = ( - localizations: StatusEntry['localizations'], - lang: string, - lunaria: LunariaInstance -): string => { - const localization = localizations.find((localization) => localization.lang === lang)!; - const isMissingKeys = 'missingKeys' in localization && localization.missingKeys.length > 0; - const status = isMissingKeys ? 'outdated' : localization.status; - const links = lunaria.gitHostingLinks(); - const link = - status === 'missing' ? links.create(localization.path) : links.source(localization.path); - return html`${EmojiFileLink(link, status)}`; -}; - -export const ContentDetailsLinks = ( - fileStatus: StatusEntry, - lang: string, - lunaria: LunariaInstance -): string => { - const localization = fileStatus.localizations.find((localization) => localization.lang === lang)!; - const isMissingKeys = - localization.status !== 'missing' && - 'missingKeys' in localization && - localization.missingKeys.length > 0; - - const links = lunaria.gitHostingLinks(); - - return html` - ${Link(links.source(fileStatus.source.path), collapsePath(fileStatus.source.path))} - (${Link( - links.source(localization.path), - isMissingKeys ? 'incomplete translation' : 'outdated translation' - )}, - ${Link( - links.history( - fileStatus.source.path, - 'git' in localization - ? new Date(localization.git.latestTrackedCommit.date).toISOString() - : undefined - ), - 'source change history' - )}) - `; -}; - -export const EmojiFileLink = ( - href: string | null, - type: 'missing' | 'outdated' | 'up-to-date' -): string => { - const statusTextOpts = { - missing: 'missing', - outdated: 'outdated', - 'up-to-date': 'done', - } as const; - - const statusEmojiOpts = { - missing: '❌', - outdated: '🔄', - 'up-to-date': '✔', - } as const; - - return href - ? html` - - ` - : html` - - `; -}; - -export const Link = (href: string, text: string): string => { - return html`${text}`; -}; - -export const CreateFileLink = (href: string, text: string): string => { - return html`${text}`; -}; - -export const ProgressBar = ( - total: number, - outdated: number, - missing: number, - { size = 20 }: { size?: number } = {} -): string => { - const outdatedSize = Math.round((outdated / total) * size); - const missingSize = Math.round((missing / total) * size); - const doneSize = size - outdatedSize - missingSize; - - const getBlocks = (size: number, type: 'missing' | 'outdated' | 'up-to-date') => { - const items = []; - for (let i = 0; i < size; i++) { - items.push(html`
`); - } - return items; - }; - - return html` - - `; -}; - -export const TitleParagraph = html` -

- If you're interested in helping us translate - docs.astro.build into one of the languages listed below, - you've come to the right place! This auto-updating page always lists all the content that could - use your help right now. -

-

- Before starting, please read our - i18n Guide - to learn about our translation process and how you can get involved. -

-`; - -/** - * Build an SVG file showing a summary of each language’s translation progress. - */ -export const SvgSummary = (config: LunariaConfig, status: LunariaStatus): string => { - const localeHeight = 56; // Each locale’s summary is 56px high. - const svgHeight = localeHeight * Math.ceil(config.locales.length / 2); - return html` - ${config.locales - .map((locale) => SvgLocaleSummary(status, locale)) - .sort((a, b) => b.progress - a.progress) - .map( - ({ svg }, index) => - html`${svg}` - )} - `; -}; - -function SvgLocaleSummary( - status: LunariaStatus, - { label, lang }: Locale -): { svg: string; progress: number } { - const missingFiles = status.filter( - (file) => - file.localizations.find((localization) => localization.lang === lang)?.status === 'missing' - ); - const outdatedFiles = status.filter((file) => { - const localization = file.localizations.find((localization) => localization.lang === lang); - if (!localization || localization.status === 'missing') { - return false; - } else if (file.type === 'dictionary') { - return 'missingKeys' in localization ? localization.missingKeys.length > 0 : false; - } else { - return ( - localization.status === 'outdated' || - ('missingKeys' in localization && localization.missingKeys.length > 0) - ); - } - }); - - const doneLength = status.length - outdatedFiles.length - missingFiles.length; - const barWidth = 184; - const doneFraction = doneLength / status.length; - const outdatedFraction = outdatedFiles.length / status.length; - const doneWidth = (doneFraction * barWidth).toFixed(2); - const outdatedWidth = ((outdatedFraction + doneFraction) * barWidth).toFixed(2); - - return { - progress: doneFraction, - svg: html`${label} (${lang}) - - ${missingFiles.length == 0 && outdatedFiles.length == 0 - ? '100% complete, amazing job! 🎉' - : html`${doneLength} done, ${outdatedFiles.length} outdated, ${missingFiles.length} - missing`} - - - - `, - }; -} diff --git a/scripts/lunaria/styles.css b/scripts/lunaria/styles.css new file mode 100644 index 0000000000000..2184a9cf9d50c --- /dev/null +++ b/scripts/lunaria/styles.css @@ -0,0 +1,42 @@ +/* Custom styles inlined into the Lunaria dashboard after its built-in styles. */ +:root { + --theme-navbar-height: 6rem; + --theme-mobile-toc-height: 4rem; + --theme-accent-secondary: hsla(22, 100%, 60%, 1); + --theme-text: hsla(250, 14%, 90%, 1); + --theme-bg: hsl(256, 27%, 19%); + --theme-bg-gradient-top: var(--theme-bg); + --theme-bg-gradient-bottom: hsl(251, 29%, 11%); + --theme-bg-gradient: linear-gradient( + 180deg, + var(--theme-bg-gradient-top), + var(--theme-bg-gradient-top) calc(var(--theme-navbar-height) + var(--theme-mobile-toc-height)), + var(--theme-bg-gradient-bottom) + ); + + --ln-color-link: #539bf5; + --ln-color-table-background: hsl(252, 34%, 25%); + --ln-color-table-border: hsl(252, 34%, 37%); + --ln-color-background: var(--theme-bg-gradient); + --ln-color-black: var(--theme-text); + --ln-color-missing: var(--ln-color-black); + --ln-color-outdated: #fb923c; + --ln-color-done: #c084fc; + + color-scheme: dark; +} + +p a { + color: var(--theme-accent-secondary); + text-decoration: underline; +} + +details summary:hover strong, +details summary:hover::marker { + color: var(--theme-accent-secondary); +} + +.create-button { + background-color: hsl(213deg 89% 64% / 20%); + border-radius: 0.5em; +} diff --git a/scripts/lunaria/styles.ts b/scripts/lunaria/styles.ts deleted file mode 100644 index 5ce1886aa51d1..0000000000000 --- a/scripts/lunaria/styles.ts +++ /dev/null @@ -1,331 +0,0 @@ -import { html } from './components.ts'; - -export const BaseStyles = html` - -`; - -export const CustomStyles = html` - -`; diff --git a/scripts/lunaria/summary.ts b/scripts/lunaria/summary.ts new file mode 100644 index 0000000000000..c3af6f2a76cf1 --- /dev/null +++ b/scripts/lunaria/summary.ts @@ -0,0 +1,67 @@ +import type { Locale, LunariaConfig, LunariaStatus } from '@lunariajs/core'; +import { getDashboardStatus, getLocalization, html } from '@lunariajs/core/dashboard'; + +/** + * Build an SVG file showing a summary of each language’s translation progress. + * Embedded in the repository README and served at `https://i18n.docs.astro.build/summary.svg`. + */ +export const SvgSummary = (config: LunariaConfig, status: LunariaStatus): string => { + const localeHeight = 56; // Each locale’s summary is 56px high. + const svgHeight = localeHeight * Math.ceil(config.locales.length / 2); + return html` + ${config.locales + .map((locale) => SvgLocaleSummary(status, locale)) + .sort((a, b) => b.progress - a.progress) + .map(({ svg }, index) => { + const x = (index % 2) * 215; + const y = Math.floor(index / 2) * localeHeight; + return html`${svg}`; + })} + `; +}; + +function SvgLocaleSummary( + status: LunariaStatus, + { label, lang }: Locale +): { svg: string; progress: number } { + let missing = 0; + let outdated = 0; + for (const entry of status) { + const dashboardStatus = getDashboardStatus(getLocalization(entry, lang)); + if (dashboardStatus === 'missing') missing++; + else if (dashboardStatus === 'outdated') outdated++; + } + + const done = status.length - outdated - missing; + const barWidth = 184; + const doneFraction = done / status.length; + const outdatedFraction = outdated / status.length; + const doneWidth = (doneFraction * barWidth).toFixed(2); + const outdatedWidth = ((outdatedFraction + doneFraction) * barWidth).toFixed(2); + const summary = + missing === 0 && outdated === 0 + ? '100% complete, amazing job! 🎉' + : `${done} done, ${outdated} outdated, ${missing} missing`; + + return { + progress: doneFraction, + svg: html`${label} (${lang}) + ${summary} + + + `, + }; +} diff --git a/src/components/RightSidebar/AstroJobs.png b/src/components/RightSidebar/AstroJobs.png deleted file mode 100644 index f9c1415997c6e..0000000000000 Binary files a/src/components/RightSidebar/AstroJobs.png and /dev/null differ diff --git a/src/components/RightSidebar/AstroJobsAd.astro b/src/components/RightSidebar/AstroJobsAd.astro deleted file mode 100644 index 294a4430a5dd9..0000000000000 --- a/src/components/RightSidebar/AstroJobsAd.astro +++ /dev/null @@ -1,126 +0,0 @@ ---- -import { LinkButton } from '@astrojs/starlight/components'; -import AstroJobs from './AstroJobs.png'; -import { Image } from 'astro:assets'; - -const offerUrl = 'https://astro.jobs?promoCode=ASTRO20'; - -const idCount = (((Astro.locals as any)._ad_render_id as number | undefined) ??= 1); -(Astro.locals as any)._ad_render_id++; -const headingId = `learn-astro-course-${idCount}`; ---- - -
- -
- - - - diff --git a/src/components/RightSidebar/RandomizedAd.astro b/src/components/RightSidebar/RandomizedAd.astro index 5540a7bfd8cd0..9afd5b199fe80 100644 --- a/src/components/RightSidebar/RandomizedAd.astro +++ b/src/components/RightSidebar/RandomizedAd.astro @@ -1,12 +1,10 @@ --- import ScrimbaAd from './ScrimbaAd.astro'; import LearnAstroAd from './LearnAstroAd.astro'; -import AstroJobsAd from './AstroJobsAd.astro'; const ads = [ - { component: ScrimbaAd, weight: 0.4 }, - { component: LearnAstroAd, weight: 0.4 }, - { component: AstroJobsAd, weight: 0.2 }, + { component: ScrimbaAd, weight: 0.5 }, + { component: LearnAstroAd, weight: 0.5 }, ]; const totalWeight = ads.reduce((sum, ad) => sum + ad.weight, 0); let random = Math.random() * totalWeight; diff --git a/src/content/docs/de/recipes/tailwind-rendered-markdown.mdx b/src/content/docs/de/recipes/tailwind-rendered-markdown.mdx new file mode 100644 index 0000000000000..a301844b335af --- /dev/null +++ b/src/content/docs/de/recipes/tailwind-rendered-markdown.mdx @@ -0,0 +1,101 @@ +--- +title: Gestalte dein gerendertes Markdown mit dem Tailwind-CSS Typography-Plugin +description: Erfahre, wie du @tailwind/typography nutzt, um das Aussehen deines gerenderten Markdowns zu gestalten. +i18nReady: true +sidebar: + label: Nutzung des Tailwind Typography-Plugins +type: recipe +--- +import { Steps } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; + +Du kannst das Typography-Plugin von [Tailwind](https://tailwindcss.com) nutzen, um das Aussehen von gerenderten Markdown-Inhalten aus Quellen wie etwa [**Inhaltssammlungen**](/de/guides/content-collections/) von Astro zu gestalten. + +In diesem Rezept erfährst du, wie du eine wiederverwendbare Astro-Komponente erstellst, um das Aussehen deiner Markdown-Inhalte mithilfe der Utility-Klassen von Tailwind zu gestalten. + +## Voraussetzungen + +Ein Astro-Projekt, welches: + + - das [Vite-Plugin von Tailwind](/de/guides/styling/#tailwind) installiert hat. + - Astros [Sammlungen von Inhalten](/de/guides/content-collections/) verwendet. + +## Einrichtung des `@tailwindcss/typography`-Plugins + +Installiere zunächst das `@tailwindcss/typography`-Plugin unter Nutzung des von dir bevorzugten Paketmanagers. + + + + ```shell + npm install -D @tailwindcss/typography + ``` + + + ```shell + pnpm add -D @tailwindcss/typography + ``` + + + ```shell + yarn add --dev @tailwindcss/typography + ``` + + + +Füge nun das Paket als Plugin in deine Tailwind Konfigurationsdatei ein. + +```css title="src/styles/global.css" ins={2} +@import 'tailwindcss'; +@plugin '@tailwindcss/typography'; +``` + +## Anleitung + + +1. Erstelle eine ``-Komponente, um ein umschließendes `
`-Element mit einem `` für Ihren gerenderten Markdown-Text bereitzustellen. Fügen Sie die Stilklasse `prose` zusammen mit den gewünschten [Tailwind-Elementwandlern](https://tailwindcss.com/docs/typography-plugin#element-modifiers) im übergeordneten `
`-Element hinzu. + + ```astro title="src/components/Prose.astro" + --- + --- +
+ +
+ ``` + :::tip + Das `@tailwindcss/typography`-Plugin nutzt [**Elementwandler**](https://tailwindcss.com/docs/typography-plugin#element-modifiers) um das Aussehen von untergeordneten Elementen innerhalb eines mit der `prose`-Klasse ausgezeichneten Containers zu gestalten. + + Diese Elementwandler folgen der folgenden allgemeinen Syntax: + + ``` + prose-[Element]:anzuwendende-Klasse + ``` + + Beispielsweise weist `prose-h1:font-bold` allen `

`-Tags die Tailwind-Klasse `font-bold` zu. + ::: + +2. Rufen Sie Ihren Collection-Eintrag auf der Seite ab, auf der Sie Ihr Markdown rendern möchten. Übergeben Sie die Komponente `` aus `await render(entry)` als untergeordnete Komponente an ``, um Ihren Markdown-Inhalt mit Tailwind-Stilen zu umgeben. + + ```astro title="src/pages/index.astro" + --- + import Prose from '../components/Prose.astro'; + import Layout from '../layouts/Layout.astro'; + import { getEntry, render } from 'astro:content'; + + const entry = await getEntry('collection', 'entry'); + const { Content } = await render(entry); + --- + + + + + + ``` + + +## Ressourcen + +- [Dokumentation zum Tailwind Typography-Plugin (EN)](https://tailwindcss.com/docs/typography-plugin) diff --git a/src/content/docs/en/guides/authentication.mdx b/src/content/docs/en/guides/authentication.mdx index 25544d2ac4689..6c38149ed187a 100644 --- a/src/content/docs/en/guides/authentication.mdx +++ b/src/content/docs/en/guides/authentication.mdx @@ -4,7 +4,7 @@ description: An intro to authentication in Astro i18nReady: true --- -import { Steps } from '@astrojs/starlight/components' +import { Steps, Tabs, TabItem } from '@astrojs/starlight/components' import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' import UIFrameworkTabs from '~/components/tabs/UIFrameworkTabs.astro' import ReadMore from '~/components/ReadMore.astro' @@ -66,12 +66,10 @@ Follow the [Better Auth Astro Guide](https://www.better-auth.com/docs/integratio ### Usage -Better Auth offers a `createAuthClient` helper for various frameworks, including Vanilla JS, React, Vue, Svelte, and Solid. +Better Auth offers a `createAuthClient()` helper for various frameworks, including Vanilla JS, React, Vue, Svelte, and Solid. For example, to create a client for React, import the helper from `'better-auth/react'`: - - ```ts title="src/lib/auth-client.ts" @@ -116,19 +114,26 @@ Once your client is set up, you can use it to authenticate users in your Astro c ```astro title="src/pages/index.astro" --- -import Layout from 'src/layouts/Base.astro'; +import Layout from "../layouts/Base.astro"; --- + ``` @@ -137,12 +142,12 @@ You can then use the `auth` object to get the user's session data in your server ```astro title="src/pages/index.astro" --- -import { auth } from "../../../lib/auth"; // import your Better Auth instance +import { auth } from "../lib/auth"; // import your Better Auth instance export const prerender = false; // Not needed in 'server' mode - + const session = await auth.api.getSession({ - headers: Astro.request.headers, + headers: Astro.request.headers, }); --- @@ -152,9 +157,9 @@ const session = await auth.api.getSession({ You can also use the `auth` object to protect your routes. The following example uses [Astro's advanced routing](/en/guides/routing/#advanced-routing) with [Hono](https://hono.dev/) to require an authenticated session for every route under `/dashboard`, redirecting to the home page otherwise: ```ts title="src/fetch.ts" -import { Hono } from "hono"; +import { Hono, type Context, type Next } from "hono"; import { astro } from "astro/hono"; -import { auth } from "../auth"; // import your Better Auth instance +import { auth } from "./lib/auth"; // import your Better Auth instance const app = new Hono(); @@ -167,12 +172,14 @@ app.use(astro()); export default app; -async function requireAuth(c, next) { - const session = await auth.api.getSession({ headers: c.req.raw.headers }); - if (!session) { - return c.redirect("/"); - } - return next(); +async function requireAuth(c: Context, next: Next) { + const session = await auth.api.getSession({ + headers: c.req.raw.headers, + }); + if (!session) { + return c.redirect("/"); + } + return next(); } ``` @@ -225,38 +232,67 @@ Clerk provides components that allow you to control the visibility of pages base ```astro title="src/pages/index.astro" --- -import Layout from 'src/layouts/Base.astro'; -import { SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/astro/components'; +import Layout from "../layouts/Base.astro"; +import { Show, UserButton, SignInButton } from "@clerk/astro/components"; export const prerender = false; // Not needed in 'server' mode --- - - - - - - + + + + + + ``` -Clerk also allows you to protect routes on the server using middleware. Specify which routes are protected, and prompt unauthenticated users to sign in: +Clerk also allows you to protect routes on the server using middleware: -```ts title="src/middleware.ts" -import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server'; + -const isProtectedRoute = createRouteMatcher([ - '/dashboard(.*)', - '/forum(.*)', -]); +1. Set `clerkMiddleware()` as the `onRequest` handler in your middleware: -export const onRequest = clerkMiddleware((auth, context) => { - if (!auth().userId && isProtectedRoute(context.request)) { - return auth().redirectToSignIn(); - } -}); -``` + ```ts title="src/middleware.ts" + import { clerkMiddleware } from "@clerk/astro/server"; + + export const onRequest = clerkMiddleware({ + /* options */ + }); + ``` + +2. Access the authentication state in your pages and API routes with `locals.auth()`. This allows you to check if a user is authenticated and take appropriate actions (e.g. redirecting to the sign-in page or returning a different response). + + + + ```astro title="src/pages/dashboard.astro" + --- + const { isAuthenticated, redirectToSignIn } = Astro.locals.auth(); + + if (!isAuthenticated) return redirectToSignIn(); + --- + +

Dashboard

+ ``` +
+ + ```ts title="src/pages/api/data.ts" + import type { APIRoute } from "astro"; + + export const GET: APIRoute = ({ locals }) => { + const { isAuthenticated, userId } = locals.auth(); + + if (!isAuthenticated) { + return new Response("Unauthorized", { status: 401 }); + } + + return Response.json({ userId }); + }; + ``` + +
+
### Next Steps @@ -286,7 +322,9 @@ export const onRequest = clerkMiddleware((auth, context) => { ## Scalekit -[Scalekit](https://scalekit.com/) is an authentication platform built for B2B and AI applications. It provides social login, enterprise SSO, magic links, and more — managing the full OAuth 2.0 / OIDC flow so you get back tokens and a user profile without building any login UI. A single Scalekit environment supports multiple applications, so users authenticate once and share the same session across all your properties (for example, `app.yourcompany.com` and `docs.yourcompany.com`). +[Scalekit](https://scalekit.com/) is an authentication platform for B2B and AI applications. It manages the full OAuth 2.0 and OIDC flow, supporting methods such as social login, enterprise SSO, and magic links. It then returns tokens and a user profile without requiring a custom login UI. + +A single Scalekit environment can support multiple applications. This allows you to authenticate once and share the same session across all your properties (e.g. `app.yourcompany.com` and `docs.yourcompany.com`). ### Guide diff --git a/src/content/docs/en/guides/backend/firebase.mdx b/src/content/docs/en/guides/backend/firebase.mdx index 9c0172a591926..0e36507d606aa 100644 --- a/src/content/docs/en/guides/backend/firebase.mdx +++ b/src/content/docs/en/guides/backend/firebase.mdx @@ -233,6 +233,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { cookies.set("__session", sessionCookie, { path: "/", + maxAge: fiveDays / 1000, }); return redirect("/dashboard"); diff --git a/src/content/docs/en/guides/build-with-ai.mdx b/src/content/docs/en/guides/build-with-ai.mdx index caa15ed7bdd35..fd8877adec096 100644 --- a/src/content/docs/en/guides/build-with-ai.mdx +++ b/src/content/docs/en/guides/build-with-ai.mdx @@ -362,11 +362,11 @@ The same technology that powers Astro's MCP server is also available as a chatbo

-When an AI coding agent is detected, `astro dev` and, since v7.2.0, `astro preview` automatically start the server as a detached background process. This prevents the server from blocking the agent's terminal and allows it to continue working while the server runs. +When an AI coding agent is detected on macOS and Linux, `astro dev` and, since v7.2.0, `astro preview` automatically start the server as a detached background process. This prevents the server from blocking the agent's terminal and allows it to continue working while the server runs. -A lock file (`.astro/dev.json` or `.astro/preview.json`) is written when the server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project. +On Windows, or without an AI coding agent, the server starts in the foreground and logs to the terminal. You can still opt in explicitly by passing the [`--background` flag](/en/reference/cli-reference/#--background). -If you are not using an AI coding agent, the server starts in the foreground process and logs to the terminal. +A lock file (`.astro/dev.json` or `.astro/preview.json`) is written when the server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project. To opt out of automatic background mode, set the `ASTRO_DEV_BACKGROUND` or `ASTRO_PREVIEW_BACKGROUND` environment variable before running the command: diff --git a/src/content/docs/en/guides/cms/apostrophecms.mdx b/src/content/docs/en/guides/cms/apostrophecms.mdx index 614d7ecc64a07..0543c7f873ed9 100644 --- a/src/content/docs/en/guides/cms/apostrophecms.mdx +++ b/src/content/docs/en/guides/cms/apostrophecms.mdx @@ -391,24 +391,24 @@ const { page, pieces } = Astro.props.aposData; To display individual blog posts, create a `BlogShow.astro` file in the Astro project `src/templates` folder with the following code: -This component uses the `` component to display any widgets added to the `content` area and the `authorName` and `publicationDate` content entered into the fields of the same names. +This component uses the `` component to display any widgets added to the `main` area and the `authorName` and `publicationDate` content entered into the fields of the same names. ```js title="src/templates/BlogShow.astro" --- -import AposArea from '@apostrophecms/apostrophe-astro/components/AposArea.astro'; -import dayjs from 'dayjs'; +import AposArea from "@apostrophecms/apostrophe-astro/components/AposArea.astro"; +import dayjs from "dayjs"; const { page, piece } = Astro.props.aposData; const { main } = piece; ---
-

{ piece.title }

-

Created by: { piece.authorName } +

{piece.title}

+

Created by: {piece.authorName}

- Released On { dayjs(piece.publicationDate).format('MMMM D, YYYY') } + Released On {dayjs(piece.publicationDate).format("MMMM D, YYYY")}

- +
``` diff --git a/src/content/docs/en/guides/cms/builderio.mdx b/src/content/docs/en/guides/cms/builderio.mdx index 24202be12d448..e00f9d7a2635e 100644 --- a/src/content/docs/en/guides/cms/builderio.mdx +++ b/src/content/docs/en/guides/cms/builderio.mdx @@ -199,9 +199,8 @@ For more ideas, read [Builder's troubleshooting guide](https://www.builder.io/c/ Add the following content to `src/pages/index.astro` in order to fetch and display a list of all post titles, each linking to its own page: -```astro title="src/pages/index.astro" {9} +```astro title="src/pages/index.astro" {8} --- - const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY; const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL; @@ -223,16 +222,15 @@ const { results: posts } = await fetch(
    { - posts.flatMap(({ data: { slug, title } }) => ( + posts.flatMap((post: any) => (
  • - {title} + {post.data.title}
  • )) }
- ``` Fetching via the content API returns an array of objects containing data for each post. The `fields` query parameter tells Builder which data is included (see highlighted code). `slug` and `title` should match the names of the custom data fields you've added to your Builder model. @@ -269,7 +267,7 @@ This file must contain: Each of these is highlighted in the following code snippet. -```astro title="src/pages/posts/[slug].astro" ins={2, 26, 33, 40, 51} +```astro title="src/pages/posts/[slug].astro" ins={2, 26, 33, 41, 52} --- export async function getStaticPaths() { const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL; @@ -280,17 +278,17 @@ export async function getStaticPaths() { apiKey: builderAPIpublicKey, fields: ["data.slug", "data.title"].join(","), cachebust: "true", - } + }, ).toString()}` ) .then((res) => res.json()) .catch // ...catch some errors...); (); - return posts.map(({ data: { slug, title } }) => ({ - params: { slug }, - props: { title }, - })) + return posts.map((post: any) => ({ + params: { slug: post.data.slug }, + props: { title: post.data.title }, + })); } const { slug } = Astro.params; const { title } = Astro.props; @@ -304,11 +302,12 @@ const { html: postHTML } = await fetch( url: encodedUrl, "query.data.slug": slug, cachebust: "true", - }).toString()}` + }).toString()}`, ) .then((res) => res.json()) .catch(); --- + {title} diff --git a/src/content/docs/en/guides/cms/buttercms.mdx b/src/content/docs/en/guides/cms/buttercms.mdx index e2e11d4d90ade..e3ac87ef4f066 100644 --- a/src/content/docs/en/guides/cms/buttercms.mdx +++ b/src/content/docs/en/guides/cms/buttercms.mdx @@ -81,18 +81,25 @@ import { butterClient } from "../lib/buttercms"; const response = await butterClient.content.retrieve(["shopitem"]); interface ShopItem { - name: string, - price: number, - description: string, + name: string; + price: number; + description: string; } -const items = response.data.data.shopitem as ShopItem[]; +const items = response?.data?.data.shopitem as ShopItem[]; --- + - {items.map(item =>
-

{item.name} - ${item.price}

-

-
)} + { + items.map((item) => ( +
+

+ {item.name} - ${item.price} +

+

+

+ )) + } ``` @@ -104,16 +111,17 @@ Similarly, you can [retrieve a page](https://buttercms.com/docs/api/#get-a-singl --- import { butterClient } from "../lib/buttercms"; const response = await butterClient.page.retrieve("*", "simple-page"); -const pageData = response.data.data; +const pageData = response?.data?.data; interface Fields { - seo_title: string, - headline: string, - hero_image: string, + seo_title: string; + headline: string; + hero_image: string; } -const fields = pageData.fields as Fields; +const fields = pageData?.fields as Fields; --- + {fields.seo_title} diff --git a/src/content/docs/en/guides/cms/cloudcannon.mdx b/src/content/docs/en/guides/cms/cloudcannon.mdx index 7b5d396af2b4d..35c78f523933c 100644 --- a/src/content/docs/en/guides/cms/cloudcannon.mdx +++ b/src/content/docs/en/guides/cms/cloudcannon.mdx @@ -165,13 +165,16 @@ const posts = await getCollection('blog'); ### Displaying a single entry -To display content from an individual post, you can import and use the `` component to [render your content to HTML](/en/guides/content-collections/#rendering-body-content): +To display content from an individual post, you can [`render()` your content to HTML](/en/guides/content-collections/#rendering-body-content) using the `` component: -```astro title="src/pages/blog/my-first-post.astro" {4-5} +```astro title="src/pages/blog/my-first-post.astro" {8,14} ", render" --- import { getEntry, render } from 'astro:content'; const entry = await getEntry('blog', 'my-first-post'); +if (!entry) { + throw new Error('Blog post not found'); +} const { Content } = await render(entry); --- diff --git a/src/content/docs/en/guides/cms/cosmic.mdx b/src/content/docs/en/guides/cms/cosmic.mdx index c9e099922cb1c..c8abdf25a6b72 100644 --- a/src/content/docs/en/guides/cms/cosmic.mdx +++ b/src/content/docs/en/guides/cms/cosmic.mdx @@ -102,7 +102,7 @@ PUBLIC_COSMIC_READ_KEY=YOUR_READ_KEY title={post.title} href={post.slug} body={post.metadata.excerpt} - tags={post.metadata.tags.map((tag) => tag)} + tags={post.metadata.tags.map((tag: any) => tag)} /> )) } @@ -151,7 +151,7 @@ const data = await getAllPosts() title={post.title} href={post.slug} body={post.metadata.excerpt} - tags={post.metadata.tags.map((tag) => tag)} + tags={post.metadata.tags.map((tag: any) => tag)} /> )) } @@ -206,7 +206,6 @@ const { post } = Astro.props format="webp" width={1200} height={675} - aspectRatio={16 / 9} quality={50} alt={`Cover image for the blog ${post.title}`} class={'my-12 rounded-md shadow-lg'} diff --git a/src/content/docs/en/guides/cms/drupal.mdx b/src/content/docs/en/guides/cms/drupal.mdx index 99b5851caa31c..98154d7e52142 100644 --- a/src/content/docs/en/guides/cms/drupal.mdx +++ b/src/content/docs/en/guides/cms/drupal.mdx @@ -383,8 +383,8 @@ With the setup above, you are now able to create a blog that uses Drupal as the import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; - import type { DrupalNode } from "../types"; - import {getArticles} from "../api/drupal"; + import type { DrupalNode } from "../../types"; + import { getArticles } from "../../api/drupal"; // Get all published articles. const articles = await getArticles(); diff --git a/src/content/docs/en/guides/cms/emdash.mdx b/src/content/docs/en/guides/cms/emdash.mdx index 142a226188080..602d0ddbb9b8a 100644 --- a/src/content/docs/en/guides/cms/emdash.mdx +++ b/src/content/docs/en/guides/cms/emdash.mdx @@ -4,13 +4,234 @@ description: Add content to your Astro project using EmDash as a CMS sidebar: label: EmDash type: cms -stub: true logo: emdash i18nReady: true --- +import { Steps } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; [EmDash](https://emdashcms.com/) is an open-source, full-stack CMS built specifically for Astro, adding database-backed content, an admin UI, a media library, menus, and taxonomies to your site. +:::tip +To start a **new Astro + EmDash project from scratch**, use the EmDash CLI to generate a pre-wired project: + + + + ```shell + npm create emdash@latest + ``` + + + ```shell + pnpm create emdash@latest + ``` + + + ```shell + yarn create emdash@latest + ``` + + +::: + +## Integrating with Astro + +EmDash runs within your Astro project and relies on a database. You can access the admin interface at `/_emdash/admin` to edit content. Your pages will display it by querying the database using [live content collections](/en/guides/content-collections/#live-content-collections). + +This guide uses the [Node.js adapter](/en/guides/integrations-guide/node/) and a local SQLite database. See the [EmDash docs for a list of supported databases](https://docs.emdashcms.com/deployment/database/). + +## Prerequisites + +- An existing Astro project (Astro 6 or later) configured [with an adapter](/en/guides/on-demand-rendering/) and [`output: "server"`](/en/reference/configuration-reference/#output). +- Node.js v22.16.0 or higher. + +## Installing dependencies + +React powers the EmDash admin interface and is a required dependency. If your project does not use React, install it using the `astro add` command for your package manager: + + + + ```shell + npx astro add react + ``` + + + ```shell + pnpm astro add react + ``` + + + ```shell + yarn astro add react + ``` + + + +You also need to install the EmDash package: + + + + ```shell + npm install emdash + ``` + + + ```shell + pnpm add emdash + ``` + + + ```shell + yarn add emdash + ``` + + + +## Adding the integration + +Add the `emdash()` integration to your Astro config file, and configure a database and a media storage backend: + +```js title="astro.config.mjs" ins={4-5, 12-18} +import { defineConfig } from "astro/config"; +import node from "@astrojs/node"; +import react from "@astrojs/react"; +import emdash, { local } from "emdash/astro"; +import { sqlite } from "emdash/db"; + +export default defineConfig({ + output: "server", + adapter: node({ mode: "standalone" }), + integrations: [ + react(), + emdash({ + database: sqlite({ url: "file:./data.db" }), + storage: local({ + directory: "./uploads", + baseUrl: "/_emdash/api/media/file", + }), + }), + ], +}); +``` + +## Adding the live collections loader + +Create a `src/live.config.ts` file so that Astro's content layer can resolve EmDash content: + +```ts title="src/live.config.ts" +import { defineLiveCollection } from "astro:content"; +import { emdashLoader } from "emdash/runtime"; + +export const collections = { + _emdash: defineLiveCollection({ loader: emdashLoader() }), +}; +``` + +The `_emdash` collection internally routes to your content types (e.g. posts and pages). Any existing file-based collections in `src/content.config.ts` keep working alongside it. + +## Running EmDash locally + +EmDash requires you to complete the setup wizard before testing your own pages. Until the setup is complete, there is no published content and queries return empty results. + + +1. Start Astro's dev server to initialize the database and launch the admin UI: + + + + ```shell + npm run dev + ``` + + + ```shell + pnpm run dev + ``` + + + ```shell + yarn run dev + ``` + + + + On first run, EmDash creates `data.db` with its schema and two default collections, `pages` and `posts`. + +2. Visit `http://localhost:4321/_emdash/admin` in the browser. EmDash redirects you to the setup wizard. + +3. In the **Site** step, enter a site title and an optional tagline. + +4. In the **Account** step, enter your email address and name. This creates the administrator account. + +5. In the **Sign In** step, secure your account. Choose **Create Passkey** to register a passkey with your device's biometric authentication, security key, or PIN. + +6. Sign in with your new passkey to reach the dashboard. + + +## Creating your first post + + +1. In the dashboard, click the **+ Post** button. + +2. Add a title and some content. EmDash stores rich text as [Portable Text](https://github.com/portabletext/portabletext), edited in a block editor. A URL slug is generated from the title and can be edited in the sidebar. + +3. Click **Save**, then **Publish**. Only published posts are visible to site visitors. + + +## Rendering EmDash content + +Query your content with `getEmDashCollection()` and `getEmDashEntry()`. Both follow the live collections pattern and return results at request time, so published changes appear without a rebuild. + +### Displaying a list of posts + +The following example displays a list of all published post titles, each linking to an individual post page: + +```astro title="src/pages/blog.astro" +--- +import { getEmDashCollection } from "emdash"; + +const { entries: posts } = await getEmDashCollection("posts", { + status: "published", +}); +--- + +``` + +### Displaying a single post + +To display content from an individual post, fetch it by its slug and render the Portable Text content with the `` component: + +```astro title="src/pages/posts/[...slug].astro" +--- +import { getEmDashEntry } from "emdash"; +import { PortableText } from "emdash/ui"; + +const { slug } = Astro.params; +const { entry: post } = await getEmDashEntry("posts", slug); + +if (!post) { + return Astro.redirect("/404"); +} +--- +
+

{post.data.title}

+ +
+``` + +See the [EmDash querying guide](https://docs.emdashcms.com/guides/querying-content/) for more information on filtering, pagination, previewing drafts, and visual editing. + +## Deploying EmDash + Astro + +EmDash deploys together with your site as a single Astro project. Choose a host that supports your adapter, and provision a production database and media storage. + +See the [EmDash deployment guide for Node.js](https://docs.emdashcms.com/deployment/nodejs/) and the [EmDash deployment guide for Cloudflare](https://docs.emdashcms.com/deployment/cloudflare/) for specific instructions. You can also visit Astro's [deployment guides](/en/guides/deploy/) and follow the instructions to deploy with your preferred hosting provider. + ## Official Resources - [EmDash Documentation for Astro Developers](https://docs.emdashcms.com/coming-from/astro/) diff --git a/src/content/docs/en/guides/cms/ghost.mdx b/src/content/docs/en/guides/cms/ghost.mdx index ef3f2371ded9c..f9123bb733111 100644 --- a/src/content/docs/en/guides/cms/ghost.mdx +++ b/src/content/docs/en/guides/cms/ghost.mdx @@ -182,7 +182,7 @@ const posts = await ghostClient.posts { - posts.map((post) => ( + posts?.map((post) => (

{post.title}

@@ -223,7 +223,7 @@ export async function getStaticPaths() { console.error(err); }); - return posts.map((post) => { + return posts?.map((post) => { return { params: { slug: post.slug, @@ -252,7 +252,7 @@ export async function getStaticPaths() { .catch((err) => { console.error(err); }); - return posts.map((post) => { + return posts?.map((post) => { return { params: { slug: post.slug, @@ -294,7 +294,7 @@ To deploy your site visit our [deployment guide](/en/guides/deploy/) and follow - + diff --git a/src/content/docs/en/guides/cms/hackmd.mdx b/src/content/docs/en/guides/cms/hackmd.mdx new file mode 100644 index 0000000000000..c7c51e617622c --- /dev/null +++ b/src/content/docs/en/guides/cms/hackmd.mdx @@ -0,0 +1,218 @@ +--- +title: HackMD & Astro +description: Add content to your Astro project using HackMD as a CMS +sidebar: + label: HackMD +type: cms +stub: false +logo: hackmd +i18nReady: true +--- + +import { FileTree } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; +import ReadMore from '~/components/ReadMore.astro'; + +[HackMD](https://hackmd.io/) is a collaborative Markdown editor and publishing platform. You can use its API to manage your content in HackMD and display it in your Astro project. + +## Integrating with Astro + +This guide uses the official [`@hackmd/api`](https://github.com/hackmdio/api-client) client to fetch your notes and [`markdown-it`](https://github.com/markdown-it/markdown-it) to render Markdown content. + +### Prerequisites + +To get started, you will need: + +1. **An Astro project** - If you don't have an Astro project yet, the [installation guide](/en/install-and-setup/) will get you up and running. +2. **A HackMD account** - You can [sign up for free](https://hackmd.io/join). +3. **A HackMD access token** - Create one from the API section of your [HackMD settings](https://hackmd.io/settings#api). +4. **At least one publicly readable note** - Set the note's read permission to **Everyone** so the example can safely publish it on your site. + +### Setting up credentials + +Create a `.env` file in the root of your project and add your HackMD access token: + +```ini title=".env" +HACKMD_API_ACCESS_TOKEN= +``` + +Do not prefix this variable with `PUBLIC_`. This keeps the token available only to your server-side code and prevents Astro from exposing it to the browser. + +Read more about [environment variables](/en/guides/environment-variables/) and `.env` files in Astro. + +### Installing dependencies + +Install the HackMD API client and Markdown renderer: + + + + ```shell + npm install @hackmd/api markdown-it + ``` + + + ```shell + pnpm add @hackmd/api markdown-it + ``` + + + ```shell + yarn add @hackmd/api markdown-it + ``` + + + +### Configuring HackMD + +Create a `hackmd.ts` file in a new `src/lib/` directory. This file initializes the API client, renders Markdown, and creates a URL-friendly identifier for each note: + +```ts title="src/lib/hackmd.ts" +import { API } from '@hackmd/api'; +import MarkdownIt from 'markdown-it'; + +export const client = new API(import.meta.env.HACKMD_API_ACCESS_TOKEN); + +const md = new MarkdownIt({ + html: false, + linkify: true, + typographer: true, +}); + +export function renderMarkdown(content: string) { + return md.render(content); +} + +export function getNoteSlug(note: { permalink: string | null; shortId: string }) { + return note.permalink ?? note.shortId; +} +``` + +The `html: false` option prevents raw HTML in a note from being passed directly to your generated page. Standard Markdown is still rendered as HTML. + +Your project will use the following files: + + +- src/ + - lib/ + - **hackmd.ts** + - pages/ + - **index.astro** + - notes/ + - **[slug].astro** +- **.env** +- astro.config.mjs +- package.json + + +## Making a blog with Astro and HackMD + +This example creates an index of publicly readable notes and a statically generated page for each note. + +### Displaying a list of notes + +Use `getNoteList()` in `src/pages/index.astro` to retrieve your notes. Filter the results so that only notes with the `guest` read permission are included in the public site: + +```astro title="src/pages/index.astro" +--- +import { client, getNoteSlug } from '../lib/hackmd'; + +const notes = await client.getNoteList(); +const publicNotes = notes.filter((note) => note.readPermission === 'guest'); +--- + + + + + + + Astro + HackMD + + +
+

My HackMD notes

+
    + { + publicNotes.map((note) => ( +
  • + {note.title} +
  • + )) + } +
+
+ + +``` + +:::caution +The access token can read private notes in your account. Keep the `guest` permission filter unless you intentionally want to include other notes in the generated site. +::: + +### Generating note pages + +Create `src/pages/notes/[slug].astro` to generate a static page for every public note. The note list provides the route and note ID, then `getNote()` retrieves the full Markdown content for that page: + +```astro title="src/pages/notes/[slug].astro" +--- +import { client, getNoteSlug, renderMarkdown } from '../../lib/hackmd'; + +export async function getStaticPaths() { + const notes = await client.getNoteList(); + + return notes + .filter((note) => note.readPermission === 'guest') + .map((note) => ({ + params: { slug: getNoteSlug(note) }, + props: { noteId: note.id }, + })); +} + +interface Props { + noteId: string; +} + +const { noteId } = Astro.props; +const note = await client.getNote(noteId); +const content = renderMarkdown(note.content); +--- + + + + + + + {note.title} + + +
+
+ +
+
+ + +``` + +:::caution +Astro's [`set:html` directive](/en/reference/directives-reference/#sethtml) inserts an HTML string without escaping it. This example first passes the note through `markdown-it` with raw HTML disabled. If you enable the `html` option for trusted authors, sanitize the rendered result before passing it to `set:html`. +::: + +### Supporting more HackMD syntax + +HackMD uses `markdown-it` with extensions for features such as task lists, footnotes, containers, and a table of contents. The minimal configuration above handles standard Markdown. Install only the [`markdown-it` plugins](https://www.npmjs.com/search?q=keywords%3Amarkdown-it-plugin) required by your notes. + +### Publishing your site + +To deploy your website, visit our [deployment guides](/en/guides/deploy/) and follow the instructions for your preferred hosting provider. + +If your project uses Astro's default static mode, you must run a new build to publish changes made in HackMD. If your hosting provider supports it, you can use its webhook function to automatically trigger a new build when HackMD sends a [webhook event](https://hackmd.io/@docs/webhooks-events). + +## Official Resources + +- [HackMD API documentation](https://hackmd.io/@docs/developer-portal) +- [HackMD OpenAPI documentation](https://api.hackmd.io/v1/docs) + +## Community Resources + +- [`daily-oops`](https://github.com/Yukaii/daily-oops) - A blog that uses HackMD as its CMS +- [`astro-hackmd`](https://github.com/EastSun5566/astro-hackmd) - A minimal Astro site that uses HackMD as its CMS diff --git a/src/content/docs/en/guides/cms/keystatic.mdx b/src/content/docs/en/guides/cms/keystatic.mdx index c8fe130303978..7eb653de0397e 100644 --- a/src/content/docs/en/guides/cms/keystatic.mdx +++ b/src/content/docs/en/guides/cms/keystatic.mdx @@ -172,7 +172,7 @@ Visit `http://127.0.0.1:4321/keystatic` in the browser to see the Keystatic Admi 5. Navigate to that file in your code editor and verify that you can see the Markdown content you entered. For example: - ```markdown + ```markdown title="src/content/posts/my-first-post.mdoc" --- title: My First Post --- @@ -189,7 +189,7 @@ Visit `http://127.0.0.1:4321/keystatic` in the browser to see the Keystatic Admi The following example displays a list of each post title, with a link to an individual post page. -```tsx {4} +```astro title="src/pages/posts/index.astro" {4} --- import { getCollection } from 'astro:content' @@ -206,21 +206,23 @@ const posts = await getCollection('posts') ### Displaying a single entry -To display content from an individual post, you can import and use the `` component to [render your content to HTML](/en/guides/content-collections/#rendering-body-content): +To display content from an individual post, you can [`render()` your content to HTML](/en/guides/content-collections/#rendering-body-content) using the `` component: -```tsx {4-5} +```astro title="src/pages/posts/my-first-post.astro" {8,13} ", render" --- -import { getEntry } from 'astro:content' +import { getEntry, render } from "astro:content"; -const post = await getEntry('posts', 'my-first-post') -const { Content } = await post.render() +const post = await getEntry("posts", "my-first-post"); +if (!post) { + throw new Error("Post not found"); +} +const { Content } = await render(post); ---

{post.data.title}

- ``` For more information on querying, filtering, displaying your collections content and more, see the full content [collections documentation](/en/guides/content-collections/). diff --git a/src/content/docs/en/guides/cms/kontent-ai.mdx b/src/content/docs/en/guides/cms/kontent-ai.mdx index c49b2145cc3fd..d375c3b3e2195 100644 --- a/src/content/docs/en/guides/cms/kontent-ai.mdx +++ b/src/content/docs/en/guides/cms/kontent-ai.mdx @@ -376,6 +376,7 @@ export async function getStaticPaths() { .items() .type(contentTypes.blog_post.codename) .toPromise() +} --- ``` @@ -437,6 +438,7 @@ const blogPost: BlogPost = Astro.props.blogPost + ``` @@ -516,6 +518,7 @@ try { + ``` diff --git a/src/content/docs/en/guides/cms/preprcms.mdx b/src/content/docs/en/guides/cms/preprcms.mdx index 5b1a073509a2f..df29613e5f5fd 100644 --- a/src/content/docs/en/guides/cms/preprcms.mdx +++ b/src/content/docs/en/guides/cms/preprcms.mdx @@ -99,24 +99,22 @@ You will fetch your data from Prepr by writing queries to interact with its Grap ``` 3. To display a linked list of your blog posts on a page, import and execute your query, including the necessary Prepr endpoint. You will then have access to all your posts titles and their slugs to render to the page. (In the next step, you will [create individual pages for your blog posts](#creating-individual-blog-post-pages).) - ```astro title="src/pages/index.astro" ins={3-4, 6-8, 15-23} + ```astro title="src/pages/index.astro" ins={3-4, 6-8, 13-21} --- - import Layout from '../layouts/Layout.astro'; - import { Prepr } from '../lib/prepr.js'; - import GetArticles from '../queries/get-articles.js'; + import Layout from "../layouts/Layout.astro"; + import { Prepr } from "../lib/prepr.js"; + import GetArticles from "../queries/get-articles.js"; - const response = await Prepr(GetArticles) - const { data } = await response.json() - const articles = data.Articles + const response = await Prepr(GetArticles); + const { data } = await response.json(); + const articles = data.Articles; --- -

- My blog site -   

-   
    +

    My blog site

    +
      { - articles.items.map((post) => ( + articles.items.map((post: any) => (
    • {post.title}
    • @@ -148,7 +146,7 @@ To create a page for each blog post, you will execute a new GraphQL query on a [ 1. Create a file called `get-article-by-slug.js` in the `queries` folder and add the following to query a specific article by its slug and return data such as the article `title` and `content`: - ```js title="src/lib/queries/get-article-by-slug.js" + ```js title="src/queries/get-article-by-slug.js" const GetArticleBySlug = ` query ($slug: String) {    Article (slug: $slug) { @@ -180,30 +178,26 @@ To create a page for each blog post, you will execute a new GraphQL query on a [ 2. Inside the `src/pages` folder, create a file called `[…slug].astro`. Add the following code to import and execute the query from the previous step and display the retrieved article: ```astro title="src/pages/[...slug].astro" --- - import Layout from '../layouts/Layout.astro'; - import {Prepr} from '../lib/prepr.js'; - import GetArticleBySlug from '../queries/get-article-by-slug.js'; + import Layout from "../layouts/Layout.astro"; + import { Prepr } from "../lib/prepr.js"; + import GetArticleBySlug from "../queries/get-article-by-slug.js"; const { slug } = Astro.params; - const response = await Prepr(GetArticleBySlug, {slug}) - const { data } = await response.json() - const article = data.Article + const response = await Prepr(GetArticleBySlug, { slug }); + const { data } = await response.json(); + const article = data.Article; ---

      {article.title}

      { - article.content.map((content) => ( + article.content.map((content: any) => (
      - { - content.__typename === "Assets" && - - } - { - content.__typename === 'Text' && -
      - } + {content.__typename === "Assets" && ( + + )} + {content.__typename === "Text" &&
      }
      )) } diff --git a/src/content/docs/en/guides/cms/statamic.mdx b/src/content/docs/en/guides/cms/statamic.mdx index 6dd2a97041605..cec8d0883cc54 100644 --- a/src/content/docs/en/guides/cms/statamic.mdx +++ b/src/content/docs/en/guides/cms/statamic.mdx @@ -48,7 +48,7 @@ const posts = await res.json() ---

      Astro + Statamic 🚀

      { - posts.map((post) => ( + posts.map((post: any) => (

      )) @@ -85,8 +85,8 @@ const graphqlQuery = { } `, variables: { - page: page, - locale: locale, + page: "my-current-page", + locale: "my-locale", }, }; @@ -101,7 +101,7 @@ const entries = data?.entries; ---

      Astro + Statamic 🚀

      { - entries.data.map((post) => ( + entries.data.map((post: any) => (

      )) diff --git a/src/content/docs/en/guides/cms/storyblok.mdx b/src/content/docs/en/guides/cms/storyblok.mdx index 6d678a7b4c894..8632e80366010 100644 --- a/src/content/docs/en/guides/cms/storyblok.mdx +++ b/src/content/docs/en/guides/cms/storyblok.mdx @@ -277,7 +277,7 @@ const { blok } = Astro.props

      { - blok.body?.map((blok) => { + blok.body?.map((blok: any) => { return }) } @@ -306,38 +306,43 @@ const content = renderRichText(blok.content) It uses the `useStoryblokApi` hook to fetch all the stories with the content type of `blogPost`. It uses the `version` query parameter to fetch the draft versions of the stories when in development mode and the published versions when building for production. `Astro.props` is used to set up the editor in Storyblok. Additional props can also be passed to your component here, if needed. + ```astro title="src/storyblok/BlogPostList.astro" --- -import { storyblokEditable } from '@storyblok/astro' -import { useStoryblokApi } from '@storyblok/astro' +import { storyblokEditable } from "@storyblok/astro"; +import { useStoryblokApi } from "@storyblok/astro"; const storyblokApi = useStoryblokApi(); -const { data } = await storyblokApi.get('cdn/stories', { +const { data } = await storyblokApi.get("cdn/stories", { version: import.meta.env.DEV ? "draft" : "published", - content_type: 'blogPost', -}) + content_type: "blogPost", +}); -const posts = data.stories.map(story => { +const posts = data.stories.map((story: any) => { return { title: story.content.title, - date: new Date(story.published_at).toLocaleDateString("en-US", {dateStyle: "full"}), + date: new Date(story.published_at).toLocaleDateString("en-US", { + dateStyle: "full", + }), description: story.content.description, slug: story.full_slug, - } -}) + }; +}); -const { blok } = Astro.props +const { blok } = Astro.props; ---
        - {posts.map(post => ( -
      • - - {post.title} -

        {post.description}

        -
      • - ))} + { + posts.map((post: any) => ( +
      • + + {post.title} +

        {post.description}

        +
      • + )) + }
      ``` @@ -405,8 +410,8 @@ Create a new directory `src/pages/blog/` and add a new file called `[...slug].as ```astro title="src/pages/blog/[...slug].astro" --- -import { useStoryblokApi } from '@storyblok/astro' -import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro' +import { useStoryblokApi } from "@storyblok/astro"; +import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro"; export async function getStaticPaths() { const sbApi = useStoryblokApi(); @@ -418,7 +423,7 @@ export async function getStaticPaths() { const stories = Object.values(data.stories); - return stories.map((story) => { + return stories.map((story: any) => { return { params: { slug: story.slug }, }; diff --git a/src/content/docs/en/guides/cms/strapi.mdx b/src/content/docs/en/guides/cms/strapi.mdx index 5a83bc07714f9..bce096853f67d 100644 --- a/src/content/docs/en/guides/cms/strapi.mdx +++ b/src/content/docs/en/guides/cms/strapi.mdx @@ -128,6 +128,11 @@ export default interface Article { createdAt: string; updatedAt: string; publishedAt: string; + image: { + data: { + url: string; + }; + }; } ``` @@ -268,8 +273,9 @@ const article = Astro.props; Create the template for each page using the properties of each post object. -```astro title="src/pages/blog/[slug].astro" ins={21-43} +```astro title="src/pages/blog/[slug].astro" ins={22-44} --- +import MyMarkdownComponent from '../../components/MyMarkdownComponent.astro'; import fetchApi from '../../lib/strapi'; import type Article from '../../interfaces/article'; @@ -325,8 +331,9 @@ Create the `src/pages/blog/[slug].astro` file: ```astro title="src/pages/blog/[slug].astro" --- -import fetchApi from '../../../lib/strapi'; -import type Article from '../../../interfaces/article'; +import MyMarkdownComponent from '../../components/MyMarkdownComponent.astro'; +import fetchApi from '../../lib/strapi'; +import type Article from '../../interfaces/article'; const { slug } = Astro.params; diff --git a/src/content/docs/en/guides/cms/tina-cms.mdx b/src/content/docs/en/guides/cms/tina-cms.mdx index 06a70801dbaad..a2aa7acb07b2a 100644 --- a/src/content/docs/en/guides/cms/tina-cms.mdx +++ b/src/content/docs/en/guides/cms/tina-cms.mdx @@ -86,11 +86,11 @@ To get started, you'll need an existing Astro project. Editing the “Hello, World!” post will update the `content/posts/hello-world.md` file in your project directory. -4. Set up your Tina collections by editing the `schema.collections` property in `.tina/config.ts`. +4. Set up your Tina collections by editing the `schema.collections` property in `tina/config.ts`. For example, you can add a required "date posted" frontmatter property to our posts: - ```js title=".tina/config.ts" ins={35-40} + ```js title="tina/config.ts" ins={35-40} import { defineConfig } from "tinacms"; // Your hosting provider likely exposes this as an environment variable diff --git a/src/content/docs/en/guides/cms/umbraco.mdx b/src/content/docs/en/guides/cms/umbraco.mdx index 91f7910b4bfdb..f54a30d9ede6c 100644 --- a/src/content/docs/en/guides/cms/umbraco.mdx +++ b/src/content/docs/en/guides/cms/umbraco.mdx @@ -57,7 +57,7 @@ const articles = await res.json(); ---

      Astro + Umbraco 🚀

      { - articles.items.map((article) => ( + articles.items.map((article: any) => (

      {article.name}

      {article.properties.articleDate}

      @@ -158,7 +158,7 @@ Note that the `params` property, which generates the URL path of the page, conta Add the following code to `[...slug].astro` which will create your individual blog post pages: -```astro title="[...slug].astro" +```astro title="src/pages/[...slug].astro" --- import Layout from '../layouts/Layout.astro'; diff --git a/src/content/docs/en/guides/cms/wordpress.mdx b/src/content/docs/en/guides/cms/wordpress.mdx index b56aac9bf72ca..8bc76efceb249 100644 --- a/src/content/docs/en/guides/cms/wordpress.mdx +++ b/src/content/docs/en/guides/cms/wordpress.mdx @@ -43,7 +43,7 @@ const posts = await res.json(); ---

      Astro + WordPress 🚀

      { - posts.map((post) => ( + posts.map((post: any) => (

      )) @@ -95,11 +95,12 @@ import Layout from "../layouts/Layout.astro"; let res = await fetch("https://norian.studio/wp-json/wp/v2/dinos"); let posts = await res.json(); --- +

      List of Dinosaurs

      { - posts.map((post) => ( + posts.map((post: any) => (

      @@ -118,7 +119,7 @@ The page `src/pages/dinos/[slug].astro` [dynamically generates a page](/en/guide ```astro title="/src/pages/dinos/[slug].astro" --- -import Layout from '../../layouts/Layout.astro'; +import Layout from "../../layouts/Layout.astro"; const { slug } = Astro.params; @@ -131,12 +132,13 @@ export async function getStaticPaths() { let data = await fetch("https://norian.studio/wp-json/wp/v2/dinos"); let posts = await data.json(); - return posts.map((post) => ({ + return posts.map((post: any) => ({ params: { slug: post.slug }, props: { post: post }, })); } --- +

      diff --git a/src/content/docs/en/guides/deploy/ishosting.mdx b/src/content/docs/en/guides/deploy/ishosting.mdx new file mode 100644 index 0000000000000..212033b568e98 --- /dev/null +++ b/src/content/docs/en/guides/deploy/ishosting.mdx @@ -0,0 +1,16 @@ +--- +title: Deploy your Astro Site to is*hosting +description: How to deploy your Astro site to the web using is*hosting +sidebar: + label: is*hosting +type: deploy +logo: ishosting +supports: ['ssr', 'static'] +i18nReady: true +--- + +[is\*hosting](https://ishosting.com/) is a hosting provider offering VPS and dedicated servers in 40+ locations that you can use to self-host a static or server-rendered (SSR) Astro site. + +## Official Resources + +- [is\*hosting guide: deploy Astro on a VPS (static and SSR)](https://blog.ishosting.com/en/astro-on-vps) diff --git a/src/content/docs/en/guides/integrations-guide/cloudflare.mdx b/src/content/docs/en/guides/integrations-guide/cloudflare.mdx index b938087f110b1..2ba16e12db204 100644 --- a/src/content/docs/en/guides/integrations-guide/cloudflare.mdx +++ b/src/content/docs/en/guides/integrations-guide/cloudflare.mdx @@ -465,16 +465,20 @@ When using these handlers in your worker entrypoint, they replace the functional For use with [`astro/fetch`](/en/reference/modules/astro-fetch/). The `cf()` function imported from `@astrojs/cloudflare/fetch` receives a [`FetchState`](/en/reference/modules/astro-fetch/#fetchstate), the Cloudflare `env`, and the `ExecutionContext`. It returns a `Response` for static asset hits, or `undefined` when the request should continue to Astro rendering: +

      + +Pass the same `FetchState` and the response from your Astro pipeline to `finalize()` before returning it. This applies cookies produced during rendering and the adapter's default Cloudflare CDN cache headers to the response. + ```ts title="src/worker.ts" import { astro, FetchState } from 'astro/fetch'; -import { cf } from '@astrojs/cloudflare/fetch'; +import { cf, finalize } from '@astrojs/cloudflare/fetch'; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const state = new FetchState(request); const asset = await cf(state, env, ctx); if (asset) return asset; - return astro(state); + return finalize(state, await astro(state)); }, }; ``` @@ -485,6 +489,8 @@ export default { For use with [`astro/hono`](/en/reference/modules/astro-hono/). The `cf()` function imported from `@astrojs/cloudflare/hono` returns a Hono middleware that reads `env` and `executionCtx` from the Hono context automatically: +In `@astrojs/cloudflare` v14.3.0 and later, this middleware also finalizes the response after downstream Hono handlers run. Cookies produced during rendering and the adapter's default Cloudflare CDN cache headers are applied automatically. + ```ts title="src/worker.ts" import { Hono } from 'hono'; import { actions, middleware, pages, i18n } from 'astro/hono'; diff --git a/src/content/docs/en/guides/integrations-guide/react.mdx b/src/content/docs/en/guides/integrations-guide/react.mdx index 5109ab8fe6f98..815a3a2c9387f 100644 --- a/src/content/docs/en/guides/integrations-guide/react.mdx +++ b/src/content/docs/en/guides/integrations-guide/react.mdx @@ -13,6 +13,10 @@ import Since from '~/components/Since.astro'; This **[Astro integration][astro-integration]** enables rendering and client-side hydration for your [React](https://react.dev/) components. +:::tip[Upgrading to v7.0.0?] +`@astrojs/react` v7.0.0 removes the `babel` integration option. See [Upgrading the React integration to v7.0.0](#upgrading-the-react-integration-to-v700) for migration instructions. +::: + ## Installation Astro includes an `astro add` command to automate the setup of official integrations. If you prefer, you can [install integrations manually](#manual-install) instead. @@ -223,6 +227,93 @@ export default defineConfig({ }); ``` +### React Compiler + +

      + +**Type:** `boolean | object`
      +**Default:** `false`
      + +

      + +By default, `@astrojs/react` uses [Oxc](https://oxc.rs/) to compile your JSX and enable Fast Refresh. It doesn't memoize your components or hooks. + +Set `compiler: true` to automatically memoize client components and hooks with the [experimental Oxc React Compiler](https://oxc.rs/docs/guide/usage/transformer/react-compiler). This can reduce unnecessary re-renders without writing `useMemo()`, `useCallback()`, or `React.memo()` yourself. + +The compiler requires the installation of [`oxc-transform-react`](https://www.npmjs.com/package/oxc-transform-react): + + + + ```sh + npm install -D oxc-transform-react + ``` + + + ```sh + pnpm add -D oxc-transform-react + ``` + + + ```sh + yarn add -D oxc-transform-react + ``` + + + +The compiler targets your installed React version. React 17 and 18 don't ship the compiler runtime helpers. If your project uses one of these versions, also install [`react-compiler-runtime`](https://www.npmjs.com/package/react-compiler-runtime): + + + + ```sh + npm install react-compiler-runtime + ``` + + + ```sh + pnpm add react-compiler-runtime + ``` + + + ```sh + yarn add react-compiler-runtime + ``` + + + +Then, enable the compiler in your React integration: + +```js title="astro.config.mjs" ins={6} +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; + +export default defineConfig({ + integrations: [ + react({ compiler: true }), + ], +}); +``` + +You can also pass an object for finer control over the compiler configuration. + +The following example configures `compilationMode` to compile only components and hooks marked with a `"use memo"` directive: + +```js title="astro.config.mjs" {7-9} +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; + +export default defineConfig({ + integrations: [ + react({ + compiler: { + compilationMode: 'annotation', + }, + }), + ], +}); +``` + +The compiler applies wherever the integration's [`include` and `exclude` options](#combining-multiple-jsx-frameworks) apply. It skips server rendering, dependencies, and `.astro` files. + ### Children parsing Children passed into a React component from an Astro component are parsed as plain strings, not React nodes. @@ -280,6 +371,66 @@ export default defineConfig({ }); ``` +## Upgrading the React integration to v7.0.0 + +`@astrojs/react` v7.0.0 replaces Babel with [Oxc](https://oxc.rs/) to compile JSX and enable Fast Refresh, and upgrades to `@vitejs/plugin-react` v6. + +### Removed: `babel` option + +Configure custom Babel transforms with [`@rolldown/plugin-babel`](https://github.com/rolldown/plugins/tree/main/packages/babel) in [`vite.plugins`](/en/reference/configuration-reference/#vite) instead of the removed `babel` option. + +Install `@rolldown/plugin-babel` and `@babel/core`: + + + + ```sh + npm install -D @rolldown/plugin-babel @babel/core + ``` + + + ```sh + pnpm add -D @rolldown/plugin-babel @babel/core + ``` + + + ```sh + yarn add -D @rolldown/plugin-babel @babel/core + ``` + + + +Then, move your Babel plugins and presets to a `babel()` plugin under `vite.plugins`. + +The following example moves `babel-plugin-styled-components` out of the removed `babel` option: + +```js title="astro.config.mjs" del={7-11} ins={2,12,14-20} +import react from '@astrojs/react'; +import babel from '@rolldown/plugin-babel'; +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + integrations: [ + react({ + babel: { + plugins: ['babel-plugin-styled-components'], + }, + }), + react(), + ], + vite: { + plugins: [ + babel({ + plugins: ['babel-plugin-styled-components'], + }), + ], + }, +}); +``` + +For conditional transforms previously configured with a `babel` callback, see the [`overrides` and preset hooks of `@rolldown/plugin-babel`](https://github.com/rolldown/plugins/tree/main/packages/babel#options). + +You can combine `babel()` with [`compiler: true`](#react-compiler). If your Babel configuration includes `babel-plugin-react-compiler`, remove it first. This avoids applying React Compiler transformations twice to the same components. + [astro-integration]: /en/guides/integrations/ [astro-ui-frameworks]: /en/guides/framework-components/#using-framework-components diff --git a/src/content/docs/en/guides/media/cloudinary.mdx b/src/content/docs/en/guides/media/cloudinary.mdx index cf6dfd189f74f..e35e9999d9135 100644 --- a/src/content/docs/en/guides/media/cloudinary.mdx +++ b/src/content/docs/en/guides/media/cloudinary.mdx @@ -121,7 +121,7 @@ The Cloudinary Astro SDK provides the `cldAssetsLoader` content loader to load C To load a collection of images or videos, set `loader: cldAssetsLoader ({})` with a `folder`, if required: -```jsx title="config.ts" +```jsx title="content.config.ts" import { defineCollection } from 'astro:content'; import { cldAssetsLoader } from 'astro-cloudinary/loaders'; diff --git a/src/content/docs/en/guides/media/imagekit.mdx b/src/content/docs/en/guides/media/imagekit.mdx index 9c90d0feed52d..1ae6153ec573c 100644 --- a/src/content/docs/en/guides/media/imagekit.mdx +++ b/src/content/docs/en/guides/media/imagekit.mdx @@ -428,7 +428,7 @@ const gallery = defineCollection({ })); }, schema: z.object({ - url: z.string().url(), + url: z.url(), width: z.number(), height: z.number(), name: z.string(), diff --git a/src/content/docs/en/guides/media/mux.mdx b/src/content/docs/en/guides/media/mux.mdx index 1600f85a5be7e..522499abf2017 100644 --- a/src/content/docs/en/guides/media/mux.mdx +++ b/src/content/docs/en/guides/media/mux.mdx @@ -86,6 +86,10 @@ You will need the `playbackId` for your asset, which can be found in your Mux da All other [options to control the Mux web player](https://www.mux.com/docs/guides/player-api-reference/?utm_campaign=21819274-Astro&utm_source=astro-docs) (e.g. hide or display controls, style elements, disable cookies) are optional: ```astro title="src/components/StarlightVideo.astro" +--- +import { MuxPlayer } from "@mux/mux-player-astro"; +--- + - ``` Every live stream is recorded and saved on Mux as a video asset for future on-demand playback. @@ -211,7 +218,7 @@ const mux = new Mux ({ To fetch information about your video to use in your Astro project, provide the video's `ASSET_ID` (available in the Mux dashboard) to the `retrieve()` helper function. This will allow you to pass values to both your Mux components and your HTML template, such as the video's title or duration: -```astro +```astro title="src/components/StarlightVideo.astro" --- import Mux from "@mux/mux-node"; import { MuxPlayer } from "@mux/mux-player-astro"; @@ -278,9 +285,9 @@ Install the Astro version of Mux Uploader using your preferred package manager: Before uploading a video, make sure you have your [Mux API access tokens](#mux-environment-api-access) configured. With those in place, you can use the `create()` function from the Mux Node SDK to start a new video upload: -```astro +```astro title="src/components/VideoUploader.astro" --- -import Layout from '../../layouts/Layout.astro'; +import Layout from '../layouts/Layout.astro'; import Mux from "@mux/mux-node"; import { MuxUploader } from "@mux/mux-uploader-astro"; @@ -306,7 +313,7 @@ const upload = await mux.video.uploads.create({ You can customize the functionality and appearance of the `` with additional component attributes. In addition to styling your element, this allows you to control options such as the ability to pause a download or set a maximum file size. -```astro +```astro title="src/components/VideoUploader.astro" --- import { MuxUploader } from '@mux/mux-uploader-astro'; --- @@ -331,7 +338,7 @@ Mux Uploader provides a feature-rich, dynamic UI that changes based on the curre You can listen for these events and handle them in your Astro component with [client-side scripts](/en/guides/client-side-scripts/). A `MuxUploaderElement` type is also available. -```astro +```astro title="src/components/VideoUploader.astro" --- import { MuxUploader } from '@mux/mux-uploader-astro'; --- @@ -373,4 +380,4 @@ For the full API and webhook reference, usage guides, and information about addi - [`@mux/mux-player-astro` API reference](https://github.com/muxinc/elements/blob/main/packages/mux-player-astro/README.md) - [`@mux/mux-uploader-astro` API reference](https://github.com/muxinc/elements/blob/main/packages/mux-uploader-astro/REFERENCE.md) - [Building a video uploader with Mux and Astro (YouTube)](https://www.youtube.com/watch?v=aaL1k5FsWfE) -- [Astro uploader and player code example (GitHub)](https://github.com/muxinc/examples/tree/main/astro-uploader-and-player) \ No newline at end of file +- [Astro uploader and player code example (GitHub)](https://github.com/muxinc/examples/tree/main/astro-uploader-and-player) diff --git a/src/content/docs/en/reference/cache-provider-reference.mdx b/src/content/docs/en/reference/cache-provider-reference.mdx index fa6375fdd7391..b46bd571470e0 100644 --- a/src/content/docs/en/reference/cache-provider-reference.mdx +++ b/src/content/docs/en/reference/cache-provider-reference.mdx @@ -371,10 +371,45 @@ The incoming `request` is passed as a second argument so a provider can read the

      -**Type:** (context: \{ request: Request; url: URL; waitUntil?: (promise: Promise\) => void \}, next: MiddlewareNext) => Promise\ +**Type:** (context: \{ request: Request; url: URL; waitUntil?: (promise: Promise\) => void; logger: AstroRuntimeLogger \}, next: MiddlewareNext) => Promise\

      -Intercepts requests to implement runtime caching. The `context` includes a `waitUntil()` function (when available in the runtime) for background work such as stale-while-revalidate. +An optional hook that intercepts a request before Astro generates the matching route. It receives a `context` object as its first argument and a callback to call the `next()` middleware in the chain. + +The `context` contains the following properties: +- `request`: the incoming [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. +- `url`: a normalized [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) derived from the request. +- `waitUntil()`: when available in the runtime, a function to define background work, such as revalidating a stale cache entry. +- `logger`: since Astro v7.3.0, a [`logger`](/en/reference/api-reference/#logger) instance that respects the [configured logging destination](/en/reference/configuration-reference/#logger-options) + +The following example implements a minimal `onRequest()` hook that logs each URL added to the cache: + +```ts title="my-provider/runtime.ts" ins={7-17} +import type { CacheProviderFactory } from 'astro'; + +const factory: CacheProviderFactory = (config) => { + const cache = new Map(); + return { + name: 'my-cache-provider', + async onRequest({ request, url, waitUntil, logger }, next) { + if (request.method !== 'GET') return next(); + + const cached = cache.get(url); + if (cached) return cached; + + const response = await next(); + cache.set(url, response.clone()); + logger.info(`Cached response for ${url}.`); + return response; + }, + async invalidate() { + // ... + }, + }; +}; + +export default factory; +``` #### `CacheProvider.invalidate()` diff --git a/src/content/docs/en/reference/cli-reference.mdx b/src/content/docs/en/reference/cli-reference.mdx index 98442f1ecac00..90d1f51a75470 100644 --- a/src/content/docs/en/reference/cli-reference.mdx +++ b/src/content/docs/en/reference/cli-reference.mdx @@ -196,25 +196,7 @@ The following hotkeys can be used in the terminal where the Astro development se - `o + enter` to open your Astro site in the browser. - `q + enter` to quit the development server. -

      Flags

      - -

      - -The command accepts [common flags](#common-flags) and the following additional flags. - -#### `--ignore-lock` - -

      - -Starts the dev server without checking or writing the lock file used to detect other running dev servers. This allows a new dev server to start alongside one that's already running for the same project, instead of erroring. - -```shell -astro dev --ignore-lock --port 4322 -``` - -The new server is not tracked by the [`stop`, `status`, or `logs` subcommands](#common-subcommands). - -When combined with `--background` (including when triggered by an AI coding agent) or `--force`, an error is thrown, as both rely on the lock file. +The command can be combined with the [common flags](#common-flags) and [common subcommands](#common-subcommands) to further control the dev experience. ## `astro build` @@ -240,7 +222,7 @@ The following hotkeys can be used in the terminal where the Astro preview server - `o` + `enter` to open your Astro site in the browser. - `q` + `enter` to quit the preview server. -The `astro preview` command can be combined with the [common flags](#common-flags) documented below to further control the preview experience. Since v7.2.0, it also accepts the [`--background` flag](#--background) and the [`stop`, `status`, and `logs` subcommands](#common-subcommands) to manage a background preview server. +The command can be combined with the [common flags](#common-flags) and [common subcommands](#common-subcommands) to further control the preview experience. ## `astro check` @@ -540,7 +522,7 @@ Starts the dev server, or the preview server since v7.2.0, as a detached backgro When the server starts, Astro writes a lock file (`.astro/dev.json` or `.astro/preview.json`) to record the server's URL, port, and PID. This avoids launching many instances of the server for the same project. -This flag is provided automatically when an AI agent is detected. You can also use it manually: +This flag is provided automatically when an AI agent is detected on macOS and Linux. On Windows, automatic backgrounding does not happen: when an AI agent is detected, the server stays in the foreground so the agent can manage the process directly. On all platforms, you can also use the flag manually: ```shell astro dev --background @@ -560,6 +542,20 @@ astro dev --background --force Enables [JSON logging](/en/reference/logger-reference/#loghandlersjson), which is useful for machine-readable output. +### `--ignore-lock` + +

      + +Prevents checking for the existence of a lock file and the need to write one. This allows a new dev server or, since v7.3.0, a preview server to start alongside an already running server, instead of erroring. + +```shell +astro dev --ignore-lock --port 4322 +``` + +The new server is not tracked by the [common subcommands](#common-subcommands). + +When combined with [`--background`](#--background) or [`--force`](#--force-string), an error is thrown, as both rely on the lock file. + ## Global flags Use these flags to get information about the `astro` CLI. diff --git a/src/content/docs/en/reference/errors/redirect-with-no-location.mdx b/src/content/docs/en/reference/errors/redirect-with-no-location.mdx index a71b66e5bbc61..41157653f393d 100644 --- a/src/content/docs/en/reference/errors/redirect-with-no-location.mdx +++ b/src/content/docs/en/reference/errors/redirect-with-no-location.mdx @@ -13,6 +13,8 @@ import DontEditWarning from '~/components/DontEditWarning.astro' +> **RedirectWithNoLocation**: The redirect `Response` has no `Location` header. Use `Astro.redirect()` to create redirects, or add a `Location` header to the `Response`. + ## What went wrong? A redirect must be given a location with the `Location` header. diff --git a/src/content/docs/en/reference/image-service-reference.mdx b/src/content/docs/en/reference/image-service-reference.mdx index 344e665ad3bdd..1998346d0b77e 100644 --- a/src/content/docs/en/reference/image-service-reference.mdx +++ b/src/content/docs/en/reference/image-service-reference.mdx @@ -31,21 +31,21 @@ An external service points to a remote URL to be used as the `src` attribute of import type { ExternalImageService, ImageTransform, AstroConfig } from "astro"; const service: ExternalImageService = { - validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) { + validateOptions(options: ImageTransform, imageConfig: AstroConfig['image'], logger) { const serviceConfig = imageConfig.service.config; // Enforce the user set max width. if (options.width && options.width > serviceConfig.maxWidth) { - console.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`); + logger.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`); options.width = serviceConfig.maxWidth; } return options; }, - getURL(options, imageConfig) { + getURL(options, imageConfig, logger) { return `https://mysupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`; }, - getHTMLAttributes(options, imageConfig) { + getHTMLAttributes(options, imageConfig, logger) { const { src, format, quality, ...attributes } = options; return { ...attributes, @@ -68,7 +68,7 @@ import type { ImageTransform, LocalImageService, AstroConfig } from "astro"; import { mySuperLibraryThatEncodesImages } from "@example/my-super-library"; const service: LocalImageService = { - getURL(options: ImageTransform, imageConfig) { + getURL(options: ImageTransform, imageConfig, logger) { const searchParams = new URLSearchParams(); searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src); options.width && searchParams.append('w', options.width.toString()); @@ -79,7 +79,7 @@ const service: LocalImageService = { // Or use the built-in endpoint, which will call your parseURL and transform functions: // return `/_image?${searchParams}`; }, - parseURL(url: URL, imageConfig) { + parseURL(url: URL, imageConfig, logger) { const params = url.searchParams; return { src: params.get('href')!, @@ -89,14 +89,14 @@ const service: LocalImageService = { quality: params.get('q'), }; }, - async transform(inputBuffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig) { + async transform(inputBuffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig, logger) { const { buffer } = await mySuperLibraryThatEncodesImages(options); return { data: buffer, format: options.format, }; }, - getHTMLAttributes(options, imageConfig) { + getHTMLAttributes(options, imageConfig, logger) { let targetWidth = options.width; let targetHeight = options.height; if (typeof options.src === "object") { @@ -141,7 +141,7 @@ import { getConfiguredImageService, imageConfig } from "astro:assets"; import * as mime from "mrmime"; import { getImageBuffer } from "./my-custom-image-fetcher.js"; -export const GET: APIRoute = async ({ request }) => { +export const GET: APIRoute = async ({ request, logger }) => { const imageService = await getConfiguredImageService(); if (!isLocalService(imageService)) { @@ -154,6 +154,7 @@ export const GET: APIRoute = async ({ request }) => { const imageTransform = await imageService.parseURL( new URL(request.url), imageConfig, + logger, ); if (!imageTransform) { @@ -166,6 +167,7 @@ export const GET: APIRoute = async ({ request }) => { inputBuffer, imageTransform, imageConfig, + logger, ); return new Response(new Uint8Array(data), { status: 200, @@ -185,7 +187,7 @@ export const GET: APIRoute = async ({ request }) => {

      -**Type:** (options: ImageTransform, imageConfig: AstroConfig['image']) => string | Promise\
      +**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => string | Promise\

      @@ -195,57 +197,71 @@ For local services, this hook returns the URL of the endpoint that generates you For external services, this hook returns the final URL of the image. -For both types of services, `options` are the properties passed by the user as attributes of the `` component or as options to `getImage()`. +For both types of services, `options` are the properties passed by the user as attributes of the `` component or as options to `getImage()`. This hook also receives the image configuration and, since Astro v7.3.0, a logger. ### `parseURL()`

      -**Type:** (url: URL, imageConfig: AstroConfig['image']) => \{ src: string, [key: string]: any \} | undefined | Promise\<\{ src: string, [key: string]: any \}\> | Promise\
      +**Type:** (url: URL, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => \{ src: string, [key: string]: any \} | undefined | Promise\<\{ src: string, [key: string]: any \}\> | Promise\

      **Required for local services only; unavailable for external services** -This hook parses the generated URLs by `getURL()` back into an object with the different properties to be used by `transform` (for on-demand rendering and in dev mode). It is unused during build. +This hook parses the generated URLs by `getURL()` back into an object with the different properties to be used by `transform`. This receives three parameters: the URL to parse, the image configuration and, since Astro v7.3.0, a logger. + +This hook is used only for on-demand rendering and in development mode. It is unused during build. ### `transform()`

      -**Type:** (inputBuffer: Uint8Array, options: \{ src: string, [key: string]: any \}, imageConfig: AstroConfig['image']) => Promise\<\{ data: Uint8Array; format: ImageOutputFormat \}\>
      +**Type:** (inputBuffer: Uint8Array, options: \{ src: string, [key: string]: any \}, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Promise\<\{ data: Uint8Array; format: ImageOutputFormat \}\>

      **Required for local services only; unavailable for external services** -This hook transforms and returns the image and is called during the build to create the final asset files. +This hook transforms and returns the image and is called during the build to create the final asset files. This receives four parameters: the input image, an options object, the image configuration and, since Astro v7.3.0, a logger. + +You must return a `format` to ensure that the proper MIME type is served to users for on-demand rendering and development mode: + +```ts +import type { LocalImageService } from 'astro'; -You must return a `format` to ensure that the proper MIME type is served to users for on-demand rendering and development mode. +const service: LocalImageService = { + // ... + async transform(inputBuffer, transform, imageConfig, logger) { + logger.warn(`Could not optimize "${transform.src}". Passing it through unchanged.`); + return { data: inputBuffer, format: 'png' }; + }, +}; +``` ### `getHTMLAttributes()`

      -**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => Record\ | Promise\\>
      +**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Record\ | Promise\\>

      **Optional for both local and external services** -This hook returns all additional attributes used to render the image as HTML, based on the parameters passed by the user (`options`). +This hook returns all additional attributes used to render the image as HTML, based on the parameters passed by the user (`options`). It also receives the image configuration and, since Astro v7.3.0, a logger. ### `getSrcSet()`

      -**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => UnresolvedSrcSetValue[] | Promise\
      +**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => UnresolvedSrcSetValue[] | Promise\

      **Optional for both local and external services.** -This hook generates multiple variants of the specified image, for example, to generate a `srcset` attribute on an `` or ``'s `source`. +This hook generates multiple variants of the specified image, for example, to generate a `srcset` attribute on an `` or ``'s `source`. This receives three parameters: an options object, the image configuration and, since Astro v7.3.0, a logger. This hook returns an array of objects with the following properties: @@ -261,13 +277,13 @@ export type UnresolvedSrcSetValue = {

      -**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => ImageTransform | Promise\ +**Type:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => ImageTransform | Promise\

      **Optional for both local and external services** -This hook allows you to validate and augment the options passed by the user. This is useful for setting default options, or telling the user that a parameter is required. +This hook allows you to validate and augment the options passed by the user. This is useful for setting default options, or telling the user that a parameter is required. It also receives the image configuration and, since Astro v7.3.0, a logger you can use to warn the user about invalid options. [See how `validateOptions()` is used in Astro built-in services](https://github.com/withastro/astro/blob/0ab6bad7dffd413c975ab00e545f8bc150f6a92f/packages/astro/src/assets/services/service.ts#L124). @@ -275,13 +291,13 @@ This hook allows you to validate and augment the options passed by the user. Thi

      -**Type:** (url: string, imageConfig: AstroConfig['image'] ) => Omit\<ImageMetadata, 'src' | 'fsPath'\> | Promise\ImageMetadata, 'src' | 'fsPath'\>\> +**Type:** (url: string, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Omit\<ImageMetadata, 'src' | 'fsPath'\> | Promise\ImageMetadata, 'src' | 'fsPath'\>\>

      **Optional for both local and external services** -This hook allows you to extend the behavior of [`inferRemoteSize()`](/en/reference/modules/astro-assets/#inferremotesize). This is useful for reducing network traffic by caching images, or when you can predict image information from the image URL. +This hook allows you to extend the behavior of [`inferRemoteSize()`](/en/reference/modules/astro-assets/#inferremotesize). This is useful for reducing network traffic by caching images, or when you can predict image information from the image URL. This receives three parameters: the image URL, the image configuration and, since Astro v7.3.0, a logger. ## User configuration diff --git a/src/content/docs/en/reference/modules/astro-assets.mdx b/src/content/docs/en/reference/modules/astro-assets.mdx index 774cb2e15bd18..52de5ee406d98 100644 --- a/src/content/docs/en/reference/modules/astro-assets.mdx +++ b/src/content/docs/en/reference/modules/astro-assets.mdx @@ -453,6 +453,11 @@ The background color to use when flattening an image to transform it into the re By default, Sharp uses a black background when flattening an image. Specifying a different background color is especially useful when transforming images with transparent backgrounds to a format that does not support transparency (e.g. `.jpeg`): ```astro title="src/components/MyComponent.astro" "background" +--- +import { Image } from 'astro:assets'; +import myImage from '../assets/my_image.png'; +--- + A description of my image` component. -This takes an options object with the [same properties as the Image component](#image-) (except `alt`) and returns a [`GetImageResult` object](#getimageresult). +This takes an options object with the [same properties as the Image component](#image-) (except `alt` and `sizes`) and returns a [`GetImageResult` object](#getimageresult). The following example generates an AVIF `background-image` for a `
      `: @@ -739,12 +744,13 @@ When called on a route [rendered on-demand](/en/guides/on-demand-rendering/), th ```ts "context.url" import type { APIRoute } from "astro"; -import { fontData, experimental_getFontFileURL } from "astro:assets" +import { fontData, experimental_getFontFileURL } from "astro:assets"; export const prerender = false; // Not needed in 'server' mode export const GET: APIRoute = async (context) => { // ... + const fontPath = fontData["--font-roboto"][0]?.src[0]?.url; const url = experimental_getFontFileURL(fontPath, context.url); // ... }; @@ -859,11 +865,13 @@ The following example reuses the `baseService` to create a new image service: import { baseService } from "astro/assets"; const newImageService = { - getURL: baseService.getURL, - parseURL: baseService.parseURL, - getHTMLAttributes: baseService.getHTMLAttributes, - async transform(inputBuffer, transformOptions) {...} -} + getURL: baseService.getURL, + parseURL: baseService.parseURL, + getHTMLAttributes: baseService.getHTMLAttributes, + async transform(inputBuffer, transformOptions) { + /* ... */ + }, +}; ``` ### `getConfiguredImageService()` @@ -1423,7 +1431,9 @@ A value ready to use in the `srcset` attribute. **Type:** `object`

      -Defines the options accepted by the image transformation service. This contains a required `src` property, optional predefined properties, and any additional properties required by the image service: +Defines the options accepted by the image transformation service. This contains a required `src` property, optional predefined properties, and any additional properties required by the image service. + +The predefined properties match those accepted by the [`` component](#image-), except for `alt` and `sizes`. The following properties use different types. #### `ImageTransform.src` @@ -1452,64 +1462,6 @@ The width of the image. The height of the image. -#### `ImageTransform.widths` - -

      - -**Type:** `number[] | undefined`
      - -

      - -A list of widths to generate for the image. - -#### `ImageTransform.densities` - -

      - -**Type:** ``(number | `${number}x`)[] | undefined``
      - -

      - -A list of pixel densities to generate for the image. - -#### `ImageTransform.quality` - -

      - -**Type:** ImageQuality | undefined -

      - -The desired quality for the output image. - -#### `ImageTransform.format` - -

      - -**Type:** ImageOutputFormat | undefined -

      - -The desired format for the output image. - -#### `ImageTransform.fit` - -

      - -**Type:** `'fill' | 'contain' | 'cover' | 'none' | 'scale-down' | string | undefined`
      - -

      - -Defines a list of allowed values for the `object-fit` CSS property, extensible with any string. - -#### `ImageTransform.position` - -

      - -**Type:** `string | undefined`
      - -

      - -Controls the value for the `object-position` CSS property. - ### `UnresolvedImageTransform`

      diff --git a/src/content/docs/en/reference/renderer-reference.mdx b/src/content/docs/en/reference/renderer-reference.mdx index ac0b478b38163..a52142f7c8142 100644 --- a/src/content/docs/en/reference/renderer-reference.mdx +++ b/src/content/docs/en/reference/renderer-reference.mdx @@ -151,8 +151,16 @@ Defines the [client directive](/en/reference/directives-reference/#client-direct Renderers can use this value to conditionally include client-side hydration state. For example, a renderer can skip serializing transfer state for components that will not be hydrated: -```ts -async function renderToStaticMarkup(Component, props, children, metadata) { +```ts title="my-renderer/server.ts" +import type { AstroComponentMetadata } from 'astro'; +import { render } from './custom-render'; + +async function renderToStaticMarkup( + Component: any, + props: Record, + slots: Record, + metadata?: AstroComponentMetadata, +) { const willHydrate = !!metadata?.hydrate; // Skip serializing hydration state if the component won't be hydrated return render(Component, props, { includeTransferState: willHydrate }); diff --git a/src/content/docs/en/reference/routing-reference.mdx b/src/content/docs/en/reference/routing-reference.mdx index df0d1ccf64a2f..c48b3f242063e 100644 --- a/src/content/docs/en/reference/routing-reference.mdx +++ b/src/content/docs/en/reference/routing-reference.mdx @@ -247,7 +247,7 @@ const { page } = Astro.props; - `pageSize` - The number of items shown per page (`10` by default) - `params` - Send additional parameters for creating dynamic routes - `props` - Send additional props to be available on each page - - `format` - **Since v7.1.0.** A function that allows to manipulate the computed URLs before being rendered. + - `format` - **Since v7.1.0.** A function that allows you to manipulate the computed URLs before being rendered. `paginate()` assumes a file name of `[page].astro` or `[...page].astro`. The `page` param becomes the page number in your URL: diff --git a/src/content/docs/es/basics/astro-components.mdx b/src/content/docs/es/basics/astro-components.mdx index 8168bb4164ea5..40101ef53e11f 100644 --- a/src/content/docs/es/basics/astro-components.mdx +++ b/src/content/docs/es/basics/astro-components.mdx @@ -18,7 +18,7 @@ Los componentes Astro son extremadamente flexibles. Un componente de Astro puede Lo más importante que hay que saber sobre los componentes de Astro es que **no se renderizan en el cliente**. Se renderizan en HTML en el momento de la compilación o bajo demanda. Puedes incluir código JavaScript dentro del frontmatter de tu componente, y todo será eliminado de la página final enviada a los navegadores de tus usuarios. El resultado es un sitio más rápido, sin ninguna huella de JavaScript añadida por defecto. -Cuando tu componente Astro necesite interactividad en el lado del cliente, puedes añadir [etiquetas HTML estándar ` ``` @@ -134,36 +141,53 @@ A continuación, puedes utilizar el objeto `auth` para obtener los datos de sesi ```astro title="src/pages/index.astro" --- -import { auth } from "../../../lib/auth"; // importa tu instancia de Better Auth +import { auth } from "../lib/auth"; // importa tu instancia de Better Auth export const prerender = false; // Innecesario en modo 'server' - + const session = await auth.api.getSession({ - headers: Astro.request.headers, + headers: Astro.request.headers, }); ---

      {session.user?.name}

      ``` -También puedes utilizar el objeto `auth` para proteger tus rutas mediante middleware. El siguiente ejemplo comprueba si un usuario que intenta acceder a una ruta del panel de control en la que ha iniciado sesión está autenticado y, si no es así, lo redirige a la página de inicio. +También puedes utilizar el objeto `auth` para proteger tus rutas. El siguiente ejemplo utiliza [el enrutamiento avanzado de Astro](/es/guides/routing/#enrutamiento-avanzado) con [Hono](https://hono.dev/) para requerir una sesión autenticada en todas las rutas bajo `/dashboard`, redirigiendo a la página de inicio en caso contrario: -```ts title="src/middleware.ts" -import { auth } from "../../../auth"; // importa tu instancia de Better Auth -import { defineMiddleware } from "astro:middleware"; - -export const onRequest = defineMiddleware(async (context, next) => { - const isAuthed = await auth.api - .getSession({ - headers: context.request.headers, - }) - if (context.url.pathname === "/dashboard" && !isAuthed) { - return context.redirect("/"); - } - return next(); -}); +```ts title="src/fetch.ts" +import { Hono, type Context, type Next } from "hono"; +import { astro } from "astro/hono"; +import { auth } from "./lib/auth"; // importa tu instancia de Better Auth + +const app = new Hono(); + +// Protege todas las rutas bajo /dashboard. +app.use("/dashboard", requireAuth); +app.use("/dashboard/*", requireAuth); + +// Ejecuta el pipeline integrado de Astro para todas las demás solicitudes. +app.use(astro()); + +export default app; + +async function requireAuth(c: Context, next: Next) { + const session = await auth.api.getSession({ + headers: c.req.raw.headers, + }); + if (!session) { + return c.redirect("/"); + } + return next(); +} ``` +:::caution +No hay garantía de que el pathname público que ve un middleware sea el mismo que la ruta que Astro resuelve internamente: una `base` configurada, la codificación de la URL y las barras duplicadas pueden hacer que difieran. Un atacante puede explotar esta brecha para acceder a una ruta protegida con un pathname que tu comprobación no reconozca. + +No autorices solicitudes comparando `context.url.pathname` con una cadena de texto (p. ej. `context.url.pathname === "/dashboard"` o `context.url.pathname.startsWith("/dashboard")`). En su lugar, restringe el acceso en un enrutador que resuelva las rutas por ti. +::: + ### Siguientes pasos - [Guía de Better Auth en Astro](https://www.better-auth.com/docs/integrations/astro) @@ -207,37 +231,67 @@ Clerk proporciona componentes que te permiten controlar la visibilidad de las p ```astro title="src/pages/index.astro" --- -import Layout from 'src/layouts/Base.astro'; -import { SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/astro/components'; +import Layout from "../layouts/Base.astro"; +import { Show, UserButton, SignInButton } from "@clerk/astro/components"; export const prerender = false; // Innecesario en modo 'server' --- + - - - - - - + + + + + + ``` -Clerk también te permite proteger rutas en el servidor usando middleware. Especifica qué rutas están protegidas y pide a los usuarios no autenticados que inicien sesión: +Clerk también te permite proteger rutas en el servidor usando middleware: -```ts title="src/middleware.ts" -import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server'; + -const isProtectedRoute = createRouteMatcher([ - '/dashboard(.*)', - '/forum(.*)', -]); +1. Configura `clerkMiddleware()` como el manejador de `onRequest` en tu middleware: -export const onRequest = clerkMiddleware((auth, context) => { - if (!auth().userId && isProtectedRoute(context.request)) { - return auth().redirectToSignIn(); - } -}); -``` + ```ts title="src/middleware.ts" + import { clerkMiddleware } from "@clerk/astro/server"; + + export const onRequest = clerkMiddleware({ + /* opciones */ + }); + ``` + +2. Accede al estado de autenticación en tus páginas y rutas de API con `locals.auth()`. Esto te permite comprobar si un usuario está autenticado y tomar las acciones adecuadas (p. ej. redirigir a la página de inicio de sesión o devolver una respuesta diferente). + + + + ```astro title="src/pages/dashboard.astro" + --- + const { isAuthenticated, redirectToSignIn } = Astro.locals.auth(); + + if (!isAuthenticated) return redirectToSignIn(); + --- + +

      Dashboard

      + ``` +
      + + ```ts title="src/pages/api/data.ts" + import type { APIRoute } from "astro"; + + export const GET: APIRoute = ({ locals }) => { + const { isAuthenticated, userId } = locals.auth(); + + if (!isAuthenticated) { + return new Response("Unauthorized", { status: 401 }); + } + + return Response.json({ userId }); + }; + ``` + +
      +
      ### Siguientes pasos @@ -248,7 +302,7 @@ export const onRequest = clerkMiddleware((auth, context) => { [Lucia](https://lucia-auth.com/) es un recurso para implementar la autenticación basada en sesiones en varios frameworks, incluido Astro. -### Guides +### Guías @@ -265,6 +319,20 @@ export const onRequest = clerkMiddleware((auth, context) => { - [Ejemplo de correo electrónico y contraseña con 2FA en Astro](https://github.com/lucia-auth/example-astro-email-password-2fa) - [Ejemplo de correo electrónico y contraseña con 2FA y WebAuthn en Astro](https://github.com/lucia-auth/example-astro-email-password-webauthn) +## Scalekit + +[Scalekit](https://scalekit.com/) es una plataforma de autenticación para aplicaciones B2B y de IA. Gestiona el flujo completo de OAuth 2.0 y OIDC, admitiendo métodos como el inicio de sesión social, SSO empresarial y enlaces mágicos. Luego, devuelve los tokens y un perfil de usuario sin requerir una UI de inicio de sesión personalizada. + +Un único entorno de Scalekit puede admitir múltiples aplicaciones. Esto te permite autenticarte una vez y compartir la misma sesión en todas tus propiedades (p. ej. `app.yourcompany.com` y `docs.yourcompany.com`). + +### Guía + +Sigue la [guía de Scalekit y Astro](/es/guides/backend/scalekit/) para agregar autenticación a tu proyecto SSR de Astro utilizando el inicio de sesión social, SSO empresarial y más. + +### Ejemplos + +- [Tutorial de un blog en Astro con autenticación de Scalekit (flujo de código de autorización)](https://github.com/scalekit-developers/astro-scalekit-auth-example) +- [Código fuente del sitio de documentación para desarrolladores de Scalekit (flujo PKCE, sin SDK)](https://github.com/scalekit-inc/developer-docs) ## Recursos de la comunidad diff --git a/src/content/docs/es/guides/backend/firebase.mdx b/src/content/docs/es/guides/backend/firebase.mdx index 0a5b66db02cb6..5f853d54d4914 100644 --- a/src/content/docs/es/guides/backend/firebase.mdx +++ b/src/content/docs/es/guides/backend/firebase.mdx @@ -233,6 +233,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { cookies.set("__session", sessionCookie, { path: "/", + maxAge: fiveDays / 1000, }); return redirect("/dashboard"); diff --git a/src/content/docs/es/guides/backend/prisma-postgres.mdx b/src/content/docs/es/guides/backend/prisma-postgres.mdx index 78bb1ac9762fd..4d9894fb73731 100644 --- a/src/content/docs/es/guides/backend/prisma-postgres.mdx +++ b/src/content/docs/es/guides/backend/prisma-postgres.mdx @@ -13,7 +13,7 @@ import ReadMore from '~/components/ReadMore.astro'; [Prisma Postgres](https://www.prisma.io/) es una base de datos Postgres totalmente gestionada y sin servidor, diseñada para aplicaciones web modernas. -## Conectar a través de Prisma ORM (recomendado) +## Conectar con Prisma ORM (recomendado) [Prisma ORM](https://www.prisma.io/orm) es la forma recomendada de conectarse a tu base de datos Prisma Postgres. Proporciona consultas seguras en cuanto a tipos, migraciones y rendimiento global. @@ -26,13 +26,13 @@ Ejecuta los siguientes comandos para instalar las dependencias necesarias de Pri ```bash npm install prisma tsx --save-dev -npm install @prisma/extension-accelerate @prisma/client +npm install @prisma/adapter-pg @prisma/client ``` Una vez instalado, inicializa Prisma en tu proyecto con el siguiente comando: ```bash -npx prisma init --db --output ../src/generated/prisma +npx prisma init --db --output ./generated ``` Deberás responder algunas preguntas mientras configuras tu base de datos Prisma Postgres. Selecciona la región más cercana a tu ubicación y un nombre fácil de recordar para tu base de datos, como "Mi proyecto Astro". @@ -47,12 +47,10 @@ Aunque aún no necesites ningún modelo de datos específico, Prisma requiere al El siguiente ejemplo define un modelo `Post` como marcador. Añade el modelo a tu esquema para empezar. Puedes eliminarlo o sustituirlo más adelante por modelos que reflejen tus datos reales. -Actualiza el proveedor del generador de `prisma-client-js` a `prisma-client` en tu archivo `prisma/schema.prisma`: - -```prisma title="prisma/schema.prisma" {2} ins={11-16} +```prisma title="prisma/schema.prisma" ins={11-16} generator client { provider = "prisma-client" - output = "../src/generated/prisma" + output = "./generated" } datasource db { @@ -70,25 +68,33 @@ model Post { Más información sobre cómo configurar Prisma ORM en la [referencia del esquema de Prisma](https://www.prisma.io/docs/concepts/components/prisma-schema). +### Generar cliente + +Ejecuta el siguiente comando para generar el Prisma Client a partir de tu esquema: + +```bash +npx prisma generate +``` + ### Generar archivos de migración -Ejecuta el siguiente comando para crear las tablas de la base de datos y generar el cliente de Prisma a partir de tu esquema. Esto también creará un directorio `prisma/migrations/` con los archivos del historial de migraciones. +Ejecuta el siguiente comando para crear las tablas de la base de datos y generar el Prisma Client a partir de tu esquema. Esto también creará un directorio `prisma/migrations/` con los archivos del historial de migraciones. ```bash npx prisma migrate dev --name init ``` -### Crear un cliente Prisma +### Crear un Prisma Client Dentro de `/src/lib`, crea un archivo `prisma.ts`. Este archivo inicializará y exportará tu instancia de Prisma Client para que puedas consultar tu base de datos en todo tu proyecto de Astro. ```typescript title="src/lib/prisma.ts" -import { PrismaClient } from "../generated/prisma/client"; -import { withAccelerate } from "@prisma/extension-accelerate"; +import { PrismaPg } from '@prisma/adapter-pg'; +import { PrismaClient } from '../../prisma/generated/client'; -const prisma = new PrismaClient({ - datasourceUrl: import.meta.env.DATABASE_URL, -}).$extends(withAccelerate()); +const connectionString = import.meta.env.DATABASE_URL; +const adapter = new PrismaPg({ connectionString }); +const prisma = new PrismaClient({ adapter }); export default prisma; ``` @@ -127,15 +133,17 @@ const posts = await prisma.post.findMany({ Lo mejor es gestionar las consultas en una ruta API. Para obtener más información sobre cómo utilizar Prisma ORM en tu proyecto de Astro, consulta la [Guía de Astro + Prisma ORM](https://www.prisma.io/docs/guides/astro). -## Conexión TCP directa -Para conectarte a Prisma Postgres a través de TCP directo, puedes crear una cadena de conexión directa en tu consola de Prisma. Esto te permite conectar cualquier otro ORM, biblioteca de bases de datos o herramienta de tu elección. +## Conectar con otros ORMs y bibliotecas -### Prerrequisitos -- Una base de datos [Prisma Postgres](https://pris.ly/ppg) con una cadena de conexión habilitada para TCP. +Puedes conectarte a Prisma Postgres a través de TCP directo usando cualquier otro ORM, biblioteca de base de datos o herramienta de tu preferencia. Crea una cadena de conexión directa en tu Prisma Console para empezar. + +### Requisitos previos +- Un proyecto de Astro con un adaptador instalado para habilitar el [renderizado bajo demanda (SSR)](/es/guides/on-demand-rendering/). +- Una base de datos de [Prisma Postgres](https://pris.ly/ppg) con una cadena de conexión con TCP habilitado. -### Instalar dependecias +### Instalar dependencias -Este ejemplo establecerá una conexión TCP directa utilizando [`pg`, un cliente PostgreSQL para Node.js](https://github.com/brianc/node-postgres). +Este ejemplo usa [`pg`, un cliente PostgreSQL para Node.js](https://github.com/brianc/node-postgres) para realizar una conexión TCP directa. Ejecuta el siguiente comando para instalar el paquete `pg`: diff --git a/src/content/docs/es/guides/build-with-ai.mdx b/src/content/docs/es/guides/build-with-ai.mdx index a7fd0c14cf63a..9db9402b5299b 100644 --- a/src/content/docs/es/guides/build-with-ai.mdx +++ b/src/content/docs/es/guides/build-with-ai.mdx @@ -9,6 +9,8 @@ i18nReady: true description: Recursos y consejos para crear sitios Astro con asistencia de IA --- +import Since from '~/components/Since.astro'; +import ReadMore from '~/components/ReadMore.astro'; import { Steps, LinkButton, Card, Tabs, TabItem } from '@astrojs/starlight/components'; Los editores potenciados por IA y las herramientas de codificación con agentes generalmente tienen un buen conocimiento de las API y conceptos principales de Astro. Sin embargo, algunos pueden usar API más antiguas y no estar al tanto de las funciones más recientes o de los cambios recientes en el framework. @@ -43,7 +45,7 @@ Muchas herramientas admiten un formato de configuración JSON común para servid - ```json title="mcp.json" {3-6} + ```json title="Configuración de MCP" {3-6} { "mcpServers": { "Astro docs": { @@ -55,7 +57,7 @@ Muchas herramientas admiten un formato de configuración JSON común para servid ``` - ```json title="mcp.json" {3-7} + ```json title="Configuración de MCP" {3-7} { "mcpServers": { "Astro docs": { @@ -357,6 +359,29 @@ La misma tecnología que impulsa el servidor MCP de Astro también está disponi **Las conversaciones con el chatbot son públicas y están sujetas a las mismas reglas del servidor sobre lenguaje y comportamiento que el resto de nuestros canales**, pero no son revisadas activamente por nuestros miembros voluntarios de soporte. Para recibir ayuda de la comunidad, crea un hilo en nuestro canal regular `#support`. +## Modo en segundo plano + +

      + +Cuando se detecta un agente de programación por IA, `astro dev` y a partir de la v7.2.0, `astro preview` inician automáticamente el servidor como un proceso en segundo plano independiente. Esto evita que el servidor bloquee la terminal del agente y le permite seguir trabajando mientras el servidor está en ejecución. + +Al iniciarse el servidor, se genera un archivo de bloqueo (`.astro/dev.json` o `.astro/preview.json`) que registra la URL, el puerto y el PID del servidor. Esto evita que se inicien servidores duplicados para un mismo proyecto. + +Si no estás utilizando un agente de programación por IA, el servidor se inicia como un proceso en primer plano y muestra los registros en la terminal. + +Para desactivar el modo automático en segundo plano, configura la variable de entorno `ASTRO_DEV_BACKGROUND` o `ASTRO_PREVIEW_BACKGROUND` antes de ejecutar el comando: + +```shell +ASTRO_DEV_BACKGROUND=0 astro dev +ASTRO_PREVIEW_BACKGROUND=0 astro preview +``` + +Consulta la referencia de la CLI para ver la lista completa de opciones y subcomandos de [`astro dev`](/es/reference/cli-reference/#astro-dev) y [`astro preview`](/es/reference/cli-reference/#astro-preview). + +### Health endpoint + +El servidor de desarrollo expone un endpoint `/_astro/status` que devuelve `{"ok": true}` como JSON. Esto permite a los agentes y a otras herramientas comprobar de forma programática si el servidor de desarrollo está listo para aceptar peticiones. Este endpoint solo está disponible en el servidor de desarrollo y no existe en las compilaciones de producción. + ## Consejos para el desarrollo de Astro potenciado por IA - **Comienza con plantillas**: En lugar de construir desde cero, pide a las herramientas de IA que comiencen con una [plantilla de Astro](https://astro.build/themes/) existente o usa `npm create astro@latest` con una opción de plantilla. diff --git a/src/content/docs/es/guides/deploy/aws-via-flightcontrol.mdx b/src/content/docs/es/guides/deploy/aws-via-flightcontrol.mdx new file mode 100644 index 0000000000000..50b495c977b92 --- /dev/null +++ b/src/content/docs/es/guides/deploy/aws-via-flightcontrol.mdx @@ -0,0 +1,38 @@ +--- +title: Despliega tu sitio de Astro en AWS con Flightcontrol +description: Cómo desplegar tu sitio de Astro en AWS con Flightcontrol +sidebar: + label: AWS via Flightcontrol +type: deploy +logo: flightcontrol +supports: ['ssr', 'static'] +i18nReady: true +--- +import { Steps } from '@astrojs/starlight/components'; + +Puedes desplegar un sitio de Astro usando [Flightcontrol](https://www.flightcontrol.dev?ref=astro), el cual ofrece despliegues completamente automatizados en tu cuenta de AWS. + +Admite sitios de Astro tanto estáticos como SSR. + +## Cómo desplegar + + +1. Crea una cuenta de Flightcontrol en [app.flightcontrol.dev/signup](https://app.flightcontrol.dev/signup?ref=astro) + +2. Ve a [app.flightcontrol.dev/projects/new/1](https://app.flightcontrol.dev/projects/new/1) + +3. Conecta tu cuenta de GitHub y selecciona tu repo + +4. Selecciona el "Config Type" que desees: + - `GUI` (toda la configuración se gestiona a través del panel de Flightcontrol) donde seleccionarás el preset `Astro Static` o `Astro SSR` + - `flightcontrol.json` (opción de "infraestructura como código" donde toda la configuración está en tu repo), donde seleccionarás una configuración de ejemplo de Astro y luego la añadirás a tu código fuente como `flightcontrol.json` + +5. Ajusta cualquier configuración según sea necesario + +6. Haz clic en "Create Project" y completa cualquier paso requerido (como vincular tu cuenta de AWS). + + +### Configuración de SSR + +{/* TODO: add link to: es/guides/integrations-guide/node/ */} +Para desplegar con soporte para SSR, asegúrate de configurar primero el adaptador `@astrojs/node`. Luego, sigue los pasos anteriores, eligiendo las configuraciones adecuadas para Astro SSR. diff --git a/src/content/docs/es/guides/deploy/cloudflare.mdx b/src/content/docs/es/guides/deploy/cloudflare.mdx new file mode 100644 index 0000000000000..90768a6326f54 --- /dev/null +++ b/src/content/docs/es/guides/deploy/cloudflare.mdx @@ -0,0 +1,169 @@ +--- +title: Despliega tu sitio Astro en Cloudflare +description: Cómo desplegar tu sitio Astro en la web usando Cloudflare +sidebar: + label: Cloudflare +type: deploy +logo: cloudflare +supports: ['ssr', 'static'] +i18nReady: true +--- +import ReadMore from '~/components/ReadMore.astro'; +import { Steps } from '@astrojs/starlight/components'; +import StaticSsrTabs from '~/components/tabs/StaticSsrTabs.astro'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' + +Puedes desplegar aplicaciones full-stack, incluyendo recursos estáticos en el front-end y APIs en el back-end, así como sitios renderizados bajo demanda, en [Cloudflare Workers](https://developers.cloudflare.com/workers/static-assets/). + + +:::note + +Cloudflare recomienda usar Cloudflare Workers para proyectos nuevos. Para proyectos de Pages existentes, consulta la [guía de migración de Cloudflare](https://developers.cloudflare.com/workers/static-assets/migration-guides/migrate-from-pages/) y su [matriz de compatibilidad](https://developers.cloudflare.com/workers/static-assets/migration-guides/migrate-from-pages/#compatibility-matrix). + +::: + +Lee más sobre el [uso del tiempo de ejecución de Cloudflare](/es/guides/integrations-guide/cloudflare/) en tu proyecto de Astro. +## Requisitos previos + +Para empezar, necesitarás: + +- Una cuenta de Cloudflare. Si aún no tienes una, puedes crear una cuenta gratuita de Cloudflare durante el proceso. + +## Cloudflare Workers + +### Cómo desplegar con Wrangler + + +1. Instala la [CLI de Wrangler](https://developers.cloudflare.com/workers/wrangler/get-started/). + + ```bash + npm install wrangler@latest --save-dev + ``` + +2. Si tu sitio utiliza renderizado bajo demanda, instala el [adaptador `@astrojs/cloudflare`](/es/guides/integrations-guide/cloudflare/). + + Esto instalará el adaptador y realizará los cambios correspondientes en tu archivo `astro.config.mjs` en un solo paso. + + + + ```sh + npx astro add cloudflare + ``` + + + ```sh + pnpm astro add cloudflare + ``` + + + ```sh + yarn astro add cloudflare + ``` + + + + Lee más sobre el [renderizado bajo demanda en Astro](/es/guides/on-demand-rendering/). + +3. Crea un [archivo de configuración de Wrangler](https://developers.cloudflare.com/workers/wrangler/configuration/). + + Ejecutar `astro add cloudflare` lo creará por ti; si no estás usando el adaptador, tendrás que crearlo tú mismo. + + + + ```jsonc title="wrangler.jsonc" + { + "name": "my-astro-app", + "compatibility_date": "YYYY-MM-DD", // Actualiza al día en que realices el despliegue + "assets": { + "directory": "./dist", + } + } + ``` + + + ```jsonc title="wrangler.jsonc" + { + "main": "@astrojs/cloudflare/entrypoints/server", + "name": "my-astro-app", + "compatibility_date": "YYYY-MM-DD", // Actualiza al día en que realices el despliegue + "compatibility_flags": [ + "nodejs_compat", + "global_fetch_strictly_public" + ], + "assets": { + "binding": "ASSETS", + "directory": "./dist" + }, + "observability": { + "enabled": true + } + } + ``` + + + +4. Previsualiza tu proyecto localmente con Wrangler. + + ```bash + npx astro build && npx wrangler dev + ``` + +5. Despliega usando `npx wrangler deploy`. + + ```bash + npx astro build && npx wrangler deploy + ``` + + +Una vez que tus recursos se hayan subido, Wrangler te proporcionará una URL de vista previa para inspeccionar tu sitio. + +Lee más sobre el uso de las [APIs del tiempo de ejecución de Cloudflare](/es/guides/integrations-guide/cloudflare/) como los bindings. + +### Cómo desplegar con CI/CD + +También puedes usar un sistema de CI/CD como [Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) para compilar y desplegar automáticamente tu sitio al hacer push. + +Si usas Workers Builds: + + +1. Sigue los pasos 1 al 3 de la sección de Wrangler anterior. + +2. Inicia sesión en el [panel de control de Cloudflare](https://dash.cloudflare.com/) y navega hasta `Compute > Workers & Pages`. Selecciona `Create application`. + +3. En la sección `Import a repository`, selecciona una cuenta de Git y luego el repositorio que contiene tu proyecto de Astro. + +4. Configura tu proyecto con: + - Comando de compilación: `npx astro build` + - Comando de despliegue: `npx wrangler deploy` + +5. Haz clic en `Save and Deploy`. Ahora puedes previsualizar tu Worker en el subdominio `workers.dev` proporcionado. + + +## Solución de problemas + +### Comportamiento 404 + +Para los proyectos de Workers, necesitarás configurar `not_found_handling` si deseas servir una página 404 personalizada. Puedes leer más sobre esto en la [sección de comportamiento de enrutamiento](https://developers.cloudflare.com/workers/static-assets/#routing-behavior) de la documentación de Cloudflare. + +```jsonc title="wrangler.jsonc" +{ + "assets": { + "directory": "./dist", + "not_found_handling": "404-page" + } +} +``` + +### Hidratación del lado del cliente + +La hidratación del lado del cliente puede fallar debido a la configuración Auto Minify de Cloudflare. Si ves el mensaje `Hydration completed but contains mismatches` en la consola, asegúrate de desactivar Auto Minify en la configuración de Cloudflare. + +### APIs del tiempo de ejecución de Node.js + +Si estás construyendo un proyecto que utiliza renderizado bajo demanda con [el adaptador de Cloudflare](/es/guides/integrations-guide/cloudflare/) y el servidor falla al compilar con un mensaje de error como `[Error] Could not resolve "XXXX. The package "XXXX" wasn't found on the file system but is built into node.`: + +- Esto significa que un paquete o importación que estás utilizando en el entorno del lado del servidor no es compatible con las [APIs del tiempo de ejecución de Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/nodejs/). + +- Si estás importando directamente una API del tiempo de ejecución de Node.js, consulta la documentación de Astro sobre la [compatibilidad con Node.js](/es/guides/integrations-guide/cloudflare/#nodejs-compatibility) en Cloudflare para ver los siguientes pasos sobre cómo resolver esto. + +- Si estás importando un paquete que a su vez importa una API del tiempo de ejecución de Node.js, comunícate con el autor del paquete para ver si admite la sintaxis de importación `node:*`. Si no lo hace, es posible que debas buscar un paquete alternativo. diff --git a/src/content/docs/es/guides/deploy/firebase.mdx b/src/content/docs/es/guides/deploy/firebase.mdx new file mode 100644 index 0000000000000..054625d5da377 --- /dev/null +++ b/src/content/docs/es/guides/deploy/firebase.mdx @@ -0,0 +1,137 @@ +--- +title: Despliega tu sitio de Astro en Firebase Hosting de Google +description: Cómo desplegar tu sitio de Astro en la web usando Firebase Hosting de Google. +sidebar: + label: Firebase +type: deploy +logo: firebase +supports: ['ssr', 'static'] +i18nReady: true +--- +import { Steps } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; + +[Firebase Hosting](https://firebase.google.com/products/hosting) es un servicio proporcionado por la plataforma de desarrollo de aplicaciones [Firebase](https://firebase.google.com/) de Google, que se puede utilizar para desplegar un sitio de Astro. + +Consulta nuestra guía separada para [agregar servicios de backend de Firebase](/es/guides/backend/firebase/) como bases de datos, autenticación y almacenamiento. + +## Configuración del proyecto + +Tu proyecto de Astro se puede desplegar en Firebase como un sitio estático o como un sitio renderizado del lado del servidor (SSR). + +### Sitio estático + +Tu proyecto de Astro es un sitio estático de forma predeterminada. No necesitas ninguna configuración adicional para desplegar un sitio estático de Astro en Firebase. + +### Adaptador para SSR + +{/* TODO: add link to: /en/guides/integrations-guide/node/ */} +Para habilitar SSR en tu proyecto de Astro y desplegarlo en Firebase, agrega el adaptador de Node.js. + +:::note +Desplegar un sitio SSR de Astro en Firebase requiere el [plan Blaze](https://firebase.google.com/pricing) o superior. +::: + +## Cómo desplegar + + +1. Instala la [Firebase CLI](https://github.com/firebase/firebase-tools). Esta es una herramienta de línea de comandos que te permite interactuar con Firebase desde la terminal. + + + + ```shell + npm install firebase-tools + ``` + + + ```shell + pnpm add firebase-tools + ``` + + + ```shell + yarn add firebase-tools + ``` + + + +2. Autentica la Firebase CLI con tu cuenta de Google. Esto abrirá una ventana del navegador donde podrás iniciar sesión en tu cuenta de Google. + + + + ```shell + npx firebase login + ``` + + + ```shell + pnpm exec firebase login + ``` + + + ```shell + yarn firebase login + ``` + + + +3. Habilita el soporte experimental para frameworks web. Esta es una característica experimental que permite a la Firebase CLI detectar y configurar tus ajustes de despliegue para Astro. + + + + ```shell + npx firebase experiments:enable webframeworks + ``` + + + ```shell + pnpm exec firebase experiments:enable webframeworks + ``` + + + ```shell + yarn firebase experiments:enable webframeworks + ``` + + + +4. Inicializa Firebase Hosting en tu proyecto. Esto creará los archivos `firebase.json` y `.firebaserc` en la raíz de tu proyecto. + + + + ```shell + npx firebase init hosting + ``` + + + ```shell + pnpm exec firebase init hosting + ``` + + + ```shell + yarn firebase init hosting + ``` + + + +5. Despliega tu sitio en Firebase Hosting. Esto construirá tu sitio de Astro y lo desplegará en Firebase. + + + + ```shell + npx firebase deploy --only hosting + ``` + + + ```shell + pnpm exec firebase deploy --only hosting + ``` + + + ```shell + yarn firebase deploy --only hosting + ``` + + + diff --git a/src/content/docs/es/guides/deploy/flyio.mdx b/src/content/docs/es/guides/deploy/flyio.mdx new file mode 100644 index 0000000000000..b7109a6ebd7b7 --- /dev/null +++ b/src/content/docs/es/guides/deploy/flyio.mdx @@ -0,0 +1,52 @@ +--- +title: Despliega tu sitio Astro en Fly.io +description: Cómo desplegar tu sitio Astro en la web usando Fly.io. +sidebar: + label: Fly.io +type: deploy +logo: flyio +supports: ['ssr', 'static'] +i18nReady: true +stub: true +--- +import { Steps } from '@astrojs/starlight/components'; + +Puedes desplegar tu proyecto Astro en [Fly.io](https://fly.io/), una plataforma para ejecutar aplicaciones full stack y bases de datos cerca de tus usuarios. + +## Configuración del Proyecto + +Tu proyecto Astro se puede desplegar en Fly.io como un sitio estático o como un sitio renderizado en el servidor (SSR). + +### Sitio Estático + +Tu proyecto Astro es un sitio estático por defecto. No necesitas ninguna configuración adicional para desplegar un sitio Astro estático en Fly.io. + +### Adaptador para SSR + +Para habilitar el renderizado bajo demanda en tu proyecto Astro y desplegarlo en Fly.io, añade [el adaptador de Node.js](/es/guides/integrations-guide/node/). + +## Cómo desplegar + + +1. [Regístrate en Fly.io](https://fly.io/docs/getting-started/log-in-to-fly/#first-time-or-no-fly-account-sign-up-for-fly) si aún no lo has hecho. + +2. [Instala `flyctl`](https://fly.io/docs/hands-on/install-flyctl/), el centro de control de tu aplicación en Fly.io. + +3. Ejecuta el siguiente comando en tu terminal. + + ```bash + fly launch + ``` + + `flyctl` detectará Astro automáticamente, aplicará la configuración correcta, construirá tu imagen y la desplegará en la plataforma de Fly.io. + + +## Generando tu Dockerfile de Astro + +Si aún no tienes un Dockerfile, `fly launch` generará uno por ti, además de preparar un archivo `fly.toml`. Para las páginas renderizadas bajo demanda, este Dockerfile incluirá el comando de inicio adecuado y las variables de entorno. + +Alternativamente, puedes crear tu propio Dockerfile usando el [generador de Dockerfile](https://www.npmjs.com/package/@flydotio/dockerfile) y luego ejecutar el comando `npx dockerfile` para aplicaciones de Node o `bunx dockerfile` para aplicaciones de Bun. + +## Recursos Oficiales + +- Consulta [la documentación oficial de Fly.io](https://fly.io/docs/js/frameworks/astro/) diff --git a/src/content/docs/es/guides/deploy/gitlab.mdx b/src/content/docs/es/guides/deploy/gitlab.mdx new file mode 100644 index 0000000000000..c31475bf87908 --- /dev/null +++ b/src/content/docs/es/guides/deploy/gitlab.mdx @@ -0,0 +1,126 @@ +--- +title: Despliega tu sitio de Astro en GitLab Pages +description: Cómo desplegar tu sitio de Astro en la web usando GitLab Pages. +sidebar: + label: GitLab Pages +type: deploy +logo: gitlab +supports: ['static'] +i18nReady: true +--- +import { Steps } from '@astrojs/starlight/components'; + +Puedes usar [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) para alojar un sitio de Astro para tus proyectos, grupos o cuenta de usuario de [GitLab](https://about.gitlab.com/). + +:::tip[¿Buscas un ejemplo?] +¡Echa un vistazo al [proyecto de ejemplo oficial de Astro en GitLab Pages](https://gitlab.com/pages/astro)! +::: + +## Cómo desplegar + +Puedes desplegar un sitio de Astro en GitLab Pages utilizando GitLab CI/CD para compilar y desplegar tu sitio automáticamente. Para hacer esto, tu código fuente debe estar alojado en GitLab y necesitas hacer los siguientes cambios en tu proyecto de Astro: + + + +1. Configura las opciones [`site`](/es/reference/configuration-reference/#site) y [`base`](/es/reference/configuration-reference/#base) en `astro.config.mjs`. + + ```js title="astro.config.mjs" ins={4-5} + import { defineConfig } from 'astro/config'; + + export default defineConfig({ + site: 'https://.gitlab.io', + base: '/', + outDir: 'public', + publicDir: 'static', + }); + ``` + + `site` + + El valor para `site` debe ser uno de los siguientes: + + - La siguiente URL basada en tu nombre de usuario: `https://.gitlab.io` + - La siguiente URL basada en el nombre de tu grupo: `https://.gitlab.io` + - Tu dominio personalizado si lo tienes configurado en los ajustes de tu proyecto de GitLab: `https://example.com` + + Para las instancias autogestionadas de GitLab, reemplaza `gitlab.io` con el dominio de Pages de tu instancia. + + `base` + + Puede que se requiera un valor para `base` para que Astro trate el nombre de tu repositorio (p. ej. `/my-repo`) como la raíz de tu sitio web. + + :::note + No configures un parámetro `base` si tu página se sirve desde la carpeta raíz. + ::: + + El valor para `base` debe ser el nombre de tu repositorio comenzando con una barra diagonal, por ejemplo `/my-blog`. Esto es para que Astro entienda que la raíz de tu sitio web es `/my-repo`, en lugar del predeterminado `/`. + + :::caution + Cuando este valor está configurado, todos los enlaces internos de tus páginas deben llevar como prefijo tu valor `base`: + + ```astro ins="/my-repo" + About + ``` + + Lee más sobre cómo [configurar un valor `base`](/es/reference/configuration-reference/#base) + ::: + + +2. Renombra el directorio `public/` a `static/`. + +3. Configura `outDir: 'public'` en `astro.config.mjs`. Esta configuración le indica a Astro que coloque el resultado de la compilación estática en una carpeta llamada `public`, que es la carpeta requerida por GitLab Pages para exponer los archivos. + + Si estabas usando el [directorio `public/`](/es/basics/project-structure/#public) como fuente de archivos estáticos en tu proyecto de Astro, renómbralo y usa ese nuevo nombre de carpeta en `astro.config.mjs` como el valor de `publicDir`. + + Por ejemplo, aquí tienes la configuración correcta en `astro.config.mjs` cuando el directorio `public/` se renombra a `static/`: + + ```js title="astro.config.mjs" ins={4-5} + import { defineConfig } from 'astro/config'; + + export default defineConfig({ + outDir: 'public', + publicDir: 'static', + }); + ``` + +4. Cambia la carpeta de salida en `.gitignore`. En nuestro ejemplo necesitamos cambiar `dist/` a `public/`: + + ```diff title=".gitignore" + # salida de compilación + -dist/ + +public/ + ``` + +5. Crea un archivo llamado `.gitlab-ci.yml` en la raíz de tu proyecto con el contenido a continuación. Esto compilará y desplegará tu sitio cada vez que realices cambios en tu contenido: + + ```yaml title=".gitlab-ci.yml" + pages: + # La imagen de Docker que se usará para compilar tu aplicación + image: node:lts + + before_script: + - npm ci + + script: + # Especifica aquí los pasos necesarios para compilar tu aplicación + - npm run build + + artifacts: + paths: + # La carpeta que contiene los archivos compilados que se publicarán. + # Esta debe llamarse "public". + - public + + only: + # Desencadena una nueva compilación y despliegue solo + # cuando se hace un push a la(s) rama(s) a continuación + - main + ``` + +6. Haz un commit con tus cambios y súbelos a GitLab. + +7. En GitLab, ve al menú **Deploy** de tu repositorio y selecciona **Pages**. Aquí verás la URL completa de tu sitio web de GitLab Pages. Para asegurarte de que estás usando el formato de URL `https://username.gitlab.io/my-repo`, desmarca la opción **Use unique domain** en esta página. + + + +¡Tu sitio ya debería estar publicado! Cuando subas cambios al repositorio de tu proyecto de Astro, el pipeline de CI/CD de GitLab los desplegará automáticamente por ti. diff --git a/src/content/docs/es/guides/deploy/google-cloud.mdx b/src/content/docs/es/guides/deploy/google-cloud.mdx new file mode 100644 index 0000000000000..5fcd32052181a --- /dev/null +++ b/src/content/docs/es/guides/deploy/google-cloud.mdx @@ -0,0 +1,91 @@ +--- +title: Despliega tu sitio Astro en Google Cloud +description: Cómo desplegar tu sitio Astro en la web usando Google Cloud. +sidebar: + label: Google Cloud +type: deploy +logo: google-cloud +supports: ['ssr', 'static'] +i18nReady: true +--- +import { Steps } from '@astrojs/starlight/components'; + +[Google Cloud](https://cloud.google.com/) es una plataforma de alojamiento de aplicaciones web con todas las funciones que se puede utilizar para desplegar un sitio Astro. + +## Cómo desplegar + +### Cloud Storage (solo estático) + + +1. [Crea un nuevo proyecto de GCP](https://console.cloud.google.com/projectcreate), o selecciona uno que ya tengas. + +2. Crea un nuevo bucket en [Cloud Storage](https://cloud.google.com/storage). + +3. Asígnale un nombre y ajusta las otras configuraciones requeridas. + +4. Sube tu carpeta `dist` allí o súbela usando [Cloud Build](https://cloud.google.com/build). + +5. Habilita el acceso público agregando un nuevo permiso a `allUsers` llamado `Storage Object Viewer`. + +6. Edita la configuración del sitio web y agrega `index.html` como punto de entrada y `404.html` como página de error. + + +### Cloud Run (SSR y estático) + +Cloud Run es una plataforma serverless que te permite ejecutar un contenedor sin tener que gestionar ninguna infraestructura. Se puede utilizar para desplegar tanto sitios estáticos como SSR. + +#### Prepara el Servicio + + +1. [Crea un nuevo proyecto de GCP](https://console.cloud.google.com/projectcreate), o selecciona uno que ya tengas. + +2. Asegúrate de que la [API de Cloud Run](https://console.cloud.google.com/apis/library/run.googleapis.com) esté habilitada. + +3. Crea un nuevo servicio. + + +#### Crea el Dockerfile y Construye el Contenedor + +Antes de que puedas desplegar tu sitio Astro en Cloud Run, necesitas crear un Dockerfile que se utilizará para construir el contenedor. Encuentra más información sobre [cómo usar Docker con Astro](/es/recipes/docker/#creating-a-dockerfile) en nuestra sección de recetas. + +Una vez que el Dockerfile esté creado, constrúyelo como una imagen y súbela a Google Cloud. Hay algunas formas de lograr esto: + +**Construir localmente usando Docker**: + +Usa el comando `docker build` para construir la imagen, `docker tag` para asignarle una etiqueta, y luego `docker push` para subirla a un registro. En el caso de Google Cloud, [`Artifact Registry`](https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling) es la opción más sencilla, pero también puedes usar [Docker Hub](https://hub.docker.com/). + +```bash +# construye tu contenedor +docker build . + +docker tag SOURCE_IMAGE HOSTNAME/PROJECT-ID/TARGET-IMAGE:TAG + +# Sube tu imagen a un registro +docker push HOSTNAME/PROJECT-ID/IMAGE:TAG +``` + +Cambia los siguientes valores en los comandos anteriores para que coincidan con tu proyecto: + +- `SOURCE_IMAGE`: el nombre de la imagen local o el ID de la imagen. +- `HOSTNAME`: el host del registro (`gcr.io`, `eu.gcr.io`, `asia.gcr.io`, `us.gcr.io`, `docker.io`). +- `PROJECT`: el ID de tu proyecto de Google Cloud. +- `TARGET-IMAGE`: el nombre de la imagen cuando se almacena en el registro. +- `TAG` es la versión asociada con la imagen. + +[Lee más en la documentación de Google Cloud.](https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling) + +**Usando otra herramienta**: + +Puedes usar una herramienta de CI/CD que soporte Docker, como [GitHub Actions](https://github.com/marketplace/actions/push-to-gcr-github-action). + +**Construir usando [Cloud Build](https://cloud.google.com/build)**: + +En lugar de construir el Dockerfile localmente, puedes indicarle a Google Cloud que construya la imagen de forma remota. Consulta la [documentación de Google Cloud Build aquí](https://cloud.google.com/build/docs/build-push-docker-image). + +#### Desplegar el contenedor + +El despliegue se puede manejar manualmente en tu terminal [usando `gcloud`](https://cloud.google.com/run/docs/deploying#service) o automáticamente usando [Cloud Build](https://cloud.google.com/build) o cualquier otro sistema de CI/CD. + +:::note[¿Necesitas acceso público?] +¡No olvides agregar el permiso `Cloud Run Invoker` al grupo `allUsers` en la configuración de permisos de Cloud Run! +::: diff --git a/src/content/docs/es/guides/deploy/hostinger.mdx b/src/content/docs/es/guides/deploy/hostinger.mdx new file mode 100644 index 0000000000000..83d7d81b2f609 --- /dev/null +++ b/src/content/docs/es/guides/deploy/hostinger.mdx @@ -0,0 +1,154 @@ +--- +title: Despliega tu sitio de Astro en Hostinger +description: Cómo desplegar tu sitio de Astro en la web usando Hostinger. +type: deploy +logo: hostinger +i18nReady: true +sidebar: + label: Hostinger +supports: ['ssr', 'static'] +--- +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; +import ReadMore from '~/components/ReadMore.astro'; +import { Steps } from '@astrojs/starlight/components'; + +[Hostinger](https://www.hostinger.com/es/) es un proveedor de alojamiento web que admite sitios estáticos y aplicaciones de Node.js. + +Esta guía explica cómo desplegar proyectos de Astro tanto estáticos como renderizados en el servidor, en Hostinger utilizando [hPanel](https://www.hostinger.com/es/support/1583483-guia-completa-de-hpanel-en-hostinger/). + +## Requisitos previos + +- Una [cuenta de Hostinger](https://www.hostinger.com/es/) con un plan de alojamiento activo. + - El despliegue de sitios estáticos es compatible con todos los planes que incluyen hPanel. + - El despliegue renderizado en el servidor requiere un plan que admita aplicaciones de Node.js, como Business Web Hosting o Cloud Hosting. +- Un proyecto de Astro listo para desplegar. + +## Despliegue de un sitio estático + +Los proyectos de Astro utilizan `output: 'static'` por defecto, por lo que no se requiere configuración adicional para desplegar un sitio estático. + +Puedes subir el contenido de tu carpeta `dist/` a Hostinger utilizando el **Administrador de archivos** de hPanel o un cliente FTP. + +### Subida con el Administrador de archivos + + +1. Construye tu proyecto de Astro localmente: + + + + ```shell + npm run build + ``` + + + ```shell + pnpm build + ``` + + + ```shell + yarn build + ``` + + + +2. Inicia sesión en [hPanel](https://hpanel.hostinger.com/) y abre tu sitio web. + +3. Abre **Archivos** > **Administrador de archivos** y navega hasta el directorio `public_html`. + +4. Sube el contenido de tu carpeta local `dist/` a `public_html`. Puedes arrastrar y soltar los archivos, o subir un archivo comprimido y extraerlo allí mismo. + +5. Visita tu dominio para confirmar que tu sitio está en línea. + + +### Subida por FTP + + +1. En hPanel, ve a **Archivos** > **Cuentas FTP** para encontrar o crear tus credenciales de FTP. + +2. Conéctate a tu cuenta de alojamiento utilizando un cliente FTP como [FileZilla](https://filezilla-project.org/). + +3. Sube el contenido de tu carpeta local `dist/` al directorio `public_html` del servidor. + +4. Visita tu dominio para confirmar que tu sitio está en línea. + + +## Despliegue renderizado en el servidor usando Node.js + +Para el [renderizado bajo demanda](/es/guides/on-demand-rendering/), despliega tu proyecto de Astro como una aplicación de Node.js en un plan de Hostinger que admita Node.js. + +### Añadir el adaptador de Node.js + +Añade el [adaptador de Node.js](/es/guides/integrations-guide/node/) para habilitar el renderizado bajo demanda con el siguiente comando `astro add`: + + + + ```shell + npx astro add node + ``` + + + ```shell + pnpm astro add node + ``` + + + ```shell + yarn astro add node + ``` + + + +Consulta la [guía del adaptador de Node.js](/es/guides/integrations-guide/node/) para obtener opciones de configuración adicionales. + +Asegúrate de que tu `package.json` tenga un script `start` que ejecute el servidor compilado: + +```json title="package.json" +{ + "scripts": { + "start": "node ./dist/server/entry.mjs" + } +} +``` + +### Despliegue desde un repositorio de Git + + +1. Sube tu proyecto de Astro a un repositorio de GitHub, GitLab o Bitbucket. + +2. Inicia sesión en [hPanel](https://hpanel.hostinger.com/) y ve a **Sitios web**. + +3. Añade un nuevo sitio web y selecciona la opción de aplicación **Node.js**. + +4. Conecta tu proveedor de Git y selecciona tu repositorio y rama. + +5. Configura los ajustes de compilación: + + - **Comando de compilación:** `npm run build` + - **Comando de inicio:** `npm run start` + +6. Selecciona una versión de Node.js que cumpla con el [requisito mínimo](/es/install-and-setup/#prerrequisitos) de Astro (Node.js 22 o posterior). + +7. Inicia el despliegue. Hostinger instalará las dependencias, ejecutará tu comando de compilación e iniciará la aplicación. + + +### Despliegue subiendo los archivos del proyecto + + +1. Construye tu proyecto de Astro localmente, luego sube la carpeta `dist/` generada junto con el `package.json` y el archivo de bloqueo (p. ej. `package-lock.json`). + +2. En hPanel, añade un nuevo sitio web y selecciona la opción de aplicación **Node.js**. + +3. Sube los archivos de tu proyecto (o un archivo `.zip`) utilizando el Administrador de archivos. + +4. Configura la aplicación: + + - **Raíz de la aplicación:** la carpeta que contiene tu `package.json`. + - **Comando de inicio:** `npm run start` + +5. Selecciona una versión de Node.js compatible e inicia la aplicación. + + +## Recursos oficiales + +- [Documentación de Hostinger](https://www.hostinger.com/es/support/) — centro de ayuda oficial para hPanel, planes de alojamiento y aplicaciones de Node.js. diff --git a/src/content/docs/es/guides/deploy/index.mdx b/src/content/docs/es/guides/deploy/index.mdx new file mode 100644 index 0000000000000..e7fcca01d2a3f --- /dev/null +++ b/src/content/docs/es/guides/deploy/index.mdx @@ -0,0 +1,121 @@ +--- +title: Despliega tu sitio Astro +description: Cómo desplegar tu sitio Astro en la web. +sidebar: + label: Descripción general de despliegue +i18nReady: true +--- + +import DeployGuidesNav from '~/components/DeployGuidesNav.astro'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; +import { Steps } from '@astrojs/starlight/components' + +**¿Listo para construir y desplegar tu sitio Astro?** Sigue una de nuestras guías para diferentes servicios de despliegue o desplázate hacia abajo para obtener orientación general sobre cómo desplegar un sitio Astro. + +## Guías de despliegue + + + +## Opciones de despliegue rápido + +Puedes construir y desplegar un sitio de Astro rápidamente en varios proveedores de alojamiento usando la interfaz de su panel de control o una CLI. + +### Interfaz web + +Una forma rápida de desplegar tu sitio web es conectar el repositorio Git en línea de tu proyecto de Astro (p. ej. GitHub, GitLab, Bitbucket) a un proveedor de alojamiento y aprovechar el despliegue continuo mediante Git. + +Estas plataformas de alojamiento detectan automáticamente los cambios subidos al repositorio de origen de tu proyecto de Astro, construyen tu sitio y lo despliegan en la web en una URL personalizada o en tu dominio personal. Por lo general, la configuración de un despliegue en estas plataformas seguirá una serie de pasos similares a los siguientes: + + +1. Añade tu repositorio a un proveedor de Git en línea (p. ej. en GitHub, GitLab, Bitbucket) + +2. Elige un proveedor de alojamiento que soporte **despliegue continuo** (p. ej. [Netlify](/es/guides/deploy/netlify/) o [Vercel](/es/guides/deploy/vercel/)) e importa tu repositorio Git como un nuevo sitio o proyecto. + + Muchos proveedores de alojamiento comunes reconocerán tu proyecto como un sitio de Astro y deberían elegir la configuración adecuada para construir y desplegar tu sitio, como se muestra a continuación. (De no ser así, esta configuración se puede cambiar.) + + :::note[Configuración de despliegue] + - **Comando de construcción:** `astro build` o `npm run build` + - **Directorio de publicación:** `dist` + ::: + +3. Haz clic en "Deploy" y tu nuevo sitio web se creará en una URL única para ese proveedor (p. ej. `new-astro-site.netlify.app`). + + +El host se configurará automáticamente para monitorear la rama principal de tu proveedor de Git en busca de cambios, y para reconstruir y republicar tu sitio con cada nuevo commit. Por lo general, estos ajustes se pueden configurar en la interfaz del panel de control de tu proveedor de alojamiento. + +### Despliegue mediante CLI + +Algunos hosts tendrán su propia interfaz de línea de comandos (CLI) que puedes instalar de forma global en tu máquina usando npm. A menudo, el uso de una CLI para realizar el despliegue es similar a lo siguiente: + + +1. Instala globalmente la CLI de tu host, por ejemplo: + + + + ```shell + npm install --global netlify-cli + ``` + + + ```shell + pnpm add --global netlify-cli + ``` + + + ```shell + yarn global add netlify-cli + ``` + + + +2. Ejecuta la CLI y sigue las instrucciones para la autorización, configuración, etc. + +3. Construye tu sitio y despliégalo en tu host + + Muchos hosts comunes construirán y desplegarán tu sitio por ti. Por lo general, reconocerán tu proyecto como un sitio de Astro, y deberían elegir los ajustes de configuración adecuados para construir y desplegar como se muestra a continuación. (Si no es así, estos ajustes se pueden cambiar). + + :::note[Configuración de despliegue] + - **Comando de construcción:** `astro build` o `npm run build` + - **Directorio de publicación:** `dist` + ::: + + + Otros hosts requerirán que [construyas tu sitio localmente](#construir-tu-sitio-de-forma-local) y lo despliegues usando la línea de comandos. + + +## Construir tu sitio de forma local + +Muchos proveedores de alojamiento como Netlify y Vercel construirán tu sitio por ti y luego publicarán esos archivos generados en la web. Sin embargo, algunos sitios requerirán que lo construyas localmente y luego ejecutes un comando de despliegue o subas los archivos generados. + +También es posible que desees construirlo localmente para obtener una vista previa de tu sitio, o para detectar posibles errores y advertencias en tu propio entorno. + +Ejecuta el comando `npm run build` para construir tu sitio de Astro. + + + + ```shell + npm run build + ``` + + + ```shell + pnpm run build + ``` + + + ```shell + yarn run build + ``` + + + +Por defecto, los archivos generados se colocarán en `dist/`. Esta ubicación se puede cambiar usando la [opción de configuración `outDir`](/es/reference/configuration-reference/#outdir). + +## Añadir un adaptador para el renderizado bajo demanda + +:::note +Antes de desplegar tu sitio de Astro con el [renderizado bajo demanda](/es/guides/on-demand-rendering/) habilitado, asegúrate de haber: + +- Instalado el [adaptador adecuado](/es/guides/on-demand-rendering/) en las dependencias de tu proyecto (ya sea manualmente o utilizando el comando `astro add` del adaptador, p. ej. `npx astro add netlify`). +- [Añadido el adaptador](/es/reference/configuration-reference/#integrations) a la importación y exportación por defecto de tu archivo `astro.config.mjs` cuando lo instalas manualmente. (¡El comando `astro add` se encargará de este paso por ti!) +::: diff --git a/src/content/docs/es/guides/deploy/ishosting.mdx b/src/content/docs/es/guides/deploy/ishosting.mdx new file mode 100644 index 0000000000000..86d80388824e6 --- /dev/null +++ b/src/content/docs/es/guides/deploy/ishosting.mdx @@ -0,0 +1,16 @@ +--- +title: Despliega tu sitio de Astro en is*hosting +description: Cómo desplegar tu sitio de Astro en la web usando is*hosting +sidebar: + label: is*hosting +type: deploy +logo: ishosting +supports: ['ssr', 'static'] +i18nReady: true +--- + +[is\*hosting](https://ishosting.com/) es un proveedor de alojamiento que ofrece VPS y servidores dedicados en más de 40 ubicaciones que puedes utilizar para alojar por tu cuenta un sitio de Astro estático o renderizado en el servidor (SSR). + +## Recursos Oficiales + +- [Guía de is\*hosting: despliega Astro en un VPS (estático y SSR)](https://blog.ishosting.com/en/astro-on-vps) diff --git a/src/content/docs/es/guides/framework-components.mdx b/src/content/docs/es/guides/framework-components.mdx new file mode 100644 index 0000000000000..b49cab53dfea5 --- /dev/null +++ b/src/content/docs/es/guides/framework-components.mdx @@ -0,0 +1,247 @@ +--- +title: Frameworks front-end +description: Construye tu sitio web de Astro con React, Svelte y más. +i18nReady: true +--- +import IntegrationsNav from '~/components/IntegrationsNav.astro' +import ReadMore from '~/components/ReadMore.astro' + +Construye tu sitio web de Astro sin sacrificar tu framework de componentes favorito. Crea [islas](/es/concepts/islands/) de Astro con los frameworks de UI de tu elección. + +## Integraciones oficiales de frameworks front-end + +Astro es compatible con una variedad de frameworks populares, incluyendo [React](https://react.dev/), [Preact](https://preactjs.com/), [Svelte](https://svelte.dev/), [Vue](https://vuejs.org/), [SolidJS](https://www.solidjs.com/) y [AlpineJS](https://alpinejs.dev/) mediante integraciones oficiales. + +Encuentra aún más [integraciones de frameworks mantenidas por la comunidad](https://astro.build/integrations/?search=&categories%5B%5D=frameworks) (p. ej., Angular, Qwik, Elm) en nuestro directorio de integraciones. + + + +## Instalación de integraciones + +Una o varias de estas integraciones de Astro se pueden instalar y configurar en tu proyecto. + +{/*TODO: add link to /es/guides/integrations */} +Consulta la Guía de Integraciones para obtener más detalles sobre cómo instalar y configurar las integraciones de Astro. + +:::tip +¿Quieres ver un ejemplo con el framework de tu elección? Visita [astro.new](https://astro.new/latest/frameworks) y selecciona una de las plantillas de frameworks. +::: + +## Uso de componentes de frameworks + +¡Usa los componentes de tu framework de JavaScript en tus páginas, layouts y componentes de Astro igual que los componentes de Astro! Todos tus componentes pueden convivir en `/src/components`, o pueden organizarse de la manera que prefieras. + +Para usar un componente de un framework, impórtalo desde su ruta relativa en el script de tu componente de Astro. Luego, usa el componente junto con otros componentes, elementos HTML y expresiones de tipo JSX en la plantilla del componente. + +```astro title="src/pages/static-components.astro" ins={2,7} +--- +import MyReactComponent from '../components/MyReactComponent.jsx'; +--- + + +

      ¡Usa componentes de React directamente en Astro!

      + + + +``` + +Por defecto, los componentes de tu framework solo se renderizarán en el servidor, como HTML estático. Esto es útil para los componentes de plantilla que no son interactivos y evita enviar JavaScript innecesario al cliente. + +## Hidratación de componentes interactivos + +Un componente de un framework puede hacerse interactivo (hidratarse) mediante una [directiva `client:*`](/es/reference/directives-reference/#directivas-del-cliente). Estos son atributos del componente que determinan cuándo se debe enviar el JavaScript de tu componente al navegador. + +Con todas las directivas del cliente, excepto `client:only`, tu componente primero se renderizará en el servidor para generar HTML estático. El JavaScript del componente se enviará al navegador de acuerdo con la directiva que elijas. Luego, el componente se hidratará y se volverá interactivo. + +```astro title="src/pages/interactive-components.astro" /client:\S+/ +--- +// Ejemplo: hidratación de componentes de frameworks en el navegador. +import InteractiveButton from '../components/InteractiveButton.jsx'; +import InteractiveCounter from '../components/InteractiveCounter.jsx'; +import InteractiveModal from '../components/InteractiveModal.svelte'; +--- + + + + + + + + +``` + +El framework de JavaScript (React, Svelte, etc.) necesario para renderizar el componente se enviará al navegador junto con el propio JavaScript del componente. Si dos o más componentes en una página utilizan el mismo framework, el framework solo se enviará una vez. + +:::note[Accesibilidad] +La mayoría de los patrones de accesibilidad específicos de cada framework deberían funcionar de la misma manera cuando estos componentes se utilizan en Astro. ¡Asegúrate de elegir una directiva de cliente que garantice que cualquier JavaScript relacionado con la accesibilidad se cargue y ejecute correctamente en el momento adecuado! +::: + +### Directivas de hidratación disponibles + +Hay varias directivas de hidratación disponibles para los componentes de frameworks de UI: `client:load`, `client:idle`, `client:visible`, `client:media={QUERY}` y `client:only={FRAMEWORK}`. + +Consulta nuestra página de [referencia de directivas](/es/reference/directives-reference/#directivas-del-cliente) para obtener una descripción completa de estas directivas de hidratación y su uso. + +## Mezclar frameworks + +Puedes importar y renderizar componentes de múltiples frameworks en el mismo componente de Astro. + +```astro title="src/pages/mixing-frameworks.astro" +--- +// Ejemplo: Mezclar componentes de múltiples frameworks en la misma página. +import MyReactComponent from '../components/MyReactComponent.jsx'; +import MySvelteComponent from '../components/MySvelteComponent.svelte'; +import MyVueComponent from '../components/MyVueComponent.vue'; +--- +
      + + + +
      +``` + +{/*TODO: add link to /es/guides/integrations-guide/react/#combinar-multiples-frameworks-jsx */} +Astro reconocerá y renderizará tu componente basándose en su extensión de archivo. Para distinguir entre frameworks que usan la misma extensión de archivo, se requiere configuración adicional al renderizar múltiples frameworks JSX (p. ej. React y Preact). + +:::caution +Solo los componentes de **Astro** (`.astro`) pueden contener componentes de múltiples frameworks. +::: + +## Pasar props a componentes de frameworks + +Puedes pasar props desde componentes de Astro a componentes de frameworks: + +```astro title="src/pages/frameworks-props.astro" +--- +import TodoList from '../components/TodoList.jsx'; +import Counter from '../components/Counter.svelte'; +--- +
      + + +
      +``` + + + + +Las props que se pasan a los componentes interactivos de frameworks [usando una directiva `client:*`](/es/reference/directives-reference/#directivas-del-cliente) deben ser [serializadas](https://developer.mozilla.org/en-US/docs/Glossary/Serialization): traducidas a un formato adecuado para su transferencia a través de una red, o para su almacenamiento. Sin embargo, Astro no serializa todos los tipos de estructuras de datos. Por lo tanto, existen algunas limitaciones sobre lo que se puede pasar como props a los componentes hidratados. + +Se admiten los siguientes tipos de props: +objeto plano, `number`, `string`, `Array`, `Map`, `Set`, `RegExp`, `Date`, `BigInt`, `URL`, `Uint8Array`, `Uint16Array`, `Uint32Array`, e `Infinity` + +Las estructuras de datos no compatibles que se pasan a los componentes, como las funciones, solo se pueden usar durante el renderizado en el servidor del componente y no se pueden utilizar para proporcionar interactividad. Por ejemplo, pasar funciones a componentes hidratados no es compatible porque Astro no puede pasar funciones desde el servidor de una manera que las haga ejecutables en el cliente. + +## Pasar hijos a componentes de frameworks + +Dentro de un componente de Astro, **puedes** pasar elementos hijos a los componentes de frameworks. Cada framework tiene sus propios patrones sobre cómo hacer referencia a estos hijos: React, Preact, y Solid usan una prop especial llamada `children`, mientras que Svelte y Vue usan el elemento ``. + + +```astro title="src/pages/component-children.astro" {5} +--- +import MyReactSidebar from '../components/MyReactSidebar.jsx'; +--- + +

      Aquí tienes una barra lateral con algo de texto y un botón.

      +
      +``` + +Además, puedes usar [Slots con nombre](/es/basics/astro-components/#slots-con-nombre) para agrupar elementos hijos específicos. + +Para React, Preact y Solid, estos slots se convertirán en una prop de nivel superior. Los nombres de los slots que usen `kebab-case` se convertirán a `camelCase`. + +```astro title="src/pages/named-slots.astro" /slot="(.*)"/ +--- +import MySidebar from '../components/MySidebar.jsx'; +--- + +

      Menu

      +

      Aquí tienes una barra lateral con algo de texto y un botón.

      + +
      +``` + +```jsx /{props.(title|socialLinks)}/ +// src/components/MySidebar.jsx +export default function MySidebar(props) { + return ( + + ) +} +``` + +Para Svelte y Vue, estos slots se pueden referenciar usando un elemento `` con el atributo `name`. Los nombres de los slots que usen `kebab-case` se conservarán. + +```jsx /slot name="(.*)"/ +// src/components/MySidebar.svelte + +``` + +## Anidamiento de componentes de framework + +Dentro de un archivo Astro, los elementos hijos de un componente de framework también pueden ser componentes hidratados. Esto significa que puedes anidar de forma recursiva componentes de cualquiera de estos frameworks. + +```astro title="src/pages/nested-components.astro" {9-10} +--- +import MyReactSidebar from '../components/MyReactSidebar.jsx'; +import MyReactButton from '../components/MyReactButton.jsx'; +import MySvelteButton from '../components/MySvelteButton.svelte'; +--- + +

      Aquí tienes una barra lateral con algo de texto y un botón.

      +
      + + +
      +
      +``` + +:::caution +Recuerda: los propios archivos de componentes de framework (p. ej. `.jsx`, `.svelte`) no pueden mezclar múltiples frameworks. +::: + +Esto te permite crear "apps" completas en tu framework de JavaScript preferido y renderizarlas, mediante un componente padre, en una página de Astro. + +:::note +Los componentes de Astro siempre se renderizan como HTML estático, incluso cuando incluyen componentes de framework que están hidratados. Esto significa que solo puedes pasar props que no realicen renderizado HTML. Pasar las "render props" de React a componentes de framework desde un componente de Astro no funcionará, porque los componentes de Astro no pueden proporcionar el comportamiento en tiempo de ejecución del cliente que requiere este patrón. En su lugar, usa slots con nombre. +::: + +## ¿Puedo usar componentes de Astro dentro de mis componentes de framework? + +Cualquier componente de framework de UI se convierte en una "isla" de ese framework. Estos componentes deben estar escritos completamente como código válido para ese framework, usando solo sus propias importaciones y paquetes. No puedes importar componentes `.astro` en un componente de framework de UI (p. ej., `.jsx` o `.svelte`). + +Sin embargo, puedes usar [el patrón `` de Astro](/es/basics/astro-components/#slots) para pasar contenido estático generado por componentes de Astro como elementos hijos a tus componentes de framework **dentro de un componente `.astro`**. + +```astro title="src/pages/astro-children.astro" {6} +--- +import MyReactComponent from '../components/MyReactComponent.jsx'; +import MyAstroComponent from '../components/MyAstroComponent.astro'; +--- + + + +``` + +## ¿Puedo hidratar componentes de Astro? + +Si intentas hidratar un componente de Astro con un modificador `client:`, obtendrás un error. + +[Los componentes de Astro](/es/basics/astro-components/) son componentes de plantillas de solo HTML sin tiempo de ejecución en el cliente. Pero, puedes usar una etiqueta ` + + + +
      + + +

      + Lorem ipsum +

      +
      + + +``` + +## Intellisense para TypeScript + +La integración `@astrojs/alpine` añade `Alpine` al [objeto global window](/es/guides/typescript/#window-y-globalthis). Para el autocompletado del IDE, añade lo siguiente a tu `src/env.d.ts`: + +```ts title="src/env.d.ts" +interface Window { + Alpine: import('alpinejs').Alpine; +} +``` + +## Ejemplos + +* El [ejemplo de Astro y Alpine.js](https://github.com/withastro/astro/tree/main/examples/framework-alpine) muestra cómo usar Alpine.js en un proyecto de Astro. + +[astro-integration]: /es/guides/integrations/ + +[astro-ui-frameworks]: /es/guides/framework-components/#uso-de-componentes-de-frameworks diff --git a/src/content/docs/es/guides/integrations-guide/db.mdx b/src/content/docs/es/guides/integrations-guide/db.mdx new file mode 100644 index 0000000000000..bd6b372853dbd --- /dev/null +++ b/src/content/docs/es/guides/integrations-guide/db.mdx @@ -0,0 +1,11 @@ +--- +title: '@astrojs/db' +description: Aprende a usar la integración @astrojs/db en tu proyecto de Astro. +i18nReady: true +--- + +:::caution[Eliminado] +La integración de Astro DB fue declarada obsoleta en la v6.4 y ha sido eliminada desde Astro v7.0. + +Si estabas usando esta integración en tu proyecto, te recomendamos migrar a una biblioteca de terceros para la funcionalidad de base de datos. Consulta la [guía de migración a Astro v7.0](/es/guides/upgrade-to/v7/#removed-astrojsdb) para obtener más detalles y recomendaciones sobre cómo actualizar tu proyecto. +::: diff --git a/src/content/docs/es/guides/integrations-guide/deno.mdx b/src/content/docs/es/guides/integrations-guide/deno.mdx new file mode 100644 index 0000000000000..f2c2d11f25cb5 --- /dev/null +++ b/src/content/docs/es/guides/integrations-guide/deno.mdx @@ -0,0 +1,13 @@ +--- +title: '@deno/astro-adapter' +description: El adaptador de Deno para Astro +sidebar: + label: Deno +i18nReady: true +--- + +El adaptador de Deno permite a Astro desplegar tu sitio SSR en entornos Deno, incluyendo Deno Deploy. + +El adaptador de Deno era mantenido anteriormente por Astro, pero ahora es mantenido directamente por Deno. Su uso está documentado actualmente [en el repositorio del adaptador de Deno](https://github.com/denoland/deno-astro-adapter). + +Si actualmente estás utilizando este adaptador de Astro, necesitarás migrar a la nueva versión de Deno o [añadir otro adaptador](/es/guides/on-demand-rendering/) para continuar usando SSR en tu proyecto. diff --git a/src/content/docs/es/guides/integrations-guide/preact.mdx b/src/content/docs/es/guides/integrations-guide/preact.mdx new file mode 100644 index 0000000000000..ed0063690a899 --- /dev/null +++ b/src/content/docs/es/guides/integrations-guide/preact.mdx @@ -0,0 +1,267 @@ +--- +type: integration +title: '@astrojs/preact' +description: Aprende a usar la integración del framework @astrojs/preact para extender el soporte de componentes en tu proyecto de Astro. +sidebar: + label: Preact +githubIntegrationURL: 'https://github.com/withastro/astro/tree/main/packages/integrations/preact/' +category: renderer +i18nReady: true +--- + +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' +import Since from '~/components/Since.astro'; + +{/* TODO: add link to /es/guides/integrations/ */} +Esta **integración de Astro** permite el renderizado y la hidratación del lado del cliente para tus componentes de [Preact](https://preactjs.com/). + +## ¿Por qué Preact? + +Preact es una biblioteca que te permite construir componentes de UI interactivos para la web. Si deseas crear funciones interactivas en tu sitio usando JavaScript, es posible que prefieras usar su formato de componentes en lugar de utilizar las API del navegador directamente. + +Preact también es una excelente opción si has utilizado React anteriormente. Preact proporciona la misma API que React, pero en un paquete mucho más pequeño de 3kB. Incluso admite la renderización de muchos componentes de React mediante la opción de configuración `compat` (ver a continuación). + +**¿Quieres aprender más sobre Preact antes de usar esta integración?**\ +Echa un vistazo a [“Aprende Preact”](https://preactjs.com/tutorial), un tutorial interactivo en su sitio web. + +## Instalación + +Astro incluye un comando `astro add` para automatizar la configuración de las integraciones oficiales. Si lo prefieres, puedes [instalar las integraciones manualmente](#instalación-manual) en su lugar. + +Para instalar `@astrojs/preact`, ejecuta lo siguiente desde el directorio de tu proyecto y sigue las instrucciones: + + + + ```sh + npx astro add preact + ``` + + + ```sh + pnpm astro add preact + ``` + + + ```sh + yarn astro add preact + ``` + + + +Si tienes algún problema, [no dudes en reportarlo en GitHub](https://github.com/withastro/astro/issues) e intenta seguir los pasos de instalación manual que aparecen a continuación. + +### Instalación manual + +Primero, instala el paquete `@astrojs/preact`: + + + + ```sh + npm install @astrojs/preact + ``` + + + ```sh + pnpm add @astrojs/preact + ``` + + + ```sh + yarn add @astrojs/preact + ``` + + + +La mayoría de los gestores de paquetes también instalarán las dependencias de pares asociadas. Si ves una advertencia como `Cannot find package 'preact'` (o similar) al iniciar Astro, tendrás que instalar Preact: + + + + ```sh + npm install preact + ``` + + + ```sh + pnpm add preact + ``` + + + ```sh + yarn add preact + ``` + + + +Luego, aplica la integración a tu archivo `astro.config.*` usando la propiedad `integrations`: + +```js title="astro.config.mjs" ins={2} ins="preact()" +import { defineConfig } from 'astro/config'; +import preact from '@astrojs/preact'; + +export default defineConfig({ + // ... + integrations: [preact()], +}); +``` + +Y agrega el siguiente código al archivo `tsconfig.json`. + +```json title="tsconfig.json" ins={5-8} +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"], + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "preact" + } +} +``` + +## Uso + +Para usar tu primer componente de Preact en Astro, dirígete a nuestra [documentación de frameworks de UI][astro-ui-frameworks]. Explorarás: + +* 📦 cómo se cargan los componentes del framework, +* 💧 opciones de hidratación del lado del cliente y +* 🤝 oportunidades para mezclar y anidar frameworks + +{/* TODO: add link to /es/guides/integrations/ */} +También consulta nuestra Documentación de integraciones de Astro para obtener más información sobre las integraciones. + +## Configuración + +La integración de Preact en Astro maneja cómo se renderizan los componentes de Preact y tiene sus propias opciones. Cámbialas en el archivo `astro.config.mjs` que es donde residen los ajustes de integración de tu proyecto. + +Para un uso básico, no necesitas configurar la integración de Preact. + +### `compat` + +

      + +**Tipo:** `boolean`
      + +

      + +Puedes habilitar `preact/compat`, la capa de compatibilidad de Preact para renderizar componentes de React sin necesidad de instalar o enviar las bibliotecas más pesadas de React a los navegadores web de tus usuarios. + +Para hacerlo, pasa un objeto a la integración de Preact y establece `compat: true`. + +```js title="astro.config.mjs" "compat: true" +import { defineConfig } from 'astro/config'; +import preact from '@astrojs/preact'; + +export default defineConfig({ + integrations: [preact({ compat: true })], +}); +``` + +Con la opción `compat` habilitada, la integración de Preact renderizará tanto componentes de React como componentes de Preact en tu proyecto y también te permitirá importar componentes de React dentro de componentes de Preact. Lee más en [“Cambiarse a Preact (desde React)”](https://preactjs.com/guide/v10/switching-to-preact) en el sitio web de Preact. + +Al importar bibliotecas de componentes de React, para intercambiar las dependencias de `react` y `react-dom` por `preact/compat`, puedes usar [`overrides`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides) para lograrlo. + +```json title="package.json" +{ + "overrides": { + "react": "npm:@preact/compat@latest", + "react-dom": "npm:@preact/compat@latest" + } +} +``` + +Consulta la documentación de [overrides de `pnpm`](https://pnpm.io/package_json#pnpmoverrides) y [resolutions de `yarn`](https://yarnpkg.com/configuration/manifest#resolutions) para sus respectivas características de sobrescritura. + +:::note +Actualmente, la opción `compat` solo funciona para las bibliotecas de React que exportan código como ESM. Si ocurre un error durante el tiempo de compilación, intenta agregar la biblioteca a `vite.ssr.noExternal: ['the-react-library']` en tu archivo `astro.config.mjs`. +::: + +### `babel` + +

      + +**Tipo:** [`BabelOptions`](https://github.com/preactjs/preset-vite#babel-configuration)
      + +

      + +Puedes pasar [opciones de configuración de Babel](https://babeljs.io/docs/options) adicionales al plugin de Vite para Preact. Esto te permite personalizar la transformación de Babel aplicada a tus componentes de Preact. + +Por ejemplo, la siguiente configuración le indica a Babel que cargue `.babelrc` al procesar tus componentes de Preact: + +```js title="astro.config.mjs" +import { defineConfig } from 'astro/config'; +import preact from '@astrojs/preact'; + +export default defineConfig({ + integrations: [ + preact({ + babel: { + babelrc: true, + }, + }), + ], +}); +``` + +### `devtools` + +

      + +**Tipo:** `boolean`
      + +

      + +Puedes habilitar las [devtools de Preact](https://preactjs.github.io/preact-devtools/) en desarrollo pasando un objeto con `devtools: true` a la configuración de tu integración `preact()`: + +```js title="astro.config.mjs" +import { defineConfig } from 'astro/config'; +import preact from '@astrojs/preact'; + +export default defineConfig({ + // ... + integrations: [preact({ devtools: true })], +}); +``` + +## Opciones + +### Combinando múltiples frameworks JSX + +Cuando utilizas múltiples frameworks JSX (React, Preact, Solid) en el mismo proyecto, Astro necesita determinar qué transformaciones específicas de cada framework JSX deben usarse para cada uno de tus componentes. Si solo has añadido la integración de un framework JSX a tu proyecto, no se necesita configuración adicional. + +Usa las opciones de configuración `include` (requerido) y `exclude` (opcional) para especificar qué archivos pertenecen a qué framework. Proporciona un array de archivos y/o carpetas en `include` para cada framework que estés utilizando. Se pueden usar comodines para incluir múltiples rutas de archivos. + +Recomendamos colocar los componentes de un mismo framework en la misma carpeta (p. ej. `/components/react/` y `/components/solid/`) para que sea más fácil especificar tus inclusiones, pero esto no es obligatorio: + +```js title="astro.config.mjs" +import { defineConfig } from 'astro/config'; +import preact from '@astrojs/preact'; +import react from '@astrojs/react'; +import svelte from '@astrojs/svelte'; +import vue from '@astrojs/vue'; +import solid from '@astrojs/solid-js'; + +export default defineConfig({ + // Habilita varios frameworks para soportar todo tipo de componentes. + // ¡No se necesita `include` si solo estás usando un único framework JSX! + integrations: [ + preact({ + include: ['**/preact/*'], + }), + react({ + include: ['**/react/*'], + }), + solid({ + include: ['**/solid/*'], + }), + ], +}); +``` + +## Ejemplos + +* El [ejemplo de Astro Preact](https://github.com/withastro/astro/tree/latest/examples/framework-preact) muestra cómo usar un componente interactivo de Preact en un proyecto de Astro. +* El [ejemplo de Astro Nanostores](https://github.com/withastro/astro/tree/latest/examples/with-nanostores) muestra cómo compartir el estado entre diferentes componentes — ¡e incluso entre diferentes frameworks! — en un proyecto de Astro. + +[astro-integration]: /en/guides/integrations/ + +[astro-ui-frameworks]: /es/guides/framework-components/#uso-de-componentes-de-frameworks diff --git a/src/content/docs/es/guides/integrations-guide/prefetch.mdx b/src/content/docs/es/guides/integrations-guide/prefetch.mdx new file mode 100644 index 0000000000000..84844856e8943 --- /dev/null +++ b/src/content/docs/es/guides/integrations-guide/prefetch.mdx @@ -0,0 +1,13 @@ +--- +title: '@astrojs/prefetch' +description: La integración obsoleta de prefetch. +sidebar: + label: Prefetch +i18nReady: true +--- + +:::caution[Eliminado] +`@astrojs/prefetch` ha sido reemplazado por la [funcionalidad `prefetch` integrada](/es/guides/prefetch/) introducida en Astro 3.5. Consulta la [guía de migración](/es/guides/prefetch/#migrando-desde-astrojsprefetch) para obtener instrucciones sobre cómo actualizar un proyecto más antiguo. + +Si todavía estás utilizando esta integración en un proyecto de Astro anterior a la versión 3.5, puedes leer una copia archivada del [README de `@astrojs/prefetch`](https://github.com/withastro/astro/blob/c47478bbf6b21973419f25234c68efb59466b368/packages%2Fintegrations%2Fprefetch%2FREADME.md) en GitHub. +::: diff --git a/src/content/docs/es/guides/integrations-guide/tailwind.mdx b/src/content/docs/es/guides/integrations-guide/tailwind.mdx new file mode 100644 index 0000000000000..6a02bce43469f --- /dev/null +++ b/src/content/docs/es/guides/integrations-guide/tailwind.mdx @@ -0,0 +1,11 @@ +--- +title: '@astrojs/tailwind' +description: Aprende a usar la integración @astrojs/tailwind en tu proyecto de Astro. +i18nReady: true +--- + +:::caution[Obsoleto] +Tailwind CSS ahora ofrece un plugin de Vite, que es la manera recomendada de usar Tailwind 4 en Astro. +::: + +Para usar Tailwind en Astro, sigue la [guía de estilos para Tailwind](/es/guides/styling/#tailwind). \ No newline at end of file diff --git a/src/content/docs/es/guides/integrations.mdx b/src/content/docs/es/guides/integrations.mdx new file mode 100644 index 0000000000000..b6eb0274d8d43 --- /dev/null +++ b/src/content/docs/es/guides/integrations.mdx @@ -0,0 +1,608 @@ +--- +title: Trabajando con integraciones +description: Aprende a añadir, configurar y crear integraciones para tu proyecto de Astro. +i18nReady: true +--- + +import IntegrationsNav from '~/components/IntegrationsNav.astro'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; +import { Steps } from '@astrojs/starlight/components'; +import { FileTree } from '@astrojs/starlight/components'; + +Las **integraciones de Astro** añaden nuevas funcionalidades y comportamientos a tu proyecto con solo unas pocas líneas de código. Puedes usar una integración oficial, [integraciones creadas por la comunidad](#encontrar-más-integraciones) o incluso [crear tu propia integración personalizada](#construir-tu-propia-integración). + +Las integraciones pueden… + +- Habilitar React, Vue, Svelte, Solid y otros frameworks populares de UI con un [renderizador](/es/guides/framework-components/). +- Habilitar el renderizado bajo demanda con un [adaptador SSR](/es/guides/on-demand-rendering/). +- Integrar herramientas como MDX y Partytown con unas pocas líneas de código. +- Añadir nuevas funciones a tu proyecto, como la generación automática de sitemaps. +- Escribir código personalizado que se enganche al proceso de compilación, al servidor de desarrollo y más. + +:::tip[Directorio de integraciones] +Explora o busca la colección completa de cientos de integraciones oficiales y de la comunidad en nuestro [directorio de integraciones](https://astro.build/integrations/). Encuentra paquetes para añadir a tu proyecto de Astro para autenticación, analíticas, rendimiento, SEO, accesibilidad, UI, herramientas de desarrollo y más. +::: + +## Integraciones oficiales + +Las siguientes integraciones son mantenidas por Astro. + + + +## Configuración automática de integraciones + +Astro incluye un comando `astro add` para automatizar la configuración de las integraciones oficiales. También se pueden añadir varios plugins de la comunidad usando este comando. Por favor, consulta la documentación propia de cada integración para comprobar si es compatible con `astro add`, o si debes [instalarla manualmente](#instalación-manual). + +Ejecuta el comando `astro add` utilizando el gestor de paquetes de tu preferencia y nuestro asistente automático de integraciones actualizará tu archivo de configuración e instalará cualquier dependencia necesaria. + + + + ```shell + npx astro add react + ``` + + + ```shell + pnpm astro add react + ``` + + + ```shell + yarn astro add react + ``` + + + +¡Incluso es posible añadir múltiples integraciones al mismo tiempo! + + + + ```shell + npx astro add react sitemap partytown + ``` + + + ```shell + pnpm astro add react sitemap partytown + ``` + + + ```shell + yarn astro add react sitemap partytown + ``` + + + +:::note[Manejo de dependencias de integración] +Si ves alguna advertencia como `Cannot find package '[package-name]'` después de añadir una integración, es posible que tu gestor de paquetes no haya instalado las [dependencias compartidas](https://nodejs.org/en/blog/npm/peer-dependencies/) por ti. Para instalar estos paquetes faltantes, ejecuta el siguiente comando: + + + + ```shell + npm install [nombre-del-paquete] + ``` + + + ```shell + pnpm add [nombre-del-paquete] + ``` + + + ```shell + yarn add [nombre-del-paquete] + ``` + + +::: + +### Instalación manual + +Las integraciones de Astro siempre se añaden a través de la propiedad `integrations` en tu archivo `astro.config.mjs`. + +Hay tres formas comunes de importar una integración a tu proyecto de Astro: + +1. [Instalar una integración desde un paquete npm](#instalar-un-paquete-npm). +2. Importar tu propia integración desde un archivo local dentro de tu proyecto. +3. Escribir tu integración en línea, directamente en tu archivo de configuración. + + ```js + // astro.config.mjs + import { defineConfig } from 'astro/config'; + import installedIntegration from '@astrojs/vue'; + import localIntegration from './my-integration.js'; + + export default defineConfig({ + integrations: [ + // 1. Importado desde un paquete npm instalado + installedIntegration(), + // 2. Importado desde un archivo JS local + localIntegration(), + // 3. Un objeto en línea + { name: 'namespace:id', hooks: { /* ... */ } }, + ] + }); + ``` + +{/*TODO: add link to /es/reference/integrations-reference/ */} +Consulta la referencia de la API de Integraciones para aprender todas las diferentes maneras en que puedes escribir una integración. + +#### Instalar un paquete npm + +Instala una integración de un paquete npm utilizando un gestor de paquetes y luego actualiza `astro.config.mjs` manualmente. + +Por ejemplo, para instalar la integración `@astrojs/sitemap`: + + +1. Instala la integración en las dependencias de tu proyecto utilizando tu gestor de paquetes preferido: + + + + ```shell + npm install @astrojs/sitemap + ``` + + + ```shell + pnpm add @astrojs/sitemap + ``` + + + ```shell + yarn add @astrojs/sitemap + ``` + + + +2. Importa la integración en tu archivo `astro.config.mjs` y añádela a tu array `integrations[]`, junto con cualquier opción de configuración: + + ```js title="astro.config.mjs" ins={2} ins="sitemap()" + import { defineConfig } from 'astro/config'; + import sitemap from '@astrojs/sitemap'; + + export default defineConfig({ + // ... + integrations: [sitemap()], + // ... + }); + ``` + + Ten en cuenta que diferentes integraciones pueden tener diferentes ajustes de configuración. Lee la documentación de cada integración y aplica cualquier opción de configuración necesaria a la integración que hayas elegido en `astro.config.mjs`. + + +### Opciones personalizadas + +Las integraciones casi siempre se crean como funciones factoría que devuelven el objeto de integración real. Esto te permite pasar argumentos y opciones a la función factoría para personalizar la integración según tu proyecto. + +```js +integrations: [ + // Ejemplo: Personaliza tu integración con argumentos de función + sitemap({ filter: true }) +] +``` + +### Alternar una integración + +Las integraciones falsy se ignoran, por lo que puedes activar y desactivar integraciones sin preocuparte por dejar valores booleanos o `undefined` restantes. + +```js +integrations: [ + // Ejemplo: Omite la construcción de un sitemap en Windows + process.platform !== 'win32' && sitemap() +] +``` + +## Actualizar integraciones + +Para actualizar todas las integraciones oficiales a la vez, ejecuta el comando `@astrojs/upgrade`. Esto actualizará tanto Astro como todas las integraciones oficiales a sus últimas versiones. + +### Actualización automática + + + + ```shell + # Actualizar Astro y las integraciones oficiales a la vez a la última versión + npx @astrojs/upgrade + ``` + + + ```shell + # Actualizar Astro y las integraciones oficiales a la vez a la última versión + pnpm dlx @astrojs/upgrade + ``` + + + ```shell + # Actualizar Astro y las integraciones oficiales a la vez a la última versión + yarn dlx @astrojs/upgrade + ``` + + + +### Actualización manual + +Para actualizar una o más integraciones manualmente, usa el comando adecuado para tu gestor de paquetes. + + + + ```shell + # Ejemplo: actualizar las integraciones de React y Partytown + npm install @astrojs/react@latest @astrojs/partytown@latest + ``` + + + ```shell + # Ejemplo: actualizar las integraciones de React y Partytown + pnpm add @astrojs/react@latest @astrojs/partytown@latest + ``` + + + ```shell + # Ejemplo: actualizar las integraciones de React y Partytown + yarn add @astrojs/react@latest @astrojs/partytown@latest + ``` + + + +## Eliminar una integración + + +1. Para eliminar una integración, primero desinstala la integración de tu proyecto. + + + + ```shell + npm uninstall @astrojs/react + ``` + + + ```shell + pnpm remove @astrojs/react + ``` + + + ```shell + yarn remove @astrojs/react + ``` + + + +2. A continuación, elimina la integración de tu archivo `astro.config.*`: + + ```js title="astro.config.mjs" del={2,6} + import { defineConfig } from 'astro/config'; + import react from '@astrojs/react'; + + export default defineConfig({ + integrations: [ + react() + ] + }); + ``` + + +## Encontrar más integraciones + +Puedes encontrar muchas integraciones desarrolladas por la comunidad en el [Directorio de Integraciones de Astro](https://astro.build/integrations/). Sigue los enlaces para obtener instrucciones detalladas de uso y configuración. + +## Construir tu propia integración + +La API de integraciones de Astro está inspirada en Rollup y Vite, y diseñada para resultar familiar a cualquiera que haya escrito antes un plugin de Rollup o Vite. + +{/*TODO: add link to /es/reference/integrations-reference/ */} +Consulta la referencia de la API de Integraciones para aprender qué pueden hacer las integraciones y cómo escribir una tú mismo. + +## Publicar tu integración en npm + +Publicar un componente de Astro es una excelente manera de reutilizar tu trabajo existente en tus proyectos y de compartirlo con la comunidad de Astro en general. Los componentes de Astro se pueden publicar directamente e instalar desde npm, al igual que cualquier otro paquete de JavaScript. + +¿Buscas inspiración? Revisa algunos de los [temas](https://astro.build/themes/) y [componentes](https://astro.build/integrations/) favoritos de la comunidad de Astro. También puedes [buscar en npm](https://www.npmjs.com/search?q=keywords:astro-component,withastro) para ver todo el catálogo público. + +:::tip[¿No quieres hacerlo solo?] +¡Revisa la [plantilla de componentes de la comunidad de Astro](https://github.com/Princesseuh/component-template) para obtener una plantilla lista para usar y respaldada por la comunidad! +::: + +### Inicio rápido + +Para empezar a desarrollar tu componente rápidamente, puedes usar una plantilla ya configurada. + + + + ```shell + # Inicializa la plantilla de componentes de Astro en un nuevo directorio + npm create astro@latest my-new-component-directory -- --template component + ``` + + + ```shell + # Inicializa la plantilla de componentes de Astro en un nuevo directorio + pnpm create astro@latest my-new-component-directory -- --template component + ``` + + + ```shell + # Inicializa la plantilla de componentes de Astro en un nuevo directorio + yarn create astro my-new-component-directory --template component + ``` + + + +### Crear un paquete + +:::note[Requisitos previos] +Antes de empezar, te será útil tener un conocimiento básico sobre: + +- [Módulos de Node](https://docs.npmjs.com/creating-node-js-modules) +- [Manifiesto del paquete (`package.json`)](https://docs.npmjs.com/creating-a-package-json-file) +- [Workspaces](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#workspaces) +::: + +Para crear un nuevo paquete, configura tu entorno de desarrollo para usar **workspaces** dentro de tu proyecto. Esto te permitirá desarrollar tu componente junto a una copia funcional de Astro. + + +- my-new-component-directory/ + - demo/ + - ... para pruebas y demostración + - package.json + - packages/ + - my-component/ + - index.js + - package.json + - ... archivos adicionales utilizados por el paquete + + +Este ejemplo, llamado `my-project`, crea un proyecto con un único paquete, llamado `my-component`, y un directorio `demo/` para probar y demostrar el componente. + +Esto se configura en el archivo `package.json` de la raíz del proyecto: + +```json +{ + "name": "my-project", + "workspaces": ["demo", "packages/*"] +} +``` + +En este ejemplo, se pueden desarrollar múltiples paquetes juntos desde el directorio `packages`. Estos paquetes también se pueden referenciar desde `demo`, donde puedes instalar una copia funcional de Astro. + + + + ```shell + npm create astro@latest demo -- --template minimal + ``` + + + ```shell + pnpm create astro@latest demo -- --template minimal + ``` + + + ```shell + yarn create astro demo --template minimal + ``` + + + +Hay dos archivos iniciales que conformarán tu paquete individual: `package.json` e `index.js`. + +#### `package.json` + +El `package.json` en el directorio del paquete incluye toda la información relacionada con tu paquete, incluyendo su descripción, dependencias y cualquier otro metadato del paquete. + +```json +{ + "name": "my-component", + "description": "Descripción del componente", + "version": "1.0.0", + "homepage": "https://github.com/owner/project#readme", + "type": "module", + "exports": { + ".": "./index.js", + "./astro": "./MyAstroComponent.astro", + "./react": "./MyReactComponent.jsx" + }, + "files": ["index.js", "MyAstroComponent.astro", "MyReactComponent.jsx"], + "keywords": ["astro-component", "withastro", "... etc", "... etc"] +} +``` + +##### `description` + +Una breve descripción de tu componente utilizada para ayudar a otros a saber qué hace. + +```json +{ + "description": "Un generador de elementos de Astro" +} +``` + +##### `type` + +El formato de módulo utilizado por Node.js y Astro para interpretar tus archivos `index.js`. + +```json +{ + "type": "module" +} +``` + +Usa `"type": "module"` para que tu `index.js` pueda usarse como punto de entrada con `import` y `export` . + +##### `homepage` + +La URL de la página principal del proyecto. + +```json +{ + "homepage": "https://github.com/owner/project#readme" +} +``` + +Esta es una excelente manera de dirigir a los usuarios a una demostración en línea, documentación o a la página principal de tu proyecto. + +##### `exports` + +Los puntos de entrada de un paquete cuando se importa por su nombre. + +```json +{ + "exports": { + ".": "./index.js", + "./astro": "./MyAstroComponent.astro", + "./react": "./MyReactComponent.jsx" + } +} +``` + +En este ejemplo, importar `my-component` usaría `index.js`, mientras que importar `my-component/astro` o `my-component/react` usaría `MyAstroComponent.astro` o `MyReactComponent.jsx` respectivamente. + +##### `files` + +Una optimización opcional para excluir archivos innecesarios del paquete enviado a los usuarios a través de npm. Ten en cuenta que **solo los archivos listados aquí se incluirán en tu paquete**, por lo que si agregas o cambias archivos necesarios para que tu paquete funcione, debes actualizar esta lista en consecuencia. + +```json +{ + "files": ["index.js", "MyAstroComponent.astro", "MyReactComponent.jsx"] +} +``` + +##### `keywords` + +Un array de palabras clave relevantes para tu componente, utilizado para ayudar a otros a [encontrar tu componente en npm](https://www.npmjs.com/search?q=keywords:astro-component,withastro) y en cualquier otro catálogo de búsqueda. + +Agrega `astro-component`, `astro-integration`, o `withastro` como una palabra clave especial para maximizar su visibilidad en el ecosistema de Astro. + +```json +{ + "keywords": ["astro-component", "withastro", "... etc", "... etc"] +} +``` + +:::tip +¡Nuestra [biblioteca de integraciones](https://astro.build/integrations/) también utiliza las palabras clave! [Consulta a continuación](#biblioteca-de-integraciones) la lista completa de palabras clave que buscamos en npm. +::: + +--- + +#### `index.js` + +El **punto de entrada principal del paquete** utilizado siempre que se importa tu paquete. + +```js +export { default as MyAstroComponent } from './MyAstroComponent.astro'; +export { default as MyReactComponent } from './MyReactComponent.jsx'; +``` + +Esto te permite empaquetar múltiples componentes juntos en una única interfaz. + +##### Ejemplo: Uso de importaciones con nombre + +```astro +--- +import { MyAstroComponent } from 'my-component'; +import { MyReactComponent } from 'my-component'; +--- + + +``` + +##### Ejemplo: Uso de importaciones de espacio de nombres + +```astro +--- +import * as Example from 'example-astro-component'; +--- + + +``` + +##### Ejemplo: Uso de importaciones individuales + +```astro +--- +import MyAstroComponent from 'example-astro-component/astro'; +import MyReactComponent from 'example-astro-component/react'; +--- + + +``` + +--- + +### Desarrollo de tu paquete + +Astro no tiene un "modo de paquete" dedicado para el desarrollo. En su lugar, debes usar un proyecto de demostración para desarrollar y probar tu paquete dentro de tu proyecto. Este puede ser un sitio web privado utilizado únicamente para desarrollo, o un sitio web público de demostración/documentación para tu paquete. + +Si estás extrayendo componentes de un proyecto existente, incluso puedes continuar usando ese proyecto para desarrollar tus componentes recién extraídos. + +### Probar tu componente + +Actualmente, Astro no incluye un ejecutor de pruebas. _(Si te interesa ayudar con esto, [¡únete a nosotros en Discord!](https://astro.build/chat))_ + +Mientras tanto, nuestra recomendación actual para las pruebas es: + + +1. Agrega un directorio de pruebas `fixtures` a tu directorio `demo/src/pages`. + +2. Agrega una nueva página por cada prueba que te gustaría ejecutar. + +3. Cada página debe incluir un uso diferente del componente que te gustaría probar. + +4. Ejecuta `astro build` para compilar tus fixtures y luego compara la salida del directorio `dist/__fixtures__/` con lo que esperabas. + + - my-project/demo/src/pages/\_\_fixtures\_\_/ + - test-name-01.astro + - test-name-02.astro + - test-name-03.astro + + + + +### Publicar tu componente + +Una vez que tengas tu paquete listo, puedes publicarlo en npm usando el comando `npm publish`. Si esto falla, asegúrate de haber iniciado sesión con `npm login` y de que tu archivo `package.json` esté correcto. Si se ejecuta con éxito, ¡ya terminaste! + +Ten en cuenta que no hubo un paso de `compilación` para los paquetes de Astro. Cualquier tipo de archivo que Astro admita de forma nativa, como `.astro`, `.ts`, `.jsx`, y `.css`, se puede publicar directamente sin un paso de compilación. + +Si necesitas utilizar otro tipo de archivo que Astro no admita de forma nativa, añade un paso de compilación a tu paquete. Este ejercicio avanzado corre por tu cuenta. + +### Biblioteca de integraciones + +¡Comparte tu gran trabajo añadiendo tu integración a nuestra [biblioteca de integraciones](https://astro.build/integrations/)! + +:::tip +¿Necesitas ayuda para desarrollar tu integración, o simplemente quieres conocer a otros creadores de integraciones? Tenemos un canal dedicado `#integrations` en nuestro [servidor de Discord](https://astro.build/chat). ¡Ven a saludar! +::: + +#### Datos de `package.json` + +La biblioteca se actualiza automáticamente cada semana, incorporando todos los paquetes publicados en npm con la palabra clave `astro-component`, `astro-integration`, o `withastro`. + +La biblioteca de integraciones lee los datos de `name`, `description`, `repository` y `homepage` desde tu archivo `package.json`. + +¡Los avatares son una excelente manera de destacar tu marca en la biblioteca! Una vez que tu paquete esté publicado, puedes [abrir una incidencia en GitHub](https://github.com/withastro/astro.build/issues/new/choose) con tu avatar adjunto y lo agregaremos a tu listado. + +:::tip +¿Necesitas sobrescribir la información que nuestra biblioteca lee de npm? ¡No hay problema! [Abre una incidencia](https://github.com/withastro/astro.build/issues/new/choose) con la información actualizada y nos aseguraremos de utilizar tu `name`, `description`, o `homepage` personalizado en su lugar. +::: + +#### Categorías + +Además de la palabra clave obligatoria `astro-component`, `astro-integration` o `withastro`, también se utilizan palabras clave especiales para organizar los paquetes de forma automática. Incluir cualquiera de las siguientes palabras clave añadirá tu integración a la categoría correspondiente en nuestra biblioteca de integraciones. + +| categoría | palabras clave | +|--------------------------------------- | -------------------------------------------- | +| Accesibilidad | `a11y`, `accessibility` | +| Adaptadores | `astro-adapter` | +| Analíticas | `analytics` | +| CSS + UI | `css`, `ui`, `icon`, `icons`, `renderer` | +| Frameworks | `renderer` | +| Cargadores de contenido | `astro-loader` | +| Imágenes + Multimedia | `media`, `image`, `images`, `video`, `audio` | +| Rendimiento + SEO | `performance`, `perf`, `seo`, `optimization` | +| Barra de herramientas de desarrollo | `devtools`, `dev-overlay`, `dev-toolbar` | +| Utilidades | `tooling`, `utils`, `utility` | + +Los paquetes que no incluyan ninguna palabra clave que coincida con una categoría se mostrarán como `Uncategorized`. + +### Compartir + +Te animamos a compartir tu trabajo, de verdad nos encanta ver lo que crean nuestros talentosos Astronautas. ¡Ven a compartir lo que has creado con nosotros en nuestro [Discord](https://astro.build/chat) o menciona a [@astrodotbuild](https://twitter.com/astrodotbuild) en un Tweet! diff --git a/src/content/docs/es/guides/on-demand-rendering.mdx b/src/content/docs/es/guides/on-demand-rendering.mdx index fc2005dd7c385..f82e99f6c0e4d 100644 --- a/src/content/docs/es/guides/on-demand-rendering.mdx +++ b/src/content/docs/es/guides/on-demand-rendering.mdx @@ -29,7 +29,7 @@ Astro mantiene adaptadores oficiales para [Node.js](https://nodejs.org/), [Netli ### Agrega un adaptador -Puedes agregar cualquiera de las [integraciones oficiales de adaptadores mantenidas por Astro](/es/guides/integrations/#official-integrations) con el siguiente comando `astro add`. Esto instalará el adaptador y realizará los cambios correspondientes en tu archivo `astro.config.mjs` en un solo paso. +Puedes agregar cualquiera de las [integraciones oficiales de adaptadores mantenidas por Astro](/es/guides/integrations/#integraciones-oficiales) con el siguiente comando `astro add`. Esto instalará el adaptador y realizará los cambios correspondientes en tu archivo `astro.config.mjs` en un solo paso. Por ejemplo, para instalar el adaptador de Netlify, ejecuta: @@ -51,7 +51,7 @@ Por ejemplo, para instalar el adaptador de Netlify, ejecuta: -También puedes [agregar un adaptador manualmente instalando el paquete de NPM](/es/guides/integrations/#installing-an-npm-package) (por ejemplo, `@astrojs/netlify`) y actualizando tu archivo `astro.config.mjs` por tu cuenta. +También puedes [agregar un adaptador manualmente instalando el paquete de NPM](/es/guides/integrations/#instalar-un-paquete-npm) (por ejemplo, `@astrojs/netlify`) y actualizando tu archivo `astro.config.mjs` por tu cuenta. Ten en cuenta que los diferentes adaptadores pueden tener configuraciones distintas. Lee la documentación de cada adaptador y aplica las opciones de configuración necesarias en `astro.config.mjs`. @@ -73,7 +73,7 @@ export const prerender = false Solo agrega una integración de adaptador para un entorno de servidor. ¡Todas las demás páginas se generan estáticamente en el momento de la compilación! --> - + ``` El siguiente ejemplo muestra cómo excluirse del prerenderizado para mostrar un número aleatorio cada vez que se accede al endpoint: @@ -106,7 +106,7 @@ export const prerender = true `output: 'server' `está configurado, ¡pero esta página es estática! ¡El resto de mi sitio se renderiza bajo demanda! --> - + ``` Agrega `export const prerender = true` a cualquier página o ruta para prerenderizar una página estática o endpoint: diff --git a/src/content/docs/es/reference/astro-syntax.mdx b/src/content/docs/es/reference/astro-syntax.mdx index 17131d12d7474..63b53864a81c0 100644 --- a/src/content/docs/es/reference/astro-syntax.mdx +++ b/src/content/docs/es/reference/astro-syntax.mdx @@ -115,7 +115,7 @@ Al usar etiquetas dinámicas: - **Los nombres de las variables deben ir en mayúsculas.** Por ejemplo, usa `Elemento`, no `elemento`. De lo contrario, Astro intentará representar el nombre de la variable como una etiqueta HTML literal. -- **No se admiten directivas de hidratación.** Cuando se usan las [directivas `client:*` de hidratación](/es/guides/framework-components/#hydrating-interactive-components), Astro necesita saber qué componentes empaquetar para producción, y el patrón de etiquetas dinámicas impide que esto funcione. +- **No se admiten directivas de hidratación.** Cuando se usan las [directivas `client:*` de hidratación](/es/guides/framework-components/#hidratación-de-componentes-interactivos), Astro necesita saber qué componentes empaquetar para producción, y el patrón de etiquetas dinámicas impide que esto funcione. - **La [directiva define:vars](/es/reference/directives-reference/#definevars) no está soportada.** Si no puedes envolver los hijos con un elemento adicional (por ejemplo, `
      `), entonces puedes añadir manualmente un ``style={`--myVar:${value}`}`` a tu Elemento. diff --git a/src/content/docs/es/reference/errors/client-address-not-available.mdx b/src/content/docs/es/reference/errors/client-address-not-available.mdx index ef4862d13a6c9..cd8e8ca1a44b1 100644 --- a/src/content/docs/es/reference/errors/client-address-not-available.mdx +++ b/src/content/docs/es/reference/errors/client-address-not-available.mdx @@ -12,5 +12,5 @@ Lamentablemente, el adaptador que estás utilizando no es compatible con `Astro. **Ver también:** -- [Integraciones oficiales](/es/guides/integrations/#official-integrations) +- [Integraciones oficiales](/es/guides/integrations/#integraciones-oficiales) - [Astro.clientAddress](/es/reference/api-reference/#clientaddress) diff --git a/src/content/docs/es/reference/errors/no-client-entrypoint.mdx b/src/content/docs/es/reference/errors/no-client-entrypoint.mdx index 9be65d979f4f7..fb0287175f6c6 100644 --- a/src/content/docs/es/reference/errors/no-client-entrypoint.mdx +++ b/src/content/docs/es/reference/errors/no-client-entrypoint.mdx @@ -18,4 +18,4 @@ Astro intentó hidratar un componente en el cliente, pero el renderizador utiliz **Ver también:** - [Opción addRenderer](/es/reference/integrations-reference/#addrenderer-option) -- [Hidratando componentes de framework](/es/guides/framework-components/#hydrating-interactive-components) +- [Hidratando componentes de framework](/es/guides/framework-components/#hidratación-de-componentes-interactivos) diff --git a/src/content/docs/es/reference/errors/no-matching-renderer.mdx b/src/content/docs/es/reference/errors/no-matching-renderer.mdx index 6c88341ca4649..4f7c4132b159b 100644 --- a/src/content/docs/es/reference/errors/no-matching-renderer.mdx +++ b/src/content/docs/es/reference/errors/no-matching-renderer.mdx @@ -20,4 +20,4 @@ Para archivos JSX/TSX, [@astrojs/react](/es/guides/integrations-guide/react/), [ **Ver también:** - [Componentes de framework](/es/guides/framework-components/) -- [Frameworks officiales](/es/guides/integrations/#official-integrations) +- [Frameworks oficiales](/es/guides/integrations/#integraciones-oficiales) diff --git a/src/content/docs/es/reference/modules/astro-hono.mdx b/src/content/docs/es/reference/modules/astro-hono.mdx new file mode 100644 index 0000000000000..9f1cc6ce10512 --- /dev/null +++ b/src/content/docs/es/reference/modules/astro-hono.mdx @@ -0,0 +1,36 @@ +--- +title: Referencia de la API de enrutamiento de Hono +sidebar: + label: 'astro/hono' +i18nReady: true +tableOfContents: + minHeadingLevel: 2 + maxHeadingLevel: 4 +--- +import ReadMore from '~/components/ReadMore.astro'; +import Since from '~/components/Since.astro'; + +

      + +El módulo `astro/hono` proporciona manejadores de [enrutamiento avanzado](/es/guides/routing/#enrutamiento-avanzado) construidos como envoltorios compatibles con Hono. + +## Importaciones de `astro/hono` + +```ts +import { + FetchState, + astro, + actions, + cache, + i18n, + middleware, + pages, + redirects, + sessions, + trailingSlash, +} from "astro/hono"; +``` + +El módulo `astro/hono` exporta los mismos nombres de manejadores que [`astro/fetch`](/es/reference/modules/astro-fetch/), pero cada uno devuelve una función de middleware de Hono. Esto te permite mezclar los manejadores de Astro con cualquier middleware de Hono del ecosistema. + +Aprende cómo [usar Hono](/es/guides/routing/#uso-con-hono) con Astro en la guía de enrutamiento avanzado. diff --git a/src/content/docs/es/tutorial/0-introduction/1.mdx b/src/content/docs/es/tutorial/0-introduction/1.mdx index a8610f44d45d4..84951b17a871e 100644 --- a/src/content/docs/es/tutorial/0-introduction/1.mdx +++ b/src/content/docs/es/tutorial/0-introduction/1.mdx @@ -7,7 +7,6 @@ i18nReady: true import Checklist from '~/components/Checklist.astro'; import Box from '~/components/tutorial/Box.astro'; - ## ¿Qué necesito saber para empezar? Si tienes alguna familiaridad básica con **HTML**, **Markdown**, **CSS** y un poco de **JavaScript**, ¡entonces estás totalmente preparado! Podrás completar todo el tutorial con sólo seguir las instrucciones. ¡Astro es para todos! 🧑‍🚀 👩‍🚀 👨‍🚀 diff --git a/src/content/docs/es/tutorial/0-introduction/index.mdx b/src/content/docs/es/tutorial/0-introduction/index.mdx index 05c6d0adc4d44..2dff89c58a244 100644 --- a/src/content/docs/es/tutorial/0-introduction/index.mdx +++ b/src/content/docs/es/tutorial/0-introduction/index.mdx @@ -3,7 +3,7 @@ type: tutorial unitTitle: '¡Bienvenido, mundo!' title: Construye tu primer blog con Astro sidebar: - label: 'Tutorial: Crear un blog' + label: 'Introducción' i18nReady: true description: >- Aprende los conceptos básicos de Astro con un tutorial basado en proyectos. Todos los conocimientos @@ -27,7 +27,7 @@ A lo largo del camino, tú: - Añadirás interactividad a tu sitio web - Desplegarás tu sitio en la web -¿Quieres una vista previa de lo que vas a construir? Puedes ver el proyecto final en [GitHub](https://github.com/withastro/blog-tutorial-demo) o abrir una versión funcional en un entorno de programación en línea como [IDX](https://idx.google.com/import?url=https:%2F%2Fgithub.com%2Fwithastro%2Fblog-tutorial-demo%2F) o [StackBlitz](https://stackblitz.com/github/withastro/blog-tutorial-demo/tree/complete?file=src%2Fpages%2Findex.astro). +¿Quieres una vista previa de lo que vas a construir? Puedes ver el proyecto final en [GitHub](https://github.com/withastro/blog-tutorial-demo) o abrir una versión funcional en un entorno de programación en línea como [StackBlitz](https://stackblitz.com/github/withastro/blog-tutorial-demo/tree/complete?file=src%2Fpages%2Findex.astro). :::note Si prefieres empezar a explorar Astro con un sitio de Astro preconstruido, puedes visitar https://astro.new y elegir una plantilla de inicio para abrirla y editarla en un editor en línea. diff --git a/src/content/docs/es/tutorial/1-setup/index.mdx b/src/content/docs/es/tutorial/1-setup/index.mdx index 0aed46c077df2..75575b97ef7f1 100644 --- a/src/content/docs/es/tutorial/1-setup/index.mdx +++ b/src/content/docs/es/tutorial/1-setup/index.mdx @@ -2,11 +2,13 @@ type: tutorial unitTitle: Crea y despliega tu primer sitio Astro title: 'Check in: Unidad 1 - Configuración' -i18nReady: true +sidebar: + label: 'Unidad 1 - Configuración' description: >- Tutorial: Crea tu primer blog con Astro — + Prepara tu entorno de desarrollo, crea y despliega tu primer sitio Astro - sitio +i18nReady: true head: - tag: title content: 'Tutorial: Crea un blog — Unidad 1: Configuración | Docs' @@ -15,100 +17,57 @@ import Checklist from '~/components/Checklist.astro'; import Box from '~/components/tutorial/Box.astro'; import { Steps } from '@astrojs/starlight/components'; -Ahora que ya sabes lo que vas a construir, ¡es hora a preparar todas las herramientas que vas a necesitar! +Ahora que sabes lo que vas a construir, ¡es hora de configurar todas las herramientas que necesitarás! -Esta unidad te muestra cómo configurar tu entorno de desarrollo y desplegar en Netlify. Pasa a la [Unidad 2](/es/tutorial/2-pages/) si ya te sientes cómodo con tu entorno y flujo de trabajo. +Esta unidad te muestra cómo configurar tu entorno de desarrollo y realizar el despliegue en Netlify. Salta a la [Unidad 2](/es/tutorial/2-pages/) si ya te sientes cómodo con tu entorno y flujo de trabajo. :::tip[Haz el tutorial en un editor de código en línea] -¿Quieres completar este tutorial en un editor de código en línea? Sigue las instrucciones a continuación para comenzar en Google IDX. +¿Prefieres completar este tutorial en un editor de código en línea? Sigue las instrucciones a continuación para comenzar en StackBlitz.
      -Usando Google IDX: ¡Sigue estas instrucciones y luego ve directamente a la Unidad 2! +Usando StackBlitz: ¡Sigue estas instrucciones y luego ve directamente a la Unidad 2! -**Configurar IDX** +**Configurar StackBlitz** -1. Sigue el enlace externo para [abrir la plantilla “Empty Project” en un nuevo espacio de trabajo en IDX](https://astro.new/minimal?on=idx). - -2. Sigue la indicación para iniciar sesión con tu cuenta de Google si aún no lo has hecho. +1. Sigue el enlace externo para [abrir la plantilla "Empty Project" en StackBlitz](https://astro.new/minimal?on=stackblitz). -3. Ingresa un nombre para tu proyecto si deseas cambiarlo del predeterminado “Empty Project”. Haz clic en **Create**. +2. Haz clic en "Sign in" en la esquina superior derecha para iniciar sesión usando tus credenciales de GitHub. -4. Espera a que se cree el espacio de trabajo. Esto puede tardar entre 30 y 60 segundos. Si todo va bien, verás el proyecto de Astro cargado en un editor de código en línea. +3. En la parte superior izquierda de la ventana del editor de StackBlitz, haz clic para hacer un "fork" de la plantilla (para guardarla en el panel de tu propia cuenta). -5. Espera a que IDX ejecute dos scripts: uno para instalar Astro y otro para iniciar el servidor de desarrollo. Ten en cuenta que puede aparecer brevemente un mensaje indicando que tu espacio de trabajo “no pudo encontrar Astro” si este se carga antes de que la instalación haya finalizado. Este mensaje puede ignorarse y cerrarse si no desaparece automáticamente. +4. Espera a que el proyecto se cargue y verás una vista previa en vivo del proyecto inicial "Empty Project". **Haz un cambio** -Si todo va bien, deberías ver el código del archivo `src/pages/index.astro` abierto en pantalla dividida junto con una vista previa en vivo del sitio web. Sigue la instrucción para ["Escribir tu primera línea de Astro"](/es/tutorial/1-setup/3/) y realiza un cambio en este archivo. +En el panel de archivos, deberías ver `src/pages/index.astro`. Haz clic para abrirlo y sigue [Escribe tu primera línea de Astro](/es/tutorial/1-setup/3/) para hacer un cambio en este archivo. **Crea un repositorio de GitHub** -1. Ve al elemento de navegación **“Source Control”** en la barra de menú vertical, o ábrelo con CTRL + SHIFT + G. - -2. Selecciona la opción **Publicar en GitHub**. Esto creará un nuevo repositorio en tu cuenta de GitHub. -3. Sigue las indicaciones para iniciar sesión en tu cuenta de GitHub. -4. Una vez que hayas iniciado sesión, regresa a la pestaña de IDX y se te dará la opción de nombrar tu nuevo repositorio y decidir si quieres que sea privado o público. Puedes elegir cualquier nombre y tipo de repositorio para este tutorial. -5. IDX realizará un *commit* inicial y publicará el proyecto en tu nuevo repositorio de GitHub. -6. A partir de ahora, cada vez que tengas cambios para enviar a GitHub, el ícono de **Source Control** mostrará un número. Este indica la cantidad de archivos que han cambiado desde tu último *commit*. Navega a esta pestaña y realiza dos pasos (*commit* y *publish*) para ingresar un mensaje de *commit* y actualizar tu repositorio." - - -**Despliega tu sitio** - -Si deseas implementar tu sitio en Netlify y tener una versión publicada en línea mientras trabajas, continúa en la Unidad 1 con [Despliega tu sitio en la web](/es/tutorial/1-setup/5/). - -De lo contrario, salta a la [Unidad 2](/es/tutorial/2-pages/) para comenzar a crear con Astro. - -
      - -{/* StackBlitz instructions -
      -Usando StackBlitz: Sigue estas instrucciones y pasa directamente a la Unidad 2. - -**Configura StackBlitz** - - -1. Visita [astro.new](https://astro.new) y haz clic en el botón para abrir la plantilla "Proyecto vacío" en StackBlitz. - -2. Haz clic en "Iniciar sesión" en la parte superior derecha para iniciar sesión con tus credenciales de GitHub. - -3. En la parte superior izquierda de la ventana del editor StackBlitz, haz clic para hacer un "fork" de la plantilla (guardar en el panel de tu propia cuenta). - -4. Espera a que se cargue el proyecto y verás una vista previa en vivo del inicio del "Proyecto vacío". - - -**Haz un cambio** - -En el panel de archivos, deberías ver `src/pages/index.astro`. Haz clic para abrirlo, y sigue [Escribe tu primera línea de Astro](/es/tutorial/1-setup/3/) para hacer un cambio en este archivo. - -**Crea un repositorio GitHub** - - -1. Presiona el botón Connect Repository en la parte superior de la lista de archivos, ingresa un nombre para dicho repositorio y haz clic en Create Repo & push. +1. Presiona el botón Connect Repository en la parte superior de tu lista de archivos, ingresa un nuevo nombre para tu repositorio y haz clic en Create repo & push. -2. Cuando tengas cambios para confirmar en GitHub, aparecerá un botón "Commit" en la parte superior izquierda de tu espacio de trabajo. Al hacer clic en él, podrás introducir un mensaje de confirmación y actualizar tu repositorio. +2. Cuando tengas cambios para confirmar en GitHub, aparecerá un botón "Commit" en la parte superior izquierda de tu espacio de trabajo. Hacer clic en este te permitirá ingresar un mensaje de confirmación y actualizar tu repositorio. **Despliega tu sitio** -Si quieres desplegar tu sitio en Netlify, pasa a [Despliega tu sitio en la web](/es/tutorial/1-setup/5/). +Si deseas realizar el despliegue en Netlify y tener una versión publicada en vivo de tu sitio mientras trabajas, avanza en la Unidad 1 a [Despliega tu sitio en la web](/es/tutorial/1-setup/5/). -¡Si no, pasa a [Unidad 2](/es/tutorial/2-pages/) para empezar a construir con Astro! +De lo contrario, ¡salta a la [Unidad 2](/es/tutorial/2-pages/) para comenzar a construir con Astro!
      -*/} ::: -## ¿A dónde vas? +## ¿Hacia dónde vas? -En esta unidad, **crearás un nuevo proyecto** que estará **almacenado online en GitHub** y **conectado a Netlify**. +En esta unidad, **crearás un nuevo proyecto** que estará **almacenado en línea en GitHub** y **conectado a Netlify**. -A medida que escribas código, enviarás periódicamente tus cambios a GitHub. Netlify utilizará los archivos de tu repositorio de GitHub para construir tu sitio web, y luego lo publicarás en Internet en una dirección única donde cualquiera podrá verlo. +A medida que escribes código, periódicamente confirmarás tus cambios en GitHub. Netlify usará los archivos de tu repositorio de GitHub para construir tu sitio web y luego lo publicará en internet en una dirección única donde cualquiera podrá verlo. -Cada vez que envíes un cambio a GitHub, se enviará una notificación a Netlify. Entonces, Netlify automáticamente reconstruirá y volverá a publicar tu sitio en vivo para reflejar esos cambios. +Cada vez que confirmes un cambio en GitHub, se enviará una notificación a Netlify. Luego, Netlify reconstruirá y volverá a publicar automáticamente tu sitio en vivo para reflejar esos cambios. diff --git a/src/content/docs/es/tutorial/6-islands/1.mdx b/src/content/docs/es/tutorial/6-islands/1.mdx index 1fd4a76ab3c81..676436f108702 100644 --- a/src/content/docs/es/tutorial/6-islands/1.mdx +++ b/src/content/docs/es/tutorial/6-islands/1.mdx @@ -222,6 +222,6 @@ Para cada uno de los siguientes componentes, identifica lo que se enviará al na - [Guía de integraciones Astro](/es/guides/integrations/) -- [Uso de componentes de UI Framework en Astro](/es/guides/framework-components/#using-framework-components) +- [Uso de componentes de UI Framework en Astro](/es/guides/framework-components/#uso-de-componentes-de-frameworks) - [Referencia de las directivas del cliente Astro](/es/reference/directives-reference/#directivas-del-cliente) diff --git a/src/content/docs/fr/guides/authentication.mdx b/src/content/docs/fr/guides/authentication.mdx index 8f45e5aba0821..10cad95e1b77f 100644 --- a/src/content/docs/fr/guides/authentication.mdx +++ b/src/content/docs/fr/guides/authentication.mdx @@ -4,7 +4,7 @@ description: Une introduction à l'authentification avec Astro i18nReady: true --- -import { Steps } from '@astrojs/starlight/components' +import { Steps, Tabs, TabItem } from '@astrojs/starlight/components' import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' import UIFrameworkTabs from '~/components/tabs/UIFrameworkTabs.astro' import ReadMore from '~/components/ReadMore.astro' @@ -66,12 +66,10 @@ Suivez le [guide Astro de Better Auth](https://www.better-auth.com/docs/integrat ### Utilisation -Better Auth propose un assistant `createAuthClient` pour divers frameworks, dont Vanilla JS, React, Vue, Svelte et Solid. +Better Auth propose un assistant `createAuthClient()` pour divers frameworks, dont Vanilla JS, React, Vue, Svelte et Solid. Par exemple, pour créer un client pour React, importez l'assistant depuis `'better-auth/react'` : - - ```ts title="src/lib/auth-client.ts" @@ -116,19 +114,26 @@ Une fois votre client configuré, vous pouvez l'utiliser pour authentifier les u ```astro title="src/pages/index.astro" --- -import Layout from 'src/layouts/Base.astro'; +import Layout from "../layouts/Base.astro"; --- + ``` @@ -137,12 +142,12 @@ Vous pouvez ensuite utiliser l'objet `auth` pour obtenir les données de session ```astro title="src/pages/index.astro" --- -import { auth } from "../../../lib/auth"; // importe votre instance Better Auth +import { auth } from "../lib/auth"; // importez votre instance Better Auth export const prerender = false; // Pas nécessaire en mode `server` - + const session = await auth.api.getSession({ - headers: Astro.request.headers, + headers: Astro.request.headers, }); --- @@ -152,9 +157,9 @@ const session = await auth.api.getSession({ Vous pouvez également utiliser l'objet `auth` pour protéger vos routes. L'exemple suivant utilise [le routage avancé d'Astro](/fr/guides/routing/#routage-avancé) avec [Hono](https://hono.dev/) pour exiger une session authentifiée pour chaque route sous `/dashboard`, en redirigeant vers la page d'accueil dans le cas contraire : ```ts title="src/fetch.ts" -import { Hono } from "hono"; +import { Hono, type Context, type Next } from "hono"; import { astro } from "astro/hono"; -import { auth } from "../auth"; // importez votre instance Better Auth +import { auth } from "./lib/auth"; // importez votre instance Better Auth const app = new Hono(); @@ -167,12 +172,14 @@ app.use(astro()); export default app; -async function requireAuth(c, next) { - const session = await auth.api.getSession({ headers: c.req.raw.headers }); - if (!session) { - return c.redirect("/"); - } - return next(); +async function requireAuth(c: Context, next: Next) { + const session = await auth.api.getSession({ + headers: c.req.raw.headers, + }); + if (!session) { + return c.redirect("/"); + } + return next(); } ``` @@ -225,38 +232,67 @@ Clerk fournit des composants qui vous permettent de contrôler la visibilité de ```astro title="src/pages/index.astro" --- -import Layout from 'src/layouts/Base.astro'; -import { SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/astro/components'; +import Layout from "../layouts/Base.astro"; +import { Show, UserButton, SignInButton } from "@clerk/astro/components"; export const prerender = false; // Pas nécessaire en mode `server` --- - - - - - - + + + + + + ``` -Clerk vous permet également de protéger les routes sur le serveur à l'aide d'un middleware. Spécifiez les routes protégées et invitez les utilisateurs non authentifiés à se connecter : +Clerk vous permet également de protéger les routes sur le serveur à l'aide d'un middleware : -```ts title="src/middleware.ts" -import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server'; + -const isProtectedRoute = createRouteMatcher([ - '/dashboard(.*)', - '/forum(.*)', -]); +1. Définissez `clerkMiddleware()` comme gestionnaire `onRequest` dans votre middleware : -export const onRequest = clerkMiddleware((auth, context) => { - if (!auth().userId && isProtectedRoute(context.request)) { - return auth().redirectToSignIn(); - } -}); -``` + ```ts title="src/middleware.ts" + import { clerkMiddleware } from "@clerk/astro/server"; + + export const onRequest = clerkMiddleware({ + /* options */ + }); + ``` + +2. Accédez à l'état d'authentification dans vos pages et vos routes d'API avec `locals.auth()`. Cela vous permet de vérifier si un utilisateur est authentifié et de prendre les mesures appropriées (par exemple, rediriger vers la page de connexion ou renvoyer une réponse différente). + + + + ```astro title="src/pages/dashboard.astro" + --- + const { isAuthenticated, redirectToSignIn } = Astro.locals.auth(); + + if (!isAuthenticated) return redirectToSignIn(); + --- + +

      Tableau de bord

      + ``` +
      + + ```ts title="src/pages/api/data.ts" + import type { APIRoute } from "astro"; + + export const GET: APIRoute = ({ locals }) => { + const { isAuthenticated, userId } = locals.auth(); + + if (!isAuthenticated) { + return new Response("Unauthorized", { status: 401 }); + } + + return Response.json({ userId }); + }; + ``` + +
      +
      ### Prochaines étapes @@ -286,7 +322,9 @@ export const onRequest = clerkMiddleware((auth, context) => { ## Scalekit -[Scalekit](https://scalekit.com/) est une plateforme d'authentification conçue pour les applications B2B et d'IA. Elle propose la connexion via les réseaux sociaux, l'authentification unique (SSO) d'entreprise, les liens magiques et bien plus encore, en gérant l'intégralité du flux OAuth 2.0 / OIDC afin que vous récupériez des jetons et un profil utilisateur sans avoir à créer d'interface utilisateur de connexion. Un seul environnement Scalekit prend en charge plusieurs applications, de sorte que les utilisateurs s'authentifient une seule fois et partagent la même session sur toutes vos plateformes (par exemple, `app.votreentreprise.com` et `doc.votreentreprise.com`). +[Scalekit](https://scalekit.com/) est une plateforme d'authentification pour les applications B2B et d'IA. Elle gère l'intégralité du flux OAuth 2.0 et OIDC, prenant en charge des méthodes telles que la connexion via les réseaux sociaux, l'authentification unique d'entreprise (SSO) et les liens magiques. Elle renvoie ensuite des jetons ainsi qu'un profil utilisateur sans nécessiter d'interface de connexion personnalisée. + +Un seul environnement Scalekit peut prendre en charge plusieurs applications. Cela vous permet de vous authentifier une seule fois et de partager la même session sur toutes vos plateformes (par exemple, `app.votreentreprise.com` et `doc.votreentreprise.com`). ### Guide diff --git a/src/content/docs/fr/guides/backend/firebase.mdx b/src/content/docs/fr/guides/backend/firebase.mdx index ae063352858d6..5b15cd468bdd8 100644 --- a/src/content/docs/fr/guides/backend/firebase.mdx +++ b/src/content/docs/fr/guides/backend/firebase.mdx @@ -233,6 +233,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { cookies.set("__session", sessionCookie, { path: "/", + maxAge: fiveDays / 1000, }); return redirect("/dashboard"); diff --git a/src/content/docs/fr/guides/cms/emdash.mdx b/src/content/docs/fr/guides/cms/emdash.mdx index 26261ad9272fe..9c2d2eb6d8171 100644 --- a/src/content/docs/fr/guides/cms/emdash.mdx +++ b/src/content/docs/fr/guides/cms/emdash.mdx @@ -4,14 +4,235 @@ description: Ajouter du contenu à votre projet Astro en utilisant EmDash comme sidebar: label: EmDash type: cms -stub: true logo: emdash i18nReady: true --- +import { Steps } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; [EmDash](https://emdashcms.com/) est un CMS open-source full-stack conçu spécifiquement pour Astro, ajoutant du contenu reposant sur une base de données, une interface d'administration, une bibliothèque multimédia, des menus et des taxonomies à votre site. +:::tip +Pour démarrer un **nouveau projet Astro + EmDash à partir de zéro**, utilisez la CLI d'EmDash pour générer un projet préconfiguré : + + + + ```shell + npm create emdash@latest + ``` + + + ```shell + pnpm create emdash@latest + ``` + + + ```shell + yarn create emdash@latest + ``` + + +::: + +## Intégration avec Astro + +EmDash s'exécute au sein de votre projet Astro et s'appuie sur une base de données. Vous pouvez accéder à l'interface d'administration à l'adresse `/_emdash/admin` pour modifier le contenu. Vos pages l'afficheront en interrogeant la base de données à l'aide des [collections de contenu en direct](/fr/guides/content-collections/#collections-de-contenu-en-direct). + +Ce guide utilise l'[adaptateur Node.js](/fr/guides/integrations-guide/node/) et une base de données SQLite locale. Consultez la [documentation d'EmDash pour obtenir la liste des bases de données prises en charge](https://docs.emdashcms.com/deployment/database/). + +## Prérequis + +- Un projet Astro existant (Astro 6 ou une version ultérieure) configuré [avec un adaptateur](/fr/guides/on-demand-rendering/) et [`output: "server"`](/fr/reference/configuration-reference/#output). +- Node.js v22.16.0 ou version ultérieure. + +## Installation des dépendances + +React propulse l'interface d'administration d'EmDash et est une dépendance requise. Si votre projet n'utilise pas React, installez-le à l'aide de la commande `astro add` pour votre gestionnaire de paquets : + + + + ```shell + npx astro add react + ``` + + + ```shell + pnpm astro add react + ``` + + + ```shell + yarn astro add react + ``` + + + +Vous devez également installer le paquet EmDash : + + + + ```shell + npm install emdash + ``` + + + ```shell + pnpm add emdash + ``` + + + ```shell + yarn add emdash + ``` + + + +## Ajout de l'intégration + +Ajoutez l'intégration `emdash()` à votre fichier de configuration d'Astro, puis configurez une base de données ainsi qu'un backend de stockage multimédia : + +```js title="astro.config.mjs" ins={4-5, 12-18} +import { defineConfig } from "astro/config"; +import node from "@astrojs/node"; +import react from "@astrojs/react"; +import emdash, { local } from "emdash/astro"; +import { sqlite } from "emdash/db"; + +export default defineConfig({ + output: "server", + adapter: node({ mode: "standalone" }), + integrations: [ + react(), + emdash({ + database: sqlite({ url: "file:./data.db" }), + storage: local({ + directory: "./uploads", + baseUrl: "/_emdash/api/media/file", + }), + }), + ], +}); +``` + +## Ajout du chargeur de collections en direct + +Créez un fichier `src/live.config.ts` afin que la couche de contenu d'Astro puisse résoudre le contenu d'EmDash : + +```ts title="src/live.config.ts" +import { defineLiveCollection } from "astro:content"; +import { emdashLoader } from "emdash/runtime"; + +export const collections = { + _emdash: defineLiveCollection({ loader: emdashLoader() }), +}; +``` + +La collection `_emdash` redirige en interne vers vos types de contenu (par exemple, articles et pages). Toutes les collections existantes reposant sur des fichiers dans `src/content.config.ts` continuent de fonctionner parallèlement à celle-ci. + +## Exécution d'EmDash en local + +EmDash nécessite que vous complétiez l'assistant de configuration avant de tester vos propres pages. Tant que la configuration n'est pas terminée, aucun contenu n'est publié et les requêtes renvoient des résultats vides. + + +1. Démarrez le serveur de développement d'Astro pour initialiser la base de données et lancer l'interface d'administration : + + + + ```shell + npm run dev + ``` + + + ```shell + pnpm run dev + ``` + + + ```shell + yarn run dev + ``` + + + + Lors de la première exécution, EmDash crée `data.db` avec son schéma et deux collections par défaut : `pages` et `posts`. + +2. Rendez-vous sur `http://localhost:4321/_emdash/admin` dans le navigateur. EmDash vous redirige vers l'assistant de configuration. + +3. Dans l'étape **Site**, saisissez un titre de site et un slogan facultatif. + +4. Dans l'étape **Compte**, saisissez votre adresse e-mail et votre nom. Cela crée le compte administrateur. + +5. Dans l'étape **Se connecter**, sécurisez votre compte. Choisissez **Créer une clé d'accès** pour enregistrer une clé d'accès avec l'authentification biométrique de votre appareil, une clé de sécurité ou un code PIN. + +6. Connectez-vous avec votre nouvelle clé d'accès pour accéder au tableau de bord. + + +## Création de votre premier article + + +1. Dans le tableau de bord, cliquez sur le bouton **+ Post**. + +2. Ajoutez un titre et du contenu. EmDash stocke le texte enrichi au format [Portable Text](https://github.com/portabletext/portabletext), modifié dans un éditeur de blocs. Un slug d'URL est généré à partir du titre et peut être modifié dans la barre latérale. + +3. Cliquez sur **Enregistrer**, puis sur **Publier**. Seuls les articles publiés sont visibles par les visiteurs du site. + + +## Affichage du contenu EmDash + +Interrogez votre contenu avec `getEmDashCollection()` et `getEmDashEntry()`. Les deux suivent le modèle des collections en direct et renvoient les résultats au moment de la requête, de sorte que les modifications publiées apparaissent sans nouvelle compilation. + +### Affichage d'une liste d'articles + +L'exemple suivant affiche une liste de tous les titres d'articles publiés, chacun étant lié à une page d'article individuelle : + +```astro title="src/pages/blog.astro" +--- +import { getEmDashCollection } from "emdash"; + +const { entries: posts } = await getEmDashCollection("posts", { + status: "published", +}); +--- + +``` + +### Affichage d'un article individuel + +Pour afficher le contenu d'un article individuel, récupérez-le par son slug et affichez le contenu Portable Text avec le composant `` : + +```astro title="src/pages/posts/[...slug].astro" +--- +import { getEmDashEntry } from "emdash"; +import { PortableText } from "emdash/ui"; + +const { slug } = Astro.params; +const { entry: post } = await getEmDashEntry("posts", slug); + +if (!post) { + return Astro.redirect("/404"); +} +--- +
      +

      {post.data.title}

      + +
      +``` + +Consultez le [guide d'interrogation d'EmDash](https://docs.emdashcms.com/guides/querying-content/) pour plus d'informations sur le filtrage, la pagination, l'aperçu des brouillons et l'édition visuelle. + +## Déploiement d'EmDash + Astro + +EmDash se déploie en même temps que votre site sous la forme d'un projet Astro unique. Choisissez un hébergeur prenant en charge votre adaptateur, puis configurez une base de données de production et un espace de stockage multimédia. + +Consultez le [guide de déploiement d'EmDash pour Node.js](https://docs.emdashcms.com/deployment/nodejs/) et le [guide de déploiement d'EmDash pour Cloudflare](https://docs.emdashcms.com/deployment/cloudflare/) pour obtenir des instructions détaillées. Vous pouvez également consulter les [guides de déploiement](/fr/guides/deploy/) d'Astro et suivre les instructions pour effectuer le déploiement auprès de l'hébergeur de votre choix. + ## Ressources officielles -- [Documentation EmDash pour les développeurs Astro](https://docs.emdashcms.com/coming-from/astro/) +- [Documentation d'EmDash pour les développeurs Astro](https://docs.emdashcms.com/coming-from/astro/) - [EmDash sur GitHub](https://github.com/emdash-cms/emdash) diff --git a/src/content/docs/fr/guides/deploy/ishosting.mdx b/src/content/docs/fr/guides/deploy/ishosting.mdx new file mode 100644 index 0000000000000..7c7179330190c --- /dev/null +++ b/src/content/docs/fr/guides/deploy/ishosting.mdx @@ -0,0 +1,16 @@ +--- +title: Déployer votre site Astro sur is*hosting +description: Comment déployer votre site Astro sur le web en utilisant is*hosting +sidebar: + label: is*hosting +type: deploy +logo: ishosting +supports: ['ssr', 'static'] +i18nReady: true +--- + +[is\*hosting](https://ishosting.com/) est un fournisseur d'hébergement proposant des VPS et des serveurs dédiés dans plus de 40 emplacements, que vous pouvez utiliser pour auto-héberger un site Astro statique ou rendu côté serveur (SSR). + +## Ressources officielles + +- [Guide is\*hosting : déployer Astro sur un VPS (statique et SSR)](https://blog.ishosting.com/en/astro-on-vps) diff --git a/src/content/docs/fr/guides/integrations-guide/cloudflare.mdx b/src/content/docs/fr/guides/integrations-guide/cloudflare.mdx index 7af74195b2cd4..3dde384d7b76b 100644 --- a/src/content/docs/fr/guides/integrations-guide/cloudflare.mdx +++ b/src/content/docs/fr/guides/integrations-guide/cloudflare.mdx @@ -465,16 +465,20 @@ Lorsque vous utilisez ces gestionnaires dans le point d'entrée de votre worker, Pour une utilisation avec [`astro/fetch`](/fr/reference/modules/astro-fetch/). La fonction `cf()` importée depuis `@astrojs/cloudflare/fetch` reçoit un objet [`FetchState`](/fr/reference/modules/astro-fetch/#fetchstate), l'environnement Cloudflare (`env`) et le contexte d'exécution (`ExecutionContext`). Elle renvoie une réponse (`Response`) pour les accès aux ressources statiques, ou `undefined` lorsque la requête doit se poursuivre avec le rendu Astro : +

      + +Transmettez le même `FetchState` et la réponse de votre pipeline Astro à `finalize()` avant de la renvoyer. Cela applique à la réponse les cookies produits lors du rendu et les en-têtes de cache par défaut de l'adaptateur pour le CDN de Cloudflare. + ```ts title="src/worker.ts" import { astro, FetchState } from 'astro/fetch'; -import { cf } from '@astrojs/cloudflare/fetch'; +import { cf, finalize } from '@astrojs/cloudflare/fetch'; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const state = new FetchState(request); const asset = await cf(state, env, ctx); if (asset) return asset; - return astro(state); + return finalize(state, await astro(state)); }, }; ``` @@ -485,6 +489,8 @@ export default { Pour une utilisation avec [`astro/hono`](/fr/reference/modules/astro-hono/). La fonction `cf()` importée depuis `@astrojs/cloudflare/hono` renvoie un middleware Hono qui lit automatiquement `env` et `executionCtx` depuis le contexte Hono : +Dans la v14.3.0 et les versions ultérieures de `@astrojs/cloudflare`, ce middleware finalise également la réponse après l'exécution des gestionnaires Hono en aval. Les cookies produits lors du rendu et les en-têtes de cache par défaut de l'adaptateur pour le CDN de Cloudflare sont appliqués automatiquement. + ```ts title="src/worker.ts" import { Hono } from 'hono'; import { actions, middleware, pages, i18n } from 'astro/hono'; diff --git a/src/content/docs/fr/reference/cache-provider-reference.mdx b/src/content/docs/fr/reference/cache-provider-reference.mdx index c532beaa8b4d9..fb2c3cb8103f6 100644 --- a/src/content/docs/fr/reference/cache-provider-reference.mdx +++ b/src/content/docs/fr/reference/cache-provider-reference.mdx @@ -371,10 +371,45 @@ La requête (`request`) entrante est transmise en tant que second argument, ce q

      -**Type :** (context: \{ request: Request; url: URL; waitUntil?: (promise: Promise\) => void \}, next: MiddlewareNext) => Promise\ +**Type :** (context: \{ request: Request; url: URL; waitUntil?: (promise: Promise\) => void; logger: AstroRuntimeLogger \}, next: MiddlewareNext) => Promise\

      -Intercepte les requêtes pour mettre en œuvre la mise en cache à l'exécution. L'objet `context` inclut une fonction `waitUntil()` (lorsqu'elle est disponible dans l'environnement d'exécution) pour les tâches en arrière-plan, telles que la stratégie « stale-while-revalidate ». +Un hook optionnel qui intercepte une requête avant qu'Astro ne génère la route correspondante. Il reçoit un objet `context` en tant que premier argument et une fonction de rappel pour appeler le prochain middleware (`next()`) dans la chaîne. + +- Le contexte (`context`) contient les propriétés suivantes : +- `request` : l'objet [`Request`](https://developer.mozilla.org/fr/docs/Web/API/Request) entrant. +- `url` : une [URL](https://developer.mozilla.org/fr/docs/Web/API/URL) normalisée dérivée de la requête. +- `waitUntil() `: lorsqu'elle est disponible dans l'environnement d'exécution, une fonction pour définir des tâches en arrière-plan, telles que la revalidation d'une entrée de cache obsolète. +- `logger` : depuis Astro v7.3.0, une instance du [journaliseur (`logger`)](/fr/reference/api-reference/#logger) qui respecte la [destination de journalisation configurée](/fr/reference/configuration-reference/#options-du-journaliseur) + +L'exemple suivant implémente un hook `onRequest()` minimal qui journalise chaque URL ajoutée au cache : + +```ts title="mon-fournisseur/runtime.ts" ins={7-17} +import type { CacheProviderFactory } from 'astro'; + +const factory: CacheProviderFactory = (config) => { + const cache = new Map(); + return { + name: 'mon-fournisseur-cache', + async onRequest({ request, url, waitUntil, logger }, next) { + if (request.method !== 'GET') return next(); + + const cached = cache.get(url); + if (cached) return cached; + + const response = await next(); + cache.set(url, response.clone()); + logger.info(`Réponse mise en cache pour ${url}.`); + return response; + }, + async invalidate() { + // ... + }, + }; +}; + +export default factory; +``` #### `CacheProvider.invalidate()` diff --git a/src/content/docs/fr/reference/cli-reference.mdx b/src/content/docs/fr/reference/cli-reference.mdx index 5c2b434edaafd..434ad8128c46b 100644 --- a/src/content/docs/fr/reference/cli-reference.mdx +++ b/src/content/docs/fr/reference/cli-reference.mdx @@ -196,25 +196,7 @@ Les raccourcis clavier suivants peuvent être utilisés dans le terminal où le - `o + Entrée` pour ouvrir votre site Astro dans le navigateur. - `q + Entrée` pour quitter le serveur de développement. -

      Options

      - -

      - -La commande accepte [les options communes](#options-communes) et les options supplémentaires suivantes. - -#### `--ignore-lock` - -

      - -Démarre le serveur de développement sans vérifier ni écrire le fichier de verrouillage utilisé pour détecter les autres serveurs de développement en cours d'exécution. Cela permet de démarrer un nouveau serveur de développement parallèlement à un serveur déjà en cours d'exécution pour le même projet, au lieu de générer une erreur. - -```shell -astro dev --ignore-lock --port 4322 -``` - -Le nouveau serveur n'est pas suivi par les sous-commandes [`stop`, `status` ou `logs`](#sous-commandes-communes). - -Lorsqu'elle est combinée avec `--background` (y compris lorsqu'elle est déclenchée par un agent de codage IA) ou `--force`, une erreur est générée, car les deux dépendent du fichier de verrouillage. +La commande peut être combinée avec les [options communes](#options-communes) et les [sous-commandes communes](#sous-commandes-communes) pour contrôler davantage l'expérience de développement. ## `astro build` @@ -240,7 +222,7 @@ Les raccourcis clavier suivants peuvent être utilisés dans le terminal où le - `o` + `enter` pour ouvrir votre site Astro dans le navigateur. - `q` + `enter` pour quitter le serveur de prévisualisation. -La commande `astro preview` peut être combinée avec les [options communes](#options-communes) documentées ci-dessous pour contrôler davantage l'expérience de prévisualisation. Depuis la version v7.2.0, elle accepte également l'option [`--background`](#--background) ainsi que les sous-commandes [`stop`, `status` et `logs`](#sous-commandes-communes) pour gérer un serveur de prévisualisation s'exécutant en arrière-plan. +La commande peut être combinée avec les [options communes](#options-communes) et les [sous-commandes communes](#sous-commandes-communes) pour contrôler davantage l'expérience de prévisualisation. ## `astro check` @@ -560,6 +542,20 @@ astro dev --background --force Active [la journalisation au format JSON](/fr/reference/logger-reference/#loghandlersjson), ce qui est utile pour obtenir une sortie lisible par machine. +### `--ignore-lock` + +

      + +Empêche la vérification de l'existence d'un fichier de verrouillage et la nécessité d'en écrire un. Cela permet de démarrer un nouveau serveur de développement ou, depuis la v7.3.0, un serveur de prévisualisation parallèlement à un serveur déjà en cours d'exécution, au lieu de générer une erreur. + +```shell +astro dev --ignore-lock --port 4322 +``` + +Le nouveau serveur n'est pas suivi par les [sous-commandes communes](#sous-commandes-communes). + +Lorsqu'elle est combinée avec [`--background`](#--background) ou [`--force`](#--force-string), une erreur est générée, car les deux dépendent du fichier de verrouillage. + ## Options globales Utilisez ces options pour obtenir des informations à propos de la CLI `astro`. diff --git a/src/content/docs/fr/reference/errors/redirect-with-no-location.mdx b/src/content/docs/fr/reference/errors/redirect-with-no-location.mdx index 6b62284cf7c86..4512ea1bbfb3c 100644 --- a/src/content/docs/fr/reference/errors/redirect-with-no-location.mdx +++ b/src/content/docs/fr/reference/errors/redirect-with-no-location.mdx @@ -4,6 +4,8 @@ i18nReady: true githubURL: https://github.com/withastro/astro/blob/main/packages/astro/src/core/errors/errors-data.ts --- +> **RedirectWithNoLocation**: The redirect `Response` has no `Location` header. Use `Astro.redirect()` to create redirects, or add a `Location` header to the `Response`. + ## Qu'est-ce qui a mal tourné ? Une redirection doit recevoir un emplacement avec l'en-tête `Location`. diff --git a/src/content/docs/fr/reference/image-service-reference.mdx b/src/content/docs/fr/reference/image-service-reference.mdx index ba303a86a4930..c583f43b58305 100644 --- a/src/content/docs/fr/reference/image-service-reference.mdx +++ b/src/content/docs/fr/reference/image-service-reference.mdx @@ -31,21 +31,21 @@ Un service externe pointe vers une URL distante à utiliser comme attribut `src` import type { ExternalImageService, ImageTransform, AstroConfig } from "astro"; const service: ExternalImageService = { - validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) { + validateOptions(options: ImageTransform, imageConfig: AstroConfig['image'], logger) { const serviceConfig = imageConfig.service.config; // Appliquer la largeur maximale définie par l'utilisateur. if (options.width && options.width > serviceConfig.maxWidth) { - console.warn(`La largeur de l'image ${options.width} dépasse la largeur maximale ${serviceConfig.maxWidth}. Repli sur la largeur maximale.`); + logger.warn(`La largeur de l'image ${options.width} dépasse la largeur maximale ${serviceConfig.maxWidth}. Repli sur la largeur maximale.`); options.width = serviceConfig.maxWidth; } return options; }, - getURL(options, imageConfig) { + getURL(options, imageConfig, logger) { return `https://monsupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`; }, - getHTMLAttributes(options, imageConfig) { + getHTMLAttributes(options, imageConfig, logger) { const { src, format, quality, ...attributes } = options; return { ...attributes, @@ -68,7 +68,7 @@ import type { ImageTransform, LocalImageService, AstroConfig } from "astro"; import { mySuperLibraryThatEncodesImages } from "@example/ma-super-bibliotheque"; const service: LocalImageService = { - getURL(options: ImageTransform, imageConfig) { + getURL(options: ImageTransform, imageConfig, logger) { const searchParams = new URLSearchParams(); searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src); options.width && searchParams.append('w', options.width.toString()); @@ -79,7 +79,7 @@ const service: LocalImageService = { // Ou utilisez le point de terminaison intégré, qui appellera vos fonctions parseURL et transform : // return `/_image?${searchParams}`; }, - parseURL(url: URL, imageConfig) { + parseURL(url: URL, imageConfig, logger) { const params = url.searchParams; return { src: params.get('href')!, @@ -89,14 +89,14 @@ const service: LocalImageService = { quality: params.get('q'), }; }, - async transform(inputBuffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig) { + async transform(inputBuffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig, logger) { const { buffer } = await mySuperLibraryThatEncodesImages(options); return { data: buffer, format: options.format, }; }, - getHTMLAttributes(options, imageConfig) { + getHTMLAttributes(options, imageConfig, logger) { let targetWidth = options.width; let targetHeight = options.height; if (typeof options.src === "object") { @@ -141,7 +141,7 @@ import { getConfiguredImageService, imageConfig } from "astro:assets"; import * as mime from "mrmime"; import { getImageBuffer } from "./mon-recuperateur-d-images-personnalise.js"; -export const GET: APIRoute = async ({ request }) => { +export const GET: APIRoute = async ({ request, logger }) => { const imageService = await getConfiguredImageService(); if (!isLocalService(imageService)) { @@ -154,6 +154,7 @@ export const GET: APIRoute = async ({ request }) => { const imageTransform = await imageService.parseURL( new URL(request.url), imageConfig, + logger, ); if (!imageTransform) { @@ -166,6 +167,7 @@ export const GET: APIRoute = async ({ request }) => { inputBuffer, imageTransform, imageConfig, + logger, ); return new Response(new Uint8Array(data), { status: 200, @@ -185,7 +187,7 @@ export const GET: APIRoute = async ({ request }) => {

      -**Type :** (options: ImageTransform, imageConfig: AstroConfig['image']) => string | Promise\
      +**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => string | Promise\

      @@ -195,57 +197,71 @@ Pour les services locaux, ce hook renvoie l'URL du point de terminaison qui gén Pour les services externes, ce hook renvoie l'URL finale de l'image. -Pour les deux types de services, les `options` sont les propriétés passées par l'utilisateur comme attributs du composant `` ou comme options de `getImage()`. +Pour les deux types de services, les `options` sont les propriétés passées par l'utilisateur comme attributs du composant `` ou comme options de `getImage()`. Ce hook reçoit également la configuration de l'image et, depuis Astro v7.3.0, un journaliseur. ### `parseURL()`

      -**Type :** (url: URL, imageConfig: AstroConfig['image']) => \{ src: string, [key: string]: any \} | undefined | Promise\<\{ src: string, [key: string]: any \}\> | Promise\
      +**Type :** (url: URL, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => \{ src: string, [key: string]: any \} | undefined | Promise\<\{ src: string, [key: string]: any \}\> | Promise\

      **Requis pour les services locaux uniquement ; indisponible pour les services externes** -Ce hook analyse les URLs générées par `getURL()` en un objet avec les différentes propriétés à utiliser par `transform` (en SSR et en mode dev). Il n'est pas utilisé pendant la compilation. +Ce hook analyse les URLs générées par `getURL()` pour les transformer en un objet contenant les différentes propriétés à utiliser par `transform`. Il reçoit trois paramètres : l'URL à analyser, la configuration de l'image et, depuis Astro v7.3.0, un journaliseur. + +Ce hook est utilisé uniquement pour le rendu à la demande et en mode développement. Il n'est pas utilisé pendant la compilation. ### `transform()`

      -**Type :** (inputBuffer: Uint8Array, options: \{ src: string, [key: string]: any \}, imageConfig: AstroConfig['image']) => Promise\<\{ data: Uint8Array; format: ImageOutputFormat \}\>
      +**Type :** (inputBuffer: Uint8Array, options: \{ src: string, [key: string]: any \}, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Promise\<\{ data: Uint8Array; format: ImageOutputFormat \}\>

      **Requis pour les services locaux uniquement ; indisponible pour les services externes** -Ce hook transforme et renvoie l'image et est appelé pendant la compilation pour créer les fichiers de ressources finaux. +Ce hook transforme et renvoie l'image et est appelé pendant la compilation pour créer les fichiers de ressources finaux. Il reçoit quatre paramètres : l'image d'entrée, un objet d'options, la configuration de l'image et, depuis Astro v7.3.0, un journaliseur. Vous devez renvoyer un `format` pour garantir que le type MIME approprié est fourni aux utilisateurs pour le rendu à la demande et le mode de développement. +```ts +import type { LocalImageService } from 'astro'; + +const service: LocalImageService = { + // ... + async transform(inputBuffer, transform, imageConfig, logger) { + logger.warn(`Impossible d'optimiser « ${transform.src} ». Transmission sans modification.`); + return { data: inputBuffer, format: 'png' }; + }, +}; +``` + ### `getHTMLAttributes()`

      -**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => Record\ | Promise\\>
      +**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Record\ | Promise\\>

      **Facultatif pour les services locaux et externes** -Ce hook renvoie tous les attributs supplémentaires utilisés pour restituer l'image en HTML, en fonction des paramètres transmis par l'utilisateur (`options`). +Ce hook renvoie tous les attributs supplémentaires utilisés pour restituer l'image en HTML, en fonction des paramètres transmis par l'utilisateur (`options`). Il reçoit également la configuration de l'image et, depuis Astro v7.3.0, un journaliseur. ### `getSrcSet()`

      -**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => UnresolvedSrcSetValue[] | Promise\
      +**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => UnresolvedSrcSetValue[] | Promise\

      **Facultatif pour les services locaux et externes** -Ce hook génère plusieurs variantes de l'image spécifiée, par exemple, pour générer un attribut `srcset` sur une `` ou `source` sur ``. +Ce hook génère plusieurs variantes de l'image spécifiée, par exemple, pour générer un attribut `srcset` sur une `` ou `source` sur ``. Il reçoit trois paramètres : un objet d'options, la configuration de l'image et, depuis Astro v7.3.0, un journaliseur. Ce hook retourne un tableau d'objets avec les propriétés suivantes : @@ -261,13 +277,13 @@ export type UnresolvedSrcSetValue = {

      -**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => ImageTransform | Promise\ +**Type :** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => ImageTransform | Promise\

      **Facultatif pour les services locaux et externes** -Ce hook vous permet de valider et d'augmenter les options passées par l'utilisateur. C'est utile pour définir des options par défaut, ou pour indiquer à l'utilisateur qu'un paramètre est nécessaire. +Ce hook vous permet de valider et d'augmenter les options passées par l'utilisateur. C'est utile pour définir des options par défaut, ou pour indiquer à l'utilisateur qu'un paramètre est nécessaire. Il reçoit également la configuration de l'image et, depuis Astro v7.3.0, un journaliseur que vous pouvez utiliser pour avertir l'utilisateur des options invalides. [Voir comment `validateOptions()` est utilisé dans les services intégrés d'Astro](https://github.com/withastro/astro/blob/0ab6bad7dffd413c975ab00e545f8bc150f6a92f/packages/astro/src/assets/services/service.ts#L124). @@ -275,13 +291,13 @@ Ce hook vous permet de valider et d'augmenter les options passées par l'utilisa

      -**Type :** (url: string, imageConfig: AstroConfig['image'] ) => Omit\<ImageMetadata, 'src' | 'fsPath'\> | Promise\ImageMetadata, 'src' | 'fsPath'\>\> +**Type :** (url: string, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Omit\<ImageMetadata, 'src' | 'fsPath'\> | Promise\ImageMetadata, 'src' | 'fsPath'\>\>

      **Facultatif pour les services locaux et externes** -Ce hook vous permet d'étendre le comportement de [`inferRemoteSize()`](/fr/reference/modules/astro-assets/#inferremotesize). Ceci est utile pour réduire le trafic réseau en mettant les images en cache, ou lorsque vous pouvez prédire les informations d'une image à partir de son URL. +Ce hook vous permet d'étendre le comportement de [`inferRemoteSize()`](/fr/reference/modules/astro-assets/#inferremotesize). Ceci est utile pour réduire le trafic réseau en mettant les images en cache, ou lorsque vous pouvez prédire les informations d'une image à partir de son URL. Il reçoit trois paramètres : l'URL de l'image, la configuration de l'image et, depuis Astro v7.3.0, un journaliseur. ## Configuration de l'utilisateur diff --git a/src/content/docs/fr/reference/modules/astro-assets.mdx b/src/content/docs/fr/reference/modules/astro-assets.mdx index daca5477511aa..d9c5c06e8960a 100644 --- a/src/content/docs/fr/reference/modules/astro-assets.mdx +++ b/src/content/docs/fr/reference/modules/astro-assets.mdx @@ -646,7 +646,7 @@ Si vous avez besoin de l'URL de l'image résultante côté client, vous pouvez [ La fonction `getImage()` est prévue pour générer des images destinées à être utilisées ailleurs que directement en HTML, par exemple dans une [route d'API](/fr/guides/endpoints/#points-de-terminaison-du-serveur-routes-api). Elle vous permet également de créer votre propre composant `` personnalisé. -Cette fonction prend un objet d'options avec les [mêmes propriétés que le composant Image](#image-) (sauf `alt`) et renvoie un [objet `GetImageResult`](#getimageresult). +Cette fonction prend un objet d'options avec les [mêmes propriétés que le composant Image](#image-) (sauf `alt` et `sizes`) et renvoie un [objet `GetImageResult`](#getimageresult). L'exemple suivant génère un arrière-plan (`background-image`) au format AVIF pour un élément `
      ` : @@ -1423,7 +1423,9 @@ Une valeur prête à être utilisée dans l'attribut `srcset`. **Type :** `object`

      -Définit les options acceptées par le service de transformation d'images. Ceci contient une propriété `src` obligatoire, des propriétés prédéfinies facultatives et toutes les propriétés supplémentaires requises par le service d'images : +Définit les options acceptées par le service de transformation d'images. Ceci contient une propriété `src` obligatoire, des propriétés prédéfinies facultatives et toutes les propriétés supplémentaires requises par le service d'images. + +Les propriétés prédéfinies correspondent à celles acceptées par le [composant ``](#image-), à l'exception de `alt` et `sizes`. Les propriétés suivantes utilisent des types différents. #### `ImageTransform.src` @@ -1452,64 +1454,6 @@ La largeur de l'image. La hauteur de l'image. -#### `ImageTransform.widths` - -

      - -**Type :** `number[] | undefined`
      - -

      - -Une liste de largeurs à générer pour l'image. - -#### `ImageTransform.densities` - -

      - -**Type :** ``(number | `${number}x`)[] | undefined``
      - -

      - -Une liste de densités de pixels à générer pour l'image. - -#### `ImageTransform.quality` - -

      - -**Type :** ImageQuality | undefined -

      - -La qualité souhaitée pour l'image de sortie. - -#### `ImageTransform.format` - -

      - -**Type :** ImageOutputFormat | undefined -

      - -Le format souhaité pour l'image de sortie. - -#### `ImageTransform.fit` - -

      - -**Type :** `'fill' | 'contain' | 'cover' | 'none' | 'scale-down' | string | undefined`
      - -

      - -Définit une liste de valeurs autorisées pour la propriété CSS `object-fit`, extensible avec n'importe quelle chaîne de caractères. - -#### `ImageTransform.position` - -

      - -**Type :** `string | undefined`
      - -

      - -Contrôle la valeur de la propriété CSS `object-position`. - ### `UnresolvedImageTransform`

      diff --git a/src/content/docs/ja/basics/astro-pages.mdx b/src/content/docs/ja/basics/astro-pages.mdx index 1bf20e3b9ef01..3018e534f28e3 100644 --- a/src/content/docs/ja/basics/astro-pages.mdx +++ b/src/content/docs/ja/basics/astro-pages.mdx @@ -22,7 +22,7 @@ Astroは`src/pages/`ディレクトリで次のファイルタイプをサポー Astroは、**ファイルベースルーティング**と呼ばれるルーティング手法を採用しています。 `src/pages/`ディレクトリの各ファイルはそのファイルパスに基づいたエンドポイントになります。 -また、[動的ルーティング](/ja/guides/routing/#dynamic-routes)を使用して、1つのファイルから複数のページを生成できます。これにより、[コンテンツコレクション](/ja/guides/content-collections/)や[CMS](/ja/guides/cms/)など、特別な`/pages/`ディレクトリの外にコンテンツがあっても、ページを作成できます。 +また、[動的ルーティング](/ja/guides/routing/#動的ルーティング)を使用して、1つのファイルから複数のページを生成できます。これにより、[コンテンツコレクション](/ja/guides/content-collections/)や[CMS](/ja/guides/cms/)など、特別な`/pages/`ディレクトリの外にコンテンツがあっても、ページを作成できます。 [Astroのルーティング](/ja/guides/routing/)について詳しくみる。 diff --git a/src/content/docs/ja/basics/layouts.mdx b/src/content/docs/ja/basics/layouts.mdx index feca5964ad8c0..006e6f9b4b787 100644 --- a/src/content/docs/ja/basics/layouts.mdx +++ b/src/content/docs/ja/basics/layouts.mdx @@ -16,7 +16,7 @@ import ReadMore from '~/components/ReadMore.astro' レイアウトコンポーネントがページシェルを含んでいる場合、レイアウトコンポーネントの``タグは他の全てのタグの親である必要があります。 -レイアウトコンポーネントは一般的にプロジェクト内の`src/layouts`ディレクトリに配置されますが、これは必須ではなく、プロジェクト内のどこに置いても構いません。レイアウトコンポーネントをページと同じ場所に置くこともでき、その場合は[レイアウト名の先頭に`_`を付けます](/ja/guides/routing/#excluding-pages)。 +レイアウトコンポーネントは一般的にプロジェクト内の`src/layouts`ディレクトリに配置されますが、これは必須ではなく、プロジェクト内のどこに置いても構いません。レイアウトコンポーネントをページと同じ場所に置くこともでき、その場合は[レイアウト名の先頭に`_`を付けます](/ja/guides/routing/#ページの除外)。 ## レイアウトのサンプル diff --git a/src/content/docs/ja/guides/backend/firebase.mdx b/src/content/docs/ja/guides/backend/firebase.mdx index 8e32b76c1f3bd..2b0e09bc97581 100644 --- a/src/content/docs/ja/guides/backend/firebase.mdx +++ b/src/content/docs/ja/guides/backend/firebase.mdx @@ -233,6 +233,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { cookies.set("__session", sessionCookie, { path: "/", + maxAge: fiveDays / 1000, }); return redirect("/dashboard"); diff --git a/src/content/docs/ja/guides/data-fetching.mdx b/src/content/docs/ja/guides/data-fetching.mdx index 3189b3f2ab468..97ecc64b50315 100644 --- a/src/content/docs/ja/guides/data-fetching.mdx +++ b/src/content/docs/ja/guides/data-fetching.mdx @@ -100,7 +100,7 @@ const { film } = json.data; ## ヘッドレスCMSからの取得 -Astroコンポーネントは、お好みのCMSからデータを取得し、それをページコンテンツとしてレンダリングできます。[動的ルート](/ja/guides/routing/#dynamic-routes)を使えば、CMSのコンテンツをもとにページを生成することも可能です。 +Astroコンポーネントは、お好みのCMSからデータを取得し、それをページコンテンツとしてレンダリングできます。[動的ルート](/ja/guides/routing/#動的ルーティング)を使えば、CMSのコンテンツをもとにページを生成することも可能です。 Storyblok、Contentful、WordPressなどのヘッドレスCMSとAstroを統合する方法の詳細については、[CMSガイド](/ja/guides/cms/)を参照してください。 diff --git a/src/content/docs/ja/guides/endpoints.mdx b/src/content/docs/ja/guides/endpoints.mdx index fbbe0953b1feb..31e68d4289ebd 100644 --- a/src/content/docs/ja/guides/endpoints.mdx +++ b/src/content/docs/ja/guides/endpoints.mdx @@ -51,7 +51,7 @@ export const GET = (async ({ params, request }) => { /* ... */ }) satisfies APIR ### `params`と動的ルーティング -エンドポイントは、ページと同じ[動的ルーティング](/ja/guides/routing/#dynamic-routes)機能をサポートしています。ファイル名を角括弧で囲んだパラメータ名にして、[`getStaticPaths()`関数](/ja/reference/routing-reference/#getstaticpaths)をエクスポートしましょう。すると、エンドポイント関数に渡される`params`プロパティを通じて、そのパラメータにアクセスできます。 +エンドポイントは、ページと同じ[動的ルーティング](/ja/guides/routing/#動的ルーティング)機能をサポートしています。ファイル名を角括弧で囲んだパラメータ名にして、[`getStaticPaths()`関数](/ja/reference/routing-reference/#getstaticpaths)をエクスポートしましょう。すると、エンドポイント関数に渡される`params`プロパティを通じて、そのパラメータにアクセスできます。 ```ts title="src/pages/api/[id].json.ts" import type { APIRoute } from "astro"; diff --git a/src/content/docs/ja/guides/integrations-guide/markdoc.mdx b/src/content/docs/ja/guides/integrations-guide/markdoc.mdx index 37d7055e1341b..854f36cd68165 100644 --- a/src/content/docs/ja/guides/integrations-guide/markdoc.mdx +++ b/src/content/docs/ja/guides/integrations-guide/markdoc.mdx @@ -113,13 +113,16 @@ Markdocファイルは、コンテンツコレクション内でのみ使用で - quick-start.mdoc -次に、[コンテンツコレクションAPI](/ja/guides/content-collections/#querying-build-time-collections)を使用してコレクションをクエリします。 +次に、[投稿やコレクションをクエリしてレンダリングします](/ja/guides/content-collections/#querying-build-time-collections) ```astro title="src/pages/why-markdoc.astro" --- -import { getEntry, render } from 'astro:content'; +import { getEntry, render } from "astro:content"; -const entry = await getEntry('docs', 'why-markdoc'); +const entry = await getEntry("docs", "why-markdoc"); +if (!entry) { + throw new Error("Entry not found"); +} const { Content } = await render(entry); --- @@ -142,6 +145,9 @@ const { Content } = await render(entry); import { getEntry, render } from 'astro:content'; const entry = await getEntry('docs', 'why-markdoc'); +if (!entry) { + throw new Error("Entry not found"); +} const { Content } = await render(entry); --- @@ -180,6 +186,9 @@ export default defineMarkdocConfig({ import { getEntry, render } from 'astro:content'; const entry = await getEntry('docs', 'why-markdoc'); +if (!entry) { + throw new Error("Entry not found"); +} const { Content } = await render(entry); --- @@ -500,8 +509,14 @@ Markdocの`image`タグを使用すると、`![]()`構文では不可能な追 const { src, alt, width, height, caption } = Astro.props; ---

      - - {caption &&
      {caption}
      } + { + typeof src === "string" ? ( + + ) : ( + + ) + } + {caption &&
      {caption}
      }
      ``` @@ -661,6 +676,37 @@ Markdocでネストされたタグを使用する場合、タグ内のコンテ {% /custom-tag %} ``` +### `typographer` + +

      + +**Type:** `boolean`
      +**Default:** `false`
      + +

      + +Markdocのスマートクォートの組み込みサポートを有効にします。通常のシングルクォートやダブルクォートを、適切なカーリークォートに置き換えられます。 + +```js title="astro.config.mjs" ins={6} + import { defineConfig } from 'astro/config'; + import markdoc from '@astrojs/markdoc'; + + export default defineConfig({ + // ... + integrations: [markdoc({ typographer: true })], + }); +``` + +```md title="src/content/docs/why-markdoc.mdoc" +She continued, "You know what they said? They said, 'It's astonishing how fancy these smart quotes look!'. That's what they said." +``` + +通常の引用符は自動的に変換され、以下のようにレンダリングされます。 + +```md +She continued, “You know what they said? They said, ‘It’s astonishing how fancy these smart quotes look!’. That’s what they said.” +``` + ## 例 * [Astro Markdocスターターテンプレート](https://github.com/withastro/astro/tree/latest/examples/with-markdoc)は、AstroプロジェクトでMarkdocファイルを使用する方法を示しています。 diff --git a/src/content/docs/ja/guides/middleware.mdx b/src/content/docs/ja/guides/middleware.mdx index 41bbe14cb0fb3..9b7134c431113 100644 --- a/src/content/docs/ja/guides/middleware.mdx +++ b/src/content/docs/ja/guides/middleware.mdx @@ -208,9 +208,9 @@ validation response

      -`APIContext`は[`rewrite()`](/ja/reference/api-reference/#rewrite)というメソッドを公開しています。これは[Astro.rewrite](/ja/guides/routing/#rewrites)と同じように動作します。 +`APIContext`は[`rewrite()`](/ja/reference/api-reference/#rewrite)というメソッドを公開しています。これは[Astro.rewrite](/ja/guides/routing/#リライト)と同じように動作します。 -ミドルウェア内で`context.rewrite()`を使うと、訪問者を新しいページに[リダイレクト](/ja/guides/routing/#dynamic-redirects)することなく、別のページのコンテンツを表示できます。これは新しいレンダリングフェーズを引き起こし、すべてのミドルウェアが再実行されます。 +ミドルウェア内で`context.rewrite()`を使うと、訪問者を新しいページに[リダイレクト](/ja/guides/routing/#動的リダイレクト)することなく、別のページのコンテンツを表示できます。これは新しいレンダリングフェーズを引き起こし、すべてのミドルウェアが再実行されます。 ```js title="src/middleware.js" import { isLoggedIn } from "~/auth.js" diff --git a/src/content/docs/ja/guides/migrate-to-astro/from-create-react-app.mdx b/src/content/docs/ja/guides/migrate-to-astro/from-create-react-app.mdx index 42aea1b21b0a5..b3d76ffe608d5 100644 --- a/src/content/docs/ja/guides/migrate-to-astro/from-create-react-app.mdx +++ b/src/content/docs/ja/guides/migrate-to-astro/from-create-react-app.mdx @@ -37,7 +37,7 @@ import App from '../cra-project/App.jsx'; ## CRAとAstroの類似点 - [`.astro`ファイルの構文はJSXとよく似ています](/ja/reference/astro-syntax/#astroとjsxの違い)。Astroを使うのも直感的に感じられるはずです。 -- Astroはファイルベースのルーティングを採用し、[動的ルート](/ja/guides/routing/#dynamic-routes)もファイル名で定義できます。 +- Astroはファイルベースのルーティングを採用し、[動的ルート](/ja/guides/routing/#動的ルーティング)もファイル名で定義できます。 - Astroは[コンポーネントベース](/ja/basics/astro-components/)です。マークアップ構造自体は移行前後で大きく変わりません。 - React・Preact・Solid用の[公式インテグレーション](/ja/guides/integrations-guide/react/)があり、既存のJSXコンポーネントをそのまま利用できます。ただし、これらのファイルはAstro内では`.jsx`または`.tsx`拡張子を持つ**必要**があります。 - Astroは[NPMパッケージのインストール](/ja/guides/imports/#npm-packages)をサポートしており、Reactライブラリも含まれます。多くの既存依存関係はAstroでも動作することが多いでしょう。 diff --git a/src/content/docs/ja/guides/migrate-to-astro/from-hugo.mdx b/src/content/docs/ja/guides/migrate-to-astro/from-hugo.mdx index f979564715f56..5f618fd1423a9 100644 --- a/src/content/docs/ja/guides/migrate-to-astro/from-hugo.mdx +++ b/src/content/docs/ja/guides/migrate-to-astro/from-hugo.mdx @@ -63,7 +63,7 @@ Markdownファイル内で変数や表現、UIコンポーネントなど動的 - + :::note[共有したいリソースがありますか?] diff --git a/src/content/docs/ja/guides/prefetch.mdx b/src/content/docs/ja/guides/prefetch.mdx new file mode 100644 index 0000000000000..c10832139db00 --- /dev/null +++ b/src/content/docs/ja/guides/prefetch.mdx @@ -0,0 +1,273 @@ +--- +title: プリフェッチ +description: ページ間のすばやいナビゲーションのためにリンクをプリフェッチします。 +i18nReady: true +--- + +import { Steps } from '@astrojs/starlight/components' +import Since from '~/components/Since.astro' + +ページのロード時間は、サイトの使いやすさとサイト全体の快適さに大きな影響を与えます。Astroの**オプトインのプリフェッチ**を利用すると、訪問者がマルチページアプリケーション(MPA)のサイトを操作する際に、ほぼ瞬時にページナビゲーションできるようになります。 + +## プリフェッチを有効にする + +プリフェッチは、`prefetch`設定で有効にできます。 + +```js title="astro.config.mjs" ins={4} +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + prefetch: true +}); +``` + +プリフェッチスクリプトがサイトのすべてのページに追加されます。その後、サイト上の任意の``リンクに`data-astro-prefetch`属性を追加することで、プリフェッチにオプトインできます。リンク上にホバーすると、スクリプトがページをバックグラウンドでフェッチします。 + +```html + +``` + +プリフェッチはサイト内のリンクに対してのみ機能し、外部リンクに対しては機能しないことに注意してください。 + +## プリフェッチの設定 + +`prefetch`設定は、プリフェッチをさらにカスタマイズするためのオプションのオブジェクトも受け付けます。 + +### プリフェッチ戦略 + +Astroは、さまざまなユースケースのために、以下の4種類のプリフェッチ戦略をサポートします。 + +- `hover`(デフォルト)。リンク上にホバーまたはフォーカスしたときにプリフェッチします。 +- `tap`。リンクをクリックする直前にプリフェッチします。 +- `viewport`。リンクがビューポートに入ったときにプリフェッチします。 +- `load`。ページのロード後に、ページ上のすべてのリンクをプリフェッチします。 + +個別のリンクに対して戦略を指定するには、戦略を`data-astro-prefetch`属性に渡します。 + +```html +About +``` + +各戦略は、必要なときにのみプリフェッチしてユーザーの帯域幅を節約するよう、細かく調整されています。たとえば、次のように動作します。 + +- 訪問者が[データ節約モード](https://developer.mozilla.org/ja/docs/Web/API/NetworkInformation/saveData)や[低速なコネクション](https://developer.mozilla.org/ja/docs/Web/API/NetworkInformation/effectiveType)を使っている場合、プリフェッチは`tap`戦略にフォールバックします。 +- リンク上にすばやくホバーやスクロールした場合はプリフェッチしません。 + +### デフォルトのプリフェッチ戦略 + +`data-astro-prefetch`属性を追加した場合のデフォルトのプリフェッチ戦略は`hover`です。デフォルトを変更するには、`astro.config.mjs`ファイルで[`prefetch.defaultStrategy`](/ja/reference/configuration-reference/#prefetchdefaultstrategy)を設定します。 + +```js title="astro.config.mjs" ins={4-6} +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + prefetch: { + defaultStrategy: 'viewport' + } +}); +``` + +### すべてのリンクをデフォルトでプリフェッチする + +`data-astro-prefetch`属性のないリンクも含めて、すべてのリンクをプリフェッチするには、[`prefetch.prefetchAll`](/ja/reference/configuration-reference/#prefetchprefetchall)を`true`に設定します。 + +```js title="astro.config.mjs" ins={4-6} +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + prefetch: { + prefetchAll: true + } +}); +``` + +その後、`data-astro-prefetch="false"`に設定することで、個別のリンクのプリフェッチをオプトアウトできます。 + +```html +About +``` + +すべてのリンクに対するデフォルトのプリフェッチ戦略は、[デフォルトのプリフェッチ戦略](#デフォルトのプリフェッチ戦略)に示したように、`prefetch.defaultStrategy`で変更できます。 + +## プログラムによるプリフェッチ + +ナビゲーションは常に``リンクとして表示されるとは限らないため、`astro:prefetch`モジュールの`prefetch()` APIを使用してプログラムからプリフェッチすることもできます。 + +```astro + + + +``` + +`prefetch()` APIには、同様の[データ節約モード](https://developer.mozilla.org/ja/docs/Web/API/NetworkInformation/saveData)と[低速なコネクション](https://developer.mozilla.org/ja/docs/Web/API/NetworkInformation/effectiveType)の検出機能があるため、必要なときにだけプリフェッチします。 + +低速なコネクションの検出を無視するには、`ignoreSlowConnection`オプションが利用できます。 + +```js +// データ節約モードや低速なコネクションの場合でもプリフェッチする +prefetch('/about', { ignoreSlowConnection: true }); +``` + +### `eagerness` + +

      +**型:** `'immediate' | 'eager' | 'moderate' | 'conservative'`
      +**デフォルト:** `'immediate'`
      + +

      + +実験的な[`clientPrerender`](/ja/reference/experimental-flags/client-prerender/)フラグを有効にすると、`prefetch()`の`eagerness`オプションを使用して、リンク先をどの程度積極的にプリフェッチまたはプリレンダリングするかをブラウザに提案できます。 + +このオプションは[Speculation Rules API](https://developer.mozilla.org/ja/docs/Web/HTML/Element/script/type/speculationrules#eagerness)で説明されているものと同じAPIに従い、デフォルトはもっとも積極的な`immediate`です。積極度の高い順に、ほかの選択肢は`eager`、`moderate`、`conservative`です。 + +`eagerness`オプションを使用すると、待ち時間を短縮するメリットと、サイト訪問者の帯域幅、メモリ、CPUのコストとのバランスを調整できます。Chromeなど一部のブラウザには、[過剰な投機的読み込み(リンクをプリレンダリングまたはプリフェッチしすぎること)を防ぐための制限](https://developer.chrome.com/blog/speculation-rules-improvements#chrome-limits)があります。 + +```astro +--- +--- + +``` + +多くのリンクをプログラムから`prefetch()`する場合は、`eagerness: 'moderate'`を設定できます。[先入れ先出し(FIFO)](https://ja.wikipedia.org/wiki/FIFO)方式とブラウザのヒューリスティックを活用し、どのリンクをどの順序でプリレンダリングまたはプリフェッチするかをブラウザに判断させられます。 + +```astro "{eagerness: 'moderate'}" +
      A Nice Link 1 +A Nice Link 2 +A Nice Link 3 +A Nice Link 4 +... +A Nice Link 20 + + +``` + +ブラウザのAPIに依存しているため、クライアント側のスクリプト内でのみ`prefetch()`をインポートするようにしてください。 + +## ビュートランジションとともに使用する + +ページで[Astroの``](/ja/guides/view-transitions/#enabling-view-transitions-spa-mode)を使用すると、プリフェッチもデフォルトで有効になります。`{ prefetchAll: true }`がデフォルトで設定され、ページ内の[すべてのリンクに対するプリフェッチ](#すべてのリンクをデフォルトでプリフェッチする)が有効になります。 + +デフォルトを上書きするには、`astro.config.mjs`内のプリフェッチ設定をカスタマイズできます。たとえば、次のように設定します。 + +```js title="astro.config.mjs" +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + // プリフェッチを完全に無効化する + prefetch: false +}); +``` + +```js title="astro.config.mjs" +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + // プリフェッチは維持するが、`data-astro-prefetch`があるリンクのみをプリフェッチする。 + prefetch: { + prefetchAll: false + } +}); +``` + +## ブラウザサポート + +Astroのプリフェッチは、ブラウザがサポートしている場合は[``](https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/rel/prefetch)を使用し、サポートしていない場合は[`fetch()` API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API)にフォールバックします。 + +主要なブラウザはAstroのプリフェッチをサポートしていますが、動作には若干の違いがあります。 + +### Chrome + +Chromeは``をサポートしており、プリフェッチは意図どおりに動作します。 + +また、[Speculation Rules API](https://developer.mozilla.org/ja/docs/Web/API/Speculation_Rules_API)の` ``` @@ -137,12 +142,12 @@ import Layout from 'src/layouts/Base.astro'; ```astro title="src/pages/index.astro" --- -import { auth } from "../../../lib/auth"; // Better Auth 인스턴스를 가져옵니다. +import { auth } from "../lib/auth"; // Better Auth 인스턴스를 가져옵니다. export const prerender = false; // 'server' 모드에서는 필요하지 않습니다. const session = await auth.api.getSession({ - headers: Astro.request.headers, + headers: Astro.request.headers, }); --- @@ -152,9 +157,9 @@ const session = await auth.api.getSession({ `auth` 객체를 사용하여 경로를 보호할 수도 있습니다. 다음 예시는 [Astro의 고급 라우팅](/ko/guides/routing/#고급-라우팅)과 [Hono](https://hono.dev/)를 사용하여 `/dashboard` 아래의 모든 경로에 인증된 세션을 요구하고, 그렇지 않으면 홈 페이지로 리디렉션합니다: ```ts title="src/fetch.ts" -import { Hono } from "hono"; +import { Hono, type Context, type Next } from "hono"; import { astro } from "astro/hono"; -import { auth } from "../auth"; // Better Auth 인스턴스를 가져옵니다. +import { auth } from "./lib/auth"; // Better Auth 인스턴스를 가져옵니다. const app = new Hono(); @@ -167,12 +172,14 @@ app.use(astro()); export default app; -async function requireAuth(c, next) { - const session = await auth.api.getSession({ headers: c.req.raw.headers }); - if (!session) { - return c.redirect("/"); - } - return next(); +async function requireAuth(c: Context, next: Next) { + const session = await auth.api.getSession({ + headers: c.req.raw.headers, + }); + if (!session) { + return c.redirect("/"); + } + return next(); } ``` @@ -225,37 +232,67 @@ Clerk는 사용자의 인증 상태에 따라 페이지의 표시 여부를 제 ```astro title="src/pages/index.astro" --- -import Layout from 'src/layouts/Base.astro'; -import { SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/astro/components'; +import Layout from "../layouts/Base.astro"; +import { Show, UserButton, SignInButton } from "@clerk/astro/components"; export const prerender = false; // 'server' 모드에서는 필요하지 않습니다. --- + - - - - - - + + + + + + ``` -또한 Clerk를 사용하면 미들웨어를 통해 서버에서 경로를 보호할 수 있습니다. 보호할 경로를 지정하고 인증되지 않은 사용자에게 로그인하라는 메시지를 표시할 수 있습니다: +또한 Clerk를 사용하면 미들웨어를 통해 서버에서 경로를 보호할 수 있습니다: -```ts title="src/middleware.ts" -import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server'; + -const isProtectedRoute = createRouteMatcher([ - '/dashboard(.*)', - '/forum(.*)', -]); +1. 미들웨어에서 `onRequest` 핸들러로 `clerkMiddleware()`를 설정하세요: -export const onRequest = clerkMiddleware((auth, context) => { - if (!auth().userId && isProtectedRoute(context.request)) { - return auth().redirectToSignIn(); - } -}); -``` + ```ts title="src/middleware.ts" + import { clerkMiddleware } from "@clerk/astro/server"; + + export const onRequest = clerkMiddleware({ + /* 옵션 */ + }); + ``` + +2. 페이지와 API 라우트에서 `locals.auth()`로 인증 상태에 접근하세요. 이를 통해 사용자가 인증되었는지 확인하고 적절한 조치(예: 로그인 페이지로 리디렉션하거나 다른 응답 반환)를 취할 수 있습니다. + + + + ```astro title="src/pages/dashboard.astro" + --- + const { isAuthenticated, redirectToSignIn } = Astro.locals.auth(); + + if (!isAuthenticated) return redirectToSignIn(); + --- + +

      Dashboard

      + ``` +
      + + ```ts title="src/pages/api/data.ts" + import type { APIRoute } from "astro"; + + export const GET: APIRoute = ({ locals }) => { + const { isAuthenticated, userId } = locals.auth(); + + if (!isAuthenticated) { + return new Response("Unauthorized", { status: 401 }); + } + + return Response.json({ userId }); + }; + ``` + +
      +
      ### 다음 단계 @@ -280,12 +317,14 @@ export const onRequest = clerkMiddleware((auth, context) => { - [Astro에서 GitHub OAuth를 구현하는 예시](https://github.com/lucia-auth/example-astro-github-oauth) - [Astro에서 Google OAuth를 구현하는 예시](https://github.com/lucia-auth/example-astro-google-oauth) -- [Astro에서 2FA를 사용하여 이메일 및 비밀번호를 구현하는 예시 example](https://github.com/lucia-auth/example-astro-email-password-2fa) +- [Astro에서 2FA를 사용하여 이메일 및 비밀번호를 구현하는 예시](https://github.com/lucia-auth/example-astro-email-password-2fa) - [Astro에서 2FA와 WebAuthn를 사용하여 이메일 및 비밀번호를 구현하는 예시](https://github.com/lucia-auth/example-astro-email-password-webauthn) ## Scalekit -[Scalekit](https://scalekit.com/)은 B2B 및 AI 애플리케이션을 위해 구축된 인증 플랫폼입니다. 소셜 로그인, 엔터프라이즈 SSO, 매직 링크 등을 제공합니다 — 전체 OAuth 2.0 / OIDC 흐름을 관리하므로 로그인 UI를 구축할 필요 없이 토큰과 사용자 프로필을 얻을 수 있습니다. 단일 Scalekit 환경이 여러 애플리케이션을 지원하므로 사용자가 한 번 인증하면 모든 속성(예: `app.yourcompany.com` 및 `docs.yourcompany.com`)에서 동일한 세션을 공유합니다. +[Scalekit](https://scalekit.com/)은 B2B 및 AI 애플리케이션을 위한 인증 플랫폼입니다. 전체 OAuth 2.0 및 OIDC 흐름을 관리하며, 소셜 로그인, 엔터프라이즈 SSO, 매직 링크 등의 방식을 지원합니다. 그런 다음 별도의 로그인 UI 없이도 토큰과 사용자 프로필을 반환합니다. + +단일 Scalekit 환경은 여러 애플리케이션을 지원할 수 있습니다. 이를 통해 한 번 인증하면 모든 속성(예: `app.yourcompany.com` 및 `docs.yourcompany.com`)에서 동일한 세션을 공유할 수 있습니다. ### 가이드 diff --git a/src/content/docs/ko/guides/backend/firebase.mdx b/src/content/docs/ko/guides/backend/firebase.mdx index ab6a85ecbcc5b..b148f4c357d5c 100644 --- a/src/content/docs/ko/guides/backend/firebase.mdx +++ b/src/content/docs/ko/guides/backend/firebase.mdx @@ -233,6 +233,7 @@ export const GET: APIRoute = async ({ request, cookies, redirect }) => { cookies.set("__session", sessionCookie, { path: "/", + maxAge: fiveDays / 1000, }); return redirect("/dashboard"); diff --git a/src/content/docs/ko/guides/cms/apostrophecms.mdx b/src/content/docs/ko/guides/cms/apostrophecms.mdx index a3f3a6d89e66a..2551bd5881447 100644 --- a/src/content/docs/ko/guides/cms/apostrophecms.mdx +++ b/src/content/docs/ko/guides/cms/apostrophecms.mdx @@ -392,24 +392,24 @@ const { page, pieces } = Astro.props.aposData; 개별 블로그 게시물을 표시하려면 다음 코드를 사용하여 Astro 프로젝트의 `src/templates` 폴더에 `BlogShow.astro` 파일을 생성합니다. -이 컴포넌트는 `` 컴포넌트를 사용하여 `content` 영역에 추가된 모든 위젯과 동일한 이름의 필드에 입력된 `authorName` 및 `publicationDate` 콘텐츠를 표시합니다. +이 컴포넌트는 `` 컴포넌트를 사용하여 `main` 영역에 추가된 모든 위젯과 동일한 이름의 필드에 입력된 `authorName` 및 `publicationDate` 콘텐츠를 표시합니다. ```js title="src/templates/BlogShow.astro" --- -import AposArea from '@apostrophecms/apostrophe-astro/components/AposArea.astro'; -import dayjs from 'dayjs'; +import AposArea from "@apostrophecms/apostrophe-astro/components/AposArea.astro"; +import dayjs from "dayjs"; const { page, piece } = Astro.props.aposData; const { main } = piece; ---
      -

      { piece.title }

      -

      Created by: { piece.authorName } +

      {piece.title}

      +

      Created by: {piece.authorName}

      - Released On { dayjs(piece.publicationDate).format('MMMM D, YYYY') } + Released On {dayjs(piece.publicationDate).format("MMMM D, YYYY")}

      - +
      ``` diff --git a/src/content/docs/ko/guides/cms/builderio.mdx b/src/content/docs/ko/guides/cms/builderio.mdx index a29b32eb68134..fe9c69f034478 100644 --- a/src/content/docs/ko/guides/cms/builderio.mdx +++ b/src/content/docs/ko/guides/cms/builderio.mdx @@ -198,7 +198,7 @@ const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL; 모든 게시물 제목 목록을 가져와 표시하려면 각각 자체 페이지로 연결되는 `src/pages/index.astro` 파일에 다음 콘텐츠를 추가하세요. -```astro title="src/pages/index.astro" {9} +```astro title="src/pages/index.astro" {8} --- const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY; const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL; @@ -221,9 +221,9 @@ const { results: posts } = await fetch(
        { - posts.flatMap(({ data: { slug, title } }) => ( + posts.flatMap((post: any) => (
      • - {title} + {post.data.title}
      • )) } @@ -266,26 +266,28 @@ index 경로로 이동하면 블로그 게시물 제목이 포함된 링크 목 다음 코드 조각에서는 이들 각각을 강조 표시합니다. -```astro title="src/pages/posts/[slug].astro" ins={2, 26, 33, 40, 51} +```astro title="src/pages/posts/[slug].astro" ins={2, 26, 33, 41, 52} --- export async function getStaticPaths() { const builderModel = import.meta.env.BUILDER_BLOGPOST_MODEL; const builderAPIpublicKey = import.meta.env.BUILDER_API_PUBLIC_KEY; const { results: posts } = await fetch( - `https://cdn.builder.io/api/v3/content/${builderModel}?${new URLSearchParams({ - apiKey: builderAPIpublicKey, - fields: ['data.slug', 'data.title'].join(','), - cachebust: 'true', - }).toString()}` + `https://cdn.builder.io/api/v3/content/${builderModel}?${new URLSearchParams( + { + apiKey: builderAPIpublicKey, + fields: ['data.slug', 'data.title'].join(','), + cachebust: 'true', + }, + ).toString()}` ) .then((res) => res.json()) .catch // ...오류 처리...); (); - return posts.map(({ data: { slug, title } }) => ({ - params: { slug }, - props: { title }, - })) + return posts.map((post: any) => ({ + params: { slug: post.data.slug }, + props: { title: post.data.title }, + })); } const { slug } = Astro.params; const { title } = Astro.props; @@ -299,7 +301,7 @@ const { html: postHTML } = await fetch( url: encodedUrl, 'query.data.slug': slug, cachebust: 'true', - }).toString()}` + }).toString()}`, ) .then((res) => res.json()) .catch(); diff --git a/src/content/docs/ko/guides/cms/buttercms.mdx b/src/content/docs/ko/guides/cms/buttercms.mdx index d618bf4caccf6..b57e92c678359 100644 --- a/src/content/docs/ko/guides/cms/buttercms.mdx +++ b/src/content/docs/ko/guides/cms/buttercms.mdx @@ -87,18 +87,25 @@ import { butterClient } from "../lib/buttercms"; const response = await butterClient.content.retrieve(["shopitem"]); interface ShopItem { - name: string, - price: number, - description: string, + name: string; + price: number; + description: string; } -const items = response.data.data.shopitem as ShopItem[]; +const items = response?.data?.data.shopitem as ShopItem[]; --- + - {items.map(item =>
        -

        {item.name} - ${item.price}

        -

        -
        )} + { + items.map((item) => ( +
        +

        + {item.name} - ${item.price} +

        +

        +

        + )) + } ``` @@ -110,16 +117,17 @@ const items = response.data.data.shopitem as ShopItem[]; --- import { butterClient } from "../lib/buttercms"; const response = await butterClient.page.retrieve("*", "simple-page"); -const pageData = response.data.data; +const pageData = response?.data?.data; interface Fields { - seo_title: string, - headline: string, - hero_image: string, + seo_title: string; + headline: string; + hero_image: string; } -const fields = pageData.fields as Fields; +const fields = pageData?.fields as Fields; --- + {fields.seo_title} diff --git a/src/content/docs/ko/guides/cms/cloudcannon.mdx b/src/content/docs/ko/guides/cms/cloudcannon.mdx index 324f048d9ca1d..4df4643e93a18 100644 --- a/src/content/docs/ko/guides/cms/cloudcannon.mdx +++ b/src/content/docs/ko/guides/cms/cloudcannon.mdx @@ -165,13 +165,16 @@ const posts = await getCollection('blog'); ### 개별 항목 표시하기 -개별 포스트의 콘텐츠를 표시하려면 `` 컴포넌트를 가져와 [콘텐츠를 HTML로 렌더링](/ko/guides/content-collections/#본문-콘텐츠-렌더링하기)할 수 있습니다. +개별 포스트의 콘텐츠를 표시하려면 `` 컴포넌트를 사용하여 [`render()`로 콘텐츠를 HTML로 렌더링](/ko/guides/content-collections/#본문-콘텐츠-렌더링하기)할 수 있습니다. -```astro title="src/pages/blog/my-first-post.astro" {4-5} +```astro title="src/pages/blog/my-first-post.astro" {8,14} ", render" --- import { getEntry, render } from 'astro:content'; const entry = await getEntry('blog', 'my-first-post'); +if (!entry) { + throw new Error('Blog post not found'); +} const { Content } = await render(entry); --- diff --git a/src/content/docs/ko/guides/cms/cosmic.mdx b/src/content/docs/ko/guides/cms/cosmic.mdx index a3220479aa918..26e7a490ca69e 100644 --- a/src/content/docs/ko/guides/cms/cosmic.mdx +++ b/src/content/docs/ko/guides/cms/cosmic.mdx @@ -102,7 +102,7 @@ PUBLIC_COSMIC_READ_KEY=YOUR_READ_KEY title={post.title} href={post.slug} body={post.metadata.excerpt} - tags={post.metadata.tags.map((tag) => tag)} + tags={post.metadata.tags.map((tag: any) => tag)} /> )) } @@ -151,7 +151,7 @@ const data = await getAllPosts() title={post.title} href={post.slug} body={post.metadata.excerpt} - tags={post.metadata.tags.map((tag) => tag)} + tags={post.metadata.tags.map((tag: any) => tag)} /> )) } @@ -206,7 +206,6 @@ const { post } = Astro.props format="webp" width={1200} height={675} - aspectRatio={16 / 9} quality={50} alt={`Cover image for the blog ${post.title}`} class={'my-12 rounded-md shadow-lg'} diff --git a/src/content/docs/ko/guides/cms/drupal.mdx b/src/content/docs/ko/guides/cms/drupal.mdx index e4141a1d37b18..b58e1a2faaabb 100644 --- a/src/content/docs/ko/guides/cms/drupal.mdx +++ b/src/content/docs/ko/guides/cms/drupal.mdx @@ -384,8 +384,8 @@ const articles = dataFormatter.deserialize(json); import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import type {TJsonApiBody} from "jsona/lib/JsonaTypes"; - import type { DrupalNode } from "../types"; - import {getArticles} from "../api/drupal"; + import type { DrupalNode } from "../../types"; + import { getArticles } from "../../api/drupal"; // 게시된 모든 articles 가져오기 const articles = await getArticles(); diff --git a/src/content/docs/ko/guides/cms/emdash.mdx b/src/content/docs/ko/guides/cms/emdash.mdx index 3a593ca2bdf96..39fbe39d0a377 100644 --- a/src/content/docs/ko/guides/cms/emdash.mdx +++ b/src/content/docs/ko/guides/cms/emdash.mdx @@ -4,13 +4,234 @@ description: EmDash를 CMS로 사용하여 Astro 프로젝트에 콘텐츠를 sidebar: label: EmDash type: cms -stub: true logo: emdash i18nReady: true --- +import { Steps } from '@astrojs/starlight/components'; +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; [EmDash](https://emdashcms.com/)는 Astro를 위해 특별히 구축된 오픈 소스 풀스택 CMS로, 사이트에 데이터베이스 기반 콘텐츠, 관리자 UI, 미디어 라이브러리, 메뉴 및 분류 체계를 추가합니다. +:::tip +**새 Astro + EmDash 프로젝트를 처음부터 시작하려면**, EmDash CLI를 사용하여 미리 구성된 프로젝트를 생성하세요: + + + + ```shell + npm create emdash@latest + ``` + + + ```shell + pnpm create emdash@latest + ``` + + + ```shell + yarn create emdash@latest + ``` + + +::: + +## Astro와 통합 + +EmDash는 Astro 프로젝트 내에서 실행되며 데이터베이스를 사용합니다. `/_emdash/admin`에서 관리 인터페이스에 접근하여 콘텐츠를 편집할 수 있습니다. 페이지는 [라이브 콘텐츠 컬렉션](/ko/guides/content-collections/#라이브-콘텐츠-컬렉션)을 사용하여 데이터베이스를 쿼리함으로써 콘텐츠를 표시합니다. + +이 가이드에서는 [Node.js 어댑터](/ko/guides/integrations-guide/node/)와 로컬 SQLite 데이터베이스를 사용합니다. 지원되는 데이터베이스 목록은 [EmDash 문서](https://docs.emdashcms.com/deployment/database/)를 참조하세요. + +## 전제 조건 + +- [어댑터](/ko/guides/on-demand-rendering/)와 함께 구성되고 [`output: "server"`](/ko/reference/configuration-reference/#output)로 설정된 기존 Astro 프로젝트 (Astro 6 이상). +- Node.js v22.16.0 이상. + +## 종속성 설치 + +React는 EmDash 관리 인터페이스를 구동하며 필수 종속성입니다. 프로젝트에서 React를 사용하지 않는다면, 패키지 관리자에 맞는 `astro add` 명령을 사용하여 설치하세요: + + + + ```shell + npx astro add react + ``` + + + ```shell + pnpm astro add react + ``` + + + ```shell + yarn astro add react + ``` + + + +EmDash 패키지도 설치해야 합니다: + + + + ```shell + npm install emdash + ``` + + + ```shell + pnpm add emdash + ``` + + + ```shell + yarn add emdash + ``` + + + +## 통합 추가하기 + +Astro 구성 파일에 `emdash()` 통합을 추가하고, 데이터베이스와 미디어 스토리지 백엔드를 구성하세요: + +```js title="astro.config.mjs" ins={4-5, 12-18} +import { defineConfig } from "astro/config"; +import node from "@astrojs/node"; +import react from "@astrojs/react"; +import emdash, { local } from "emdash/astro"; +import { sqlite } from "emdash/db"; + +export default defineConfig({ + output: "server", + adapter: node({ mode: "standalone" }), + integrations: [ + react(), + emdash({ + database: sqlite({ url: "file:./data.db" }), + storage: local({ + directory: "./uploads", + baseUrl: "/_emdash/api/media/file", + }), + }), + ], +}); +``` + +## 라이브 컬렉션 로더 추가하기 + +Astro의 콘텐츠 계층이 EmDash 콘텐츠를 해석할 수 있도록 `src/live.config.ts` 파일을 생성하세요: + +```ts title="src/live.config.ts" +import { defineLiveCollection } from "astro:content"; +import { emdashLoader } from "emdash/runtime"; + +export const collections = { + _emdash: defineLiveCollection({ loader: emdashLoader() }), +}; +``` + +`_emdash` 컬렉션은 내부적으로 콘텐츠 타입(예: posts와 pages)으로 라우팅됩니다. `src/content.config.ts`에 있는 기존 파일 기반 컬렉션은 그대로 함께 계속 동작합니다. + +## EmDash 로컬에서 실행하기 + +자체 페이지를 테스트하기 전에 설정 마법사를 완료해야 합니다. 설정이 완료될 때까지 게시된 콘텐츠가 없으며 쿼리는 빈 결과를 반환합니다. + + +1. 데이터베이스를 초기화하고 관리 UI를 실행하려면 Astro 개발 서버를 시작하세요: + + + + ```shell + npm run dev + ``` + + + ```shell + pnpm run dev + ``` + + + ```shell + yarn run dev + ``` + + + + 처음 실행하면 EmDash는 스키마와 두 개의 기본 컬렉션인 `pages`와 `posts`를 포함하는 `data.db`를 생성합니다. + +2. 브라우저에서 `http://localhost:4321/_emdash/admin`을 방문하세요. EmDash가 설정 마법사로 리디렉션합니다. + +3. **Site** 단계에서 사이트 제목과 선택 사항인 태그라인을 입력하세요. + +4. **Account** 단계에서 이메일 주소와 이름을 입력하세요. 이렇게 하면 관리자 계정이 생성됩니다. + +5. **Sign In** 단계에서 계정을 보호하세요. 기기의 생체 인식 인증, 보안 키 또는 PIN으로 패스키를 등록하려면 **Create Passkey**를 선택하세요. + +6. 새 패스키로 로그인하여 대시보드로 이동하세요. + + +## 첫 게시물 만들기 + + +1. 대시보드에서 **+ Post** 버튼을 클릭하세요. + +2. 제목과 콘텐츠를 추가하세요. EmDash는 리치 텍스트를 블록 편집기에서 편집되는 [Portable Text](https://github.com/portabletext/portabletext)로 저장합니다. URL 슬러그는 제목에서 생성되며 사이드바에서 편집할 수 있습니다. + +3. **Save**를 클릭한 다음 **Publish**를 클릭하세요. 게시된 게시물만 사이트 방문자에게 표시됩니다. + + +## EmDash 콘텐츠 렌더링하기 + +`getEmDashCollection()`과 `getEmDashEntry()`로 콘텐츠를 쿼리하세요. 둘 다 라이브 컬렉션 패턴을 따르며 요청 시점에 결과를 반환하므로, 다시 빌드하지 않아도 게시된 변경 사항이 표시됩니다. + +### 게시물 목록 표시하기 + +다음 예시는 게시된 모든 게시물 제목 목록을 표시하며, 각 제목은 개별 게시물 페이지로 연결됩니다: + +```astro title="src/pages/blog.astro" +--- +import { getEmDashCollection } from "emdash"; + +const { entries: posts } = await getEmDashCollection("posts", { + status: "published", +}); +--- + +``` + +### 단일 게시물 표시하기 + +개별 게시물의 콘텐츠를 표시하려면 슬러그로 게시물을 가져온 뒤 `` 컴포넌트로 Portable Text 콘텐츠를 렌더링하세요: + +```astro title="src/pages/posts/[...slug].astro" +--- +import { getEmDashEntry } from "emdash"; +import { PortableText } from "emdash/ui"; + +const { slug } = Astro.params; +const { entry: post } = await getEmDashEntry("posts", slug); + +if (!post) { + return Astro.redirect("/404"); +} +--- +
        +

        {post.data.title}

        + +
        +``` + +필터링, 페이지네이션, 초안 미리보기, 비주얼 편집에 대한 자세한 내용은 [EmDash 쿼리 가이드](https://docs.emdashcms.com/guides/querying-content/)를 참조하세요. + +## EmDash + Astro 배포하기 + +EmDash는 단일 Astro 프로젝트로서 사이트와 함께 배포됩니다. 어댑터를 지원하는 호스트를 선택하고, 프로덕션 데이터베이스와 미디어 스토리지를 프로비저닝하세요. + +구체적인 지침은 [Node.js용 EmDash 배포 가이드](https://docs.emdashcms.com/deployment/nodejs/)와 [Cloudflare용 EmDash 배포 가이드](https://docs.emdashcms.com/deployment/cloudflare/)를 참조하세요. 선호하는 호스팅 제공업체로 배포하려면 Astro의 [배포 가이드](/ko/guides/deploy/)를 방문하여 지침을 따를 수도 있습니다. + ## 공식 리소스 - [Astro 개발자를 위한 EmDash 문서](https://docs.emdashcms.com/coming-from/astro/) diff --git a/src/content/docs/ko/guides/cms/ghost.mdx b/src/content/docs/ko/guides/cms/ghost.mdx index 098bf8de32c29..8b1cd8ea93ba6 100644 --- a/src/content/docs/ko/guides/cms/ghost.mdx +++ b/src/content/docs/ko/guides/cms/ghost.mdx @@ -183,7 +183,7 @@ const posts = await ghostClient.posts { - posts.map((post) => ( + posts?.map((post) => (

        {post.title}

        @@ -224,7 +224,7 @@ export async function getStaticPaths() { console.error(err); }); - return posts.map((post) => { + return posts?.map((post) => { return { params: { slug: post.slug, @@ -253,7 +253,7 @@ export async function getStaticPaths() { .catch((err) => { console.error(err); }); - return posts.map((post) => { + return posts?.map((post) => { return { params: { slug: post.slug, @@ -296,7 +296,7 @@ const { post } = Astro.props; - + diff --git a/src/content/docs/ko/guides/cms/keystatic.mdx b/src/content/docs/ko/guides/cms/keystatic.mdx index 9e470b8444245..f36b5111f933c 100644 --- a/src/content/docs/ko/guides/cms/keystatic.mdx +++ b/src/content/docs/ko/guides/cms/keystatic.mdx @@ -173,7 +173,7 @@ Keystatic 관리 UI 대시보드를 시작하려면 Astro의 개발 서버를 5. 코드 편집기에서 해당 파일로 이동하여 입력한 Markdown 콘텐츠를 볼 수 있는지 확인합니다. 예를 들어: - ```markdown + ```markdown title="src/content/posts/my-first-post.mdoc" --- title: My First Post --- @@ -190,7 +190,7 @@ Keystatic 관리 UI 대시보드를 시작하려면 Astro의 개발 서버를 다음 예시에서는 개별 게시물 페이지에 대한 링크와 함께 각 게시물 제목 목록을 표시합니다. -```tsx {4} +```astro title="src/pages/posts/index.astro" {4} --- import { getCollection } from 'astro:content' @@ -207,21 +207,23 @@ const posts = await getCollection('posts') ### 단일 항목 표시 -개별 게시물의 콘텐츠를 표시하려면 `` 컴포넌트를 가져와 사용하여 [콘텐츠를 HTML로 렌더링](/ko/guides/content-collections/#본문-콘텐츠-렌더링하기)할 수 있습니다. +개별 게시물의 콘텐츠를 표시하려면 `` 컴포넌트를 사용하여 [`render()`로 콘텐츠를 HTML로 렌더링](/ko/guides/content-collections/#본문-콘텐츠-렌더링하기)할 수 있습니다. -```tsx {4-5} +```astro title="src/pages/posts/my-first-post.astro" {8,13} ", render" --- -import { getEntry } from 'astro:content' +import { getEntry, render } from "astro:content"; -const post = await getEntry('posts', 'my-first-post') -const { Content } = await post.render() +const post = await getEntry("posts", "my-first-post"); +if (!post) { + throw new Error("Post not found"); +} +const { Content } = await render(post); ---

        {post.data.title}

        - ``` 쿼리, 필터링, 컬렉션 콘텐츠 표시 등에 대한 자세한 내용은 전체 콘텐츠 [컬렉션 문서](/ko/guides/content-collections/)를 참조하세요. diff --git a/src/content/docs/ko/guides/cms/kontent-ai.mdx b/src/content/docs/ko/guides/cms/kontent-ai.mdx index 43d66a56ec5db..9150ffac69583 100644 --- a/src/content/docs/ko/guides/cms/kontent-ai.mdx +++ b/src/content/docs/ko/guides/cms/kontent-ai.mdx @@ -377,6 +377,7 @@ export async function getStaticPaths() { .items() .type(contentTypes.blog_post.codename) .toPromise() +} --- ``` @@ -438,6 +439,7 @@ const blogPost: BlogPost = Astro.props.blogPost +
      ``` @@ -517,6 +519,7 @@ try { +

      ``` diff --git a/src/content/docs/ko/guides/cms/preprcms.mdx b/src/content/docs/ko/guides/cms/preprcms.mdx index 18888977ec507..95b3528d1e8e5 100644 --- a/src/content/docs/ko/guides/cms/preprcms.mdx +++ b/src/content/docs/ko/guides/cms/preprcms.mdx @@ -101,24 +101,22 @@ GraphQL API와 상호 작용하는 쿼리를 작성하여 Prepr에서 데이터 3. 블로그 게시물의 링크된 목록을 페이지에 표시하려면 필요한 Prepr 엔드포인트를 포함한 쿼리를 가져와 실행하세요. 그러면 모든 게시물 제목과 해당 슬러그에 액세스하여 페이지에 렌더링할 수 있습니다. (다음 단계에서는 [블로그 게시물에 대한 개별 페이지를 생성](#개별-블로그-게시물-페이지-생성)합니다.) - ```astro title="src/pages/index.astro" ins={3-4, 6-8, 15-23} + ```astro title="src/pages/index.astro" ins={3-4, 6-8, 13-21} --- - import Layout from '../layouts/Layout.astro'; - import { Prepr } from '../lib/prepr.js'; - import GetArticles from '../queries/get-articles.js'; + import Layout from "../layouts/Layout.astro"; + import { Prepr } from "../lib/prepr.js"; + import GetArticles from "../queries/get-articles.js"; - const response = await Prepr(GetArticles) - const { data } = await response.json() - const articles = data.Articles + const response = await Prepr(GetArticles); + const { data } = await response.json(); + const articles = data.Articles; --- -

      - My blog site -   

      -   
        +

        My blog site

        +
          { - articles.items.map((post) => ( + articles.items.map((post: any) => (
        • {post.title}
        • @@ -151,7 +149,7 @@ GraphQL API와 상호 작용하는 쿼리를 작성하여 Prepr에서 데이터 1. `queries` 폴더에 `get-article-by-slug.js`라는 파일을 만들고 다음을 추가하여 해당 슬러그로 특정 아티클을 쿼리하고 아티클의 `title` 및 `content`와 같은 데이터를 반환합니다. - ```js title="src/lib/queries/get-article-by-slug.js" + ```js title="src/queries/get-article-by-slug.js" const GetArticleBySlug = ` query ($slug: String) { @@ -184,30 +182,26 @@ GraphQL API와 상호 작용하는 쿼리를 작성하여 Prepr에서 데이터 ```astro title="src/pages/[...slug].astro" --- - import Layout from '../layouts/Layout.astro'; - import {Prepr} from '../lib/prepr.js'; - import GetArticleBySlug from '../queries/get-article-by-slug.js'; + import Layout from "../layouts/Layout.astro"; + import { Prepr } from "../lib/prepr.js"; + import GetArticleBySlug from "../queries/get-article-by-slug.js"; const { slug } = Astro.params; - const response = await Prepr(GetArticleBySlug, {slug}) - const { data } = await response.json() - const article = data.Article + const response = await Prepr(GetArticleBySlug, { slug }); + const { data } = await response.json(); + const article = data.Article; ---

          {article.title}

          { - article.content.map((content) => ( + article.content.map((content: any) => (
          - { - content.__typename === "Assets" && - - } - { - content.__typename === 'Text' && -
          - } + {content.__typename === "Assets" && ( + + )} + {content.__typename === "Text" &&
          }
          )) } diff --git a/src/content/docs/ko/guides/cms/statamic.mdx b/src/content/docs/ko/guides/cms/statamic.mdx index f031b2a845dad..53b0253543e5e 100644 --- a/src/content/docs/ko/guides/cms/statamic.mdx +++ b/src/content/docs/ko/guides/cms/statamic.mdx @@ -49,7 +49,7 @@ const posts = await res.json() ---

          Astro + Statamic 🚀

          { - posts.map((post) => ( + posts.map((post: any) => (

          )) @@ -86,8 +86,8 @@ const graphqlQuery = { } `, variables: { - page: page, - locale: locale, + page: "my-current-page", + locale: "my-locale", }, }; @@ -102,7 +102,7 @@ const entries = data?.entries; ---

          Astro + Statamic 🚀

          { - entries.data.map((post) => ( + entries.data.map((post: any) => (

          )) diff --git a/src/content/docs/ko/guides/cms/storyblok.mdx b/src/content/docs/ko/guides/cms/storyblok.mdx index 73f63478d35a9..7f8bd5bdcf99e 100644 --- a/src/content/docs/ko/guides/cms/storyblok.mdx +++ b/src/content/docs/ko/guides/cms/storyblok.mdx @@ -278,7 +278,7 @@ const { blok } = Astro.props

          { - blok.body?.map((blok) => { + blok.body?.map((blok: any) => { return }) } @@ -310,36 +310,40 @@ const content = renderRichText(blok.content) ```astro title="src/storyblok/BlogPostList.astro" --- -import { storyblokEditable } from '@storyblok/astro' -import { useStoryblokApi } from '@storyblok/astro' +import { storyblokEditable } from "@storyblok/astro"; +import { useStoryblokApi } from "@storyblok/astro"; const storyblokApi = useStoryblokApi(); -const { data } = await storyblokApi.get('cdn/stories', { +const { data } = await storyblokApi.get("cdn/stories", { version: import.meta.env.DEV ? "draft" : "published", - content_type: 'blogPost', -}) + content_type: "blogPost", +}); -const posts = data.stories.map(story => { +const posts = data.stories.map((story: any) => { return { title: story.content.title, - date: new Date(story.published_at).toLocaleDateString("en-US", {dateStyle: "full"}), + date: new Date(story.published_at).toLocaleDateString("en-US", { + dateStyle: "full", + }), description: story.content.description, slug: story.full_slug, - } -}) + }; +}); -const { blok } = Astro.props +const { blok } = Astro.props; ---
            - {posts.map(post => ( -
          • - - {post.title} -

            {post.description}

            -
          • - ))} + { + posts.map((post: any) => ( +
          • + + {post.title} +

            {post.description}

            +
          • + )) + }
          ``` @@ -407,8 +411,8 @@ Astro의 기본 정적 사이트 생성을 사용하는 경우 [동적 경로](/ ```astro title="src/pages/blog/[...slug].astro" --- -import { useStoryblokApi } from '@storyblok/astro' -import StoryblokComponent from '@storyblok/astro/StoryblokComponent.astro' +import { useStoryblokApi } from "@storyblok/astro"; +import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro"; export async function getStaticPaths() { const sbApi = useStoryblokApi(); @@ -420,7 +424,7 @@ export async function getStaticPaths() { const stories = Object.values(data.stories); - return stories.map((story) => { + return stories.map((story: any) => { return { params: { slug: story.slug }, }; diff --git a/src/content/docs/ko/guides/cms/strapi.mdx b/src/content/docs/ko/guides/cms/strapi.mdx index df3713cec0116..40ad8a3659536 100644 --- a/src/content/docs/ko/guides/cms/strapi.mdx +++ b/src/content/docs/ko/guides/cms/strapi.mdx @@ -129,6 +129,11 @@ export default interface Article { createdAt: string; updatedAt: string; publishedAt: string; + image: { + data: { + url: string; + }; + }; } ``` @@ -271,8 +276,9 @@ const article = Astro.props; 각 게시물 객체의 속성을 사용하여 각 페이지의 템플릿을 만듭니다. -```astro title="src/pages/blog/[slug].astro" ins={21-43} +```astro title="src/pages/blog/[slug].astro" ins={22-44} --- +import MyMarkdownComponent from '../../components/MyMarkdownComponent.astro'; import fetchApi from '../../lib/strapi'; import type Article from '../../interfaces/article'; @@ -328,8 +334,9 @@ const article = Astro.props; ```astro title="src/pages/blog/[slug].astro" --- -import fetchApi from '../../../lib/strapi'; -import type Article from '../../../interfaces/article'; +import MyMarkdownComponent from '../../components/MyMarkdownComponent.astro'; +import fetchApi from '../../lib/strapi'; +import type Article from '../../interfaces/article'; const { slug } = Astro.params; diff --git a/src/content/docs/ko/guides/cms/tina-cms.mdx b/src/content/docs/ko/guides/cms/tina-cms.mdx index 26fd0733c5879..255b4dcf92a6a 100644 --- a/src/content/docs/ko/guides/cms/tina-cms.mdx +++ b/src/content/docs/ko/guides/cms/tina-cms.mdx @@ -87,11 +87,11 @@ import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro'; "Hello, World!" 게시물을 편집하면 프로젝트 디렉터리의 `content/posts/hello-world.md` 파일을 업데이트합니다. -4. `.tina/config.ts` 파일에서 `schema.collections` 속성을 편집하여 Tina 컬렉션을 설정하세요. +4. `tina/config.ts` 파일에서 `schema.collections` 속성을 편집하여 Tina 컬렉션을 설정하세요. 예를 들어, 필수 "date posted" 프런트매터 속성을 게시물에 추가할 수 있습니다. - ```js title=".tina/config.ts" ins={35-40} + ```js title="tina/config.ts" ins={35-40} import { defineConfig } from "tinacms"; // 호스팅 제공업체는 이를 환경 변수로 노출할 가능성이 높습니다. diff --git a/src/content/docs/ko/guides/cms/umbraco.mdx b/src/content/docs/ko/guides/cms/umbraco.mdx index ef34d3be2393b..fd7fb26f515e7 100644 --- a/src/content/docs/ko/guides/cms/umbraco.mdx +++ b/src/content/docs/ko/guides/cms/umbraco.mdx @@ -56,7 +56,7 @@ const articles = await res.json(); ---

          Astro + Umbraco 🚀

          { - articles.items.map((article) => ( + articles.items.map((article: any) => (

          {article.name}

          {article.properties.articleDate}

          @@ -157,7 +157,7 @@ const articles = await res.json(); 개별 블로그 게시물 페이지를 생성하는 `[...slug].astro` 파일에 다음 코드를 추가하세요. -```astro title="[...slug].astro" +```astro title="src/pages/[...slug].astro" --- import Layout from '../layouts/Layout.astro'; diff --git a/src/content/docs/ko/guides/cms/wordpress.mdx b/src/content/docs/ko/guides/cms/wordpress.mdx index 06f5c2146ce03..484d56b03dfe8 100644 --- a/src/content/docs/ko/guides/cms/wordpress.mdx +++ b/src/content/docs/ko/guides/cms/wordpress.mdx @@ -44,7 +44,7 @@ const posts = await res.json(); ---

          Astro + WordPress 🚀

          { - posts.map((post) => ( + posts.map((post: any) => (

          )) @@ -96,11 +96,12 @@ import Layout from "../layouts/Layout.astro"; let res = await fetch("https://norian.studio/wp-json/wp/v2/dinos"); let posts = await res.json(); --- +

          List of Dinosaurs

          { - posts.map((post) => ( + posts.map((post: any) => (

          @@ -119,7 +120,7 @@ let posts = await res.json(); ```astro title="/src/pages/dinos/[slug].astro" --- -import Layout from '../../layouts/Layout.astro'; +import Layout from "../../layouts/Layout.astro"; const { slug } = Astro.params; @@ -132,12 +133,13 @@ export async function getStaticPaths() { let data = await fetch("https://norian.studio/wp-json/wp/v2/dinos"); let posts = await data.json(); - return posts.map((post) => ({ + return posts.map((post: any) => ({ params: { slug: post.slug }, props: { post: post }, })); } --- +

          diff --git a/src/content/docs/ko/guides/deploy/ishosting.mdx b/src/content/docs/ko/guides/deploy/ishosting.mdx new file mode 100644 index 0000000000000..f8ed741f32b09 --- /dev/null +++ b/src/content/docs/ko/guides/deploy/ishosting.mdx @@ -0,0 +1,16 @@ +--- +title: Astro 사이트를 is*hosting에 배포 +description: is*hosting을 사용하여 Astro 사이트를 웹에 배포하는 방법. +sidebar: + label: is*hosting +type: deploy +logo: ishosting +supports: ['ssr', 'static'] +i18nReady: true +--- + +[is\*hosting](https://ishosting.com/)은 40개 이상의 지역에서 VPS 및 전용 서버를 제공하는 호스팅 제공업체로, 정적 또는 서버 렌더링(SSR) Astro 사이트를 셀프 호스팅하는 데 사용할 수 있습니다. + +## 공식 리소스 + +- [is\*hosting 가이드: VPS에 Astro 배포하기(정적 및 SSR)](https://blog.ishosting.com/en/astro-on-vps) diff --git a/src/content/docs/ko/guides/integrations-guide/cloudflare.mdx b/src/content/docs/ko/guides/integrations-guide/cloudflare.mdx index 437e3860d3dcc..57840194f3d44 100644 --- a/src/content/docs/ko/guides/integrations-guide/cloudflare.mdx +++ b/src/content/docs/ko/guides/integrations-guide/cloudflare.mdx @@ -389,12 +389,12 @@ Cloudflare `workerd` 런타임은 일부 [비표준 모듈 유형](https://devel 다음은 요청의 숫자 매개변수를 함께 더하여 요청에 응답하는 Wasm 모듈을 가져오는 예시입니다. -```js title="pages/add/[a]/[b].js" +```js title="src/pages/add/[a]/[b].js" // WebAssembly 모듈 가져오기 import mod from '../util/add.wasm'; // 사용하려면 먼저 인스턴스화해야 합니다. -const addModule: any = new WebAssembly.Instance(mod); +const addModule = new WebAssembly.Instance(mod); export async function GET(context) { const a = Number.parseInt(context.params.a); @@ -463,16 +463,20 @@ Cloudflare 어댑터는 [고급 라우팅 파이프라인](/ko/guides/routing/# [`astro/fetch`](/ko/reference/modules/astro-fetch/)와 함께 사용하기 위한 API입니다. `@astrojs/cloudflare/fetch`에서 가져온 `cf()` 함수는 [`FetchState`](/ko/reference/modules/astro-fetch/#fetchstate), Cloudflare `env`, 그리고 `ExecutionContext`를 인자로 받습니다. 이 함수는 정적 자산과 일치할 때는 `Response`를 반환하고, 요청이 Astro 렌더링으로 계속 진행되어야 할 때는 `undefined`를 반환합니다. +

          + +같은 `FetchState`와 Astro 파이프라인에서 받은 응답을 반환하기 전에 `finalize()`에 전달하세요. 이렇게 하면 렌더링 중 생성된 쿠키와 어댑터의 기본 Cloudflare CDN 캐시 헤더가 응답에 적용됩니다. + ```ts title="src/worker.ts" import { astro, FetchState } from 'astro/fetch'; -import { cf } from '@astrojs/cloudflare/fetch'; +import { cf, finalize } from '@astrojs/cloudflare/fetch'; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const state = new FetchState(request); const asset = await cf(state, env, ctx); if (asset) return asset; - return astro(state); + return finalize(state, await astro(state)); }, }; ``` @@ -483,6 +487,8 @@ export default { [`astro/hono`](/ko/reference/modules/astro-hono/)와 함께 사용하기 위한 API입니다. `@astrojs/cloudflare/hono`에서 가져온 `cf()` 함수는 Hono 컨텍스트에서 `env`와 `executionCtx`를 자동으로 읽어오는 Hono 미들웨어를 반환합니다. +`@astrojs/cloudflare` v14.3.0 이상에서는 이 미들웨어가 다운스트림 Hono 핸들러가 실행된 뒤 응답을 finalize합니다. 렌더링 중 생성된 쿠키와 어댑터의 기본 Cloudflare CDN 캐시 헤더가 자동으로 적용됩니다. + ```ts title="src/worker.ts" import { Hono } from 'hono'; import { actions, middleware, pages, i18n } from 'astro/hono'; diff --git a/src/content/docs/ko/guides/integrations-guide/markdoc.mdx b/src/content/docs/ko/guides/integrations-guide/markdoc.mdx index e7b5eb6fdb3af..f1a8e2bf5a974 100644 --- a/src/content/docs/ko/guides/integrations-guide/markdoc.mdx +++ b/src/content/docs/ko/guides/integrations-guide/markdoc.mdx @@ -117,9 +117,12 @@ Markdoc 파일은 콘텐츠 컬렉션에서만 사용할 수 있습니다. `.mdo ```astro title="src/pages/why-markdoc.astro" --- -import { getEntry, render } from 'astro:content'; +import { getEntry, render } from "astro:content"; -const entry = await getEntry('docs', 'why-markdoc'); +const entry = await getEntry("docs", "why-markdoc"); +if (!entry) { + throw new Error("항목을 찾을 수 없습니다."); +} const { Content } = await render(entry); --- @@ -142,6 +145,9 @@ const { Content } = await render(entry); import { getEntry, render } from 'astro:content'; const entry = await getEntry('docs', 'why-markdoc'); +if (!entry) { + throw new Error("항목을 찾을 수 없습니다."); +} const { Content } = await render(entry); --- @@ -180,6 +186,9 @@ entry 객체의 `data` 속성을 콘텐츠를 렌더링하기 위한 변수로 import { getEntry, render } from 'astro:content'; const entry = await getEntry('docs', 'why-markdoc'); +if (!entry) { + throw new Error("항목을 찾을 수 없습니다."); +} const { Content } = await render(entry); --- @@ -495,9 +504,16 @@ Markdoc `image` 태그를 사용하면 `![]()` 구문으로는 불가능한 이 } const { src, alt, width, height, caption } = Astro.props; --- +
          - - {caption &&
          {caption}
          } + { + typeof src === "string" ? ( + + ) : ( + + ) + } + {caption &&
          {caption}
          }
          ``` diff --git a/src/content/docs/ko/guides/integrations-guide/mdx.mdx b/src/content/docs/ko/guides/integrations-guide/mdx.mdx index af5625dd5ff06..24f4af6494607 100644 --- a/src/content/docs/ko/guides/integrations-guide/mdx.mdx +++ b/src/content/docs/ko/guides/integrations-guide/mdx.mdx @@ -133,11 +133,11 @@ export const title = 'My first MDX post' ```astro title="src/pages/index.astro" --- -const matches = import.meta.glob('./posts/*.mdx', { eager: true }); +const matches = import.meta.glob("./posts/*.mdx", { eager: true }); const posts = Object.values(matches); --- -{posts.map(post =>

          {post.title}

          )} +{posts.map((post: any) =>

          {post.title}

          )} ``` #### 내보낸 속성 @@ -240,11 +240,15 @@ MDX 파일이 콘텐츠 컬렉션 항목인 경우, `astro:content`의 `render() ```astro title="src/pages/blog/post-1.astro" ins="components={{ h1: CustomHeading }}" --- -import { getEntry, render } from 'astro:content'; -import CustomHeading from '../../components/CustomHeading.astro'; -const entry = await getEntry('blog', 'post-1'); +import { getEntry, render } from "astro:content"; +import CustomHeading from "../../components/CustomHeading.astro"; +const entry = await getEntry("blog", "post-1"); +if (!entry) { + throw new Error("항목을 찾을 수 없습니다."); +} const { Content } = await render(entry); --- + ``` @@ -358,20 +362,22 @@ MDX는 기본적으로 [프로젝트의 기존 Markdown 구성](/ko/reference/co ```js title="astro.config.mjs" import { defineConfig } from 'astro/config'; -import { satteri } from '@astrojs/markdown-satteri'; -import mdx from '@astrojs/mdx'; +import { satteri } from "@astrojs/markdown-satteri"; +import mdx from "@astrojs/mdx"; +import mdastPlugin1 from "./src/mdast-plugin-1"; +import mdastPlugin2 from "./src/mdast-plugin-2"; export default defineConfig({ // ... markdown: { - syntaxHighlight: 'prism', + syntaxHighlight: "prism", processor: satteri({ mdastPlugins: [mdastPlugin1] }), }, integrations: [ mdx({ // Markdown `syntaxHighlight` 재정의 // `.mdx` 파일은 대신 Shiki 사용 - syntaxHighlight: 'shiki', + syntaxHighlight: "shiki", // 이 옵션은 `.mdx` 파일의 `markdown.processor`를 재정의함 processor: satteri({ mdastPlugins: [mdastPlugin2] }), @@ -384,8 +390,9 @@ MDX에서 `markdown` 구성 확장을 비활성화해야 할 수도 있습니다 ```js title="astro.config.mjs" import { defineConfig } from 'astro/config'; -import { satteri } from '@astrojs/markdown-satteri'; -import mdx from '@astrojs/mdx'; +import { satteri } from "@astrojs/markdown-satteri"; +import mdx from "@astrojs/mdx"; +import mdastPlugin from "./src/mdast-plugin"; export default defineConfig({ // ... @@ -466,10 +473,10 @@ MDX 최적화 프로그램이 [컴포넌트 prop을 통해 가져온 MDX 콘텐 예를 들어 다음 의도된 MDX 출력은 모두 `"

          ...

          "`이 아닌 `...`입니다. -```astro +```astro title="src/pages/any-page.astro" --- -import { Content, components } from '../content.mdx'; -import Heading from '../Heading.astro'; +import { Content, components } from "../content.mdx"; +import Heading from "../components/Heading.astro"; --- diff --git a/src/content/docs/ko/guides/integrations-guide/partytown.mdx b/src/content/docs/ko/guides/integrations-guide/partytown.mdx index f7dcab16966c0..955563d973227 100644 --- a/src/content/docs/ko/guides/integrations-guide/partytown.mdx +++ b/src/content/docs/ko/guides/integrations-guide/partytown.mdx @@ -153,7 +153,10 @@ export default defineConfig({ 일부 타사 스크립트는 서비스 워커에서 실행되는 `config.resolveUrl()`을 통한 [프록시](https://partytown.qwik.dev/proxying-requests/)가 필요할 수 있습니다. 이 구성 옵션을 설정하여 특정 URL을 확인하고, 대신 프록시된 URL을 선택적으로 반환할 수 있습니다. -```js title="astro.config.mjs" {7-13} +```js title="astro.config.mjs" {10-15} +import { defineConfig } from 'astro/config'; +import partytown from "@astrojs/partytown"; + export default defineConfig({ // ... integrations: [ @@ -161,13 +164,12 @@ export default defineConfig({ // 예시: Facebook의 분석 스크립트 프록시 config: { resolveUrl: (url) => { - const proxyMap = { - "connect.facebook.net": "my-proxy.com" - } - url.hostname = proxyMap[url.hostname] || url.hostname; + const proxyMap = new Map([["connect.facebook.net", "my-proxy.com"]]); + const proxiedHost = proxyMap.get(url.hostname); + if (proxiedHost) url.hostname = proxiedHost; return url; }, - } + }, }), ], }); diff --git a/src/content/docs/ko/guides/media/cloudinary.mdx b/src/content/docs/ko/guides/media/cloudinary.mdx index 7a54a2aa5fda6..d2e452caea3ef 100644 --- a/src/content/docs/ko/guides/media/cloudinary.mdx +++ b/src/content/docs/ko/guides/media/cloudinary.mdx @@ -121,7 +121,7 @@ Cloudinary Astro SDK는 콘텐츠 컬렉션을 위한 Cloudinary 자산을 불 이미지 또는 동영상 컬렉션을 불러오려면 `loader: cldAssetsLoader ({})`의 `folder`를 설정합니다 (필요한 경우): -```jsx title="config.ts" +```jsx title="content.config.ts" import { defineCollection } from 'astro:content'; import { cldAssetsLoader } from 'astro-cloudinary/loaders'; diff --git a/src/content/docs/ko/guides/media/imagekit.mdx b/src/content/docs/ko/guides/media/imagekit.mdx index c22ed310bb97d..6ff10090dc74a 100644 --- a/src/content/docs/ko/guides/media/imagekit.mdx +++ b/src/content/docs/ko/guides/media/imagekit.mdx @@ -428,7 +428,7 @@ const gallery = defineCollection({ })); }, schema: z.object({ - url: z.string().url(), + url: z.url(), width: z.number(), height: z.number(), name: z.string(), diff --git a/src/content/docs/ko/guides/media/mux.mdx b/src/content/docs/ko/guides/media/mux.mdx index cd1dac4f70904..93c1c67324521 100644 --- a/src/content/docs/ko/guides/media/mux.mdx +++ b/src/content/docs/ko/guides/media/mux.mdx @@ -87,6 +87,10 @@ Astro 프로젝트에서 Mux Player를 다음과 같이 사용할 수도 있습 [Mux 웹 플레이어를 제어하는 다른 모든 옵션](https://www.mux.com/docs/guides/player-api-reference/?utm_campaign=21819274-Astro&utm_source=astro-docs) (예: 컨트롤 표시/숨기기, 스타일 요소, 쿠키 비활성화)은 선택 사항입니다. ```astro title="src/components/StarlightVideo.astro" +--- +import { MuxPlayer } from "@mux/mux-player-astro"; +--- + - ``` 모든 라이브 스트림은 향후 요청 시 재생할 수 있도록 Mux에 동영상 자산으로 녹화 및 저장됩니다. @@ -212,7 +219,7 @@ const mux = new Mux ({ Astro 프로젝트에서 사용할 동영상 정보를 가져오려면 동영상의 `ASSET_ID` (Mux 대시보드에서 확인 가능)를 `retrieve()` 도우미 함수에 제공하세요. 이를 통해 동영상 제목이나 재생 시간과 같은 값을 Mux 컴포넌트와 HTML 템플릿에 전달할 수 있습니다. -```astro +```astro title="src/components/StarlightVideo.astro" --- import Mux from "@mux/mux-node"; import { MuxPlayer } from "@mux/mux-player-astro"; @@ -278,9 +285,9 @@ Mux Uploader는 파일 업로드 시 수동 파일 선택과 드래그 앤 드 동영상을 업로드하기 전에 [Mux API 액세스 토큰](#mux-환경-api-액세스)이 구성되어 있는지 확인하세요. 토큰이 구성되면 Mux Node SDK의 `create()` 함수를 사용하여 새 동영상 업로드를 시작할 수 있습니다. -```astro +```astro title="src/components/VideoUploader.astro" --- -import Layout from '../../layouts/Layout.astro'; +import Layout from '../layouts/Layout.astro'; import Mux from "@mux/mux-node"; import { MuxUploader } from "@mux/mux-uploader-astro"; @@ -306,7 +313,7 @@ const upload = await mux.video.uploads.create({ 추가 컴포넌트 속성을 사용하여 ``의 기능과 모양을 사용자 정의할 수 있습니다. 요소 스타일링 외에도, 다운로드 일시 중지 기능이나 최대 파일 크기 설정과 같은 옵션을 제어할 수 있습니다. -```astro +```astro title="src/components/VideoUploader.astro" --- import { MuxUploader } from '@mux/mux-uploader-astro'; --- @@ -331,7 +338,7 @@ Mux Uploader는 풍부한 기능과 미디어 업로드의 현재 상태에 따 이러한 이벤트를 수신 대기하고 Astro 컴포넌트의 [클라이언트 측 스크립트](/ko/guides/client-side-scripts/)에서 처리할 수 있습니다. `MuxUploaderElement` 타입도 사용할 수 있습니다. -```astro +```astro title="src/components/VideoUploader.astro" --- import { MuxUploader } from '@mux/mux-uploader-astro'; --- diff --git a/src/content/docs/ko/reference/cache-provider-reference.mdx b/src/content/docs/ko/reference/cache-provider-reference.mdx index e50fdcb91c262..5a05069b72628 100644 --- a/src/content/docs/ko/reference/cache-provider-reference.mdx +++ b/src/content/docs/ko/reference/cache-provider-reference.mdx @@ -371,10 +371,46 @@ stale-while-revalidate 유효 기간을 초 단위로 지정합니다. 백그라

          -**타입:** (context: \{ request: Request; url: URL; waitUntil?: (promise: Promise\) => void \}, next: MiddlewareNext) => Promise\ +**타입:** (context: \{ request: Request; url: URL; waitUntil?: (promise: Promise\) => void; logger: AstroRuntimeLogger \}, next: MiddlewareNext) => Promise\

          -런타임 캐싱을 구현하기 위해 요청을 가로챕니다. `context`에는 배포 환경의 런타임이 기능을 지원하는 경우, stale-while-revalidate와 같은 백그라운드 작업을 처리할 수 있는 `waitUntil()` 함수가 포함되어 있습니다. +Astro가 일치하는 라우트를 생성하기 전에 요청을 가로채는 선택적 훅입니다. 첫 번째 인자로 `context` 객체를 받고, 체인의 `next()` 미들웨어를 호출하기 위한 콜백을 받습니다. + +`context`에는 다음 속성이 포함됩니다: + +- `request`: 들어오는 [`Request`](https://developer.mozilla.org/ko/docs/Web/API/Request) 객체입니다. +- `url`: 요청에서 파생된 정규화된 [`URL`](https://developer.mozilla.org/ko/docs/Web/API/URL)입니다. +- `waitUntil()`: 런타임에서 지원되는 경우, 오래된 캐시 항목 재검증과 같은 백그라운드 작업을 정의하는 함수입니다. +- `logger`: Astro v7.3.0부터 사용할 수 있는 [`logger`](/ko/reference/api-reference/#logger) 인스턴스로, [구성된 로깅 대상](/ko/reference/configuration-reference/#로거-옵션)을 따릅니다. + +다음 예시는 캐시에 추가된 각 URL에 대한 로그를 남기는 최소한의 `onRequest()` 훅을 구현합니다. + +```ts title="my-provider/runtime.ts" ins={7-17} +import type { CacheProviderFactory } from 'astro'; + +const factory: CacheProviderFactory = (config) => { + const cache = new Map(); + return { + name: 'my-cache-provider', + async onRequest({ request, url, waitUntil, logger }, next) { + if (request.method !== 'GET') return next(); + + const cached = cache.get(url); + if (cached) return cached; + + const response = await next(); + cache.set(url, response.clone()); + logger.info(`Cached response for ${url}.`); + return response; + }, + async invalidate() { + // ... + }, + }; +}; + +export default factory; +``` #### `CacheProvider.invalidate()` diff --git a/src/content/docs/ko/reference/cli-reference.mdx b/src/content/docs/ko/reference/cli-reference.mdx index d1191dbfbfd1a..582de74aad0dc 100644 --- a/src/content/docs/ko/reference/cli-reference.mdx +++ b/src/content/docs/ko/reference/cli-reference.mdx @@ -194,25 +194,7 @@ Astro 개발 서버가 실행 중인 터미널에서 다음 단축키를 사용 - `o + enter` 브라우저에서 Astro 사이트를 엽니다. - `q + enter` 개발 서버를 종료합니다. -

          플래그

          - -

          - -이 명령어는 [공통 플래그](#공통-플래그)와 다음과 같은 추가 플래그를 허용합니다. - -#### `--ignore-lock` - -

          - -다른 실행 중인 개발 서버를 감지하는 데 사용되는 잠금 파일을 확인하거나 기록하지 않고 개발 서버를 시작합니다. 이를 통해 오류를 발생시키는 대신, 동일한 프로젝트에서 이미 실행 중인 개발 서버와 함께 새 개발 서버를 시작할 수 있습니다. - -```shell -astro dev --ignore-lock --port 4322 -``` - -새로 시작한 서버는 [`stop`, `status`, `logs` 하위 명령어](#공통-하위-명령어)로 추적되지 않습니다. - -`--background`(AI 코딩 에이전트가 실행한 경우 포함) 또는 `--force`와 함께 사용하면 오류가 발생합니다. 두 옵션 모두 잠금 파일에 의존하기 때문입니다. +개발 환경을 더욱 세부적으로 제어하기 위해 이 명령어는 [공통 플래그](#공통-플래그) 및 [공통 하위 명령어](#공통-하위-명령어)와 함께 사용할 수 있습니다. ## `astro build` @@ -238,7 +220,7 @@ Astro 미리보기 서버가 실행 중인 터미널에서 다음 단축키를 - `o` + `enter`: 브라우저에서 Astro 사이트를 엽니다. - `q` + `enter`: 미리보기 서버를 종료합니다. -`astro preview` 명령은 아래에 설명된 [공통 플래그](#공통-플래그)와 결합하여 미리보기 환경을 더욱 세부적으로 제어할 수 있습니다. Astro v7.2.0부터는 [`--background` 플래그](#--background)와 [`stop`, `status`, `logs` 하위 명령어](#공통-하위-명령어)도 지원하여 백그라운드 미리보기 서버를 관리할 수 있습니다. +이 명령어는 미리보기 환경을 더욱 세부적으로 제어하기 위해 [공통 플래그](#공통-플래그) 및 [공통 하위 명령어](#공통-하위-명령어)와 함께 사용할 수 있습니다. ## `astro check` @@ -558,6 +540,20 @@ astro dev --background --force 기계가 읽을 수 있는 형식의 출력에 유용한 [JSON 로깅](/ko/reference/logger-reference/#loghandlersjson)을 활성화합니다. +### `--ignore-lock` + +

          + +잠금 파일의 존재 여부를 확인하거나 해당 파일을 작성할 필요가 없도록 합니다. v7.3.0부터, 이를 통해 새로운 개발 서버나 프리뷰 서버가 이미 실행 중인 서버와 함께 시작될 수 있으며, 오류가 발생하지 않습니다. + +```shell +astro dev --ignore-lock --port 4322 +``` + +새 서버는 [공통 하위 명령어](#공통-하위-명령어)로 추적되지 않습니다. + +[`--background`](#--background) 또는 [`--force`](#--force-string)와 함께 사용하면 오류가 발생합니다. 두 옵션 모두 잠금 파일에 의존하기 때문입니다. + ## 전역 플래그 이 플래그를 사용하여 `astro` CLI에 대한 정보를 얻으세요. diff --git a/src/content/docs/ko/reference/errors/redirect-with-no-location.mdx b/src/content/docs/ko/reference/errors/redirect-with-no-location.mdx index 64f7540cb861d..2b97a9c79e43f 100644 --- a/src/content/docs/ko/reference/errors/redirect-with-no-location.mdx +++ b/src/content/docs/ko/reference/errors/redirect-with-no-location.mdx @@ -4,6 +4,8 @@ i18nReady: true githubURL: https://github.com/withastro/astro/blob/main/packages/astro/src/core/errors/errors-data.ts --- +> **RedirectWithNoLocation**: The redirect `Response` has no `Location` header. Use `Astro.redirect()` to create redirects, or add a `Location` header to the `Response`. + ## 무엇이 잘못되었나요? 리디렉션에는 `Location` 헤더가 있는 위치가 제공되어야 합니다. diff --git a/src/content/docs/ko/reference/image-service-reference.mdx b/src/content/docs/ko/reference/image-service-reference.mdx index 0821f348cc2e4..a056a323ba9ab 100644 --- a/src/content/docs/ko/reference/image-service-reference.mdx +++ b/src/content/docs/ko/reference/image-service-reference.mdx @@ -32,21 +32,21 @@ Astro는 로컬과 외부라는 두 가지 유형의 이미지 서비스를 제 import type { ExternalImageService, ImageTransform, AstroConfig } from "astro"; const service: ExternalImageService = { - validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) { + validateOptions(options: ImageTransform, imageConfig: AstroConfig['image'], logger) { const serviceConfig = imageConfig.service.config; // 사용자가 설정한 최대 너비를 적용합니다. if (options.width && options.width > serviceConfig.maxWidth) { - console.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`); + logger.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`); options.width = serviceConfig.maxWidth; } return options; }, - getURL(options, imageConfig) { + getURL(options, imageConfig, logger) { return `https://mysupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`; }, - getHTMLAttributes(options, imageConfig) { + getHTMLAttributes(options, imageConfig, logger) { const { src, format, quality, ...attributes } = options; return { ...attributes, @@ -69,7 +69,7 @@ import type { ImageTransform, LocalImageService, AstroConfig } from "astro"; import { mySuperLibraryThatEncodesImages } from "@example/my-super-library"; const service: LocalImageService = { - getURL(options: ImageTransform, imageConfig) { + getURL(options: ImageTransform, imageConfig, logger) { const searchParams = new URLSearchParams(); searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src); options.width && searchParams.append('w', options.width.toString()); @@ -77,10 +77,10 @@ const service: LocalImageService = { options.quality && searchParams.append('q', options.quality.toString()); options.format && searchParams.append('f', options.format); return `/my_custom_endpoint_that_transforms_images?${searchParams}`; - // 또는 내장된 엔드포인트를 사용하여 parsURL 및 변환 함수를 호출합니다. - // 이 함수는 `/_image?${searchParams}`를 반환합니다. + // 또는 내장 엔드포인트를 사용하면 parseURL 및 transform 함수가 호출됩니다: + // return `/_image?${searchParams}`; }, - parseURL(url: URL, imageConfig) { + parseURL(url: URL, imageConfig, logger) { const params = url.searchParams; return { src: params.get('href')!, @@ -90,14 +90,14 @@ const service: LocalImageService = { quality: params.get('q'), }; }, - async transform(inputBuffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig) { + async transform(inputBuffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig, logger) { const { buffer } = await mySuperLibraryThatEncodesImages(options); return { data: buffer, format: options.format, }; }, - getHTMLAttributes(options, imageConfig) { + getHTMLAttributes(options, imageConfig, logger) { let targetWidth = options.width; let targetHeight = options.height; if (typeof options.src === "object") { @@ -142,7 +142,7 @@ import { getConfiguredImageService, imageConfig } from "astro:assets"; import * as mime from "mrmime"; import { getImageBuffer } from "./my-custom-image-fetcher.js"; -export const GET: APIRoute = async ({ request }) => { +export const GET: APIRoute = async ({ request, logger }) => { const imageService = await getConfiguredImageService(); if (!isLocalService(imageService)) { @@ -155,6 +155,7 @@ export const GET: APIRoute = async ({ request }) => { const imageTransform = await imageService.parseURL( new URL(request.url), imageConfig, + logger, ); if (!imageTransform) { @@ -167,6 +168,7 @@ export const GET: APIRoute = async ({ request }) => { inputBuffer, imageTransform, imageConfig, + logger, ); return new Response(new Uint8Array(data), { status: 200, @@ -185,7 +187,7 @@ export const GET: APIRoute = async ({ request }) => {

          -**타입:** (options: ImageTransform, imageConfig: AstroConfig['image']) => string | Promise\
          +**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => string | Promise\

          @@ -195,57 +197,71 @@ export const GET: APIRoute = async ({ request }) => { 외부 서비스의 경우 이 훅은 이미지의 최종 URL을 반환합니다. -두 서비스 유형 모두 `options`는 사용자가 `` 컴포넌트의 속성 또는 `getImage()`에 대한 옵션으로 전달한 속성입니다. +두 서비스 유형 모두 `options`는 사용자가 `` 컴포넌트의 속성 또는 `getImage()`에 대한 옵션으로 전달한 속성입니다. 이 훅은 이미지 구성과 함께 Astro v7.3.0부터는 `logger`도 받습니다. ### `parseURL()`

          -**타입:** (url: URL, imageConfig: AstroConfig['image']) => \{ src: string, [key: string]: any \} | undefined | Promise\<\{ src: string, [key: string]: any \}\> | Promise\
          +**타입:** (url: URL, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => \{ src: string, [key: string]: any \} | undefined | Promise\<\{ src: string, [key: string]: any \}\> | Promise\

          **로컬 서비스에만 필요합니다. 외부 서비스에 사용할 수 없음** -이 훅은 `getURL()`에 의해 생성된 URL을 `transform` (요청 시 렌더링 및 개발 모드에서)에 사용되는 다른 속성을 가진 객체로 다시 구문 분석합니다. 빌드 중에는 사용되지 않습니다. +이 훅은 `getURL()`에 의해 생성된 URL을 `transform`에서 사용할 수 있도록 다양한 속성을 가진 객체로 다시 파싱합니다. 이 훅은 세 가지 매개변수를 받습니다: 파싱할 URL, 이미지 구성, 그리고 Astro v7.3.0부터는 로거도 포함됩니다. + +이 훅은 요청 시 렌더링 및 개발 모드에서만 사용되며, 빌드 중에는 사용되지 않습니다. ### `transform()`

          -**타입:** (inputBuffer: Uint8Array, options: \{ src: string, [key: string]: any \}, imageConfig: AstroConfig['image']) => Promise\<\{ data: Uint8Array; format: ImageOutputFormat \}\>
          +**타입:** (inputBuffer: Uint8Array, options: \{ src: string, [key: string]: any \}, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Promise\<\{ data: Uint8Array; format: ImageOutputFormat \}\>

          **로컬 서비스에만 필요합니다. 외부 서비스에 사용할 수 없음** -이 훅은 이미지를 변환하고 반환하며 빌드 중에 호출되어 최종 자산 파일을 생성합니다. +이 훅은 이미지를 변환하고 반환하며 빌드 중에 호출되어 최종 자산 파일을 생성합니다. 이 훅은 입력 이미지, 옵션 객체, 이미지 구성, Astro v7.3.0부터는 `logger`까지 네 가지 매개변수를 받습니다. 요청 시 렌더링 및 개발 모드에서 사용자에게 적절한 MIME 유형이 제공되도록 하려면 `format`을 반환해야 합니다. +```ts +import type { LocalImageService } from 'astro'; + +const service: LocalImageService = { + // ... + async transform(inputBuffer, transform, imageConfig, logger) { + logger.warn(`Could not optimize "${transform.src}". Passing it through unchanged.`); + return { data: inputBuffer, format: 'png' }; + }, +}; +``` + ### `getHTMLAttributes()`

          -**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => Record\ | Promise\\>
          +**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Record\ | Promise\\>

          **로컬 및 외부 서비스 모두 선택 사항** -이 훅은 사용자가 전달한 매개변수 (`options`)를 기반으로 이미지를 HTML로 렌더링하는 데 사용되는 모든 추가 속성을 반환합니다. +이 훅은 사용자가 전달한 매개변수(`options`)를 기반으로 이미지를 HTML로 렌더링하는 데 사용되는 모든 추가 속성을 반환합니다. 이 훅은 이미지 구성과 함께 Astro v7.3.0부터는 `logger`도 받습니다. ### `getSrcSet()`

          -**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => UnresolvedSrcSetValue[] | Promise\
          +**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => UnresolvedSrcSetValue[] | Promise\

          **로컬 및 외부 서비스 모두 선택 사항입니다.** -이 훅은 `` 또는 ``의 `source`에 `srcset` 속성을 생성하기 위해 지정된 이미지의 여러 변형을 생성합니다. +이 훅은 지정된 이미지의 여러 변형을 생성합니다. 예를 들어 ``나 ``의 `source`에 대한 `srcset` 속성을 생성하기 위한 것입니다. 이 훅은 옵션 객체, 이미지 구성, Astro v7.3.0부터는 `logger`까지 세 가지 매개변수를 받습니다. 이 훅은 다음 속성을 가진 객체 배열을 반환합니다. @@ -261,13 +277,13 @@ export type UnresolvedSrcSetValue = {

          -**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'] ) => ImageTransform | Promise\ +**타입:** (options: ImageTransform, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => ImageTransform | Promise\

          **로컬 및 외부 서비스 모두 선택 사항** -이 훅을 사용하면 사용자가 전달한 옵션을 검증하고 강화할 수 있습니다. 이는 기본 옵션을 설정하거나 사용자에게 매개변수가 필요함을 알리는 데 유용합니다. +이 훅을 사용하면 사용자가 전달한 옵션을 검증하고 강화할 수 있습니다. 이는 기본 옵션을 설정하거나 사용자에게 매개변수가 필요함을 알리는 데 유용합니다. 이 훅은 이미지 구성과 함께 Astro v7.3.0부터는 잘못된 옵션에 대해 사용자에게 경고하는 데 사용할 수 있는 `logger`도 받습니다. [Astro 내장 서비스에서 `validateOptions()`가 어떻게 사용되는지 확인하세요.](https://github.com/withastro/astro/blob/0ab6bad7dffd413c975ab00e545f8bc150f6a92f/packages/astro/src/assets/services/service.ts#L124) @@ -275,13 +291,13 @@ export type UnresolvedSrcSetValue = {

          -**Type:** (url: string, imageConfig: AstroConfig['image'] ) => Omit\<ImageMetadata, 'src' | 'fsPath'\> | Promise\ImageMetadata, 'src' | 'fsPath'\>\> +**타입:** (url: string, imageConfig: AstroConfig['image'], logger: AstroRuntimeLogger) => Omit\<ImageMetadata, 'src' | 'fsPath'\> | Promise\ImageMetadata, 'src' | 'fsPath'\>\>

          **로컬 및 외부 서비스 모두 선택 사항** -이 훅을 사용하면 [`inferRemoteSize()`](/ko/reference/modules/astro-assets/#inferremotesize)의 동작을 확장할 수 있습니다. 이는 이미지를 캐싱하여 네트워크 트래픽을 줄이거나, 이미지 URL에서 이미지 정보를 예측할 수 있는 경우에 유용합니다. +이 훅을 사용하면 [`inferRemoteSize()`](/ko/reference/modules/astro-assets/#inferremotesize)의 동작을 확장할 수 있습니다. 이는 이미지를 캐싱하여 네트워크 트래픽을 줄이거나, 이미지 URL에서 이미지 정보를 예측할 수 있는 경우에 유용합니다. 이 훅은 이미지 URL, 이미지 구성, Astro v7.3.0부터는 `logger`까지 세 가지 매개변수를 받습니다. ## 사용자 구성 diff --git a/src/content/docs/ko/reference/modules/astro-assets.mdx b/src/content/docs/ko/reference/modules/astro-assets.mdx index 5ee476f02f90c..c7708b703b789 100644 --- a/src/content/docs/ko/reference/modules/astro-assets.mdx +++ b/src/content/docs/ko/reference/modules/astro-assets.mdx @@ -450,6 +450,11 @@ import myImage from '../assets/my_image.png'; 기본적으로 Sharp는 이미지를 평면화할 때 검은색 배경을 사용합니다. 다른 배경색을 지정하는 것은 투명한 배경을 가진 이미지를 투명도를 지원하지 않는 형식 (예: `.jpeg`)으로 변환할 때 특히 유용합니다. ```astro title="src/components/MyComponent.astro" "background" +--- +import { Image } from 'astro:assets'; +import myImage from '../assets/my_image.png'; +--- + A description of my image` 컴포넌트를 만들 수도 있습니다. -이 함수는 [Image 컴포넌트와 동일한 속성](#image-)을 가진 (`alt` 제외) 옵션 객체를 사용하며, [`GetImageResult` 객체](#getimageresult)를 반환합니다. +이 함수는 [Image 컴포넌트와 동일한 속성](#image-)을 가진 (`alt`와 `sizes` 제외) 옵션 객체를 사용하며, [`GetImageResult` 객체](#getimageresult)를 반환합니다. 다음 예시는 `
          `에 AVIF `background-image`를 생성합니다. @@ -735,12 +740,13 @@ const buffer = await fetch(url).then((res) => res.arrayBuffer()); ```ts "context.url" import type { APIRoute } from "astro"; -import { fontData, experimental_getFontFileURL } from "astro:assets" +import { fontData, experimental_getFontFileURL } from "astro:assets"; export const prerender = false; // 'server' 모드에서는 필요하지 않습니다. export const GET: APIRoute = async (context) => { // ... + const fontPath = fontData["--font-roboto"][0]?.src[0]?.url; const url = experimental_getFontFileURL(fontPath, context.url); // ... }; @@ -855,11 +861,13 @@ import { import { baseService } from "astro/assets"; const newImageService = { - getURL: baseService.getURL, - parseURL: baseService.parseURL, - getHTMLAttributes: baseService.getHTMLAttributes, - async transform(inputBuffer, transformOptions) {...} -} + getURL: baseService.getURL, + parseURL: baseService.parseURL, + getHTMLAttributes: baseService.getHTMLAttributes, + async transform(inputBuffer, transformOptions) { + /* ... */ + }, +}; ``` ### `getConfiguredImageService()` @@ -1420,6 +1428,8 @@ import type { 이미지 변환 서비스에서 허용하는 옵션을 정의합니다. 여기에는 필수 `src` 속성, 선택적 사전 정의된 속성, 이미지 서비스에서 필요한 추가 속성이 포함됩니다. +사전 정의된 속성은 `alt`와 `sizes`를 제외하고 [`` 컴포넌트](#image-)에서 허용하는 속성과 일치합니다. 다음 속성들은 다른 타입을 사용합니다. + #### `ImageTransform.src`

          @@ -1447,64 +1457,6 @@ import type { 이미지의 높이입니다. -#### `ImageTransform.widths` - -

          - -**타입:** `number[] | undefined`
          - -

          - -이미지에 대해 생성할 너비 목록입니다. - -#### `ImageTransform.densities` - -

          - -**타입:** ``(number | `${number}x`)[] | undefined``
          - -

          - -이미지에 대해 생성할 픽셀 밀도 목록입니다. - -#### `ImageTransform.quality` - -

          - -**타입:** ImageQuality | undefined -

          - -출력 이미지에 대해 원하는 품질입니다. - -#### `ImageTransform.format` - -

          - -**타입:** ImageOutputFormat | undefined -

          - -출력 이미지에 대해 원하는 형식입니다. - -#### `ImageTransform.fit` - -

          - -**타입:** `'fill' | 'contain' | 'cover' | 'none' | 'scale-down' | string | undefined`
          - -

          - -`object-fit` CSS 속성에 허용되는 값 목록을 정의하며, 어떤 문자열로든 확장 가능합니다. - -#### `ImageTransform.position` - -

          - -**타입:** `string | undefined`
          - -

          - -`object-position` CSS 속성의 값을 제어합니다. - ### `UnresolvedImageTransform`

          diff --git a/src/content/docs/ko/reference/renderer-reference.mdx b/src/content/docs/ko/reference/renderer-reference.mdx index f8e51f50c0d27..c9493558416d2 100644 --- a/src/content/docs/ko/reference/renderer-reference.mdx +++ b/src/content/docs/ko/reference/renderer-reference.mdx @@ -151,8 +151,16 @@ import type { 렌더러는 이 값을 사용하여 조건부로 클라이언트 측 하이드레이션 상태를 포함할 수 있습니다. 예를 들어, 렌더러는 하이드레이션되지 않을 컴포넌트에 대한 전송 상태 직렬화를 건너뛸 수 있습니다: -```ts -async function renderToStaticMarkup(Component, props, children, metadata) { +```ts title="my-renderer/server.ts" +import type { AstroComponentMetadata } from 'astro'; +import { render } from './custom-render'; + +async function renderToStaticMarkup( + Component: any, + props: Record, + slots: Record, + metadata?: AstroComponentMetadata, +) { const willHydrate = !!metadata?.hydrate; // 하이드레이션되지 않을 컴포넌트의 경우 전송 상태 직렬화 건너뛰기 return render(Component, props, { includeTransferState: willHydrate }); diff --git a/src/content/docs/pt-br/guides/backend/firebase.mdx b/src/content/docs/pt-br/guides/backend/firebase.mdx new file mode 100644 index 0000000000000..434354c6d0f97 --- /dev/null +++ b/src/content/docs/pt-br/guides/backend/firebase.mdx @@ -0,0 +1,932 @@ +--- +title: Firebase & Astro +description: Adicione um backend ao seu projeto com o Firebase +sidebar: + label: Firebase +type: backend +logo: firebase +stub: false +i18nReady: true +--- +import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' +import { FileTree } from '@astrojs/starlight/components'; + + +[Firebase](https://firebase.google.com/) é uma plataforma de desenvolvimento de aplicativos que fornece banco de dados NoSQL, autenticação, inscrições em tempo real, funções e armazenamento. + +Veja nosso guia separado para [implantar na hospedagem do Firebase](/pt-br/guides/deploy/firebase/). + +## Inicializando o Firebase no Astro + +### Pré-requisitos + +- Um [projeto Firebase com um aplicativo web configurado](https://firebase.google.com/docs/web/setup). +- Um projeto Astro com [`output: 'server'` para renderização sob demanda](/pt-br/guides/on-demand-rendering/) ativado. +- Credenciais do Firebase: Você precisará de dois conjuntos de credenciais para conectar o Astro ao Firebase: + - Credenciais de aplicativo web: Essas credenciais serão usadas pelo lado do cliente do seu aplicativo. Você pode encontrá-las no console do Firebase em *Configurações do projeto > Geral*. Role para baixo até a seção **Seus aplicativos** e clique no ícone **Aplicativo Web**. + - Credenciais do projeto: Essas credenciais serão usadas pelo lado do servidor do seu aplicativo. Você pode gerá-las no console do Firebase em *Configurações do projeto > Contas de serviço > SDK Admin do Firebase > Gerar nova chave privada*. + +### Adicionando credenciais do Firebase + +Para adicionar suas credenciais do Firebase ao Astro, crie um arquivo `.env` na raiz do seu projeto com as seguintes variáveis: + +```ini title=".env" +FIREBASE_PRIVATE_KEY_ID=SUA_CHAVE_PRIVADA_ID +FIREBASE_PRIVATE_KEY=SUA_CHAVE_PRIVADA +FIREBASE_PROJECT_ID=SEU_PROJECT_ID +FIREBASE_CLIENT_EMAIL=SEU_CLIENT_EMAIL +FIREBASE_CLIENT_ID=SEU_CLIENT_ID +FIREBASE_AUTH_URI=SEU_AUTH_URI +FIREBASE_TOKEN_URI=SEU_TOKEN_URI +FIREBASE_AUTH_CERT_URL=SEU_AUTH_CERT_URL +FIREBASE_CLIENT_CERT_URL=SEU_CLIENT_CERT_URL +``` + +Agora, essas variáveis de ambiente estão disponíveis para uso no seu projeto. + +Se você gostaria de ter IntelliSense para suas variáveis de ambiente do Firebase, edite ou crie o arquivo `env.d.ts` no seu diretório `src/` e configure seus tipos: + +```ts title="src/env.d.ts" +interface ImportMetaEnv { + readonly FIREBASE_PRIVATE_KEY_ID: string; + readonly FIREBASE_PRIVATE_KEY: string; + readonly FIREBASE_PROJECT_ID: string; + readonly FIREBASE_CLIENT_EMAIL: string; + readonly FIREBASE_CLIENT_ID: string; + readonly FIREBASE_AUTH_URI: string; + readonly FIREBASE_TOKEN_URI: string; + readonly FIREBASE_AUTH_CERT_URL: string + readonly FIREBASE_CLIENT_CERT_URL: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} +``` + +:::tip +Leia mais sobre [variáveis de ambiente](/pt-br/guides/environment-variables/) e arquivos `.env` no Astro. +::: + +Seu projeto agora deve incluir estes novos arquivos: + + +- src/ + - **env.d.ts** +- **.env** +- astro.config.mjs +- package.json + + + +### Instalando dependências + +Para conectar o Astro ao Firebase, instale os seguintes pacotes usando o único comando abaixo no seu gerenciador de pacotes preferido: + +- `firebase` - o SDK do Firebase para o lado do cliente +- `firebase-admin` - o SDK Admin do Firebase para o lado do servidor + + + + ```shell + npm install firebase firebase-admin + ``` + + + ```shell + pnpm add firebase firebase-admin + ``` + + + ```shell + yarn add firebase firebase-admin + ``` + + + +Em seguida, crie uma pasta chamada `firebase` no diretório `src/` e adicione dois novos arquivos a essa pasta: `client.ts` e `server.ts`. + +Em `client.ts`, adicione o seguinte código para inicializar o Firebase no cliente usando as credenciais do seu aplicativo web e o pacote `firebase`: + +```ts title="src/firebase/client.ts" +import { initializeApp } from "firebase/app"; + +const firebaseConfig = { + apiKey: "minha-chave-api-publica", + authDomain: "meu-dominio-autenticacao", + projectId: "meu-id-projeto", + storageBucket: "meu-bucket-armazenamento", + messagingSenderId: "meu-id-remetente", + appId: "meu-id-aplicacao", +}; + +export const app = initializeApp(firebaseConfig); +``` + +:::note +Lembre-se de substituir o objeto `firebaseConfig` com suas próprias credenciais de aplicativo web. +::: + +Em `server.ts`, adicione o seguinte código para inicializar o Firebase no servidor usando as credenciais do seu projeto e o pacote `firebase-admin`: + +```ts title="src/firebase/server.ts" +import type { ServiceAccount } from "firebase-admin"; +import { initializeApp, cert, getApps } from "firebase-admin/app"; + +const activeApps = getApps(); +const serviceAccount = { + type: "service_account", + project_id: import.meta.env.FIREBASE_PROJECT_ID, + private_key_id: import.meta.env.FIREBASE_PRIVATE_KEY_ID, + private_key: import.meta.env.FIREBASE_PRIVATE_KEY, + client_email: import.meta.env.FIREBASE_CLIENT_EMAIL, + client_id: import.meta.env.FIREBASE_CLIENT_ID, + auth_uri: import.meta.env.FIREBASE_AUTH_URI, + token_uri: import.meta.env.FIREBASE_TOKEN_URI, + auth_provider_x509_cert_url: import.meta.env.FIREBASE_AUTH_CERT_URL, + client_x509_cert_url: import.meta.env.FIREBASE_CLIENT_CERT_URL, +}; + +const initApp = () => { + if (import.meta.env.PROD) { + console.info('Ambiente de produção detectado. Usando a conta de serviço padrão.') + // Usar configuração padrão nas funções do firebase. Já deve estar injetada no servidor pelo Firebase. + return initializeApp() + } + console.info('Carregando a conta de serviço das variáveis de ambiente.') + return initializeApp({ + credential: cert(serviceAccount as ServiceAccount) + }) +} + +export const app = activeApps.length === 0 ? initApp() : activeApps[0]; +``` + +:::note +Lembre-se de substituir o objeto `serviceAccount` com suas próprias credenciais do projeto. +::: + +Por fim, seu projeto agora deve incluir estes novos arquivos: + + +- src + - env.d.ts + - firebase + - **client.ts** + - **server.ts** +- .env +- astro.config.mjs +- package.json + + +## Adicionando autenticação com o Firebase + +### Pré-requisitos + +- Um projeto Astro [inicializado com o Firebase](#inicializando-o-firebase-no-astro). +- Um projeto Firebase com autenticação de e-mail/senha ativada no console do Firebase sob o método *Authentication > Sign-in*. + +### Criando endpoints de servidor para auth + +A autenticação do Firebase no Astro exige os três seguintes [endpoints de servidor do Astro](/pt-br/guides/endpoints/): + +- `GET /api/auth/entrar` - para entrar com um usuário +- `GET /api/auth/sair` - para desconectar um usuário +- `POST /api/auth/registrar` - para registrar um usuário + +Crie três endpoints relacionados à autenticação em um novo diretório `src/pages/api/auth/`: `entrar.ts`, `sair.ts` e `registrar.ts`. + +`entrar.ts` contém o código para autenticar um usuário usando o Firebase: + +```ts title="src/pages/api/auth/entrar.ts" +import type { APIRoute } from "astro"; +import { app } from "../../../firebase/server"; +import { getAuth } from "firebase-admin/auth"; + +export const GET: APIRoute = async ({ request, cookies, redirect }) => { + const auth = getAuth(app); + + /* Obter token dos cabeçalhos da requisição */ + const tokenId = request.headers.get("Authorization")?.split("Bearer ")[1]; + if (!tokenId) { + return new Response( + "Token não encontrado", + { status: 401 } + ); + } + + /* Verificar token de id */ + try { + await auth.verifyIdToken(tokenId); + } catch (erro) { + return new Response( + "Token inválido", + { status: 401 } + ); + } + + /* Criar e definir cookie de sessão */ + const fiveDays = 60 * 60 * 24 * 5 * 1000; + const cookieSessao = await auth.createSessionCookie(tokenId, { + expiresIn: fiveDays, + }); + + cookies.set("__session", cookieSessao, { + path: "/", + }); + + return redirect("/dashboard"); +}; +``` + +:::caution +O Firebase permite apenas o uso de [um cookie, e ele deve se chamar `__session`](https://firebase.google.com/docs/hosting/manage-cache#using_cookies). Quaisquer outros cookies enviados pelo cliente não serão visíveis para sua aplicação. +::: + +:::note +Esta é uma implementação básica do endpoint de entrada. Você pode adicionar mais lógica a este endpoint para atender às suas necessidades. +::: + +`sair.ts` contém o código para desconectar um usuário excluindo o cookie de sessão: + +```ts title="src/pages/api/auth/sair.ts" +import type { APIRoute } from "astro"; + +export const GET: APIRoute = async ({ redirect, cookies }) => { + cookies.delete("__session", { + path: "/", + }); + return redirect("/entrar"); +}; +``` + +:::note +Esta é uma implementação básica do endpoint de saída. Você pode adicionar mais lógica a este endpoint para atender às suas necessidades. +::: + +`registrar.ts` contém o código para registrar um usuário usando o Firebase: + +```ts title="src/pages/api/auth/registrar.ts" +import type { APIRoute } from "astro"; +import { getAuth } from "firebase-admin/auth"; +import { app } from "../../../firebase/server"; + +export const POST: APIRoute = async ({ request, redirect }) => { + const auth = getAuth(app); + + /* Obter dados do formulário */ + const dadosFormulario = await request.formData(); + const email = dadosFormulario.get("email")?.toString(); + const senha = dadosFormulario.get("senha")?.toString(); + const nome = dadosFormulario.get("nome")?.toString(); + + if (!email || !senha || !nome) { + return new Response( + "Dados do formulário ausentes", + { status: 400 } + ); + } + + /* Criar usuário */ + try { + await auth.createUser({ + email, + password: senha, + displayName: nome, + }); + } catch (error: any) { + return new Response( + "Ocorreu um erro ao criar o usuário", + { status: 400 } + ); + } + return redirect("/entrar"); +}; +``` + +:::note +Esta é uma implementação básica do endpoint de registro. Você pode adicionar mais lógica a este endpoint para atender às suas necessidades. +::: + +Após criar os endpoints de servidor para autenticação, o diretório do seu projeto agora deve incluir estes novos arquivos: + + +- src + - env.d.ts + - firebase + - client.ts + - server.ts + - pages + - api + - auth + - **entrar.ts** + - **sair.ts** + - **registrar.ts** +- .env +- astro.config.mjs +- package.json + + +### Criando páginas + +Crie as páginas que usarão os endpoints do Firebase: + +- `src/pages/registrar` - conterá um formulário para registrar um usuário +- `src/pages/entrar` - conterá um formulário para autenticar um usuário +- `src/pages/dashboard` - conterá um painel que só pode ser acessado por usuários autenticados + +O exemplo `src/pages/registrar.astro` abaixo inclui um formulário que enviará uma requisição `POST` para o endpoint `/api/auth/registrar`. Este endpoint criará um novo usuário usando os dados do formulário e então redirecionará o usuário para a página `/entrar`. + +```astro title="src/pages/registrar.astro" +--- +import Layout from "../layouts/Layout.astro"; +--- + + +

          Registrar

          +

          Já possui uma conta? Entrar

          +
          + + + + + + + +
          + +``` + +`src/pages/entrar.astro` usa o app do Firebase Server para verificar o cookie de sessão do usuário. Se o usuário estiver autenticado, a página redirecionará o usuário para a página `/dashboard`. + +A página de exemplo abaixo contém um formulário que enviará uma requisição `POST` para o endpoint `/api/auth/entrar` com o token de ID gerado pelo aplicativo cliente do Firebase. + +O endpoint verificará o token de ID e criará um novo cookie de sessão para o usuário. Em seguida, o endpoint redirecionará o usuário para a página `/dashboard`. + +```astro title="src/pages/entrar.astro" +--- +import { app } from "../firebase/server"; +import { getAuth } from "firebase-admin/auth"; +import Layout from "../layouts/Layout.astro"; + +/* Verificar se o usuário está autenticado */ +const auth = getAuth(app); +if (Astro.cookies.has("__session")) { + const cookieSessao = Astro.cookies.get("__session")!.value; + const cookieDecodificado = await auth.verifySessionCookie(cookieSessao); + if (cookieDecodificado) { + return Astro.redirect("/dashboard"); + } +} +--- + + +

          Entrar

          +

          Novo por aqui? Criar uma conta

          +
          + + + + + +
          +
          + +``` + +`src/pages/dashboard.astro` verificará o cookie de sessão do usuário usando o aplicativo de servidor do Firebase. Se o usuário não estiver autenticado, a página redirecionará o usuário para a página `/entrar`. + +A página de exemplo abaixo exibe o nome do usuário e um botão para sair. Clicar no botão enviará uma requisição `GET` para o endpoint `/api/auth/sair`. + +O endpoint excluirá o cookie de sessão do usuário e redirecionará o usuário para a página `/entrar`. + +```astro title="src/pages/dashboard.astro" +--- +import { app } from "../firebase/server"; +import { getAuth } from "firebase-admin/auth"; +import Layout from "../layouts/Layout.astro"; + +const auth = getAuth(app); + +/* Verificar sessão atual */ +if (!Astro.cookies.has("__session")) { + return Astro.redirect("/entrar"); +} +const cookieSessao = Astro.cookies.get("__session")!.value; +const cookieDecodificado = await auth.verifySessionCookie(cookieSessao); +const usuario = await auth.getUser(cookieDecodificado.uid); + +if (!usuario) { + return Astro.redirect("/entrar"); +} +--- + + +

          Bem-vindo(a) {usuario.displayName}

          +

          Ficamos felizes em ver você aqui

          +
          + +
          +
          +``` + +### Adicionando provedores OAuth + +Para adicionar provedores OAuth ao seu aplicativo, você precisa ativá-los no console do Firebase. + +No console do Firebase, vá para a seção **Authentication** e clique na aba **Método de login**. Em seguida, clique no botão **Adicionar novo fornecedor** e ative os provedores que deseja usar. + +O exemplo abaixo usa o provedor do **Google**. + +Edite a página `entrar.astro` para adicionar: +- um botão para entrar com o Google abaixo do formulário existente +- um ouvinte de evento no botão para manipular o processo de login no ` +``` + +Quando clicado, o botão de entrar com o Google abrirá uma janela pop-up para fazer login com o Google. Assim que o usuário entrar, ele enviará uma requisição `POST` para o endpoint `/api/auth/entrar` com o token de ID gerado pelo provedor OAuth. + +O endpoint verificará o token de ID e criará um novo cookie de sessão para o usuário. Em seguida, o endpoint redirecionará o usuário para a página `/dashboard`. + +## Conectando ao banco de dados Firestore + +### Pré-requisitos + +- Um projeto Astro inicializado com o Firebase conforme descrito na seção [Inicializando o Firebase no Astro](#inicializando-o-firebase-no-astro). + +- Um projeto Firebase com um banco de dados Firestore. Você pode seguir a [documentação do Firebase para criar um novo projeto e configurar um banco de dados Firestore](https://firebase.google.com/docs/firestore/quickstart?hl=pt-br). + +Nesta receita, a coleção do Firestore será chamada de **amigos** e conterá documentos com os seguintes campos: + +- `id`: gerado automaticamente pelo Firestore +- `nome`: um campo do tipo string +- `idade`: um campo do tipo number +- `eMelhorAmigo`: um campo do tipo boolean + +### Criando os endpoints do servidor + +Crie dois novos arquivos em um novo diretório `src/pages/api/amigos/`: `index.ts` e `[id].ts`. Eles criarão dois endpoints de servidor para interagir com o banco de dados Firestore das seguintes maneiras: + +- `POST /api/amigos`: para criar um novo documento na coleção de amigos. +- `POST /api/amigos/:id`: para atualizar um documento na coleção de amigos. +- `DELETE /api/amigos/:id`: para excluir um documento na coleção de amigos. + +`index.ts` conterá o código para criar um novo documento na coleção de amigos: + +```ts title="src/pages/api/amigos/index.ts" +import type { APIRoute } from "astro"; +import { app } from "../../../firebase/server"; +import { getFirestore } from "firebase-admin/firestore"; + +export const POST: APIRoute = async ({ request, redirect }) => { + const dadosFormulario = await request.formData(); + const nome = dadosFormulario.get("nome")?.toString(); + const idade = dadosFormulario.get("idade")?.toString(); + const eMelhorAmigo = dadosFormulario.get("eMelhorAmigo") === "on"; + + if (!nome || !idade) { + return new Response("Campos obrigatórios ausentes", { + status: 400, + }); + } + try { + const bd = getFirestore(app); + const refAmigos = bd.collection("amigos"); + await refAmigos.add({ + nome, + idade: parseInt(idade), + eMelhorAmigo, + }); + } catch (erro) { + return new Response("Ocorreu um erro", { + status: 500, + }); + } + return redirect("/dashboard"); +}; +``` + +:::note +Esta é uma implementação básica do endpoint `amigos`. Você pode adicionar mais lógica a este endpoint para atender às suas necessidades. +::: + +`[id].ts` conterá o código para atualizar e excluir um documento na coleção de amigos: + +```ts title="src/pages/api/amigos/[id].ts" +import type { APIRoute } from "astro"; +import { app } from "../../../firebase/server"; +import { getFirestore } from "firebase-admin/firestore"; + +const bd = getFirestore(app); +const refAmigos = bd.collection("amigos"); + +export const POST: APIRoute = async ({ params, redirect, request }) => { + const dadosFormulario = await request.formData(); + const nome = dadosFormulario.get("nome")?.toString(); + const idade = dadosFormulario.get("idade")?.toString(); + const eMelhorAmigo = dadosFormulario.get("eMelhorAmigo") === "on"; + + if (!nome || !idade) { + return new Response("Campos obrigatórios ausentes", { + status: 400, + }); + } + + if (!params.id) { + return new Response("Amigo não encontrado", { + status: 404, + }); + } + + try { + await refAmigos.doc(params.id).update({ + nome, + idade: parseInt(idade), + eMelhorAmigo, + }); + } catch (erro) { + return new Response("Ocorreu um erro", { + status: 500, + }); + } + return redirect("/dashboard"); +}; + +export const DELETE: APIRoute = async ({ params, redirect }) => { + if (!params.id) { + return new Response("Amigo não encontrado", { + status: 404, + }); + } + + try { + await refAmigos.doc(params.id).delete(); + } catch (erro) { + return new Response("Ocorreu um erro", { + status: 500, + }); + } + return redirect("/dashboard"); +}; +``` + +:::note +Esta é uma implementação básica do endpoint `amigos/:id`. Você pode adicionar mais lógica a este endpoint para atender às suas necessidades. +::: + +Após criar os endpoints de servidor para o Firestore, o diretório do seu projeto agora deve incluir estes novos arquivos: + + +- src + - env.d.ts + - firebase + - client.ts + - server.ts + - pages + - api + - amigos + - **index.ts** + - **[id].ts** +- .env +- astro.config.mjs +- package.json + + +### Criando páginas + +Crie as páginas que usarão os endpoints do Firestore: + +- `src/pages/adicionar.astro` - conterá um formulário para adicionar um novo amigo. +- `src/pages/editar/[id].astro` - conterá um formulário para editar um amigo e um botão para excluir um amigo. +- `src/pages/amigo/[id].astro` - conterá os detalhes de um amigo. +- `src/pages/dashboard.astro` - exibirá uma lista de amigos. + +#### Adicionar um novo registro + +O exemplo `src/pages/adicionar.astro` abaixo inclui um formulário que enviará uma requisição `POST` para o endpoint `/api/amigos`. Este endpoint criará um novo amigo usando os dados do formulário e então redirecionará o usuário para a página `/dashboard`. + +```astro title="src/pages/adicionar.astro" +--- +import Layout from "../layouts/Layout.astro"; +--- + + +

          Adicionar um novo amigo

          +
          + + + + + + + +
          +
          +``` + +#### Editar ou Excluir um registro + +`src/pages/editar/[id].astro` conterá um formulário para editar os dados de um amigo e um botão para excluir um amigo. Ao enviar, esta página enviará uma requisição `POST` para o endpoint `/api/amigos/:id` para atualizar os dados de um amigo. + +Se o usuário clicar no botão de excluir, esta página enviará uma requisição `DELETE` para o endpoint `/api/amigos/:id` para excluir o amigo. + +```astro title="src/pages/editar/[id].astro" +--- +import Layout from "../../layouts/Layout.astro"; +import { app } from "../../firebase/server"; +import { getFirestore } from "firebase-admin/firestore"; + +interface Amigo { + name: string; + idade: number; + eMelhorAmigo: boolean; +} + +const { id } = Astro.params; + +if (!id) { + return Astro.redirect("/404"); +} + +const bd = getFirestore(app); +const refAmigos = bd.collection("amigos"); +const snapshotAmigo = await refAmigos.doc(id).get(); + +if (!snapshotAmigo.exists) { + return Astro.redirect("/404"); +} + +const amigo = snapshotAmigo.data() as Amigo; +--- + + +

          Editar {amigo.nome}

          +

          Aqui você pode editar ou excluir os dados do seu amigo.

          +
          + + + + + + + +
          + +
          + +``` + +#### Exibir um registro individual + +`src/pages/amigo/[id].astro` exibirá os detalhes de um amigo. + +```astro title="src/pages/amigo/[id].astro" +--- +import Layout from "../../layouts/Layout.astro"; +import { app } from "../../firebase/server"; +import { getFirestore } from "firebase-admin/firestore"; + +interface Amigo { + nome: string; + idade: number; + eMelhorAmigo: boolean; +} + +const { id } = Astro.params; + +if (!id) { + return Astro.redirect("/404"); +} + +const bd = getFirestore(app); +const refAmigos = bd.collection("amigos"); +const snapshotAmigo = await refAmigos.doc(id).get(); + +if (!snapshotAmigo.exists) { + return Astro.redirect("/404"); +} + +const amigo = snapshotAmigo.data() as Amigo; +--- + + +

          {amigo.nome}

          +

          Idade: {amigo.idade}

          +

          É melhor amigo: {amigo.eMelhorAmigo ? "Sim" : "Não"}

          +
          +``` + +#### Exibir uma lista de registros com um botão de edição + +Por fim, `src/pages/dashboard.astro` exibirá uma lista de amigos. Cada amigo terá um link para a página de detalhes e um botão de edição que redirecionará o usuário para a página de edição. + +```astro title="src/pages/dashboard.astro" +--- +import { app } from "../firebase/server"; +import { getFirestore } from "firebase-admin/firestore"; +import Layout from "../layouts/Layout.astro"; + +interface Amigo { + id: string; + nome: string; + idade: number; + eMelhorAmigo: boolean; +} + +const bd = getFirestore(app); +const refAmigos = bd.collection("amigos"); +const amigosSnapshot = await refAmigos.get(); +const amigos = amigosSnapshot.docs.map((doc) => ({ + id: doc.id, + ...doc.data(), +})) as Amigo[]; +--- + + +

          Amigos

          +
            + { + amigos.map((amigo) => ( +
          • + {amigo.nome} + ({amigo.idade}) + {amigo.eMelhorAmigo ? "Melhor Amigo" : "Amigo"} + Editar +
          • + )) + } +
          +
          + +``` + +Após criar todas as páginas, você deve ter a seguinte estrutura de arquivos: + + +- src + - env.d.ts + - firebase + - client.ts + - server.ts + - pages + - dashboard.astro + - adicionar.astro + - editar + - [id].astro + - amigo + - [id].astro + - api + - amigos + - index.ts + - [id].ts +- .env +- astro.config.mjs +- package.json + + +## Recursos da Comunidade + +- [Exemplo de app SSR com Astro e Firebase](https://github.com/kevinzunigacuellar/astro-firebase) +- [Usando Firebase Realtime Database no Astro com Vue: Um Guia Passo a Passo](https://www.launchfa.st/blog/vue-astro-firebase-realtime-database) diff --git a/src/content/docs/pt-br/guides/backend/prisma-postgres.mdx b/src/content/docs/pt-br/guides/backend/prisma-postgres.mdx new file mode 100644 index 0000000000000..8ca3d91d0c2c3 --- /dev/null +++ b/src/content/docs/pt-br/guides/backend/prisma-postgres.mdx @@ -0,0 +1,191 @@ +--- +title: Prisma Postgres & Astro +description: Adicione um banco de dados Postgres serverless ao seu projeto Astro com o Prisma Postgres +sidebar: + label: Prisma Postgres +type: backend +logo: 'prisma-postgres' +stub: false +i18nReady: true +--- + +import ReadMore from '~/components/ReadMore.astro'; + +[Prisma Postgres](https://www.prisma.io/) é um banco de dados Postgres totalmente gerenciado e serverless construído para aplicativos web modernos. + +## Conectar com o Prisma ORM (Recomendado) + +O [Prisma ORM](https://www.prisma.io/orm) é a forma recomendada de se conectar ao seu banco de dados Prisma Postgres. Ele oferece consultas com segurança de tipos (type-safe), migrações e desempenho global. + +### Pré-requisitos +- Um projeto Astro com um adaptador instalado para ativar a [renderização sob demanda (SSR)](/pt-br/guides/on-demand-rendering/). + +### Instalar dependências e inicializar o Prisma + +Execute os seguintes comandos para instalar as dependências necessárias do Prisma: + +```bash +npm install prisma tsx --save-dev +npm install @prisma/adapter-pg @prisma/client +``` + +Uma vez instalado, inicialize o Prisma no seu projeto com o seguinte comando: + +```bash +npx prisma init --db --output ./generated +``` + +Você precisará responder a algumas perguntas ao configurar seu banco de dados Prisma Postgres. Selecione a região mais próxima da sua localização e um nome memorável para o seu banco de dados, como "Meu Projeto Astro". + +Isso criará: +- Um diretório `prisma/` com um arquivo `schema.prisma` +- Um arquivo `.env` com a variável `DATABASE_URL` já configurada + +### Definir um Modelo + +Mesmo se você não precisar de modelos de dados específicos ainda, o Prisma requer pelo menos um modelo no schema para gerar um cliente e aplicar migrações. + +O exemplo a seguir define um modelo `Postagem` como modelo provisório. Adicione o modelo ao seu schema para começar. Você pode excluí-lo ou substituí-lo com segurança mais tarde por modelos que reflitam seus dados reais. + +```prisma title="prisma/schema.prisma" ins={11-16} +generator client { + provider = "prisma-client" + output = "./generated" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model Postagem { + id Int @id @default(autoincrement()) + titulo String + conteudo String? + publicada Boolean @default(false) +} +``` + +Saiba mais sobre como configurar a sua instalação do Prisma ORM na [referência do schema do Prisma](https://www.prisma.io/docs/concepts/components/prisma-schema). + +### Gerar cliente + +Execute o seguinte comando para gerar o Prisma Client a partir do seu schema: + +```bash +npx prisma generate +``` + +### Gerar arquivos de migração + +Execute o seguinte comando para criar as tabelas do banco de dados e gerar o Prisma Client a partir do seu schema. Isso também criará um diretório `prisma/migrations/` com arquivos do histórico de migrações. + +```bash +npx prisma migrate dev --name init +``` + +### Criar um Prisma Client + +Dentro de `/src/lib`, crie um arquivo `prisma.ts`. Este arquivo inicializará e exportará a instância do seu Prisma Client para que você possa consultar seu banco de dados em todo o seu projeto Astro. + +```typescript title="src/lib/prisma.ts" +import { PrismaPg } from '@prisma/adapter-pg'; +import { PrismaClient } from '../../prisma/generated/client'; + +const stringConexao = import.meta.env.DATABASE_URL; +const adaptador = new PrismaPg({ connectionString: stringConexao }); +const prisma = new PrismaClient({ adapter: adaptador }); + +export default prisma; +``` + +### Consultando e exibindo dados + +O exemplo a seguir mostra a busca apenas das suas postagens publicadas com o Prisma Client ordenadas por `id`, e então exibindo os títulos e o conteúdo da postagem no seu modelo do Astro: + +```astro title="src/pages/postagens.astro" {2, 4-7} +--- +import prisma from '../lib/prisma'; + +const postagens = await prisma.post.findMany({ + where: { publicado: true }, + orderBy: { id: 'desc' } +}); +--- + + + + Postagens Publicadas + + +

          Postagens Publicadas

          +
            + {postagens.map((post) => ( +
          • +

            {post.titulo}

            + {post.conteudo &&

            {post.conteudo}

            } +
          • + ))} +
          + + +``` + +A melhor prática é lidar com consultas em uma rota de API. Para mais informações sobre como usar o Prisma ORM no seu projeto Astro, veja o [Guia Astro + Prisma ORM](https://www.prisma.io/docs/guides/frameworks/astro). + +## Conectar com Outros ORMs e Bibliotecas + +Você pode se conectar ao Prisma Postgres via TCP direto usando qualquer outro ORM, biblioteca de banco de dados ou ferramenta de sua escolha. Crie uma string de conexão direta no seu Console Prisma para começar. + +### Pré-requisitos +- Um projeto Astro com um adaptador instalado para ativar a [renderização sob demanda (SSR)](/pt-br/guides/on-demand-rendering/). +- Um banco de dados [Prisma Postgres](https://pris.ly/ppg) com uma string de conexão com TCP ativado + +### Instalar dependências + +Este exemplo usa [`pg`, um cliente PostgreSQL para Node.js](https://github.com/brianc/node-postgres) para fazer uma conexão TCP direta. + +Execute o seguinte comando para instalar o pacote `pg`: + +```bash +npm install pg +``` + +### Consultar o cliente do seu banco de dados + +Forneça sua string de conexão para o cliente `pg` para se comunicar com o seu servidor SQL e buscar dados do seu banco de dados. + +O exemplo a seguir de criação de uma tabela e inserção de dados pode ser usado para validar sua URL de consulta e conexão TCP: + +```astro title="src/pages/index.astro" {2-19} +--- +import { Client } from 'pg'; +const client = new Client({ + connectionString: import.meta.env.DATABASE_URL, + ssl: { rejectUnauthorized: false } +}); +await client.connect(); + +await client.query(` + CREATE TABLE IF NOT EXISTS postagens ( + id SERIAL PRIMARY KEY, + titulo TEXT UNIQUE, + conteudo TEXT + ); + + INSERT INTO postagens (titulo, conteudo) + VALUES ('Olá', 'Mundo') + ON CONFLICT (titulo) DO NOTHING; +`); + +const { rows } = await client.query('SELECT * FROM postagens'); +await client.end(); +--- + +

          Postagens

          +

          {rows[0].titulo}: {rows[0].conteudo}

          +``` + +## Recursos Oficiais + +- [Guia Astro + Prisma ORM](https://www.prisma.io/docs/guides/frameworks/astro) diff --git a/src/content/docs/ru/guides/data-fetching.mdx b/src/content/docs/ru/guides/data-fetching.mdx new file mode 100644 index 0000000000000..4f268fe401583 --- /dev/null +++ b/src/content/docs/ru/guides/data-fetching.mdx @@ -0,0 +1,110 @@ +--- +title: Получение данных +description: Узнайте, как получать удалённые данные в Astro с помощью fetch API. +i18nReady: true +--- + +Файлы `.astro` могут получать удалённые данные, которые помогут вам генерировать страницы. + +## `fetch()` в Astro + +Все [компоненты Astro](/ru/basics/astro-components/) имеют доступ к [глобальной функции `fetch()`](https://developer.mozilla.org/ru/docs/Web/API/Window/fetch) в скрипте компонента, чтобы выполнять HTTP-запросы к API по полному URL (например, `https://example.com/api`). +Кроме того, с помощью [`new URL("/api", Astro.url)`](/ru/reference/api-reference/#url) можно построить URL к страницам и эндпойнтам вашего проекта, которые рендерятся на сервере по запросу. + +Такой вызов fetch будет выполнен во время сборки, и данные будут доступны шаблону компонента для генерации динамического HTML. Если включён режим [SSR](/ru/guides/on-demand-rendering/), все вызовы fetch будут выполняться во время работы сервера. + +💡 Используйте [**`await` верхнего уровня**](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/await#top_level_await) в скрипте компонента Astro. + +💡 Передавайте полученные данные как пропсы и в компоненты Astro, и в компоненты фреймворков. + +```astro /await fetch\\(.*?\\);/ +--- +// src/components/User.astro +import Contact from "../components/Contact.jsx"; +import Location from "../components/Location.astro"; + +const response = await fetch("https://randomuser.me/api/"); +const data = await response.json(); +const randomUser = data.results[0]; +--- + +

          Пользователь

          +

          {randomUser.name.first} {randomUser.name.last}

          + + + + +``` + +:::note +Помните: все данные в компонентах Astro запрашиваются в момент рендеринга компонента. + +Развёрнутый сайт Astro запрашивает данные **один раз, во время сборки**. В режиме разработки запросы данных происходят при обновлении компонентов. Если данные нужно многократно запрашивать на стороне клиента, используйте в компоненте Astro [компонент фреймворка](/ru/guides/framework-components/) или [клиентский скрипт](/ru/guides/client-side-scripts/). +::: + +## `fetch()` в компонентах фреймворков + +Функция `fetch()` также глобально доступна в любых [компонентах фреймворков](/ru/guides/framework-components/): + +```tsx title="src/components/Movies.tsx" /await fetch\\(.*?\\)/ +import type { FunctionalComponent } from 'preact'; + +const data = await fetch('https://example.com/movies.json').then((response) => response.json()); + +// Компоненты, рендерящиеся во время сборки, выводят логи в CLI. +// При рендеринге с директивой `client:*` они выводят логи и в консоль браузера. +console.log(data); + +const Movies: FunctionalComponent = () => { + // Выводим результат на страницу + return
          {JSON.stringify(data)}
          ; +}; + +export default Movies; +``` + +## GraphQL-запросы + +Astro также может использовать `fetch()` для обращения к GraphQL-серверу с любым корректным GraphQL-запросом. + +```astro title="src/components/Film.astro" "await fetch" +--- +const response = await fetch( + "https://swapi-graphql.netlify.app/.netlify/functions/index", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query: ` + query getFilm ($id:ID!) { + film(id: $id) { + title + releaseDate + } + } + `, + variables: { + id: "ZmlsbXM6MQ==", + }, + }), + } +); + + +const json = await response.json(); +const { film } = json.data; +--- +

          Получаем информацию о фильме «Звёздные войны: Новая надежда»

          +

          Название: {film.title}

          +

          Год: {film.releaseDate}

          +``` + +## Получение данных из headless CMS + +Компоненты Astro могут получать данные из вашей любимой CMS и рендерить их как содержимое страницы. С помощью [динамических маршрутов](/ru/guides/routing/#dynamic-routes) компоненты могут даже генерировать страницы на основе содержимого CMS. + +Подробности об интеграции Astro с headless CMS, включая Storyblok, Contentful и WordPress, смотрите в наших [руководствах по CMS](/ru/guides/cms/). + +## Ресурсы сообщества + +- [Создание fullstack-приложения с Astro + GraphQL](https://robkendal.co.uk/blog/how-to-build-astro-site-with-graphql/) diff --git a/src/content/docs/ru/guides/server-islands.mdx b/src/content/docs/ru/guides/server-islands.mdx new file mode 100644 index 0000000000000..06eca045eceb1 --- /dev/null +++ b/src/content/docs/ru/guides/server-islands.mdx @@ -0,0 +1,125 @@ +--- +title: Серверные островки +description: Сочетайте высокопроизводительный статический HTML с динамическим контентом, отрендеренным на сервере. +i18nReady: true +--- + +Серверные островки позволяют рендерить по запросу динамические или персонализированные «островки» по отдельности, не жертвуя производительностью остальной части страницы. + +Это значит, что посетитель раньше увидит самые важные части страницы, а основной контент можно будет кешировать агрессивнее, что ускорит работу сайта. + +## Компоненты серверных островков + +Серверный островок — это обычный [компонент Astro](/ru/basics/astro-components/), рендерящийся на сервере, которому указано отложить рендеринг до тех пор, пока его содержимое не станет доступно. + +Страница будет отрендерена сразу с указанным [резервным контентом в качестве заглушки](#резервный-контент-серверного-островка). Затем собственное содержимое компонента запрашивается на клиенте и отображается, когда оно готово. + +Установив [адаптер](/ru/guides/on-demand-rendering/#серверные-адаптеры) для выполнения отложенного рендеринга, добавьте [директиву `server:defer`](/ru/reference/directives-reference/#server-directives) любому компоненту на странице, чтобы превратить его в отдельный островок: + +```astro title="src/pages/index.astro" "server:defer" +--- +import Avatar from '../components/Avatar.astro'; +--- + +``` + +Такие компоненты могут делать [всё, что обычно доступно на странице, рендерящейся по запросу](/ru/guides/on-demand-rendering/#возможности-рендеринга-по-запросу) через адаптер, — например, запрашивать контент и обращаться к кукам: + +```astro title="src/components/Avatar.astro" +--- +import { getUserAvatar } from '../sessions'; +const userSession = Astro.cookies.get('session'); +const avatarURL = await getUserAvatar(userSession); +--- +Аватар пользователя +``` + +### Передача пропсов серверным островкам + +Пропсы, передаваемые компонентам серверных островков, должны быть [сериализуемыми](https://developer.mozilla.org/ru/docs/Glossary/Serialization): то есть их можно преобразовать в формат, пригодный для передачи по сети или для хранения. Кроме того, Astro сериализует не все типы сериализуемых структур данных. Поэтому на то, что можно передать серверному островку в качестве пропсов, есть ограничения. + +В частности, компонентам с директивой `server:defer` нельзя передавать функции, так как они не сериализуются. Объекты с циклическими ссылками также не сериализуемы. + +Поддерживаются следующие типы пропсов: +простой объект, `number`, `string`, `Array`, `Map`, `Set`, `RegExp`, `Date`, `BigInt`, `URL`, `Uint8Array`, `Uint16Array`, `Uint32Array` и `Infinity` + +## Резервный контент серверного островка + +Используя атрибут `server:defer` на компоненте для откладывания его рендеринга, вы можете «подставить» контент загрузки по умолчанию через встроенный именованный слот `"fallback"`. + +Резервный контент отрендерится вместе с остальной страницей при её первоначальной загрузке и будет заменён содержимым компонента, когда оно станет доступно. + +Чтобы добавить резервный контент, укажите `slot="fallback"` на дочернем элементе (другом компоненте или HTML-элементе), переданном компоненту серверного островка: + +```astro +--- +import Avatar from '../components/Avatar.astro'; +import GenericAvatar from '../components/GenericAvatar.astro'; +--- + + + +``` + +Резервным контентом могут быть, например: + +- Обобщённый аватар вместо аватара пользователя. +- UI-заглушки, например собственные сообщения. +- Индикаторы загрузки, например спиннеры. + +## Как это работает + +Реализация серверных островков происходит в основном на этапе сборки, когда содержимое компонента подменяется небольшим скриптом. + +Каждый островок с директивой `server:defer` выделяется в собственный специальный маршрут, который этот скрипт запрашивает во время выполнения. При сборке сайта Astro пропустит компонент и вставит на его место скрипт, а также контент, помеченный `slot="fallback"`. + +Когда страница загрузится в браузере, эти компоненты будут запрошены со специального эндпоинта, который отрендерит их и вернёт HTML. Благодаря этому пользователи мгновенно увидят самые важные части страницы. Резервный контент будет виден непродолжительное время, пока не загрузятся динамические островки. + +Каждый островок загружается независимо от остальных. Это значит, что более медленный островок не задержит появление остального персонализированного контента. + +Этот паттерн рендеринга создавался переносимым. Он не зависит от какой-либо серверной инфраструктуры, поэтому будет работать с любым хостингом — от сервера Node.js в Docker-контейнере до бессерверного провайдера на ваш выбор. + +## Кеширование + +Данные для серверных островков запрашиваются через `GET`-запрос, а пропсы передаются в виде зашифрованной строки в query-параметрах URL. Это позволяет кешировать данные с помощью [HTTP-заголовка `Cache-Control`](https://developer.mozilla.org/ru/docs/Web/HTTP/Reference/Headers/Cache-Control), используя его стандартные директивы. + +Однако [браузер ограничивает длину URL максимумом в 2048 байт](https://chromium.googlesource.com/chromium/src/+/master/docs/security/url_display_guidelines/url_display_guidelines.md#url-length) из практических соображений и во избежание проблем типа «отказ в обслуживании». Если из-за строки запроса URL превышает этот лимит, Astro вместо этого отправит `POST`-запрос со всеми пропсами в теле. + +`POST`-запросы не кешируются браузерами, поскольку используются для отправки данных и могли бы привести к проблемам с целостностью данных или безопасностью. Поэтому существующая логика кеширования в вашем проекте перестанет работать. По возможности передавайте серверным островкам только необходимые пропсы и не отправляйте целые объекты данных и массивы, чтобы строка запроса оставалась небольшой. + +## Доступ к URL страницы в серверном островке + +В большинстве случаев компонент серверного островка может получить информацию о странице, которая его рендерит, через [передачу пропсов](/ru/basics/astro-components/#пропсы-компонента), как в обычных компонентах. + +Однако серверные островки выполняются в собственном изолированном контексте вне запроса страницы. `Astro.url` и `Astro.request.url` в компоненте серверного островка возвращают URL вида `/_server-islands/Avatar`, а не URL текущей страницы в браузере. Кроме того, при предварительном рендеринге страницы у вас не будет доступа, например, к query-параметрам, чтобы передать их как пропсы. + +Чтобы получить информацию из URL страницы, проверьте заголовок [Referer](https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Referer) — он содержит адрес страницы, загружающей островок в браузере: + +```astro +--- +const referer = Astro.request.headers.get("Referer"); + +if (!referer) { + throw new Error("Referer header is missing"); +} + +const url = new URL(referer); +const productId = url.searchParams.get("product"); +--- +``` + +## Повторное использование ключа шифрования + +Astro использует [криптографию](https://developer.mozilla.org/ru/docs/Glossary/Cryptography) для шифрования пропсов, передаваемых серверным островкам, защищая чувствительные данные от случайного раскрытия. Шифрование опирается на новый случайный ключ, который генерируется при каждой сборке и встраивается в серверный бандл. + +Большинство хостингов автоматически позаботятся о синхронизации фронтенда и бэкенда. Однако постоянный ключ шифрования может понадобиться, если вы используете скользящие развёртывания (rolling deployments), мультирегиональный хостинг или CDN, кеширующий страницы с серверными островками. + +В окружениях со скользящими развёртываниями (например, Kubernetes), где фронтенд-ресурсы (шифрующие пропсы) и бэкенд-функции (расшифровывающие их) могут временно использовать разные ключи, а также когда CDN всё ещё отдаёт страницы, собранные со старым ключом, зашифрованные пропсы серверного островка расшифровать не удастся. + +В таких ситуациях сгенерируйте с помощью Astro CLI многоразовый закодированный ключ шифрования и задайте его переменной окружения в сборочном окружении: + +```shell +astro create-key +``` + +Используйте полученное значение для настройки переменной окружения `ASTRO_KEY` (например, в файле `.env`) и добавьте её в настройки сборки CI/CD или хостинга. Так в собираемом бандле всегда будет использоваться один и тот же ключ, а шифрование и расшифровка останутся синхронизированными. diff --git a/src/data/contributors.json b/src/data/contributors.json index 098bfa9fe7858..c7cca67cfcd96 100644 --- a/src/data/contributors.json +++ b/src/data/contributors.json @@ -123,25 +123,25 @@ "id": 29927270, "login": "Chrissdroid" }, - { - "id": 45708948, - "login": "kecrily" - }, { "id": 70939128, "login": "jp-knj" }, { - "id": 11063910, - "login": "Fryuni" + "id": 45708948, + "login": "kecrily" + }, + { + "id": 3019731, + "login": "Princesseuh" }, { "id": 13069, "login": "kyosuke" }, { - "id": 3019731, - "login": "Princesseuh" + "id": 11063910, + "login": "Fryuni" }, { "id": 10626596, @@ -156,13 +156,17 @@ "login": "HiDeoo" }, { - "id": 31162600, - "login": "agustinmulet" + "id": 59626670, + "login": "angelmarfil" }, { "id": 188426, "login": "jonathantneal" }, + { + "id": 31162600, + "login": "agustinmulet" + }, { "id": 88548999, "login": "at-the-vr" @@ -175,6 +179,10 @@ "id": 11240579, "login": "Genteure" }, + { + "id": 326143391, + "login": "mixto" + }, { "id": 25723446, "login": "casungo" @@ -187,14 +195,14 @@ "id": 271088549, "login": "yan-thomas" }, - { - "id": 34116392, - "login": "bluwy" - }, { "id": 12275019, "login": "Egpereira" }, + { + "id": 34116392, + "login": "bluwy" + }, { "id": 7118177, "login": "natemoo-re" @@ -203,25 +211,25 @@ "id": 11237366, "login": "Hanawa02" }, - { - "id": 28299972, - "login": "aFuzzyBear" - }, { "id": 64152685, "login": "Yusaku01" }, { - "id": 113022468, - "login": "fkatsuhiro" + "id": 28299972, + "login": "aFuzzyBear" + }, + { + "id": 81974850, + "login": "MoustaphaDev" }, { "id": 25883220, "login": "JuanPabloDiaz" }, { - "id": 81974850, - "login": "MoustaphaDev" + "id": 113022468, + "login": "fkatsuhiro" }, { "id": 7684330, @@ -231,10 +239,6 @@ "id": 49518790, "login": "pioupia" }, - { - "id": 59626670, - "login": "angelmarfil" - }, { "id": 36927158, "login": "maxchang3" @@ -263,10 +267,6 @@ "id": 103585995, "login": "bjohansebas" }, - { - "id": 16712703, - "login": "JuanM04" - }, { "id": 213306, "login": "ascorbic" @@ -275,6 +275,10 @@ "id": 669326, "login": "mrienstra" }, + { + "id": 16712703, + "login": "JuanM04" + }, { "id": 20650404, "login": "VoxelMC" @@ -287,29 +291,29 @@ "id": 47194884, "login": "oscarxdev" }, - { - "id": 11986753, - "login": "kimulaco" - }, { "id": 74939915, "login": "glopzel" }, + { + "id": 11986753, + "login": "kimulaco" + }, { "id": 11766500, "login": "antonyfaris" }, { - "id": 3756185, - "login": "afucher" + "id": 150704902, + "login": "randomguy-2650" }, { "id": 1425259, "login": "shuuji3" }, { - "id": 150704902, - "login": "randomguy-2650" + "id": 3756185, + "login": "afucher" }, { "id": 11061182, @@ -335,29 +339,29 @@ "id": 101558384, "login": "luoingly" }, - { - "id": 12174733, - "login": "itskitto" - }, { "id": 45965090, "login": "alexanderniebuhr" }, { - "id": 69170106, - "login": "lilnasy" + "id": 12174733, + "login": "itskitto" }, { - "id": 42184309, - "login": "asgoshawk" + "id": 58832428, + "login": "erbierc" }, { "id": 77632836, "login": "clemenzi" }, { - "id": 58832428, - "login": "erbierc" + "id": 42184309, + "login": "asgoshawk" + }, + { + "id": 69170106, + "login": "lilnasy" }, { "id": 110926935, @@ -412,41 +416,45 @@ "login": "teinett" }, { - "id": 114303361, - "login": "ryuapp" - }, - { - "id": 44868357, - "login": "codersjj" - }, - { - "id": 175721795, - "login": "rafaelcoelhox" + "id": 990216, + "login": "leoj3n" }, { "id": 141838499, "login": "cristhian-fs" }, - { - "id": 990216, - "login": "leoj3n" - }, { "id": 19967622, "login": "BryceRussell" }, + { + "id": 66965600, + "login": "louisescher" + }, { "id": 115520730, "login": "OliverSpeir" }, { - "id": 66965600, - "login": "louisescher" + "id": 114303361, + "login": "ryuapp" + }, + { + "id": 44868357, + "login": "codersjj" + }, + { + "id": 175721795, + "login": "rafaelcoelhox" }, { "id": 89195061, "login": "anaxite" }, + { + "id": 703248, + "login": "agriffard" + }, { "id": 7007253, "login": "mottox2" @@ -467,21 +475,21 @@ "id": 44106297, "login": "hkbertoson" }, - { - "id": 57179957, - "login": "yeonjulee1005" - }, { "id": 33442948, "login": "apatel369" }, { - "id": 703248, - "login": "agriffard" + "id": 57179957, + "login": "yeonjulee1005" }, { - "id": 92775570, - "login": "HashCookie" + "id": 25793187, + "login": "arisa-fukuzaki" + }, + { + "id": 243528248, + "login": "bulebrainbrand" }, { "id": 42414986, @@ -508,44 +516,44 @@ "login": "crutchcorn" }, { - "id": 22087604, - "login": "clearlyTHUYDOAN" + "id": 46154381, + "login": "magnum-zx" }, { "id": 74556046, "login": "fhiromasa" }, { - "id": 46154381, - "login": "magnum-zx" - }, - { - "id": 58094796, - "login": "Jothsa" + "id": 22087604, + "login": "clearlyTHUYDOAN" }, { - "id": 139560930, - "login": "Panelinio" + "id": 66757451, + "login": "avilyre" }, { - "id": 67210629, - "login": "jdwilkin4" + "id": 14830190, + "login": "caioferrarezi" }, { "id": 92606530, "login": "Elib27" }, { - "id": 25793187, - "login": "arisa-fukuzaki" + "id": 67210629, + "login": "jdwilkin4" }, { - "id": 66757451, - "login": "avilyre" + "id": 58094796, + "login": "Jothsa" }, { - "id": 14830190, - "login": "caioferrarezi" + "id": 139560930, + "login": "Panelinio" + }, + { + "id": 92775570, + "login": "HashCookie" }, { "id": 37566594, @@ -575,6 +583,18 @@ "id": 2342458, "login": "palmiak" }, + { + "id": 17983739, + "login": "imbant" + }, + { + "id": 190183925, + "login": "ankddev" + }, + { + "id": 16765690, + "login": "bandantonio" + }, { "id": 34824645, "login": "staticWagomU" @@ -583,13 +603,21 @@ "id": 65327974, "login": "manchan4869" }, + { + "id": 100040151, + "login": "BassamXYZ" + }, + { + "id": 37726261, + "login": "wtchnm" + }, { "id": 51922004, "login": "Maxframe" }, { - "id": 17983739, - "login": "imbant" + "id": 10464497, + "login": "garysassano" }, { "id": 1110792, @@ -603,6 +631,10 @@ "id": 78506637, "login": "gacek1123" }, + { + "id": 12196684, + "login": "yamotech" + }, { "id": 5608239, "login": "chalkygames123" @@ -611,42 +643,6 @@ "id": 168486811, "login": "sigma7863" }, - { - "id": 1091472, - "login": "chenxsan" - }, - { - "id": 19380403, - "login": "ralacerda" - }, - { - "id": 63650415, - "login": "RafidMuhymin" - }, - { - "id": 100040151, - "login": "BassamXYZ" - }, - { - "id": 10464497, - "login": "garysassano" - }, - { - "id": 37726261, - "login": "wtchnm" - }, - { - "id": 16765690, - "login": "bandantonio" - }, - { - "id": 3282350, - "login": "olets" - }, - { - "id": 190183925, - "login": "ankddev" - }, { "id": 49127376, "login": "IgorKowalczyk" @@ -659,6 +655,10 @@ "id": 79452224, "login": "Kenzo-Wada" }, + { + "id": 3282350, + "login": "olets" + }, { "id": 15347255, "login": "lorenzolewis" @@ -676,44 +676,68 @@ "login": "tinymachine" }, { - "id": 26341224, - "login": "mogeko" + "id": 1091472, + "login": "chenxsan" }, { - "id": 73933669, - "login": "nermalcat69" + "id": 19380403, + "login": "ralacerda" }, { - "id": 77222233, - "login": "NightFeather0615" + "id": 63650415, + "login": "RafidMuhymin" + }, + { + "id": 58347116, + "login": "Pukimaa" }, { "id": 81039882, "login": "Njong392" }, { - "id": 58347116, - "login": "Pukimaa" + "id": 26341224, + "login": "mogeko" }, { - "id": 3241026, - "login": "mantaroh" + "id": 77222233, + "login": "NightFeather0615" }, { - "id": 51779800, - "login": "ktym4a" + "id": 73933669, + "login": "nermalcat69" }, { - "id": 75212478, - "login": "dorasans" + "id": 111561, + "login": "tordans" + }, + { + "id": 16623919, + "login": "shupianx" + }, + { + "id": 148322070, + "login": "divyeshb13" }, { "id": 49699333, "login": "dependabot[bot]" }, { - "id": 16623919, - "login": "shupianx" + "id": 75212478, + "login": "dorasans" + }, + { + "id": 51779800, + "login": "ktym4a" + }, + { + "id": 9054858, + "login": "lostra01" + }, + { + "id": 3241026, + "login": "mantaroh" }, { "id": 35761035, @@ -739,33 +763,25 @@ "id": 362261, "login": "chriswburke" }, - { - "id": 13678847, - "login": "mitian233" - }, - { - "id": 9054858, - "login": "lostra01" - }, - { - "id": 148322070, - "login": "divyeshb13" - }, { "id": 30044630, "login": "AkashRajpurohit" }, { - "id": 87678248, - "login": "David-Large" + "id": 86967271, + "login": "coding-in-public" }, { "id": 81493003, "login": "danielcuque" }, { - "id": 86967271, - "login": "coding-in-public" + "id": 29717818, + "login": "danielmlr" + }, + { + "id": 87678248, + "login": "David-Large" }, { "id": 77161808, @@ -828,8 +844,8 @@ "login": "simonswiss" }, { - "id": 111561, - "login": "tordans" + "id": 119287439, + "login": "silvecor" }, { "id": 69125074, @@ -843,10 +859,6 @@ "id": 18255987, "login": "coderfee" }, - { - "id": 243528248, - "login": "bulebrainbrand" - }, { "id": 87353286, "login": "vedxp" @@ -884,12 +896,8 @@ "login": "BlackdestinyXX" }, { - "id": 17054057, - "login": "thepassle" - }, - { - "id": 119287439, - "login": "silvecor" + "id": 78465651, + "login": "newtoallofthis123" }, { "id": 53004404, @@ -943,6 +951,10 @@ "id": 162127610, "login": "xingwangzhe" }, + { + "id": 13678847, + "login": "mitian233" + }, { "id": 20949060, "login": "bengeois" @@ -979,10 +991,6 @@ "id": 92310163, "login": "davidumoru" }, - { - "id": 29717818, - "login": "danielmlr" - }, { "id": 7117993, "login": "cravend" @@ -1007,14 +1015,14 @@ "id": 867257, "login": "Because789" }, - { - "id": 62723180, - "login": "ahmed-n-abdeltwab" - }, { "id": 26602940, "login": "0xflotus" }, + { + "id": 62723180, + "login": "ahmed-n-abdeltwab" + }, { "id": 66678395, "login": "DevRohit06" @@ -1023,6 +1031,10 @@ "id": 7355835, "login": "radenpioneer" }, + { + "id": 17054057, + "login": "thepassle" + }, { "id": 28926450, "login": "nicdun" @@ -1044,48 +1056,40 @@ "login": "mhdcodes" }, { - "id": 36402166, - "login": "debiru" - }, - { - "id": 91272406, - "login": "baevm" - }, - { - "id": 90469240, - "login": "MartinFerret" + "id": 24359130, + "login": "swift502" }, { - "id": 61264139, - "login": "kannansuresh" + "id": 1029022, + "login": "sunapi386" }, { - "id": 8854718, - "login": "kanadgupta" + "id": 34408108, + "login": "Je12emy" }, { "id": 18559798, "login": "jdbruxelles" }, { - "id": 34408108, - "login": "Je12emy" + "id": 36402166, + "login": "debiru" }, { - "id": 1029022, - "login": "sunapi386" + "id": 91272406, + "login": "baevm" }, { - "id": 24359130, - "login": "swift502" + "id": 90469240, + "login": "MartinFerret" }, { - "id": 78465651, - "login": "newtoallofthis123" + "id": 8854718, + "login": "kanadgupta" }, { - "id": 299173, - "login": "lukemcdonald" + "id": 61264139, + "login": "kannansuresh" }, { "id": 50741, @@ -1095,6 +1099,10 @@ "id": 18482346, "login": "Rolanddoda" }, + { + "id": 16581093, + "login": "vitoriapena" + }, { "id": 5978625, "login": "rolginroman" @@ -1148,13 +1156,17 @@ "login": "XinChou16" }, { - "id": 193320883, - "login": "MareStare" + "id": 3964466, + "login": "aaronlamz" }, { "id": 129096443, "login": "MaxTheTurtle0" }, + { + "id": 137305666, + "login": "1t1sCooL" + }, { "id": 37586974, "login": "mingXta" @@ -1216,13 +1228,17 @@ "login": "RobertAKARobin" }, { - "id": 29157111, - "login": "lhz960904" + "id": 91918142, + "login": "vivitt" }, { "id": 31998110, "login": "lstephensca" }, + { + "id": 287639636, + "login": "mrchatam" + }, { "id": 190968675, "login": "my-astro" @@ -1284,12 +1300,8 @@ "login": "isyuah" }, { - "id": 16581093, - "login": "vitoriapena" - }, - { - "id": 91918142, - "login": "vivitt" + "id": 58039870, + "login": "wahidrizka" }, { "id": 82119938, @@ -1352,8 +1364,8 @@ "login": "kikonavarro" }, { - "id": 58281730, - "login": "CheukTsai" + "id": 29157111, + "login": "lhz960904" }, { "id": 1045274, @@ -1420,8 +1432,8 @@ "login": "ogabrielp" }, { - "id": 3964466, - "login": "aaronlamz" + "id": 830515, + "login": "gaeulbyul" }, { "id": 51356696, @@ -1488,12 +1500,12 @@ "login": "calebeby" }, { - "id": 88730883, - "login": "Jhon-H" + "id": 58281730, + "login": "CheukTsai" }, { - "id": 29053796, - "login": "jcha0713" + "id": 1514154, + "login": "neotherapper" }, { "id": 62016742, @@ -1556,12 +1568,12 @@ "login": "Trombach" }, { - "id": 830515, - "login": "gaeulbyul" + "id": 299173, + "login": "lukemcdonald" }, { - "id": 1514154, - "login": "neotherapper" + "id": 193320883, + "login": "MareStare" }, { "id": 68320771, @@ -1605,7 +1617,7 @@ }, { "id": 140852203, - "login": "vandorsx" + "login": "ilyisx" }, { "id": 7950094, @@ -1624,23 +1636,19 @@ "login": "jazzypants1989" }, { - "id": 3164034, - "login": "notjb" - }, - { - "id": 52315048, - "login": "3w36zj6" + "id": 88730883, + "login": "Jhon-H" }, { - "id": 1002694, - "login": "aaronkai" + "id": 29053796, + "login": "jcha0713" }, { - "id": 6192554, - "login": "arafays" + "id": 3164034, + "login": "notjb" }, { - "id": 61620817, - "login": "alfawal" + "id": 52315048, + "login": "3w36zj6" } ] diff --git a/src/data/logos.ts b/src/data/logos.ts index e178b569b912b..be50539c7e9e6 100644 --- a/src/data/logos.ts +++ b/src/data/logos.ts @@ -59,6 +59,7 @@ export const logos = LogoCheck({ gitlab: { file: 'gitlab.svg' }, 'google-cloud': { file: 'google-cloud.svg', padding: '.1875em' }, gridsome: { file: 'gridsome.svg', padding: '.15em' }, + hackmd: { file: 'hackmd.svg', padding: '0' }, hashnode: { file: 'hashnode.png', padding: '.1875em' }, heroku: { file: 'heroku.svg', padding: '.25em' }, hostinger: { file: 'hostinger.svg', padding: '.2em' }, @@ -66,6 +67,7 @@ export const logos = LogoCheck({ hygraph: { file: 'hygraph.svg', padding: '.1em .125em .1em .1em' }, image: { file: 'astro-image.svg', padding: '.1875em' }, imagekit: { file: 'imagekit.svg', padding: '.15em' }, + ishosting: { file: 'ishosting.svg', padding: '.1625em' }, jekyll: { file: 'jekyll.png', padding: '.1em .05em 0' }, jekyllpad: { file: 'jekyllpad.svg', padding: '0.2em' }, keystatic: { file: 'keystatic.svg', padding: '0' }, diff --git a/wrangler.jsonc b/wrangler.jsonc index bf35f3ad8926f..3b24286b8d08e 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -8,6 +8,7 @@ "binding": "ASSETS", "not_found_handling": "none", }, + "previews": {}, "preview_urls": true, "workers_dev": false, } diff --git a/wrangler.lunaria.jsonc b/wrangler.lunaria.jsonc index ae5a60831786f..f55c6c0c638ad 100644 --- a/wrangler.lunaria.jsonc +++ b/wrangler.lunaria.jsonc @@ -6,4 +6,5 @@ "directory": "./dist/lunaria", "html_handling": "auto-trailing-slash", }, + "previews": {}, }