Images in halogen with parcel as bundler

Hi fellow Purescripters,

I’m struggling with a very simple problem in Purescript. I want to add an image to my halogen site. The Purescript code for that is simple: HH.img [ HP.src "images/foo.png" ]. I’m running my application in dev-mode using parcel. Specifically: parcel dev/index.html. But where do I put the image in order for parcel to see it? Or do I have to do something special to make it “referrable” from Purescript? Copying it to dev/ didn’t do anything. I think parcel has to somehow “find out” that my code references this image, so it’s included in the bundle?

Sadly, the real world halogen

parcel works by taking all input files as “roots” in a tree, and then recursively finding all other things those roots refer to and their children refer to. So, if the image doesn’t show up in the HTML file (because you only need it to wrap a script tag that loads your Halogen app), I believe the solution is to tell parcel about another root (e.g. your images directory).

So, perhaps something like this command would work?
parcel build dev/index.html images/*.png

Thanks! I tried that and it didn’t work (neither with the image directory itself, nor with the png glob). You’re right, somehow parcel needs to recursively discover my png. I think in my Typescript days, I actually “imported” the images (though I was using webpack at the time), but I’m not sure how to accomplish this with Purescript.

Parcel works like you said, with images/css and other assets you need to import them from JavaScript/TypeScript if you don’t do it directly thruogh HTML. Eg.

import imageURL from './purescript.png'

const img = document.createElement('img')
img.setAttribute('src', imageURL)
const body = document.getElementsByTagName('body')
body[0].append(img)
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Test</title>
</head>
<body>
  <script src="/index.js"></script>
</body>
</html>

This copies the ./purescript.png file to the dist directory. So there are two ways I can think of that you can fix your problem. You either reference the file in your HTML somehow and hide the element, or you do a post-process pass on your output from Purescript.

Or maybe you do something with the Purescript FFI?! I’m not sure, I haven’t explored the other options. But I don’t think you can just do it through vanilla Purescript, at least I can’t think of a way.

I experienced the same frustrations with Parcel, and ended up switching to webpack for better image handling, although that introduced a whole bunch of other frustrations.

I expect this situation to get a whole lot easier in 0.15, when we’ll have ES modules and simpler bundling options.

Check out some of the plugins and example at the end (if you’re using Parcel v2) from this issue.

That’s too bad. Can you share some of your webpack configuration so I’m maybe not as frustrated as you?

Here’s a basic example of using images with webpack in a PS project: https://github.com/milesfrain/tps-save-gist/tree/ace-mode-fixed

Here’s a more full-blown project using the same strategy: https://github.com/milesfrain/tps/tree/demo-fixes/client

1 Like

For what it’s worth, I settled on using parcel (version 1) for now, installing parcel-plugin-static-files-copy and http-server and putting my images into static/. My serve npm script now does http-server dist & parcel serve dev/index.html. It’s not “nice”, but it works.

Thanks all of you for the guidance!

A couple of days ago a friend of mine pointed me to esbuild which is the bundler used by hugo. I haven’t tried it yet, but from its description it seems like it should be as non-configurable as parcel except much much faster.

I tried ESBuild on a PS project myself, and I can indeed vouch that it was (at least in my case) as non-configurable as Parcel, but much, much faster. Like, stupidly so.

1 Like

I personally use the esbuild webpack plugin as an in-between

hey @mhmdanas, was there any overhead in configuring esbuild to work? like, is it a big yak-shaving effort to get it to replace spago-bundle for a standard sort of project?

I made this a while ago (for personal use), maybe it helps you: https://github.com/Mateiadrielrafael/purescript-esbuild-template

Note: I think esbuild supports css now, so my css setup is probably outdated

1 Like

Not really, but to be clear, I didn’t replace spago bundle with ESBuild; I used them together. To be more specific, in development I would just run spago build then use esbuild to bundle them, while to build in production mode, I would run spago bundle-app and then run the resulting file through esbuild for the NPM dependencies, minification, and other stuff.

Here’s the command that I used in development (with irrelevancies stripped away):

spago build && esbuild src/index.js --bundle '--define:process.env.NODE_ENV="development"' --outfile=dist/index.js --format=iife

Where src/index.js would be:

import { main } from '../output/Main';

main();

I’m not going to explain the commands, since I think they’re fairly understandable without need an explanation.

Note: I can’t really remember if the format argument and NODE_ENV definition are necessary.

If you want file watching, ESBuild doesn’t have it built-in AFAIK, but with its insane performance, I think you can do something like this just fine:

spago build -w --then "esbuild src/index.js --bundle '--define:process.env.NODE_ENV=\"development\"' --outfile=dist/index.js --format=iife"

On to the production command:

spago bundle-app -t dist/index.unminified.js && esbuild dist/index.unminified.js --bundle --minify --format=iife '--define:process.env.NODE_ENV=\"production\"' --outfile=dist/index.js && rm dist/index.unminified.js

Again, not sure why I added format.

If you have any questions, face any difficulties, or think I made a mistake, feel free to tell me.

1 Like

thank you very much!

that is exactly the sort of yak-shaving i was trying to avoid. my bundling is now pretty damn fast…which is great because i’m working on something in JS that requires pretty incessant rebuild / reload cycle

1 Like

I don’t really see where the yak shaving is. It seems pretty clean to me. I guess that means you’re sticking to your existing workflow? Feel free to ask me anything if you want to try ESBuild out.

Oh, sorry i wasn’t clear - what i meant is that you have saved me from trying to work out how it works - which might be interesting but would be a complete diversion for me at the moment. I was able to more or less cut and paste what you put in above and get much faster builds/bundles with no pain.

so, many thanks, once again.

Oh, you’re very welcome. It’s great that the work I did on a tiny side project of mine has helped someone out there. :slight_smile:

1 Like

BTW, you may want want to check if process.env.NODE_ENV actually has the correct value for each command. I might’ve actually been doing this part wrong all along, so it would probably be better for you to confirm it works as intended.

1 Like