vuejs / vuepress

📝 Minimalistic Vue-powered static site generator
https://vuepress.vuejs.org
MIT License
22.58k stars 4.76k forks source link

Build with Relative URLs #796

Open ghost opened 6 years ago

ghost commented 6 years ago

Feature request

If there was a feature to build with relative urls, we could run the files without opening a server. Might be something like: vuepress build --relative

And urls would be like: assets/js/5.3ca37a5b.js instead of: /assets/js/5.3ca37a5b.js

What problem does this feature solve?

Run static files without a server.

Are you willing to work on this yourself?**

Sure.

ulivz commented 6 years ago

Change you base to ""

ghost commented 6 years ago

@ulivz I've already tried couple of base configs. And i tried again like yours. It doesn't work. Paths are still start with /. Can you open the issue again? screen shot 2018-09-05 at 16 07 32

petzerhub commented 6 years ago

@atilkan Probably related to #387 (see https://github.com/vuejs/vuepress/issues/387#issuecomment-388082281), also https://github.com/vuejs/vuepress/issues/575 and https://github.com/vuejs/vuepress/issues/667 As i understand vuepress just cannot be used without a static server (see https://github.com/vuejs/vuepress/issues/667#issuecomment-407704352).

ghost commented 6 years ago

@petzerhub I changed paths manually and some of it worked.

ulivz commented 6 years ago

Since "" is negative, so "" truly doesn't work for now (it will fallback to "/") ...

We need to enhance it.

tiptronic commented 6 years ago

