parcel-bundler/parcel

Is there a way to build html with relative assets url?

xcatliu opened this issue ยท 39 comments

Choose one: is this a ๐Ÿ› bug report or ๐Ÿ™‹ feature request?

๐Ÿ™‹ feature request

๐Ÿค” Expected Behavior

When exec parcel build docs/index.html, the generated html should have relative assets url, such as:

<script src="./89ee6b6452b6649c72b85a19637b8362.js"></script>

๐Ÿ˜ฏ Current Behavior

The url is an absolute path:

<script src="/dist/89ee6b6452b6649c72b85a19637b8362.js"></script>

๐Ÿ’ Possible Solution

Is there a way to build html with relative assets url? Maybe some configuration?

๐Ÿ”ฆ Context

A common practice is to only host the dist (or public) dir using a static server. But with absolute path, it cannot find the assets resource.

Yes, use --public-url when building:
check this PR: parcel-bundler/website#30

Built files written in ./dist are fine but nothing except the index page gets served by the dev-server.

Example:

test.html:

<h1>test.html</h1>
<script src="test.js"></script>

test.js:

console.log('test.js');

Command, using version 1.10.2:

parcel --public-url '.' test.html

Now http://localhost:1234/ returns the index page as stored in ./dist/test.html. Notice there is no slash in front of the file name, that is the desired outcome of setting --public-url.

<h1>test.html</h1>
<script src="test.e98b79dd.js"></script>

The file ./dist/test.e98b79dd.js looks fine but http://localhost:1234/test.e98b79dd.js does not serve the file.

--public-url ./ still not work.
I just want <script src="./test.e98b79dd.js"></script> instead <script src="test.e98b79dd.js"></script> but a can't do that with --public-url ./ settings - output still without './' =(

For anyone that just really wants to see a './' before the url, what I did was to change the build command (on Mac) to:
parcel build src/index.html --public-url replacethislinewithadot && sed -i '' 's/replacethislinewithadot/./g' dist/index.html

I found no other way to force the '.' in the url.

Relative urls are necessary if one is using parcel to build for a subdomain. This is a necessary feature. @albinotonnina

Wouldn't it also be saver in general to have ./-relative paths per default in the index.html?
I mean, it does not care if sub-directory or not, it will just take the file relative from its location.

I can imagine other scenarios were relative paths are not necessary or need to be altered are way less common than this one.

Note that if you are using something like react-router, the relative paths won't work. Because the href is something like site.com/myApp/router-path and it will try to load site.com/myApp/router-path/styles.css instead of site.com/myApp/styles.css.

Is there any way to have the asset paths be relative to index.html, si it will work wherever you place the index file?

maybe --public-url .

I did it! I managed to make a production build that has react-router and works in any subfolder.
Steps I took:

  • Build parcel with relative paths --public-path ./
  • Use .htaccess to make sure index.html and all assets are loaded correctly when refreshing a virtual path generated by react-router. This was done by simply loading all 404 files from the folder root instead of current path.
  • Make all the links in react-router correct by automatically detecting the app path (basename) on page load based on current location.pathname.

I wrote a more detailed blog post here.

Note, this feels like a very hacky solution, but I did not find any other way to make a fully build application be completely path independent and use only relative paths.

I would recommend deleting your .cache and dist folders whenever you change your --public-url for the changes to actually take effect

Why it's closed? I still don't see a way to produce <script src="./test.e98b79dd.js"></script> output instead <script src="test.e98b79dd.js">

@pawelkedra --public-url ./doesn't work for you?

@mischnic it doesn't, it produces <script src="my-app.0ad7f820.js"> </script>. Looks like ./ part is always skipped

Well,
<script src="./test.e98b79dd.js"></script> and
<script src="test.e98b79dd.js"></script>
do the same thing. The ./ is actually redudant, every path that doesn't start with a protocol or / is relative anyway.

@mischnic it matters if your site is not deployed at the root of the domain, e.g. example.com/site/index.html - without dot browser will try to download all resources from example.com/test.e98b79dd.js, not from example.com/site/test.e98b79dd.js. I can't use --public-url site, I have to consider site part "dynamic", it may be changed for reasons beyond my control.

I agree with @pawelkedra. Having an option to output files that use ./ for paths would be quite useful ๐Ÿ˜Š

Any update on this? Why is it closed?

--public-url ./ works for me.

Any updates?

@exsesx Have you tried this? It was the only way I could get it to work.

@EmmaGoodliffe I'm trying, but my index.html points to {public-url}/src...js, but the actual file is not there :c

@exsesx Sorry, I'm not quite sure what you are trying to do ๐Ÿค”.

What is the path in your source index.html and what do you want to change it to?

For anyone that just really wants to see a './' before the url, what I did was to change the build command (on Mac) to:
parcel build src/index.html --public-url replacethislinewithadot && sed -i '' 's/replacethislinewithadot/./g' dist/index.html

I found no other way to force the '.' in the url.

