Skip to content

[🐞] Running a build results in variable content hash on generated assets #267

@maxrpeterson

Description

@maxrpeterson

Which component is affected?

Build process

Describe the bug

What is happening

Given the same source code, it is possible to have different assets generated by a production build, with different content hash filenames.

What is expected

Given the same source code, there is no variability in the way assets are generated, and the same assets (and, by extension, the content hashes in those assets' filenames) are generated on every production build run.

Why this is a problem

When creating a production build for an Astro + Qwik app, there can be scenarios where one run of the build has generated server assets which point to non-existent client side assets. This is especially true for deployment scenarios where client assets are pushed to an external location (say, an AWS S3 bucket). If for some reason, a second build is run, there's no guarantee that that run's built server assets will point to the same client side assets as were built and uploaded to the external location previously.

Reproduction

https://github.com/maxrpeterson/qwik-astro-test-build-asset-hash-changing

Steps to reproduce

Reproduction steps

  1.  npm i
     npm run build
  2. Make note of the hash prefix of dist/assets/*-bundle-graph.json file
  3. Without changing the source code at all, keep running npm run build until a different hashed *-bundle-graph.json file is seen
  4. Compare file tree of dist folder from one build to the next (it helps to copy each run to a separate location to compare between builds)

System Info

System:
    OS: macOS 15.5
    CPU: (12) arm64 Apple M2 Max
    Memory: 3.46 GB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.17.1 - ~/.nvm/versions/node/v22.17.1/bin/node
    npm: 10.9.2 - ~/.nvm/versions/node/v22.17.1/bin/npm
  Browsers:
    Chrome: 138.0.7204.158
    Safari: 18.5

Additional Information

Additional Information

The two variations of the built code I've observed have also been committed to the example repo for demonstration purposes. It's possible there are more than two possible build variations, but I've definitely seen at least the two.

Note that when the hash prefix of the *-bundle-graph.json file is different, there are also changes to several of the other assets in the dist folder. Many of these are "snowball effect" changes, where a change to one generated module causes the content hash of that module to be different, which is then imported into another module with the different content hash, causing that file to in turn have a different content hash, and so on.

However, if you look at the files that have meaningful changes, you'll see that the changes are from the ordering of variables in the files.

// dist-run-CcgmEgmB/build/q-DnyphD2t.js
const o = "_componentB_1n717_1",
  t = {
    componentB: o
  },
  n = "_componentA_fov7x_1",
  s = {
    componentA: n
  };
export {
  t as a, s
};
// dist-run-BnTTJgbo/build/q-DJnZVtP7.js
const o = "_componentA_fov7x_1",
  t = {
    componentA: o
  },
  n = "_componentB_1n717_1",
  s = {
    componentB: n
  };
export {
  s as a, t as s
};

Removing the import of the styles (e.g. import styles from './componentA.module.scss) from both components in src/components seems to fix the issue and prevent the variable declarations from swapping and causing builds with different hashes. I haven't been able to reproduce this issue in a simple reproduction app without the style imports, but I also don't have any insight as to whether the CSS modules imports are integral to the issue or not.

In the real app where we first noticed this, when inspecting one of the generated modules with changes between builds, it appears that ordering of non-CSS imports is the cause of the variability in at least one case. That is, one module is being imported out of order with another in a generated file, causing the change in the file and by extension the content hash.

Also worth noting, in the real app where we first noticed this issue (not just a minimal reproduction app), we are using SCSS for style files, but as demonstratred here in this minimal reproduction app, the problem is reproducible with vanilla CSS as well. Neither changing to use lightningcss as the CSS transformer, nor using the sass-embedded library (instead of regular sass library) had any effect on the issue, in both our real app and this minimal reproduction app.

Other things already tried

There was this issue in Vite where some users said they were seeing similar issues and were able to fix it using the build.rollupOptions.maxParallelFileOps vite config property. I tried this as well, however was still seeing variability in the content hashes after running the build.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions