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

Feature: referencing variables from markdown pages (placeholder substitution) #395

Closed
julen opened this issue Jan 12, 2018 · 63 comments
Closed
Labels
difficulty: advanced Issues that are complex, e.g. large scoping for long-term maintainability. feature This is not a bug or issue with Docusausus, per se. It is a feature request for the future.

Comments

@julen
Copy link

julen commented Jan 12, 2018

First of all, thanks for sharing and supporting Docusaurus! It's been pretty easy to get started.

I'm posting a feature request — I haven't found anything similar in the issue tracker so forgive me if it's been discussed before.

Motivation: sometimes docs need to reference information such as version numbers, URLs etc. and having them manually typed in the markdown pages is not ideal because this type of information often gets referenced multiple times.

Taking that into account, it would be great if Docusaurus supported passing variables into pages (from e.g. siteConfig). It already supports special Markdown markup via the <AUTOGENERATED_TABLE_OF_CONTENTS> marker, and it could potentially do something similar for variables.

For example, typing in<VAR:projectName> would get replaced with the value of siteConfig.projectName. Ideally this works in code blocks too (```).

@JoelMarcey JoelMarcey added the feature This is not a bug or issue with Docusausus, per se. It is a feature request for the future. label Feb 27, 2018
@JoelMarcey
Copy link
Contributor

Interesting idea. We did something similar for nuclide.io using JS and referencing the package.json file. I think this is something that could definitely be implemented for some upcoming version of Docusaurus.

@JoelMarcey JoelMarcey added mentorship difficulty: advanced Issues that are complex, e.g. large scoping for long-term maintainability. labels Feb 28, 2018
@tibdex
Copy link

tibdex commented May 1, 2018

That would be an awesome feature!

Julen mentioned "version numbers, URLs, etc." but it would also be great to insert automatically computed markup from JSDoc for instance. Thus, if you decide to implement it, it would be nice to support the injection of Markdown (or HTML) markup and not only scalar values.

@gedeagas
Copy link
Contributor

HI, @JoelMarcey I am planning to take this after finishing #694 but I think I will work with scalar values first. Is that okay?

@JoelMarcey
Copy link
Contributor

@gedeagas Sounds great! I think this is a feature that can be done in increments.

@zenflow
Copy link
Contributor

zenflow commented May 27, 2018

Isn't this another thing best left to markdown plugins?

@endiliey
Copy link
Contributor

While I agree with @zenflow, if @gedeagas intends to build his own plugin, that's welcome too☺

@gedeagas
Copy link
Contributor

Hi @zenflow @endiliey, I am planning to do just that 😄 .
Making a plugin for remarkable to support this on docusaurus 📦

@zenflow
Copy link
Contributor

zenflow commented May 27, 2018

Ok, but just to be clear: that plugin will not be included as a dependency of Docusaurus, right? It would be configured by users in their siteConfig.markdownPlugins?

I'm just a bit confused by the fact that this is a feature request for Docusaurus when nothing actually needs to change in Docusaurus for this to be supported

@tibdex
Copy link

tibdex commented May 31, 2018

Here's how variable injection can be done:

Put this in siteConfig.js:

const {Plugin: Embed} = require('remarkable-embed');

// Our custom remarkable plugin factory.
const createVariableInjectionPlugin = variables => {
  // `let` binding used to initialize the `Embed` plugin only once for efficiency.
  // See `if` statement below.
  let initializedPlugin;

  const embed = new Embed();
  embed.register({
    // Call the render method to process the corresponding variable with
    // the passed Remarkable instance.
    // -> the Markdown markup in the variable will be converted to HTML.
    inject: key => initializedPlugin.render(variables[key])
  });

  return (md, options) => {
    if (!initializedPlugin) {
      initializedPlugin = {
        render: md.render.bind(md),
        hook: embed.hook(md, options)
      };
    }

    return initializedPlugin.hook;
  };
};

const siteVariables = {
  scalar: 'https://example.com',
  // Since the variables are processed by Docusaurus's Markdown converter,
  // this will become a nice syntax-highlighted code block.
  markdown: [
    '```javascript',
    'const highlighted = true;',
    '```',
  ].join('\n'),
  // We can use HTML directly too as HTML is valid Markdown.
  html: [
    '<details>',
    '  <summary>More details</summary>',
    '  <pre>Some details</pre>',
    '</details>'
  ].join('\n')
};

const siteConfig = {
  markdownPlugins: [
    createVariableInjectionPlugin(siteVariables)
  ],

  // rest of the config
};

