parcel-bundler/parcel

[RFC] Specify sub directory for all the generated assets for easy deploy and serve

npup opened this issue Β· 33 comments

npup commented

TLDR;

My point is that serving the built project (both in prod and dev) could be so much easier in the common case if generated assets were not necessarily put alongside the main index file -- and an option for it could carry its weight.

What am I talking about?

Consider having a src/index.html file, referring css and js files (living anywhere in the src tree).
Without any options used when bundling the resulting assets are generated alongside the index.html in ./dist/ like:

dist/
  index.html
  50c20e2a4174028d1e9d4574129f6104.css
  50c20e2a4174028d1e9d4574129f6104.js

-- and the index.html is rewritten to refer them nicely.

SPA stylee, the dist folder is to be statically served by nginx/equivalent, and for a seamless dev experience I'd like to deploy it as is -- or even build it in place on Heroku or such.

For that, I would love a build option to say "all the generated assets should live in a sub directory by the name of foo". (We already have an output-dir option, which sets the name of the whole build output, and a public-url option, though the latter just affects the referring paths in index.html (in this case) ).

This could the the result of my idea:

dist/
  index.html
  foo/
    50c20e2a4174028d1e9d4574129f6104.css
    50c20e2a4174028d1e9d4574129f6104.js

That way nginx/whatever could happily serve index.html for all requests but foo and under.

An alternative would be

  • using the public-url option to have index.html refer assets in foo
  • having a post build step to "manually" move asset files to dist/foo

, but this juggling does not work very well in development mode, wanting nginx to serve files in the same manner from the local dist/.

Creating a more elaborate bunch of rules for the file server to have it avoid serving index for any generated asset file is another cumbersome route I'd happily avoid :D .

Is this essential to making SPAs? Can we avoid having to add more to our CLI by just letting NGINX handel all of this rather than parcel?

npup commented

@davidnagli
Thanks for chiming in!

An SPA was my initial use case (and used in my example), though I'd like to see it as a generally nice feature just to to be able to say where my generated files shall end up.
If we are talking developer experience, this is really quite helpful (for other post processing too!). Hey, in comparison, the output-diroption does not help as much, but it is still there ;).

As for the SPA case -- leaving the generated structure flat with no options (leaving the web server to handle this) is really not very ergonomic in the general case. Again, makes for a really smooth developer experience, which is something I sensed is a goal of this cool tool.

Ya I definetly see where you’re going with this. It seems like a pretty important feature.

How do you think we should implement this?

The whole point of Parcel is that there is no configure file, so we would want to implement it in a way that’s very automatic. I just can’t figure out how to design it in a way that is actually easy and productive for developers.

wouldn't nginx's try_files directive solve the problem?

location / {
    try_files $uri /index.html;
}

this will make nginx to try and serve a file under the request uri and otherwise fallback to index.html

it might be worth creating a guide for such case for common web servers.

npup commented

@mrbar42 that's a nice one, but nginx and/or SPA are not the only reasons to need the generated files/assets to be easily distinguished in the general case -- be it in the form of naming a directory for them or having the files prefixed or whatever.

@davidnagli What would the major problem be with a cli option like the existing public-url and output-dir, that makes the generated files land (and be referenced) in a directory with a certain name?

From an SPA development perspective, is it essential that we give you control over the name of that directory (/foo in your example)? Can we just throw all the generated bundles into a subdirectory called bundles or something?

npup commented

@davidnagli That would be the next best thing I suppose (and not only for SPA use cases, sorry if that part of my argument stuck), in that it facilitates various post build operations. I really can see (and appreciate) the effort to keep the configuration explosion on a leash.

Though I foresee continuous proposals about being able to name the bundles dir too, especially since one already can set the name of the main output dir. Let's see what happens :)

I also have the same problem:

parcel Can not be configured html, js, css such as multiple files, output to multiple directories /views/dist, public/assets/js/hash.js, public/assets/css/hash.css

For example, the following is the file structure of the backend rendering project based on express