What timeframe do you expect for this enhancement? I am about starting a new project and this is really required. (Manually changing the paths later is simply pita - but I'm sure, you guessed that already ;))

rebz commented 5 years ago

Any updates on this? Working on a project that would be perfect for VuePress, but need to build out with relative URLs as the deliverable will live on a Windows Filesystem.

benmccann commented 5 years ago

It looks to me like this may be supported in html-webpack-plugin 4.0.0 https://github.com/jantimon/html-webpack-plugin/pull/1114

However, vuepress uses a fork https://github.com/vuejs/vuepress/commit/4816bef of that plugin (https://github.com/vuejs/vuepress/issues/1303), so we'd need to update the fork or switch back to html-webpack-plugin

BernardZhao commented 5 years ago

Any more updates on this?

shigma commented 5 years ago

@BernardZhao @atilkan

I wrote a plugin which may satisfy your needs.

vuepress serve docs

And vuepress will build with absolute URLs and then serve them.

Waterstraal commented 5 years ago

@BernardZhao @atilkan

I wrote a plugin which may satisfy your needs.

vuepress serve docs

And vuepress will build with absolute URLs and then serve them.

You would still need a server that serves the files though... OP wants to open the generated files straight from disk without any server. This can only be done when the urls are relative.

simonbrunel commented 5 years ago

I agree with @Waterstraal, it's not a workaround in many cases, for example when using GitHub pages.

shigma commented 5 years ago

I agree with @Waterstraal, it's not a workaround in many cases, for example when using GitHub pages.

Since you are using GitHub pages, why do you need a relative URL? Can you provide a use case for me? I really would like to help but I am not so sure I understand your needs.

simonbrunel commented 5 years ago

@Shigma we are deploying our docs in subfolders, per version. For example, v2.8.0 is located at https://www.chartjs.org/docs/2.8.0/, with a latest symbolic link alias (https://www.chartjs.org/docs/latest/). Note that we are currently using GitBook but are considering to switch to VuePress.

tiptronic commented 5 years ago

Simple use-case: collect your pages on localhost without external ip... or in a subfolder on a shared space

shigma commented 5 years ago

@Shigma we are deploying our docs in subfolders, per version. For example, v2.8.0 is located at https://www.chartjs.org/docs/2.8.0/, with a latest symbolic link alias (https://www.chartjs.org/docs/latest/). Note that we are currently using GitBook but are considering to switch to VuePress.

And setting base to /docs/2.8.0/ doesn't work?

simonbrunel commented 5 years ago

I'm sure it works setting base (though maybe not with the latest alias), but base can't be hard-coded in the repository since it depends of the current branch / tag (master, 2.7.0, 2.8.0, etc...) and would break testing the docs locally. It can't be set at build time either, because the target sub-path is unknown at that time, the build process being different from the deploy one (and it would also break local builds).

tiptronic commented 5 years ago

@Shigma I don't understand what your problem with relative path is. Hardcoding something to get something else to work is always possible, but I can't imagine VuePress general aim is to hardcode simple things like that.

alko89 commented 5 years ago

Posting to show interest in this enhancement.

Besides being useful for opening the site without a server it is also required to properly display websites on IPFS.

lenowng commented 5 years ago

Interested in this enhancement as well, emphasizing on generating static files with relative paths for non-server file serving purpose.

Migrating out of gitbook (which is currently hosted in AWS S3) and would minimize the complexity/effort of the migration (e.g. effort on redoing the deployment pipeline)

TGreifenberg commented 5 years ago

I'm also highly interested in this enhancement. Are there any concrete plans for its implementation?

NanChen6 commented 5 years ago

Any updates? This is also a strong need for our team because we want to host on our build server.

bitwisecook commented 5 years ago

My usecase for this is Dash/Zeal/Velocity offline docs. It just renders from file URLs, but has a per-install random string at the start of the URI to separate each docset. It's per install so you can't predict it between systems. Using a base of ./ doesn't work. Using a base of / doesn't work. Serving up the docs doesn't fix the offline portion of the usecase.

I hacked on this for a while, you can certainly get a decent amount of the docs working without JS enabled, but obviously any embedded Vue component won't render in that case.

mcamiano commented 4 years ago

We ran into the absolute base path as well. In our case, our needs in development can be partially satisfied by injecting the basename of the repo directory via a shell environment variable on the package.json script command line, and reading that via process.env to set the dest and base settings in the .vuepress/config.js module. This assumes that the access path matches the path it is built under, which is just a little nuisance of re-building for each of a few different target locations. (This works for one fixed path per content build, but inhibits moving the static content elsewhere or accessing it through concurrent alternative paths.)

renepardon commented 4 years ago

Same problem here. Why the heck you need a server to run simple HTML files with a bit of Javascript and Styles? I wrote a documentation for a project which should be shipped with the project and not being hosted somewhere, so the customer must be able to open the documentation without development skills.

mcamiano commented 4 years ago

https://github.com/vuejs/vuepress/blob/master/packages/%40vuepress/markdown/lib/link.js#L20 seems to suggest that the presence or absence of an absolute path (starting with the http protocol), is being used to infer whether or not the link is intended to be an internal (via vue router) or external link. This may not be the only place this assumption was made, and there may be other slightly tenuous inferences made about the absolute URL pattern - it could be somewhat difficult to allow for a relative URL base unless such inferences are removed. Seems also like that would come with some risk of breaking things elsewhere, but I don't know much about this codebase.

benmccann commented 4 years ago

I sent https://github.com/yyx990803/html-webpack-plugin/pull/1, which I believe is necessary for fixing this issue

benmccann commented 4 years ago

I took a look at using Docusaurus instead of Vuepress, but they don't support it either. They provided a much better explanation of why they don't support it though: https://github.com/facebook/docusaurus/issues/448#issuecomment-563973732

ghost commented 4 years ago

@benmccann That is a poor view rather than explanation. It is more like opening a pdf from your local machine. There are security holes in web too. Do you stop using it?

tiptronic commented 4 years ago

@benmccann I agree with @atilkan - that's not an explanation, but (very, very, outdated) paranoia ;)

0az commented 4 years ago

@ulivz Would it be possible to re-fork html-webpack-plugin under the NPM @vuepress and the GH @vuepressjs namespaces?

thorny-thorny commented 4 years ago

Somewhat a solution vuepress-offlinify

Poikilos commented 3 years ago

@simonbrunel said:

@Shigma we are deploying our docs in subfolders, per version. For example, v2.8.0 is located at https://www.chartjs.org/docs/2.8.0/, with a latest symbolic link alias (https://www.chartjs.org/docs/latest/). Note that we are currently using GitBook but are considering to switch to VuePress.

I found this thread because I seem to have that problem, but I suggest changing the title of the issue to include "offline" and/or "no slash at beginning" so people aren't confused between the issue I solve there which is slashes after address (assets "relative" to .com) and "offline" (this issue--wanting to avoid the automatic the initial slash at the beginning of asset paths).

I think there is some confusion on this thread about "offline" (technically relative) and "subdirectory" (relative to .com). If I understand @simonbrunel, you are serving it online so having a "/" at the beginning is fine.

@simonbrunel Please let me know if this solves your issue without a plugin or patch: https://github.com/vuejs/vuepress/issues/1935#issuecomment-737614227

Artefact-Team commented 3 years ago

@poikilos your suggestion don't solve the problem: What problem does this feature solve? Run static files without a server.

Plese have some one found a trick to do that?

thorny-thorny commented 3 years ago

@poikilos your suggestion don't solve the problem: What problem does this feature solve? Run static files without a server.

Plese have some one found a trick to do that?

I've discovered that it's possible to run static vuepress files without a server by overwriting links to scripts, styles and pages inside html, and routing-related js code. I poked around vuepress output and did it, check out my project https://github.com/vuejs/vuepress/issues/796#issuecomment-644035856 It overwrites minified js code and this is bad, there should be more elegant solution by changing source files, but that sounds like much more effort to me.

Poikilos commented 3 years ago

@poikilos your suggestion don't solve the problem: What problem does this feature solve? Run static files without a server.

Plese have some one found a trick to do that?

My instructions allow you to save static html files. I have an article on doing it from a subdirectory and another article for doing it from the root of the domain. I'm not sure what you're asking--what did you think I meant? Don't be confused by the "Fix the nonworking..." issue, that has little to do with this one other than that my fix shows that issue is invalid and you can fix that with proper settings. :edit: You must mean without the leading slash for online use. My problem was that those were wrong even when running on a server and I solved that by not running via yarn/npm, which both change the current working directory and confuse vuepress so it doesn't find .vuepress and instead reverts to defaults including the site root setting. I remember now that I mentioned it because others may find this issue like I did when having that incorrect relative path for static html even though my fix doesn't solve the problem of the leading slash breaking offline use. That's why I said specifically that I had a different issue than the title, that the title of this issue is misleading, and suggested how to change or add to the issue title.

alko89 commented 3 years ago

Tried with vuepress-next, now it throws an error when trying to build with base: ''

JanBN commented 3 years ago

this all vuepress is just shit. I spent too much time struggling with it (making sidebar correct, changing favicon, etc.) and now it can not run without server.

soulshined commented 2 years ago

@atilkan although they are rude by most people's standard, banning someone for a 'bad word' when voicing an opinion is a bit extreme; bad words are subjective and they didn't direct it towards any particular individual. But that's my 2 cents.

I think their comments come out of a sensible frustration. For what it's worth, vuepress team, I had the same issue as them, spent a bunch of personal time learning, configuring and exploring to only find out it does not create a usable local file out-of-box. Perhaps 'static site generator' is too loose of a term our industry is adopting? It sort of has multiple implicit meanings now apparently.

I think there are really 3 solutions to this problem

  1. Don't fix it, for whatever reason the vuepress development team finds, but be decisive about it and just announce your decision.
  2. Enhance vuepress to support this
  3. At least update the documentation to predominately display a disclaimer about this so it doesn't get lost in translation.

At minimum, I would encourage the team to at least continue explore this, as there clearly is a need for it. Whether you find it useful, or logical, or not as the core team developing of this, is irrelevant in my opinion - the fact is people want this feature, and if you don't find a reason to support it, consumers will move on to something that does support it. I have already done that and moved on to Hugo, which supports in natively, without plugin intervention, though I truly sincerely wish I didn't have to because I really like vuepress

qiuyuhang commented 2 years ago

Highly interested in this enhancement, here is why:

  1. I need to archive vuepress build result in jenkins, and I'd like to view it on jenkins. But jenkins already hosts a web server, so index.html is not in web root.
  2. I'd like to ship our vuepress doc to my users.
  3. I'd like a built-in solution.
seletz commented 1 year ago

Any news on this? It seems multiple people have valid use cases, @simonbrunel explained one of them which is IMHO very reasonable when trying to actually ship versioned docs and make them available to whatever audience one needs, i.e. serving multiple versions of the same documentation and have a symbolic link to the "latest" version.

Sure, we can build the documentation multiple times, every time just changing the base. But that seems very silly, it's 2022 after all ...

I'd be very much interested in an actual explanation as of why relative paths cannot be used and VuePress actively enforces absolute paths in "base".

Poikilos commented 1 year ago

Any news on this? It seems multiple people have valid use cases, @simonbrunel explained one of them which is IMHO very reasonable when trying to actually ship versioned docs and make them available to whatever audience one needs, i.e. serving multiple versions of the same documentation and have a symbolic link to the "latest" version.

Sure, we can build the documentation multiple times, every time just changing the base. But that seems very silly, it's 2022 after all ...

I'd be very much interested in an actual explanation as of why relative paths cannot be used and VuePress actively enforces absolute paths in "base".

Probably, no one wanted to write this boring function for 2hrs :)