module.exports = siteConfig;

Then, in one of the doc page, write:

## Scalar injection
{@inject: scalar}

## Markdown injection
{@inject: markdown}

## HTML injection
{@inject: html}

And this is what Docusaurus would render:

screenshot-2018-5-30 variable injection demo test site

As you can see, by leveraging remarkable-embed, the implementation of the custom plugin for variable injection is quite small. I'm not even sure it's worth making a dedicated npm package out of it.

The only drawback I can see is that the injected markup isn't pre-processed by Docusaurus. For instance, if one of the variables contains a Markdown title such as ## Custom title, this title won't appear in the secondary on-page navigation.

@gedeagas
Copy link
Contributor

gedeagas commented Jun 2, 2018

Awesome @tibdex.
I guess my comment above is obsolete 😄

@cmdcolin
Copy link
Contributor

cmdcolin commented Aug 8, 2018

This is really sweet tibdex...it is not replacing variables that are inside of a markdown code block though. I guess those are kind of regarded as literals but as I might want some code to depend on a variable, is there anything I should do?

Edit: also realized the original post requests having this feature available in code blocks

@endiliey
Copy link
Contributor

This issue is closed due to stale activity.

If you think there is a need for it, please open up a new issue with new template.

@emanic emanic mentioned this issue Jun 17, 2019
@camcamd
Copy link

camcamd commented Jul 4, 2019

First of all, thanks @tibdex for this plugin example !
Is there a way to have the injected markdown processed by docusaurus, so that we could inject markdown with docusaurus code tabs for instance ?

@tibdex
Copy link

tibdex commented Jul 4, 2019

An option is to preprocess the markdown files outside of Docusaurus. You can develop a pipeline that reads the unprocessed markdown files from another directory, process them, write them to the Docusaurus's markdown files directory, and then build your website with Docusaurus.

@anidotnet
Copy link

Is there any official way to reference variables in v2?

@JoelMarcey
Copy link
Contributor

What type of variables are you trying to reference?

@anidotnet
Copy link

Hi @JoelMarcey, I want to declare a global javascript variable, say version, which I can reference and replace in doc files. This will be useful while writing download link or library version in the doc. I don't have to manually change the version in the doc every time I make a release. So is there any support of such kind in v2?

@JoelMarcey
Copy link
Contributor

@yangshun Did this ever get implemented for v2, do you know?

cc @slorber

@yangshun
Copy link
Contributor

Yeah you can write JS in MDX so you can import anything and use variables.

@JoelMarcey
Copy link
Contributor

https://v2.docusaurus.io/docs/markdown-features

@anidotnet See if you can get that to work, and, if you are so inclined, you could send a pull request to update the documentation to talk about how to make it work.

@anidotnet
Copy link

Hi @JoelMarcey thanks. But I never used react and a newbie in js world. So if I write my version in a .env file like

VERSION=1.0

or write it in a version.js file at the root of the project like

export const siteVariables = {
    versionNumber: '1.0.0'
};

how do I use any one of these in an mdx file?

Mainly I am trying to replace a javascript variable in a code block like

<Tabs
  groupId="installation"
  defaultValue="java"
  values={[
    { label: 'Java', value: 'java', },
  ]
}>
<TabItem value="java">
    ```groovy
compile 'mylib:mylib:$versionNumber'
     ```
</TabItem>
</Tabs>

Any help would be highly appreciated.

@yangshun
Copy link
Contributor

I don't know if you can use variables within the markdown syntax (fenced code blocks), but you can use it within JSX.

import {siteVariables} from '@site/src/versions';

// ...

<Tabs
  groupId="installation"
  defaultValue="java"
  values={[
    { label: 'Java', value: 'java', },
  ]
}>
<TabItem value="java">
// Cannot use Markdown syntax here
<code>compile 'mylib:mylib:{siteVariables.versionNumber}'</code>

</TabItem>
</Tabs>

@slorber
Copy link
Collaborator

slorber commented Jan 18, 2023

The planned createFrontMatter proposal would allow you to do the substitution of variables found in frontMatter, as you'd be able to process/transform frontMatter the way you want.

Track this issue: #5568

@JakeSCahill
Copy link

@Zenahr thanks for providing this example. Is there a way to get remark plugins to process content in code blocks?

@Zenahr
Copy link
Contributor

Zenahr commented Jan 27, 2023

@JakeSCahill yeah I think so. Haven't done it myself but you can check the official docusaurus-admonitions-plugin source. They do something with code blocks.

@JakeSCahill
Copy link

