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

Real URLs, Preserved Hashes w/ pageResolver #19

Open
AlbinoGeek opened this issue Sep 3, 2021 · 2 comments
Open

Real URLs, Preserved Hashes w/ pageResolver #19

AlbinoGeek opened this issue Sep 3, 2021 · 2 comments

Comments

@AlbinoGeek
Copy link

Since this would be a departure from how this plugin works (at least, it would break the current functionality) I don't expect this change to be implemented by you necessarily, however, I'd love to know where to start should I maintain that change for our project.

Summary

I want to link to pages with hash marks, without removing the hash marks, but while benefiting from pageResolver;

Such that all of the following are true:

Input Default Currently Expected
hello world #/page/hello_world /hello+world /hello+world
food #/page/food /food /food
foods #/page/foods /food /food
#help (fails) (fails) ... href="permalink#help" ...
drinks#unhealthy (fails) /drinks (fails) /drinks#unhealthy
category:food #/page/category (fails) /food (fails) /Category:food

The Ask

I either need to:

  • Modify the signature of pageResolver to allow changing the href and label (breaking); or
  • Have another config function called after pageResolver which allows changing the label
  • Plugin supporting hash URLs specifically (unlikely, large breaking changes based on default function.)

If there was an option to parse the link before/after pageResolver matching, I would be able to apply this logic myself.

Detail

Our project works closer to WikiPedia, with the following rules on URL generation:

alphabet: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+
separators: _^-~()<>+.,:;/\\|!&*

Slug is sourced from the document's "Title", with the following transformations:

  1. accented characters are replaced with their latin/arabic equivalents
  2. separators are replaced with +, series of + are squashed into one +
  3. any leading or trailing + are removed
  4. if reserved, /page/${slug}, otherwise, /${slug}

Then when it comes to linking, we have some WikiPedia-esque conveniences:

  1. Every singular word slug is also available at its equivalent plural
  2. Every plural word slug is also available at its equivalents singular
  3. Links with hashes should allow you to link to sections (:boom:)

Using the options provided by the plugin, we could get rather close to realizing this:

    .use(remarkWikiLink, {
      hrefTemplate: (permalink: string) => `/page/${permalink}`,
      permalinks:   getAllDocs().map((x) => x.slug),
      pageResolver: (pageName: string) => {
        const slug = pageName.replace(/ /g, '+').toLowerCase()
        const possible = [slug]
        if (pluralize.isPlural(slug)) {
          possible.push(pluralize.singular(slug))
        } else {
          possible.push(pluralize.plural(slug))
        }

        return possible
      }
    }

However, if I want 3 to work, I need some logic around how the link itself is parsed.

I tried to do this with pageResolver, but this removes the hash from the link.

        const parts = pageName.split('#', 2)
        // part[0] is the actual permalink / page slug
        // part.length > 1 && part[1] is a section hash
        const slug = parts[0].replace(/ /g, '_').toLowerCase()

        // ...
@landakram
Copy link
Owner

Sorry for the delay. I see what you're saying -- you need the hash fragment for further processing, but to match against permalinks, the page returned by pageResolver mustn't have the hash (or the permalinks list must include hashes, which is unwieldy). And then the matched permalink of course doesn't include the hash.

Here's my proposed solution:

Instead of having pageResolver return a list of permalinks that are matched with permalinks, have pageResolver actually resolve the page and return an object, whose type looks something like this:

{
  id: string,  // Previously `permalink`
  exists: boolean, 
  href: string | undefined, 
  data: any | undefined
}

TBD on the exact key names, but the idea is that you would do the actual matching yourself in pageResolver (ensuring that exists is set). You could also attach whatever data you wanted, including the hash, for retrieval from the AST later. You could also define the href there instead of using hrefTemplate.

Less relevant to you, but we could retain backward compatibility by using hrefTemplate and permalinks conditionally on the return type of pageResolver.

Would that solve your problem?

@AlbinoGeek
Copy link
Author

AlbinoGeek commented Sep 13, 2021

{
  id: string,  // Previously `permalink`
  exists: boolean, 
  href: string | undefined, 
  data: any | undefined
}

Would that solve your problem?

Hmm, then I'd be sending out something like:

[[Food#Important]]

[[Food#References]]

[[Food#nonexist]]

[[Food#nonexist:Alias Always Wins]]

Hypothetical usage of the type you suggested

const allDocs = getAllDocs() // TODO: I need to cache this
// const allDocs = ['food', 'bar', 'baz', 'foods#important']

pageResolver: (pageName: string) => {
  const slug = pageName.replace(/ /g, '+').toLowerCase()
  const base = {
    data: null, // what consumes this?
    href: slug,
    id: slug,
  }
  
  // 4ace13:
  // add the slug and it's plural or singular (whichever is missing)
  // this will fail for unknown hashes, that's okay, see below
  const possible = [{ ...base, exists: allDocs.any(x => x == slug) }]
  if (pluralize.isPlural(slug)) {
    const tmp = pluralize.singular(slug)
    possible.push({ ... base, href: tmp, id: tmp, exists: allDocs.any(x => x == tmp) })
  } else {
    const tmp = pluralize.plural(slug)
    possible.push({ ... base, href: tmp, id: tmp, exists: allDocs.any(x => x == tmp) })
  }
  
  // if ( slug contains # )
  // ... now what? I can't re-write the label.

  return possible
}

Output I want to make

<a class="internal" href="/foods#important">Important Foods</a> <!-- this would be caught by 4ace13 -->

<a class="internal" href="/food#references">References (Food)</a> <!-- this would be rewritten by slug contains # -->

<a class="internal new" href="/food#nonexist">Nonexist (Food)</a> <!-- I also want to rewrite non-exist labels -->

<a class="internal new" href="/food#nonexist">Alias Always Wins</a> <!-- but a coded alias should always in out -->

Hmm.. I think I didn't understand how to use the example, I realize after trying to think up a usage ^


I really tried to think about how I would end up using this, and hit a bit of a visualization wall.

The pieces of input data that exist are what?

type Input = {
  name:   string
  alias?: string
}

And the output data seems to be something like:

type Output = {
  className: string    // or w/e adds `internal` and/or `new`
  label:     string // doesn't seem I can see/change this atm
  permalink: string
}

What I'm having trouble understanding is how I would match the permalink while masking the label.


I apologize that my ask seems far away from what the plugin does at the moment, and that I'm bad at explaining things (haha.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants