Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolving Trailing Slash Issue in Breadcrumbs Links #212

Open
shah-iq opened this issue Mar 19, 2024 · 12 comments
Open

Resolving Trailing Slash Issue in Breadcrumbs Links #212

shah-iq opened this issue Mar 19, 2024 · 12 comments
Assignees
Labels
bug Something isn't working

Comments

@shah-iq
Copy link

shah-iq commented Mar 19, 2024

I'm encountering an issue with the using astro-breadcrumbs with Astrowind template on my website. I'm using the astro-breadcrumbs integration to display breadcrumbs. However, after running the build command pnpm run build, the links in the breadcrumbs are having trailing slashes appended to them, despite having set the trailing slash option to "never." Additionally, I've included trailingSlash={false} in the Breadcrumb component for added assurance. It seems the problem only pertains to the links generated by the breadcrumbs. Can you suggest any potential solutions?

@felix-berlin
Copy link
Owner

Hi @shah-iq thanks for creating this issue. Do you have a link to your repo?

@shah-iq
Copy link
Author

shah-iq commented Mar 20, 2024

Hi @shah-iq thanks for creating this issue. Do you have a link to your repo?

Actually this is a project for a client and is a private repo

@felix-berlin
Copy link
Owner

@shah-iq I will check this issue within this (hopefully) week. By the way the traillingSlash prop has been deprecated since V2. Please make sure you read the Migration Guide https://docs.astro-breadcrumbs.kasimir.dev/guides/migration-to-v2/

@felix-berlin
Copy link
Owner

Did you use the current release?

@shah-iq
Copy link
Author

shah-iq commented Mar 20, 2024

Did you use the current release?

Yes I am using the latest release and I used the traillingSlash prop only to just double check if by any chance resolved the issue. I had seen the migration guide that this prop had been deprecated.

@felix-berlin felix-berlin self-assigned this Apr 8, 2024
@felix-berlin felix-berlin added the bug Something isn't working label Apr 8, 2024
@felix-berlin
Copy link
Owner

@shah-iq is the problem still there? Otherwise I would close the issue.

@ccoyotedev
Copy link

Hey @felix-berlin

My team is also encountering this issue. It work's fine in dev mode, but in the production build the links are appending a / even if there isn't one in the URL.

This is problematic for SEO

@felix-berlin felix-berlin reopened this Aug 27, 2024
@felix-berlin
Copy link
Owner

Hi @ccoyotedev ,

can you maybe provide a working demo of your problem?

When this is not possible, can you tell me your current astro-breadcrumb version, astro version, used adapters, astro config and the astro-breadcrumbs usage?

@ccoyotedev
Copy link

@felix-berlin I cannot share as it's a private repository for the company I work for.

"astro": "^3.6.5",
"astro-breadcrumbs": "^3.0.1",

In production it is a static build, hosted with Cloudflare.

// astro.config.mjs

export default defineConfig({
  output: process.env.PUBLIC_APP_ENV === 'preview' ? 'server' : 'static',
  adapter: process.env.PUBLIC_APP_ENV === 'preview' ? node({ mode: 'standalone' }) : undefined,
  site: SITE_URL,
  trailingSlash: process.env.PUBLIC_APP_ENV === 'preview' ? 'ignore' : 'never',
  integrations: [
    react(),
    tailwind({
      config: {
        applyBaseStyles: false
      }
    }),
    // https://docs.astro.build/en/guides/integrations-guide/sitemap/
    sitemap({
      customPages: externalPages
    }),
    storyblok({
      ...
    })
  ]
})

Used like so:

<Breadcrumbs linkTextFormat="capitalized">
  <span slot="separator">/</span>
</Breadcrumbs>

@ccoyotedev
Copy link

FYI, I have got around this issue by extracting your generateCrumbs function and removing the addTrailingSlash logic. I then pass the returned value to the crumbs prop.

// Breadcrumbs.astro
---
import { Breadcrumbs as AstroBreadcrumbs } from 'astro-breadcrumbs'

