Parcel: [Craft CMS] Production Build Asset Hash?

Created on 1 Feb 2019  ยท  3Comments  ยท  Source: parcel-bundler/parcel

โ” Question

Howdy! I've been using Parcel on a Craft CMS project and so far everything has been going swimmingly. I've noticed, though, that my production JS + CSS assets don't have hashes in their filenames, which has caused some issues re: cache busting on my live site. I'd love to know what I'm doing wrong, and how I might be able to fix this.

๐Ÿ”ฆ Context

Craft uses the Twig templating language which is pretty neat. Unfortunately, I can't use Parcel in the way that most of your documentation is written (e.g., parcel index.html), and I've had to resort to some hacky workarounds. For example, my dev script looks thusly:

parcel watch src/js/app.js -d web/tmp --https

And in my Twig template, I conditionally include the stylesheet (and JS file) that lives in the .tmp directory during development.

{# {% if craft.app.config.general.devMode %}
 <link rel="stylesheet" href="/tmp/app.css">
{% else %}
  <link rel="stylesheet" href="/dist/app.css">
{% endif %} #}

For building my project, I have a similar script which dumps my assets into the dist directory.

parcel build src/js/app.js -d web/dist

Trouble is, these built assets _never_ have a hash added to the end of the filename. They always remain app.css or app.js. I do a bunch of code splitting in app.js and _those_ bundles do in fact have the hash in their filename. What gives?


I'm really just trying to enable hashing of those production asset filenames. Couple that with some kind of manifest.json, and I should be able to programmatically choose with dist files to use in production (ร  la https://github.com/clubstudioltd/craft-asset-rev)

In an ideal world, Parcel would be able to do HMR for Twig files (and not just HTML files). Not sure if there's something easy I'm missing here.

Hope this all makes sense!

๐ŸŒ Your Environment

| Software | Version(s) |
| ---------------- | ---------- |
| Parcel |1.10.3
| Node |10.14
| Yarn |1.13.0
| Operating System | Mojave 10.14.2

Most helpful comment

In an ideal world, Parcel would be able to do HMR for Twig files (and not just HTML files). Not sure if there's something easy I'm missing here.

You could create a plugin that can process Twig files and processes js/css imports.

Trouble is, these built assets never have a hash added to the end of the filename.

Parcel doesn't add hashes for the entrypoints (the files you pass to the cli, src/js/app.js in your case):
https://github.com/parcel-bundler/parcel/blob/791b4b80462fee453d82f4c711b86553daa3427e/packages/core/parcel-bundler/src/Bundle.js#L147-L148


I would solve this like this:
To get hashed names, include your assets in a dummy html file:

<!-- entry.html of script below -->
<script src="./app.js"></script>
<link href="./style.css"/>

Using a script instead of the cli gives you information about the changed names:

// build.js
const Bundler = require("parcel-bundler")
const path = require("path");
const fs = require("fs");

const entryFiles = "./src/entry.html";
const outDir = process.env.NODE_ENV === "production" ?  "web/dist" : "web/tmp";

const bundler = new Bundler(entryFiles, {
  outDir,
  https: true
});

bundler.on('bundled', (bundle) => {
    let manifest = {};
    for(let b of bundle.childBundles) {
        manifest[b.entryAsset.relativeName] = path.relative(outDir, b.name);
    }
    fs.writeFileSync(outDir+"/manifest.json", JSON.stringify(manifest));
});

bundler.bundle();

NODE_ENV=production node build.js to build for production
nodeNODE_ENV=production node index.jsto build for production.js to watch for development (not sure if you even need to manifest.json in this case)

All 3 comments

In an ideal world, Parcel would be able to do HMR for Twig files (and not just HTML files). Not sure if there's something easy I'm missing here.

You could create a plugin that can process Twig files and processes js/css imports.

Trouble is, these built assets never have a hash added to the end of the filename.

Parcel doesn't add hashes for the entrypoints (the files you pass to the cli, src/js/app.js in your case):
https://github.com/parcel-bundler/parcel/blob/791b4b80462fee453d82f4c711b86553daa3427e/packages/core/parcel-bundler/src/Bundle.js#L147-L148


I would solve this like this:
To get hashed names, include your assets in a dummy html file:

<!-- entry.html of script below -->
<script src="./app.js"></script>
<link href="./style.css"/>

Using a script instead of the cli gives you information about the changed names:

// build.js
const Bundler = require("parcel-bundler")
const path = require("path");
const fs = require("fs");

const entryFiles = "./src/entry.html";
const outDir = process.env.NODE_ENV === "production" ?  "web/dist" : "web/tmp";

const bundler = new Bundler(entryFiles, {
  outDir,
  https: true
});

bundler.on('bundled', (bundle) => {
    let manifest = {};
    for(let b of bundle.childBundles) {
        manifest[b.entryAsset.relativeName] = path.relative(outDir, b.name);
    }
    fs.writeFileSync(outDir+"/manifest.json", JSON.stringify(manifest));
});

bundler.bundle();

NODE_ENV=production node build.js to build for production
nodeNODE_ENV=production node index.jsto build for production.js to watch for development (not sure if you even need to manifest.json in this case)

@mischnic Thanks for the thorough reply โ€“ this is _extremely_ helpful.

agree it's working good

only thing I've changed is manifest pretty format:

-    fs.writeFileSync(outDir + '/manifest.json', JSON.stringify(manifest)
+    fs.writeFileSync(outDir + '/manifest.json', JSON.stringify(manifest, null, '\t'))
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dotdash picture dotdash  ยท  3Comments

adamreisnz picture adamreisnz  ยท  3Comments

philipodev picture philipodev  ยท  3Comments

donaldallen picture donaldallen  ยท  3Comments

davidnagli picture davidnagli  ยท  3Comments