JakeSCahill commented Jan 27, 2023

For anyone who's interested, you can provide the code node to the visit() function:

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

const plugin = (options) => {
  const transformer = async (ast) => {
    visit(ast, ['text, 'code'], (node) => {
      // Replace all occurrences of VAR::varName with the value of varName
      node.value = node.value.replace(/VAR::([A-Z_]+)/g, (match, varName) => {
        return options.replacements[varName] || match;
      });
    });
  };
  return transformer;
};

module.exports = plugin;

@alexandernst
Copy link
Contributor

alexandernst commented Apr 4, 2023

In case you want to make it work for links:

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

const plugin = (options) => {
  const transformer = async (ast) => {
    visit(ast, ['text', 'code', 'link'], (node) => {
      // Replace all occurrences of VAR::varName with the value of varName
      let value;
      switch (node.type) {
        case "link":
          value = node.url;
          break;

        case "text":
        case "code":
          value = node.value;
          break;
      }
      value = value.replace(/VAR::([A-Z_]+)/ig, (match, varName) => {
        return options.replacements[varName] || match;
      });

      switch (node.type) {
        case "link":
          node.url = value;
          break;

        case "text":
        case "code":
          node.value = value;
          break;
      }
    });
  };
  return transformer;
};

module.exports = plugin;

Note that you must install "unist-util-visit": "2.0.3" for any of this to work. Later versions require ESM, which is not available "here".

@slorber slorber changed the title Feature: referencing variables from markdown pages Feature: referencing variables from markdown pages (placeholder substitution) Apr 7, 2023
yisding added a commit to run-llama/LlamaIndexTS that referenced this issue Jul 26, 2023
In theory this will stop the API docs thrashing on every build.

Of course, there is a drawback in that if main changes but we haven't
released a new version of the docs yet the links will go out of date.

So longer term we might want to investigate some kind of variable where
we can continue to have up to date revs but keep the rev in a single
variable somewhere:

facebook/docusaurus#395

In the meantime much smaller commits will be a relief
mnonnenmacher added a commit to oss-review-toolkit/ort that referenced this issue Sep 15, 2023
Fix all markdown links and configure the Docusaurus build to fail on
broken links.

Compared to before there are two main differences for links within
Docusuaurus:
* Links between URL links need to be URL links to be resolved. This will
  be fixed in Docusaurus 3 [1].
* Links that point to files outside the "docusaurus" directory need to
  be absolute links to be resolvable in the final website. This could
  mabye be improved by adding support for variables [2].

[1]: facebook/docusaurus#6370
[2]: facebook/docusaurus#395

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.io>
mnonnenmacher added a commit to oss-review-toolkit/ort that referenced this issue Sep 15, 2023
Fix all Markdown links and configure the Docusaurus build to fail on
broken links.

Compared to before there are two main differences for links within
Docusuaurus:
* Links between URL links need to be URL links to be resolved. This will
  be fixed in Docusaurus 3 [1].
* Links that point to files outside the "docusaurus" directory need to
  be absolute links to be resolvable in the final website. This could
  mabye be improved by adding support for variables [2].

[1]: facebook/docusaurus#6370
[2]: facebook/docusaurus#395

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.io>
mnonnenmacher added a commit to oss-review-toolkit/ort that referenced this issue Sep 15, 2023
Fix all Markdown links and configure the Docusaurus build to fail on
broken links.

Compared to before there are two main differences for links within
Docusuaurus:
* Links between URL links need to be URL links to be resolved. This will
  be fixed in Docusaurus 3 [1].
* Links that point to files outside the "docusaurus" directory need to
  be absolute links to be resolvable in the final website. This could
  mabye be improved by adding support for variables [2].

[1]: facebook/docusaurus#6370
[2]: facebook/docusaurus#395

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.io>
mnonnenmacher added a commit to oss-review-toolkit/ort that referenced this issue Sep 15, 2023
Fix all Markdown links and configure the Docusaurus build to fail on
broken links.

Compared to before there are two main differences for links within
Docusuaurus:
* Links between URL links need to be URL links to be resolved. This will
  be fixed in Docusaurus 3 [1].
* Links that point to files outside the "docusaurus" directory need to
  be absolute links to be resolvable in the final website. This could
  mabye be improved by adding support for variables [2].

[1]: facebook/docusaurus#6370
[2]: facebook/docusaurus#395

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.io>
mnonnenmacher added a commit to oss-review-toolkit/ort that referenced this issue Sep 15, 2023
Fix all Markdown links and configure the Docusaurus build to fail on
broken links.