I wrote it today. The project can consider it a PR. If you commit this, please use (if making a commit with the git command or another program without a co-author feature, put two completely blank lines then): Co-authored-by: Poikilos <7557867+poikilos@users.noreply.github.com>):

/**
 * Convert a full path to a relative path.
 * @param {string} fromPath - The absolute href of the current directory
 *   or file starting "/". If the leaf (part after last slash) has a "." it is
 *   assumed to be a file unless there is a slash after it (so if the
 *   leaf is a directory but contains a ".", put a slash after it).
 * @param {string} toPath - The absolute href of the destination
 *   file or directory starting with "/".
 */
function getRelativePath(fromPath, toPath) {
  if (!fromPath.startsWith('/')) {
    console.log('Error: The fromPath "' + fromPath + '" given to getRelativePath is relative, preventing changing toPath.')
    return toPath
  }
  if (!toPath.startsWith('/')) {
    console.log('Error: The toPath "' + toPath + '" given to getRelativePath already appears relative, so it will not be changed.')
    return toPath
  }

  var toLeafI = toPath.lastIndexOf('/')
  var toLeaf = ''
  var toDir = toPath
  //if (toLeafI >= 0) {  // always true since required to be absolute
  toLeaf = toPath.substring(toLeafI + 1)
  // ^ +1 to exclude the leading slash
  toDir = toPath.substring(0, toLeafI + 1)
  // ^ include the slash so there is always
  //   a slash (for easier calculation of return)
  //}
  var removeCount = 0
  if (!toLeaf.includes(".")) {
    // It is apparently not a file, so
    // move the segment from the leaf
    // to the dir.
    if (toLeaf.length > 0) {
      toDir += toLeaf + "/"
      toLeaf = ""
      removeCount = 1
      // ^ The original didn't end in slash,
      //   so should be changed back (1
      //   character should be removed)
      //   before returning.
    }
  }

  var fromLeafI = fromPath.lastIndexOf('/')
  var fromLeaf = ''

  if (fromLeafI >= 0) {
    fromLeaf = fromPath.substring(fromLeafI+1)
  }

  var fromDir = fromPath
  if (fromLeaf.includes(".")) {
    // It is apparently a file.
    fromDir = fromPath.substring(0, fromLeafI)
  }
  if (!fromDir.endsWith('/')) {
    // It must be a directory at this point
    // so add the '/' so all directories have
    // / (for easier calculation of return).
    fromDir += '/'
  }

  // console.log('  fromDir='+fromDir)
  // console.log('  fromLeaf='+fromLeaf)
  // console.log('  toDir='+toDir)
  // console.log('  toLeaf='+toLeaf)
  // console.log('  removeCount='+removeCount)
  // Both fromDir and toDir end with '/' at this point.

  if (toDir.startsWith(fromDir)) {
    toPath = toDir.substring(fromDir.length) + toLeaf;
    // The deeper path is now relative.
    // This will also remove the leading slash because
    // fromDir always has a slash by now.
  }
  else if (fromDir.startsWith(toDir)) {
    // It is a shallower path, so repeat .. as necessary.
    var uncommonSide = fromDir.substring(0, fromDir.length-toDir.length)
    var uncommonSlashCount = uncommonSide.split("/").length
    // console.log('  uncommonSide='+uncommonSide)
    // console.log('  uncommonSlashCount='+uncommonSlashCount)
    var trail = "../".repeat(uncommonSlashCount-1)
    // trail.substring(0, trail.length-1)
    toPath = trail + toLeaf
  }
  toPath = toPath.substring(0, toPath.length-removeCount)
  return toPath
}
Tests (all work as expected in jsfiddle)


