diff --git a/.editorconfig b/.editorconfig index dc0858c028e..e8844ac3951 100755 --- a/.editorconfig +++ b/.editorconfig @@ -4,4 +4,4 @@ root = true end_of_line = lf indent_style = space indent_size = 4 -charset = utf-8 \ No newline at end of file +charset = utf-8 diff --git a/.github/scripts/build-preview-urls-comment.js b/.github/scripts/build-preview-urls-comment.js index 86864136aa6..03a903e8d6a 100644 --- a/.github/scripts/build-preview-urls-comment.js +++ b/.github/scripts/build-preview-urls-comment.js @@ -82,15 +82,17 @@ export const buildPreviewURLComment = (prNumber) => { - [High Contrast Mode | Medium | LTR](${hcmUrl})`); // Documentation and Storybook URLs - const docsUrl = `${baseUrl}/${prHash}/docs/`; - const storybookUrl = `${baseUrl}/${prHash}/docs/storybook/`; + const docsFirstGenUrl = `${baseUrl}/${prHash}/docs/first-gen-docs/`; + const storybookFirstGenUrl = `${baseUrl}/${prHash}/docs/first-gen-storybook/`; + const storybookSecondGenUrl = `${baseUrl}/${prHash}/docs/second-gen-storybook/`; - let comment = `## 📚 Branch Preview + let comment = `## 📚 Branch Preview Links -- [Documentation Site](${docsUrl}) -- [Storybook](${storybookUrl}) +- [Documentation Site (first-gen)](${docsFirstGenUrl}) +- [Storybook (first-gen)](${storybookFirstGenUrl}) +- [Storybook (second-gen)](${storybookSecondGenUrl}) -

🔍 Visual Regression Test Results

+

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs: diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index b470ba9af6e..b432c7d957a 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -1,32 +1,32 @@ name: Coveralls Code Coverage on: - push: - branches: - - main - pull_request: - branches: - - main + push: + branches: + - main + pull_request: + branches: + - main jobs: - code-coverage-report: - name: Generate and upload coverage report - runs-on: ubuntu-latest - steps: - - name: Checkout PR branch - uses: actions/checkout@v4 + code-coverage-report: + name: Generate and upload coverage report + runs-on: ubuntu-latest + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 - - name: Setup Job and Install Dependencies - uses: ./.github/actions/setup-job + - name: Setup Job and Install Dependencies + uses: ./.github/actions/setup-job - - name: Install Playwright - run: cd 1st-gen && yarn playwright install --with-deps + - name: Install Playwright + run: cd 1st-gen && yarn playwright install --with-deps - - name: Run unit tests with coverage - run: cd 1st-gen && yarn test:ci --config web-test-runner.config.ci-chromium.js --group coveralls-ci --coverage - continue-on-error: true + - name: Run unit tests with coverage + run: cd 1st-gen && yarn test:ci --config web-test-runner.config.ci-chromium.js --group coveralls-ci --coverage + continue-on-error: true - - name: Upload coverage to Coveralls - uses: coverallsapp/github-action@v2 - with: - allow-empty: true + - name: Upload coverage to Coveralls + uses: coverallsapp/github-action@v2 + with: + allow-empty: true diff --git a/.github/workflows/preview-docs.yml b/.github/workflows/preview-docs.yml index 772fc411e93..eb16abba726 100644 --- a/.github/workflows/preview-docs.yml +++ b/.github/workflows/preview-docs.yml @@ -1,146 +1,246 @@ name: Preview Documentation (Azure Blob Storage) on: - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - "**" + pull_request: + types: [opened, synchronize, reopened, closed] permissions: - contents: read - pull-requests: write + contents: read + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true env: - AZCOPY_AUTO_LOGIN_TYPE: SPN - AZCOPY_SPA_APPLICATION_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZCOPY_SPA_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZCOPY_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZCOPY_AUTO_LOGIN_TYPE: SPN + AZCOPY_SPA_APPLICATION_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZCOPY_SPA_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZCOPY_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} jobs: - build_and_deploy_job: - if: github.event_name == 'pull_request' && github.event.action != 'closed' - runs-on: ubuntu-latest - name: Build and Deploy Job - outputs: - doc_url: ${{ steps.deploy.outputs.docs_url }} - steps: - ## --- SETUP --- ## - - name: Check out code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Use Node LTS version - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: yarn - - - name: Enable Corepack - run: corepack enable - - - name: Generate PR hash - id: pr_hash - run: | - # Use just PR number so each commit overwrites the previous deployment - pr_hash="pr-${{ github.event.pull_request.number }}" - echo "hash=${pr_hash}" >> "$GITHUB_OUTPUT" - echo "Generated PR hash: ${pr_hash}" - - ## --- YARN CACHE --- ## - - name: Check for cached dependencies - continue-on-error: true - id: cache-dependencies - uses: actions/cache@v4 - with: - path: | - .cache/yarn - node_modules - key: ubuntu-latest-node20-${{ hashFiles('yarn.lock', 'package.json') }} - - ## --- INSTALL & BUILD --- ## - - name: Install dependencies - shell: bash - run: yarn install --immutable - - - name: Build 1st-gen - run: yarn workspace @spectrum-web-components/1st-gen build - - - name: Build 2nd-gen core - run: yarn workspace @spectrum-web-components/core build - - - name: Build 2nd-gen swc - run: yarn workspace @adobe/swc build - - - name: Generate Custom Elements Manifest - run: yarn workspace @spectrum-web-components/1st-gen docs:analyze - - - name: Move CEM to Storybook directory - run: cp 1st-gen/projects/documentation/custom-elements.json 1st-gen/storybook/ - - - name: Build documentation with path prefix - env: - SWC_DIR: ${{ steps.pr_hash.outputs.hash }}/docs - run: | - yarn workspace @spectrum-web-components/1st-gen docs:build - - - name: Build Storybook - run: yarn workspace @spectrum-web-components/1st-gen storybook:build - - ## --- DEPLOY TO AZURE BLOB STORAGE --- ## - - name: Setup AzCopy - uses: ./.github/actions/setup-azcopy - - - name: Deploy to Azure Blob Storage - id: deploy - env: - PR_HASH: ${{ steps.pr_hash.outputs.hash }} - run: | - # Upload documentation - echo "Uploading documentation to ${PR_HASH}/docs/" - azcopy copy "1st-gen/projects/documentation/dist/*" \ - "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/docs/" \ - --recursive \ - --from-to LocalBlob - - # Upload Storybook - echo "Uploading Storybook to ${PR_HASH}/docs/storybook/" - azcopy copy "1st-gen/storybook-static/*" \ - "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/docs/storybook/" \ - --recursive \ - --from-to LocalBlob - - # Set output URLs - docs_url="https://swcpreviews.z13.web.core.windows.net/${PR_HASH}/docs/" - storybook_url="https://swcpreviews.z13.web.core.windows.net/${PR_HASH}/docs/storybook/" - echo "docs_url=${docs_url}" >> "$GITHUB_OUTPUT" - echo "storybook_url=${storybook_url}" >> "$GITHUB_OUTPUT" - echo "Deployed to: ${docs_url}" - - close_pull_request_job: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Clean up PR deployment - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - name: Generate PR hash - id: pr_hash - run: | - # Create the same hash as in the deploy job - pr_hash="pr-${{ github.event.pull_request.number }}" - echo "hash=${pr_hash}" >> "$GITHUB_OUTPUT" - - - name: Setup AzCopy - uses: ./.github/actions/setup-azcopy - - - name: Clean up PR deployment - env: - PR_HASH: ${{ steps.pr_hash.outputs.hash }} - run: | - echo "Cleaning up deployment: ${PR_HASH}/" - azcopy remove "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/" \ - --recursive || echo "Cleanup completed (some files may not exist)" - echo "Cleanup completed for PR deployment: ${PR_HASH}/" + build_and_deploy_job: + if: github.event_name == 'pull_request' && github.event.action != 'closed' + runs-on: ubuntu-latest + name: Build and Deploy Job + timeout-minutes: 30 + outputs: + first_gen_docs_url: ${{ steps.deploy.outputs.first_gen_docs_url }} + first_gen_storybook_url: ${{ steps.deploy.outputs.first_gen_storybook_url }} + second_gen_storybook_url: ${{ steps.deploy.outputs.second_gen_storybook_url }} + steps: + ## --- SETUP --- ## + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Use Node LTS version + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + + - name: Enable Corepack + run: corepack enable + + - name: Generate PR hash + id: pr_hash + run: | + # Use just PR number so each commit overwrites the previous deployment + pr_hash="pr-${{ github.event.pull_request.number }}" + echo "hash=${pr_hash}" >> "$GITHUB_OUTPUT" + echo "Generated PR hash: ${pr_hash}" + + ## --- YARN CACHE --- ## + - name: Check for cached dependencies + continue-on-error: true + id: cache-dependencies + uses: actions/cache@v4 + with: + path: | + .cache/yarn + node_modules + key: ubuntu-latest-node20-${{ hashFiles('yarn.lock', 'package.json') }} + + ## --- INSTALL & BUILD --- ## + - name: Install dependencies + shell: bash + run: yarn install --immutable + + - name: Build all generations + run: yarn build + + - name: Generate Custom Elements Manifest + run: yarn workspace @spectrum-web-components/1st-gen docs:analyze + + - name: Move CEM to Storybook directory + run: cp 1st-gen/projects/documentation/custom-elements.json 1st-gen/storybook/ + + - name: Build documentation with path prefix + env: + SWC_DIR: ${{ steps.pr_hash.outputs.hash }}/docs/first-gen-docs + run: | + yarn workspace @spectrum-web-components/1st-gen docs:build + + - name: Build first-gen Storybook + run: yarn workspace @spectrum-web-components/1st-gen storybook:build + + - name: Build second-gen Storybook + run: yarn workspace @spectrum-web-components/2nd-gen storybook:build + + ## --- DEPLOY TO AZURE BLOB STORAGE --- ## + - name: Setup AzCopy + uses: ./.github/actions/setup-azcopy + + - name: Deploy first-gen documentation to Azure Blob Storage + env: + PR_HASH: ${{ steps.pr_hash.outputs.hash }} + run: | + echo "Uploading first-gen documentation to ${PR_HASH}/docs/first-gen-docs/" + azcopy copy "1st-gen/projects/documentation/dist/*" \ + "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/docs/first-gen-docs/" \ + --recursive \ + --from-to LocalBlob + echo "✅ First-gen documentation uploaded successfully" + + - name: Deploy first-gen Storybook to Azure Blob Storage + env: + PR_HASH: ${{ steps.pr_hash.outputs.hash }} + run: | + echo "Uploading first-gen Storybook to ${PR_HASH}/docs/first-gen-storybook/" + azcopy copy "1st-gen/projects/documentation/dist/storybook/*" \ + "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/docs/first-gen-storybook/" \ + --recursive \ + --from-to LocalBlob + echo "✅ First-gen Storybook uploaded successfully" + + - name: Deploy second-gen Storybook to Azure Blob Storage + env: + PR_HASH: ${{ steps.pr_hash.outputs.hash }} + run: | + echo "Uploading second-gen Storybook to ${PR_HASH}/docs/second-gen-storybook/" + azcopy copy "2nd-gen/packages/swc/storybook-static/*" \ + "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/docs/second-gen-storybook/" \ + --recursive \ + --from-to LocalBlob + echo "✅ Second-gen Storybook uploaded successfully" + + - name: Set deployment URLs + id: deploy + env: + PR_HASH: ${{ steps.pr_hash.outputs.hash }} + run: | + first_gen_docs_url="https://swcpreviews.z13.web.core.windows.net/${PR_HASH}/docs/first-gen-docs/" + first_gen_storybook_url="https://swcpreviews.z13.web.core.windows.net/${PR_HASH}/docs/first-gen-storybook/" + second_gen_storybook_url="https://swcpreviews.z13.web.core.windows.net/${PR_HASH}/docs/second-gen-storybook/" + + echo "first_gen_docs_url=${first_gen_docs_url}" >> "$GITHUB_OUTPUT" + echo "first_gen_storybook_url=${first_gen_storybook_url}" >> "$GITHUB_OUTPUT" + echo "second_gen_storybook_url=${second_gen_storybook_url}" >> "$GITHUB_OUTPUT" + + echo "📦 All deployments completed:" + echo " - Documentation: ${first_gen_docs_url}" + echo " - First-gen Storybook: ${first_gen_storybook_url}" + echo " - Second-gen Storybook: ${second_gen_storybook_url}" + + - name: Post Previews Comment + uses: actions/github-script@v7 + with: + script: | + const { buildPreviewURLComment } = await import('${{ github.workspace }}/.github/scripts/build-preview-urls-comment.js'); + const { commentOrUpdate } = await import('${{ github.workspace }}/.github/scripts/comment-or-update.js'); + + const prNumber = context.payload.pull_request.number; + const body = buildPreviewURLComment(prNumber); + + console.log(`Posting comment to PR #${prNumber}`); + commentOrUpdate(github, context, '## 📚 Branch Preview Links', body); + + smoke_tests: + if: github.event_name == 'pull_request' && github.event.action != 'closed' + needs: build_and_deploy_job + runs-on: ubuntu-latest + name: Smoke Tests + timeout-minutes: 15 + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + + - name: Wait for deployment + env: + DOC_URL: ${{ needs.build_and_deploy_job.outputs.first_gen_docs_url }} + run: | + echo "Waiting for deployment to be available at: ${DOC_URL}" + + # Wait up to 10 minutes for the deployment to be available + max_attempts=60 + attempt=1 + + while [ $attempt -le $max_attempts ]; do + echo "Attempt $attempt/$max_attempts: Checking if site is available..." + + if curl -f -s --max-time 10 "${DOC_URL}" > /dev/null; then + echo "✅ ${DOC_URL} is now available!" + break + else + echo "❌ Site not ready yet, waiting 10 seconds..." + sleep 10 + attempt=$((attempt + 1)) + fi + done + + if [ $attempt -gt $max_attempts ]; then + echo "❌ Timeout: Site was not available after 10 minutes" + exit 1 + fi + + - name: Setup Job and Install Dependencies + uses: ./.github/actions/setup-job + + - name: Install Playwright Browsers + run: cd 1st-gen && yarn playwright install --with-deps + + - name: Run Playwright tests + run: cd 1st-gen && yarn playwright test projects/documentation/e2e/published.spec.ts + env: + DOC_PREVIEW_URL: ${{ needs.build_and_deploy_job.outputs.first_gen_docs_url }} + SWC_DIR: pr-${{ github.event.pull_request.number }} + NODE_ENV: CI + + - name: Upload Playwright Report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 + + close_pull_request_job: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Clean up PR deployment + timeout-minutes: 5 + steps: + - name: Check out code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + - name: Generate PR hash + id: pr_hash + run: | + # Create the same hash as in the deploy job + pr_hash="pr-${{ github.event.pull_request.number }}" + echo "hash=${pr_hash}" >> "$GITHUB_OUTPUT" + + - name: Setup AzCopy + uses: ./.github/actions/setup-azcopy + + - name: Clean up PR deployment + env: + PR_HASH: ${{ steps.pr_hash.outputs.hash }} + run: | + echo "Cleaning up deployment: ${PR_HASH}/" + azcopy remove "https://swcpreviews.blob.core.windows.net/\$web/${PR_HASH}/" \ + --recursive || echo "Cleanup completed (some files may not exist)" + echo "Cleanup completed for PR deployment: ${PR_HASH}/" diff --git a/.github/workflows/urls-smoke-test.yml b/.github/workflows/urls-smoke-test.yml deleted file mode 100644 index 6f6ab56bedf..00000000000 --- a/.github/workflows/urls-smoke-test.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: Review URLs and Smoke Tests - -on: - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - "**" - -permissions: - contents: read - pull-requests: write - -jobs: - wait-for-deployment: - name: Wait for deployment to complete - runs-on: ubuntu-latest - outputs: - doc_url: ${{ steps.extract_doc_url.outputs.DOC_URL }} - swc_dir: ${{ steps.pr_hash.outputs.hash }} - steps: - - name: Checkout PR branch - uses: actions/checkout@v4 - - - name: Setup Job and Install Dependencies - uses: ./.github/actions/setup-job - - - name: Generate PR hash - id: pr_hash - run: | - pr_hash="pr-${{ github.event.pull_request.number }}" - echo "hash=${pr_hash}" >> "$GITHUB_OUTPUT" - echo "Generated PR hash: ${pr_hash}" - - - name: Extract doc preview url - run: echo "DOC_URL=https://swcpreviews.z13.web.core.windows.net/${{ steps.pr_hash.outputs.hash }}/docs/" >> "$GITHUB_OUTPUT" - id: extract_doc_url - - - name: Post Previews Comment - uses: actions/github-script@v7 - with: - script: | - const { buildPreviewURLComment } = await import('${{ github.workspace }}/.github/scripts/build-preview-urls-comment.js'); - const { commentOrUpdate } = await import('${{ github.workspace }}/.github/scripts/comment-or-update.js'); - - const prNumber = context.payload.pull_request.number; - const body = buildPreviewURLComment(prNumber); - - console.log(`Posting comment to PR #${prNumber}`); - commentOrUpdate(github, context, '## 📚 Branch Preview', body); - - - name: Wait for deployment - run: | - echo "Waiting for deployment to be available at: ${{ steps.extract_doc_url.outputs.DOC_URL }}" - - # Wait up to 10 minutes for the deployment to be available - max_attempts=60 - attempt=1 - - while [ $attempt -le $max_attempts ]; do - echo "Attempt $attempt/$max_attempts: Checking if site is available..." - - if curl -f -s --max-time 10 "${{ steps.extract_doc_url.outputs.DOC_URL }}" > /dev/null; then - echo "✅ Site is now available!" - break - else - echo "❌ Site not ready yet, waiting 10 seconds..." - sleep 10 - attempt=$((attempt + 1)) - fi - done - - if [ $attempt -gt $max_attempts ]; then - echo "❌ Timeout: Site was not available after 10 minutes" - exit 1 - fi - - playwright-smoke-tests: - timeout-minutes: 60 - runs-on: ubuntu-latest - needs: wait-for-deployment - steps: - - name: Checkout PR branch - uses: actions/checkout@v4 - - - name: Setup Job and Install Dependencies - uses: ./.github/actions/setup-job - - - name: Install Playwright Browsers - run: cd 1st-gen && yarn playwright install --with-deps - - - name: Run Playwright tests - run: cd 1st-gen && yarn playwright test projects/documentation/e2e/published.spec.ts - env: - DOC_PREVIEW_URL: ${{ needs.wait-for-deployment.outputs.doc_url }} - SWC_DIR: ${{ needs.wait-for-deployment.outputs.swc_dir }} - NODE_ENV: CI - - - name: Upload Playwright Report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/1st-gen/projects/documentation/e2e/published.spec.ts b/1st-gen/projects/documentation/e2e/published.spec.ts index c4f6a9d3a91..9c5921b2e48 100644 --- a/1st-gen/projects/documentation/e2e/published.spec.ts +++ b/1st-gen/projects/documentation/e2e/published.spec.ts @@ -29,11 +29,11 @@ test.describe('search and go', () => { const formattedSearchString = searchString.replace(/\s+/g, '-'); let href = category - ? `/${category}/${formattedSearchString}` - : `/${formattedSearchString}`; + ? `/${category}/${formattedSearchString}/` + : `/${formattedSearchString}/`; // add the SWC_DIR to the href - href = `${process.env.SWC_DIR ? `/${process.env.SWC_DIR}/docs` : ''}${href}`; + href = `${process.env.SWC_DIR ? `/${process.env.SWC_DIR}/docs/first-gen-docs` : ''}${href}`; const menuItem = page.locator(menuItemSelector(href)); @@ -63,8 +63,13 @@ test.describe('search and go', () => { const searchField = await page.getByRole('searchbox', { name: 'Search', }); - + await expect(searchField, 'Search field should be visible').toBeVisible( + { timeout: 10000 } + ); await searchField.focus(); + await expect(searchField, 'Search field should be focused').toBeFocused( + { timeout: 10000 } + ); await page.waitForTimeout(500); // Ensure focus is set }); diff --git a/1st-gen/projects/documentation/rollup.config.js b/1st-gen/projects/documentation/rollup.config.js index a44b2e6bfae..641037e0a24 100644 --- a/1st-gen/projects/documentation/rollup.config.js +++ b/1st-gen/projects/documentation/rollup.config.js @@ -9,17 +9,17 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -import { copy } from '@web/rollup-plugin-copy'; import { createBasicConfig } from '@open-wc/building-rollup'; -import { injectManifest } from 'rollup-plugin-workbox'; -import { minify } from 'html-minifier-terser'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import { rollupPluginHTML as html } from '@web/rollup-plugin-html'; import alias from '@rollup/plugin-alias'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; import replace from '@rollup/plugin-replace'; +import { copy } from '@web/rollup-plugin-copy'; +import { rollupPluginHTML as html } from '@web/rollup-plugin-html'; +import { minify } from 'html-minifier-terser'; import path from 'path'; +import { injectManifest } from 'rollup-plugin-workbox'; import Terser from 'terser'; const stringReplaceHtml = (source) => { @@ -237,7 +237,7 @@ export default async () => { entries: [ { find: '@swc-packages-internal', - replacement: '../../packages/', + replacement: path.resolve('../../packages'), }, { find: /^@spectrum-web-components\/core\/(.*)$/, diff --git a/1st-gen/projects/documentation/scripts/build-search-index.js b/1st-gen/projects/documentation/scripts/build-search-index.js index b81abda42eb..3e989ece92b 100644 --- a/1st-gen/projects/documentation/scripts/build-search-index.js +++ b/1st-gen/projects/documentation/scripts/build-search-index.js @@ -91,7 +91,7 @@ async function main() { name: title, url: `/${ process.env.SWC_DIR ? `${process.env.SWC_DIR}/` : '' - }components/${componentName}`, + }components/${componentName}/`, }), }); } @@ -120,7 +120,7 @@ async function main() { name: title, url: `/${ process.env.SWC_DIR ? `${process.env.SWC_DIR}/` : '' - }tools/${componentName}`, + }tools/${componentName}/`, }), }); } @@ -154,7 +154,7 @@ async function main() { name: title, url: `/${process.env.SWC_DIR ? `${process.env.SWC_DIR}/` : ''}${ guideDir !== 'content' ? `${guideDir}/` : '' - }${guideName}`, + }${guideName}/`, }), }); }