Compared to before there are two main differences for links within
Docusuaurus:
* Links between URL links need to be URL links to be resolved. This will
  be fixed in Docusaurus 3 [1].
* Links that point to files outside the "docusaurus" directory need to
  be absolute links to be resolvable in the final website. This could
  mabye be improved by adding support for variables [2].

[1]: facebook/docusaurus#6370
[2]: facebook/docusaurus#395

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.io>
@alexfornuto
Copy link

Many thanks to @Zenahr and @alexandernst for the plugin! In testing the latest version posted I found that it doesn't work for inline code snippets. How difficult would it be to expand the plugin to cover this use case?

@slorber
Copy link
Collaborator

slorber commented Sep 19, 2023

Many thanks to @Zenahr and @alexandernst for the plugin! In testing the latest version posted I found that it doesn't work for inline code snippets. How difficult would it be to expand the plugin to cover this use case?

Just adding support for AST node type inlineCode in addition to the already supported code type.

@Zenahr
Copy link
Contributor

Zenahr commented Sep 20, 2023

Many thanks to @Zenahr and @alexandernst for the plugin! In testing the latest version posted I found that it doesn't work for inline code snippets. How difficult would it be to expand the plugin to cover this use case?

@alexfornuto see below.

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

const plugin = (options) => {
  const transformer = async (ast) => {
    visit(ast, ['text', 'code', 'link'], (node) => {
      // Replace all occurrences of VAR::varName with the value of varName
      let value;
      switch (node.type) {
        case "link":
          value = node.url;
          break;

        case "text":
        case "code":
          value = node.value;
          break;
      }
      value = value.replace(/VAR::([A-Z_]+)/ig, (match, varName) => {
        return options.replacements[varName] || match;
      });

      switch (node.type) {
        case "link":
          node.url = value;
          break;

        case "text":
        case "code":
          node.value = value;
          break;

        case "inlineCode": // <-- THIS HANDLES INLINE CODE BLOCKS.
          node.value = value;
          break;
      }
    });
  };
  return transformer;
};

module.exports = plugin;

@slorber
Copy link
Collaborator

slorber commented Sep 20, 2023

@Zenahr you probably also need to visit inlineCode nodes no? And also add it to the first switch? Didn't try but this would look better to me:

const visit = require(https://github.com/syntax-tree/unist-util-visit);

const plugin = (options) => {
  const transformer = async (ast) => {
    visit(ast, ['text', 'code', 'inlineCode', 'link'], (node) => {
      // Replace all occurrences of VAR::varName with the value of varName
      let value;
      switch (node.type) {
        case "link":
          value = node.url;
          break;

        case "text":
        case "code":
        case "inlineCode":
          value = node.value;
          break;
      }
      value = value.replace(/VAR::([A-Z_]+)/ig, (match, varName) => {
        return options.replacements[varName] || match;
      });

      switch (node.type) {
        case "link":
          node.url = value;
          break;

        case "text":
        case "code":
        case "inlineCode":
          node.value = value;
          break;
      }
    });
  };
  return transformer;
};

module.exports = plugin;

@alexfornuto
Copy link

Thanks, gents! I used @slorber's version FTW. This is my derp, as I tried doing the same thing but with pre instead of inlineCode.

janus-dev87 added a commit to janus-dev87/llama-index-typescript that referenced this issue Mar 1, 2024
In theory this will stop the API docs thrashing on every build.

Of course, there is a drawback in that if main changes but we haven't
released a new version of the docs yet the links will go out of date.

So longer term we might want to investigate some kind of variable where
we can continue to have up to date revs but keep the rev in a single
variable somewhere:

facebook/docusaurus#395

In the meantime much smaller commits will be a relief
@de-abreu
Copy link

Evening, @slorber. I'm not familiar with Javascript and would like to know how I could install your plugin.

@lunny
Copy link

lunny commented Sep 1, 2024

Sorry to disturb everyone here. I found a workaround after a long time searching. And it's not mentioned in the above comments. Maybe useful for somebody. The documentation is https://docusaurus.io/docs/api/docusaurus-config#preprocessor

Here is an example of how to use it. https://gitea.com/gitea/docs/src/branch/main/docusaurus.config.js#L213 . This will replace all @variable@ into the value whether it's in a code block or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty: advanced Issues that are complex, e.g. large scoping for long-term maintainability. feature This is not a bug or issue with Docusausus, per se. It is a feature request for the future.
Projects
None yet
Development

No branches or pull requests