console.log('Test shallower dir (next 3 should be: ".."):')
console.log(getRelativePath("/services/design/", "/services"))
console.log(getRelativePath("/services/design/index.html", "/services"))
console.log(getRelativePath("/services/design", "/services"))

console.log()
console.log('Test shallower file (next 3 should be: "../index.html"):')
console.log(getRelativePath("/services/design/", "/services/index.html"))
console.log(getRelativePath("/services/design/index.html", "/services/index.html"))
console.log(getRelativePath("/services/design", "/services/index.html"))

console.log()
console.log('Test shallower root (should be "../../"):')
console.log(getRelativePath("/services/design/index.html", "/"))
console.log('Test file in shallower root (should be "../../index.html"):')
console.log(getRelativePath("/services/design/index.html", "/index.html"))
console.log('Test shallower directory (should be "../"):')
console.log(getRelativePath("/services/design/index.html", "/services/"))

console.log('Test deeper directory without slash (next 3 should be "design"):')
console.log(getRelativePath("/services/", "/services/design"))
console.log(getRelativePath("/services/index.php", "/services/design"))
console.log(getRelativePath("/services", "/services/design"))
// ^ expected output is: 'design'

console.log()
console.log('Test to ensure errors happen on incorrect input (next 2 should be "services"):')
// Incorrect input tests:
console.log(getRelativePath("/services/design", "services"))
// ^ should have an error since not absolute
console.log(getRelativePath("services/design", "services"))
// ^ should have an error since not absolute

