Skip to content

Commit

Permalink
Consistent file hashes (#11628)
Browse files Browse the repository at this point in the history
* Fix css plugin having inconsistent builds in different environments

Astro's css plugin generates chunk ids that include a hash of all of
the chunk's parent ids.  These ids are currently the absolute file paths
of the parent files.  The generated chunk ids are then inserted into
those pages as import statements.

Because these import statements include a hash based on these absolute
file paths, this causes rollup to generate different hashes for those
pages when a build is run in different environments.  The exact same
project will produce identical assets with different filenames when
built on different machines, or when built from different directories
on the same machine, etc.

To fix this, I've stripped out the working directory of these file paths
before they are added to the hash.  This means that the hash will still
change if the files referencing it chacnge (which I believe is the
intended behavior), but will be stable if the entire project is built
in different environments.

* add changeset

* fixup! use settings.config.root and vite's normalizePath

I've chosen to update the function signature of shortHashedName
to match createSlugger's, so it now accepts the settings object
and returns the actual hashing function.  This way, createSlugger's
function signature doesn't need to update to needlessly accept
an additional argument.

* fixup! remove unused import

* Update .changeset/young-pillows-shave.md

Co-authored-by: Bjorn Lu <[email protected]>

* fixup! use fileURLtoPath

---------

Co-authored-by: Matt Lee <[email protected]>
Co-authored-by: Bjorn Lu <[email protected]>
  • Loading branch information
3 people authored Aug 7, 2024
1 parent ca45fd9 commit 9aaf58c
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-pillows-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Ensures consistent CSS chunk hashes across different environments
31 changes: 23 additions & 8 deletions packages/astro/src/core/build/css-asset-name.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { GetModuleInfo, ModuleInfo } from 'rollup';

import crypto from 'node:crypto';
import npath from 'node:path';
import { fileURLToPath } from 'node:url';
import { normalizePath } from 'vite';
import type { AstroSettings } from '../../@types/astro.js';
import { viteID } from '../util.js';
import { getTopLevelPageModuleInfos } from './graph.js';
Expand All @@ -13,19 +15,32 @@ const confusingBaseNames = ['404', '500'];
// The short name for when the hash can be included
// We could get rid of this and only use the createSlugger implementation, but this creates
// slightly prettier names.
export function shortHashedName(id: string, ctx: { getModuleInfo: GetModuleInfo }): string {
const parents = getTopLevelPageModuleInfos(id, ctx);
return createNameHash(
getFirstParentId(parents),
parents.map((page) => page.id)
);
export function shortHashedName(settings: AstroSettings) {
return function (id: string, ctx: { getModuleInfo: GetModuleInfo }): string {
const parents = getTopLevelPageModuleInfos(id, ctx);
return createNameHash(
getFirstParentId(parents),
parents.map((page) => page.id),
settings
);
};
}

export function createNameHash(baseId: string | undefined, hashIds: string[]): string {
export function createNameHash(
baseId: string | undefined,
hashIds: string[],
settings: AstroSettings
): string {
const baseName = baseId ? prettifyBaseName(npath.parse(baseId).name) : 'index';
const hash = crypto.createHash('sha256');
const root = fileURLToPath(settings.config.root);

for (const id of hashIds) {
hash.update(id, 'utf-8');
// Strip the project directory from the paths before they are hashed, so that assets
// that import these css files have consistent hashes when built in different environments.
const relativePath = npath.relative(root, id);
// Normalize the path to fix differences between windows and other environments
hash.update(normalizePath(relativePath), 'utf-8');
}
const h = hash.digest('hex').slice(0, 8);
const proposedName = baseName + '.' + h;
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/core/build/plugins/plugin-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
const assetFileNames = outputOptions.assetFileNames;
const namingIncludesHash = assetFileNames?.toString().includes('[hash]');
const createNameForParentPages = namingIncludesHash
? assetName.shortHashedName
? assetName.shortHashedName(settings)
: assetName.createSlugger(settings);

extendManualChunks(outputOptions, {
Expand All @@ -94,7 +94,7 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
if (hasAssetPropagationFlag(pageInfo.id)) {
// Split delayed assets to separate modules
// so they can be injected where needed
const chunkId = assetName.createNameHash(id, [id]);
const chunkId = assetName.createNameHash(id, [id], settings);
internals.cssModuleToChunkIdMap.set(id, chunkId);
return chunkId;
}
Expand Down

0 comments on commit 9aaf58c

Please sign in to comment.