β”œβ”€β”€ README.md
β”œβ”€β”€ app.js
β”œβ”€β”€ config.js
β”œβ”€β”€ middlewares
β”‚   β”œβ”€β”€ auth
β”‚   β”œβ”€β”€ csrf
β”‚   β”œβ”€β”€ jwt
β”‚   └── util
β”œβ”€β”€ public
β”‚   β”œβ”€β”€ assets
β”‚   β”‚   β”œβ”€β”€ css
β”‚   β”‚   β”œβ”€β”€ img
β”‚   β”‚   └── js
β”‚   └── debugger
β”‚       β”œβ”€β”€ css
β”‚       β”œβ”€β”€ img
β”‚       └── js
β”œβ”€β”€ ssl
└── views
    β”œβ”€β”€ dist
    └── src
        β”œβ”€β”€ index.html
        └── layer.html

parcel build /views/src/index.html -d /views/dist/index.html --public-url /assets/js ?

parcel is great, packaging efficiency is very fast, but it can not configure the corresponding file path, a bit regrettable... But I'll still pay attention to it

Is this still not possible? I have a Flask app with the following structure:

β”œβ”€β”€β”€main.py
β”œβ”€β”€β”€package.json
β”œβ”€β”€β”€js
β”‚   β”œβ”€β”€β”€app.js
β”‚   └───utilities.js
β”œβ”€β”€β”€sass
β”‚   β”œβ”€β”€β”€base.scss
β”‚   β”œβ”€β”€β”€home.scss
β”‚   └───vars.scss
β”œβ”€β”€β”€static
β”‚   β”œβ”€β”€β”€images
β”‚   β”œβ”€β”€β”€scripts
β”‚   └───style
└───templates
    └───index.html

By default, Flask apps serve static content (js, css, images, etc) from static/ and html files from templates/.

I want Parcel to bundle the files from js/ and sass/ into static/scripts/ and static/style/ respectively. templates/index.html should also be updated to reference these files.

Currently everything is put into a dist/ folder, losing all structure. Is there are workaround for this?

Ideally, to cater for all types of projects, it should be possible to specify different output directories for different entry points. In Webpack, you can specify to output css into path/a/, js into another/path/b/, and html into c/ (for example).

Would love to see some official word on this. I don't find much sense for a project where the index.html is the build entry point and all the assets are just grouped together and outted to the same directory. I don't find this to be a practical, useful or even standard approach to file organization across any language or project.

This is the one issue where I find Parcel's "no config" approach to be a fatal flaw as it basically defines how all projects must be structured and does so without any logic.

Imho Parcel should recreate the existing project structure for imported files. This would allow infinite flexibility (users could just do whatever and it would work for them) and it could always be overwritten using a config file.

This would be useful for me too, maybe even a /assets/ folder or anything similar so when i have 200 + images they arnt all in the root

I've run into this limitation today.
I'm using parcel to build my express app which is server side renderering a react app.

I was about to add this line to the express app:

app.use(express.static(path.join(__dirname)));

to serve all the web-app assets but clearly this would be really bad because with the build folder being flat that means GET /index.js would return my server side code!

@Place1 how would that happen? do you store your index.js in the dist folder?

if your project looks like this

-/
 - dist/
   - *.{html,js,css,png,woff2,...}
 - index.js

then you should do something like this

app.use(express.static(path.join('dist')));

which wouldn't expose your server code

I run my backend through parcel to concert jsx using β€”target=node

then you can call parcel twice with different out-dir, once for the clinet with dist/client and once with dist/server
and have your express static point dist/client

I was surprised to see that if I have a folder structure:

src
  index.html
    css/style.css
    js/app.js

Everything would just be dumped out in a flat file structure in the dist folder. It would be nice if the dist folder followed the same structure as my src folder. Not sure if this is what @hacknug meant.

Yes! This is exactly what I meant.

It's hard to make this the standard as you could end up having a very deeply nested tree. And in most cases it just gets dumped onto a server anyways, besides that some terminal magic and you can find any file in seconds no matter how flat and big the folder is.