I don't know all of the places in the code to use the function because I've never looked at the code of vuepress itself, or not enough to recall. Therefore, please take a look at this issue again and maybe you can make the change far more quickly than I can. If you use my function to do so please use the co-author information I supplied.

Caveats:

stephane888 commented 1 year ago

Highly interested in this enhancement, I'd like to ship our vuepress doc to my users.

Waterstraal commented 1 year ago

I just tried the vuepress successor vitepress. Sadly, it has this exact same issue. It's not possible to build with an empty base url. Setting base in config.ts to '' will prepend paths with /, resulting in this:

  <link rel="preload stylesheet" href="/assets/style.0e94836e.css" as="style">
  <script type="module" src="/assets/app.6a80f313.js"></script>
  <link rel="preload" href="/assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
  <link rel="modulepreload" href="/assets/chunks/framework.e3082fbd.js">
  <link rel="modulepreload" href="/assets/chunks/theme.ba0b17c6.js">
  <link rel="modulepreload" href="/assets/index.md.d37213a0.lean.js">
Shuunen commented 11 months ago

Jeez 6 years after and still not possible to use relative urls 😅

eward957 commented 7 months ago

vuepress 1, edit your .vuepress/config.js:

class ReplaceHistoryToHashWebpackPlugin {
  apply(compiler) {
    compiler.hooks.emit.tap("ReplaceHistoryToHash", (compilation) => {
      Object.keys(compilation.assets)
        .filter((c) => c.endsWith(".js"))
        .forEach((key) => {
          if (compilation.assets[key] && compilation.assets[key]._value) {
            const start =
              compilation.assets[key]._value.indexOf('mode:"history"');
            if (start > -1) {
              compilation.assets[key]._value =
                compilation.assets[key]._value.slice(0, start + 6) +
                "hash" +
                compilation.assets[key]._value.slice(start + 13);
            }
          }
        });
    });
  }
}

module.exports = {
  base: process.env.NODE_ENV === "production" ? __dirname + "/dist/" : "/",
  configureWebpack: (config, isServer) => {
    config.plugins.push(new ReplaceHistoryToHashWebpackPlugin());
  },
};

1935

seletz commented 7 months ago

Well, we have since moved to JetBrains WriterSide.