import { generateCrumbs } from '@/helpers/breadcrumbs'

const paths = Astro.url.pathname.split('/').filter((crumb: any) => crumb)
const crumbs = generateCrumbs({ paths, indexText: 'Home', linkTextFormat: 'capitalized' })
---

<div class="container text-[#F6F7F4]/50">
  <AstroBreadcrumbs crumbs={crumbs}>
    <span slot="separator">/</span>
  </AstroBreadcrumbs>
</div>
// src/helpers/breadcrumbs.ts

interface BreadcrumbItem {
  text: string
  href: string
  'aria-current'?: string
}

type GenerateCrumbs = {
  paths: string[]
  indexText: string
  linkTextFormat: 'lower' | 'capitalized' | 'sentence'
}

export const generateCrumbs = ({ paths, indexText = 'Home', linkTextFormat }: GenerateCrumbs) => {
  const parts: Array<BreadcrumbItem> = []
  const baseUrl = import.meta.env.BASE_URL

  const basePartCount = baseUrl.split('/').filter((s) => s).length

  const hasBaseUrl = baseUrl !== '/'

  /**
   * Loop through the paths and create a breadcrumb item for each.
   */
  paths.forEach((text: string, index: number) => {
    /**
     * generateHref will create the href out of the paths array.
     * Example: ["path1", "path2"] => /path1/path2
     */
    const finalHref = `/${paths.slice(0, index + 1).join('/')}`

    // strip out any file extensions
    const matches = text.match(/^(.+?)(\.[a-z0-9]+)?\/?$/i)

    if (matches?.[2]) {
      text = matches[1]
    }

    parts.push({
      text: formatLinkText(text, linkTextFormat),
      href: finalHref
    })
  })

  /**
   * If there is NO base URL, the index item is missing.
   * Add it to the start of the array.
   */
  if (!hasBaseUrl) {
    parts.unshift({
      text: indexText!,
      href: baseUrl
    })
  }

  /**
   * If there more than one part in the base URL,
   * we have to remove all those extra parts at the start.
   */
  if (basePartCount > 1) {
    let toRemove = basePartCount - 1
    while (toRemove--) {
      parts.shift()
    }
  }

  /**
   * If there is a base URL, the index item is present.
   * Modify the first item to use the index page text.
   */
  parts[0] = {
    text: indexText!,
    href: parts[0]?.href
  }

  return parts
}

const findSeparator = (slug: string): string | undefined => {
  const separators = ['-', '_']
  for (const separator of separators) {
    if (slug.includes(separator)) {
      return separator
    }
  }
}

const unSlugTrimmed = (slug: string): string => {
  const separator = findSeparator(slug)
  if (separator) {
    return slug.split(separator).join(' ').trim()
  }
  return slug
}

const formatLinkText = (slug: string, format?: GenerateCrumbs['linkTextFormat']) => {
  const slugToFormat = unSlugTrimmed(slug)

  switch (format) {
    case 'lower':
      return slugToFormat.toLowerCase()

    case 'capitalized':
      return slugToFormat
        .split(' ')
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
        .join(' ')

    case 'sentence':
      return slugToFormat.charAt(0).toUpperCase() + slugToFormat.slice(1)

    default:
      return slug
  }
}

@felix-berlin
Copy link
Owner

@ccoyotedev Interesting, if I find time at the weekend I'll take a closer look at the problem and create a bugfix.

@felix-berlin
Copy link
Owner

I just tried to reproduce this behavior with the docs site, there I also use Cloudflare (Pages). But had no luck.

image

I checked whether hasTrailingSlash shows any abnormalities. This was not the case, at this point I am now a little perplexed.

If const hasTrailingSlash = Astro.url.pathname.endsWith("/"); returns a wrong result under certain circumstances, I could try to query this logic without the values on Astro.url, i.e. do the whole thing directly in generateCrumbs().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

When branches are created from issues, their pull requests are automatically linked.

3 participants