gatsbyjs / gatsby

The best React-based framework with performance, scalability and security built in.
https://www.gatsbyjs.com
MIT License
55.27k stars 10.31k forks source link

[gatsby-plugin-mdx] Markdown links are broken, prefixed with assetPrefix #38362

Open nemvalid opened 1 year ago

nemvalid commented 1 year ago

Preliminary Checks

Description

If you specify an assetPrefix in your gatsby config and build the site with gatsby build --prefix-paths then root-relative links in MDX will be prefixed with assetPrefix and will be broken: The link href will become https://cdn.example.com/page-2/ instead of /page-2.

This is because the remark-path-prefix-plugin (which is a default built-in plugin in gatsby-plugin-mdx) will prefix every link with pathPrefix, which also contains the assetPrefix (not just the base path) in this context. (See the documentation for the pathPrefix node api helper: https://www.gatsbyjs.com/docs/reference/config-files/node-api-helpers/#pathPrefix)

Source of remark-path-prefix-plugin: https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-mdx/src/remark-path-prefix-plugin.ts
How pathPrefix is being constructed: https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/utils/api-runner-node.js#L451 and https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/utils/get-public-path.ts#L6-L22

I think gatsby-plugin-mdx should use basePath instead of pathPrefix in remark-path-prefix-plugin to apply the right path prefix from the gatsby config, without applying the asset prefix which is for static non-html assets (images, css, js, icons, fonts, etc).

Please see the reproduction link for more details: https://github.com/nemvalid/gatsby-mdx-assetprefix-repro

Reproduction Link

https://github.com/nemvalid/gatsby-mdx-assetprefix-repro

Steps to Reproduce

Expected Result

Root-relative links in MDX pages shouldn't be prefixed with the assetPrefix.

Actual Result

Root-relative links are prefixed with assetPrefix.

Environment

System:
    OS: macOS 12.6
    CPU: (8) arm64 Apple M1
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 19.1.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 8.11.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 114.0.5735.198
    Firefox: 105.0.3
    Safari: 16.0
  npmPackages:
    gatsby: ^5.12.0-next.1 => 5.12.0-next.1
    gatsby-plugin-mdx: ^5.11.0 => 5.11.0
    gatsby-source-filesystem: ^5.11.0 => 5.11.0

Config Flags

No response

nemvalid commented 1 year ago

related issue: https://github.com/gatsbyjs/gatsby/issues/21462

kennethkalmer commented 9 months ago

I can confirm I see the same issues with the following configuration:

  System:
    OS: macOS 14.2.1
    CPU: (10) arm64 Apple M1 Max
    Shell: 5.9 - /opt/homebrew/bin/zsh
  Binaries:
    Node: 18.12.0 - ~/.asdf/installs/nodejs/18.12.0/bin/node
    npm: 8.19.2 - ~/.asdf/plugins/nodejs/shims/npm
  Browsers:
    Chrome: 120.0.6099.216
    Safari: 17.2.1
  npmPackages:
    gatsby: ^5.10.0 => 5.13.1
    gatsby-plugin-client-side-redirect: ^1.1.0 => 1.1.0
    gatsby-plugin-image: ^3.3.0 => 3.13.0
    gatsby-plugin-manifest: ^5.3.0 => 5.13.0
    gatsby-plugin-mdx: ^5.12.0 => 5.13.0
    gatsby-plugin-postcss: ^6.3.0 => 6.13.0
    gatsby-plugin-react-helmet: ^6.3.0 => 6.13.0
    gatsby-plugin-root-import: ^2.0.9 => 2.0.9
    gatsby-plugin-sharp: ^5.8.1 => 5.13.0
    gatsby-plugin-sitemap: ^6.12.1 => 6.13.0
    gatsby-plugin-styled-components: ^6.3.0 => 6.13.0
    gatsby-source-filesystem: ^5.12.0 => 5.13.0
    gatsby-transformer-remark: ^6.12.0 => 6.13.0
    gatsby-transformer-sharp: ^5.3.0 => 5.13.0
    gatsby-transformer-yaml: ^5.8.0 => 5.13.0
nemvalid commented 8 months ago

As a temporary fix you can create a local remark plugin that fixes the incorrect internal path prefixes

plugins/fix-remark-path-prefix-plugin/index.js

const visit = require('unist-util-visit');

module.exports = ({ markdownAST, pathPrefix, reporter }, { basePath = ''}) => {
  // https://github.com/gatsbyjs/gatsby/issues/38362

  if (pathPrefix?.match('^(https?:\\/\\/)?')) {
    const url = pathPrefix.replace(/(http(s)*:)\/\//, `$1/`);
    visit(markdownAST, 'link', node => {
      if (node.url.includes(url)) {
        const newUrl = node.url.replace(url, basePath) || '/';
        reporter.info(`Fixing link href for "${node.url}" → "${newUrl}"`);
        node.url = newUrl;
        if (node.data?.hProperties?.target === '_blank') {
          delete node.data.hProperties.target;
          delete node.data.hProperties.rel;
        }
      }
    });
  }

  return markdownAST;
}

...and pass it to gatsby-plugin-mdx as part of your options.gatsbyRemarkPlugins in your gatsby config:


plugins: [
  {
    resolve: 'gatsby-plugin-mdx',
    options: {
      extensions: ['.mdx', '.md'],
      gatsbyRemarkPlugins: [
        // add the local plugin after your existing gatsby remark plugins
        {
          resolve: require.resolve(`./plugins/fix-remark-path-prefix-plugin`),
          options: {
            basePath: '' // your site's basePath, optional
          }
        }
      ]
    }
  }
]