Thanks to @ErikSom
This is my package.json, which is working fine for me.
Inside the build script:
First, I removed the old dist folder
Then, I moved my project's assets to the files folder.
And at the end, I updated the @font-face URL from files/ to ./ path

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "keywords": [],
  "author": "Panbehkar",
  "license": "ISC",
  "scripts": {
    "build": " npm run clean & npm run buildIntoFiles && npm run updatePath",
    "clean": "rmdir /Q /S dist",
    "buildIntoFiles": "parcel build src/index.html --no-source-maps --public-url ./files",
    "updatePath": "sed -i -- 's/files/.\\//g' dist/files/*.css"
  },
  "dependencies": {},
  "devDependencies": {
    "autoprefixer": "^9.8.6",
    "parcel-bundler": "^1.12.4",
    "parcel-plugin-custom-dist-structure": "^1.1.16",
    "postcss-modules": "^3.2.2",
    "prettier": "^2.1.2",
    "sass": "^1.26.11"
  },
  "customDistStructure": {
    "config": {
      "files": [
        ".css",
        ".js",
        ".woff",
        ".png",
        ".jpg"
      ]
    }
  }
}

would recommend deleting your .cache and dist folders whenever you change your --public-url for the changes to actually take effect

I did

$ rm -rf .parcel-cache
$ rm -rf dist
$ parcel build --public-url ./ --dist-dir dist src/html/index.html

And now my file paths are relative (the / is gone).

I think using --public-url / solves it if you serve the files after building with something like server dist -p 3000. But it's not useful for uploading the files into a folder on a host like example.com/site.

Are any updates on it?

foo.js and ./foo.js are different things. Please, keep things simple and straightforward, do not remove ./ only because your opinion is that it is redundant. Many folks will burn a lot of time debugging why ./ is getting stripped.

@klausbreyer

--public-url ./ works for me.

Thanks, using --public-url ./ stripped off the leading "/" and the .css and .js files worked correctly for me as well.

It does not work for me neither I'm using parcel build --public-url ./ -d docs demo/index.html and its still producing <script src="main.4a6d51be.js" type="text/javascript"> instead of <script src="./main.4a6d51be.js" type="text/javascript">

Things I tried

  • delete cache
  • delete build folder
  • use --public-url ./
  • use --public-url .

the repo at: https://github.com/codingedgar/macos-multi-select

workaround

I manually changed the src because its just one file and i have like 40min debugging this.

#### Edit
idk if this is relevant, the environment is: macOS Big Sur

It does not work for me neither I'm using parcel build --public-url ./ -d docs demo/index.html and its still producing <script src="main.4a6d51be.js" type="text/javascript"> instead of <script src="./main.4a6d51be.js" type="text/javascript">

Things I tried

  • delete cache
  • delete build folder
  • use --public-url ./
  • use --public-url .

the repo at: https://github.com/codingedgar/macos-multi-select

workaround

I manually changed the src because its just one file and i have like 40min debugging this.

#### Edit
idk if this is relevant, the environment is: macOS Big Sur

Would recommend to do something like this:
#206 (comment)

parcel --public-url '.' test.html

thankss
but I believe it should be

parcel --public-url . test.html
not
parcel --public-url '.' test.html

I feel like there's a smarter way to do this. Parcel is all about being minimal-config right? The reason I'm here was trying to build a basic html page (I know, not main use-case, but still, hear me out).

When I link the script in my HTML, I already 'chose' if I want that href to be relative to the file, the domain root etc. Why not have parcel analyse & repsect that?

Can anyone help me out I

For anyone that just really wants to see a './' before the url, what I did was to change the build command (on Mac) to: parcel build src/index.html --public-url replacethislinewithadot && sed -i '' 's/replacethislinewithadot/./g' dist/index.html

I found no other way to force the '.' in the url.

What's the equivalent windows command for
sed -i '' 's/replacethislinewithadot/./g' dist/index.html
I'm stuck at this.

Is there an official solution to this? Been stuck on it for days. --public-url ./ removes the dot after building and I need the asset paths to be relative to the subdirectory.

If you know the subdomain the resources are on, you can use that as the public-url option. Not the prettiest, but the only thing that worked without using tools like sed.

- "build:prod": "npx parcel build --public-url ./ index.html"
+ "build:prod": "npx parcel build --public-url /My-Subdomain/ index.html"

'test.hash.js' will now resolve to '/My-Subdomain/test.hash.js' as is expected.

If you know the subdomain the resources are on, you can use that as the public-url option. Not the prettiest, but the only thing that worked without using tools like sed.

- "build:prod": "npx parcel build --public-url ./ index.html"
+ "build:prod": "npx parcel build --public-url /My-Subdomain/ index.html"

'test.hash.js' will now resolve to '/My-Subdomain/test.hash.js' as is expected.

Thanks! This helped me out.

Why is this still not fixed? I have a repo using Parcel and I also have a /docs where I have some html-pages. I use parcel build docs/src/index.html --dist-dir docs to build it to /docs in my repo. I then set Github Pages to use /docs as the directory for publishing my docs. However, the CSS and JS don't work, because they are referenced incorrectly. Haven't found a workaround yet. I could set --public-url <repo-name> but that will fault when changing the name of the repo...

For the linux people:
parcel build src/index.html --public-url replacethislinewithadot && sed -i.bak 's/replacethislinewithadot/./g' dist/index.html

Since ./ does not result in relative imports, it's not possible to move the dist output into a sub directory when including a service worker. This can't be fixed with the sed workaround as service worker imports are handled via the import.meta.url

Any official statement on this?

Could we please reopen this issue, as the sed workaround is not applicable for using with service workers