Note: this will be possible once parcel 2 gets released as you'll be able to use your own naming strategy (or a premade one {the default will remain the same}), this will allow anyone to use hashes, filenames and folders however they want

I don't think we'll support recreating the entire source file structure as it could be quite deep and nested as Jasper mentioned. I could see supporting a single "assets" directory where content hashed files could go, with other entry points at the top.

That being said, in Parcel 2 you'll be able to override the naming strategy using plugins, so I assume the community will come up with some good options for different use cases which we can bring back into core as needed.

Closing this since it will be solved in Parcel 2 via plugins.

I would like to add a possible solution to create high-level asset folders:

|---/js
|    |---index.js
|---/css
|    |---index.css
|---/assets
|    |---/fonts
|    |    |---font1.woff2
|    |    |---font2.woff2

parcel build js/index.js assets/fonts/* css/index.css

which creates:

|---/dist
|    |---/js
|    |    |---index.js
|    |---/css
|    |    |---index.css
|    |---/assets
|    |    |---/fonts
|    |    |    |---font1.woff2
|    |    |    |---font2.woff2
Lcfvs commented

My plugin makes it, based on the publicURL : https://www.npmjs.com/package/parcel-plugin-pre-dist

My plugin makes it, based on the publicURL : https://www.npmjs.com/package/parcel-plugin-pre-dist

give us more information about usage of your plugin..

When developing parcel does not use the dist folder. This means that if you copy assets to the dist folder, the server cannot see them. So when using something like parceljs etc they're not visible during development and cannot be tested.

His plugin allows you to set a pre-dist folder, which you can copy your assets to. This means that you can test assets in development.

The solution for this problem and any other asset naming and structure is can be solved by a "custom Namer plugin" in Parcel 2:
(ref: #2991, #2991 (comment))

You can write a custom Namer plugin to support things like this. See how the default namer works for an example: https://github.com/parcel-bundler/parcel/blob/v2/packages/namers/default/src/DefaultNamer.js

When will Parcel 2 be released(stable)?

This question has been bothering me,Hope that the official solution

When developing parcel does not use the dist folder.

Has this changed since then, because when I run parcel it definitely does. Which is annoying as hell as parcel index.html and parcel build index.html both dumps everything in the dist folder, but since parcel renames everything I have two copies of every asset. One from my development phase and one from the build.

I really want parcel to be THE tool to use for web development, but at the moment it seem to still be in its infancy and not ready for production.

I have created a Parcel 2 namer plugin which puts assets into a directory called a/. It is the simplest possible solution but solves my use case. It should be easy to add some configuration to support more naming schemes.

https://gitlab.com/kevincox/parcel-namer-asset-dir

Having this issue for too long, I've decided to make a plugin that will solve this.
The simple, but very powerful plugin that supports modern web development needs.
https://github.com/VladimirMikulic/parcel-plugin-custom-dist-structure

Look how simple it is!

First, install it by running npm i parcel-plugin-custom-dist-structure -D.

In your package.json provide a configuration object.

"customDistStructure": {
    "config": {
      ".css": "css",
      ".js": "js",
      "images": [
        ".jpg",
        ".svg",
        ".png"
      ]
   }
},
# Build your project
parcel build index.html

Congratulations, this your dist folder:

dist
β”œβ”€β”€ css
β”‚    └── styles.hash.js
β”œβ”€β”€ images
β”‚    └── img1.hash.png
β”‚    └── img2.hash.jpg
β”œβ”€β”€ js
β”‚    └── script.hash.js
└── index.html

Let me all know what you think!

Is there built-in functionality in version 2 to configure output directories or do I need to install a plugin? If I need to install a plugin, which plugin? I tried VladimirMikulic's plugin but apparently, the plugin only works for Parcel version 1. The link to version 2 takes you to somewhere seemingly unrelated. I would be surprised if there isn't built-in functionality for something so basic.