scullyio / scully

The Static Site Generator for Angular apps

Home Page:https://scully.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[SEO] - Add trailing slash to generated href's that have a query string

guerital opened this issue · comments

🧩 Feature request

Description

When a tag is using the [queryParams] attribute, the generated URL doesn't have the trailing slash at the end:
<a [routerLink]="['/page']" [queryParams]="{ index: 0 }"></a>
.../page?index=0

instead of

.../page/?index=0

The missing trailing slash led to a 301 redirect which is not very good for the SEO

Describe the solution you'd like

I have overridden the seoHrefOptimise plugin:

import { JSDOM } from 'jsdom';
import { HandledRoute } from "@scullyio/scully";

const { registerPlugin, logWarn, yellow, getHandledRoutes } = require('@scullyio/scully');

export const impulsoSeoHref = 'impulsoSeoHrefOptimise';

const impulsoSeoHrefOptimise = async (dom: JSDOM, route: HandledRoute): Promise<JSDOM> => {
  try {
    const routes = await getHandledRoutes();
    const { window } = dom;
    const anchors = window.document.querySelectorAll<HTMLAnchorElement>('a[href]');
    anchors.forEach((a) => {
      const href = a.getAttribute('href');
      const isExternal = routes.find((r) => r.route === basePathOnly(href)) === undefined;
      /** Add noopener and noreferrer to _blank links */
      if ((href && a.getAttribute('target') === '_blank') || isExternal) {
        /** get the attribute add the options and filter out duplicates */
        if ((!href.includes('?') && !href.includes('#') && href.startsWith('//')) || href.startsWith('http')) {
          /** only upgrade links that are not startting with '/'   */
          const rel = ((a.getAttribute('rel') || '') + ' noreferrer noopener')
            .trim()
            .split(' ')
            .filter((v, i, a) => a.indexOf(v) === i)
            .join(' ');
          a.setAttribute('rel', rel);
        }
      }
      if (!isExternal && !href.endsWith('/') && !href.includes('#')) {
        /** don't handle routes that are not inside our app. */
        const splittedHref = href.split('?');
        if (splittedHref.length == 1) {
          a.setAttribute('href', href + '/');
        } else {
          a.setAttribute('href', splittedHref[0] + '/?' + splittedHref[1])
        }
      }
      /** add the trailing slash */
    });
  } catch (e) {
    console.log(e);
    logWarn(`Error in the seoHrefOptimise plugin, didn't update href's for route: "${yellow(route.route)}"`);
  }
  return dom;
};

registerPlugin('postProcessByDom', impulsoSeoHref, impulsoSeoHrefOptimise);

/** copied from ng-lib  */
function basePathOnly(str: string): string {
  if (str.includes('#')) {
    str = str.split('#')[0];
  }
  if (str.includes('?')) {
    str = str.split('?')[0];
  }
  const cleanedUpVersion = str.endsWith('/') ? str.slice(0, -1) : str;
  return cleanedUpVersion === '' ? '/' : cleanedUpVersion;
}

It would be nice to have a flag to switch from my version to the original one