tools/__tasks__/compile/hash/index.mjs (154 lines of code) (raw):
import path from 'node:path';
import fs from 'node:fs';
import glob from 'glob';
import hasha from 'hasha';
import cpFile from 'cp-file';
import mkdirp from 'mkdirp';
import pify from 'pify';
const writeFile = pify(fs.writeFile);
import clean from './clean.mjs';
import { paths } from '../../config.mjs';
/** @type {import('listr2').ListrTask} */
const task = {
title: 'Version assets',
task: (ctx, task) =>
task.newListr(
[
clean,
{
title: 'Hash assets',
task: () => {
const webpackRegex = /graun\./;
const webpackChunkRegex = /chunk/;
const sourcemapRegex = /\.map$/;
// create the hashed asset map for all files in target
const assetMap = glob
.sync('**/!(*.map)', {
nodir: true,
cwd: paths.target,
})
.reduce((map, assetPath) => {
const assetLocation = path.resolve(
paths.target,
assetPath,
);
const hasSourceMap = fs.existsSync(
`${assetLocation}.map`,
);
// webpack bundles come pre-hashed, so we won't hash them, just add them
if (webpackRegex.test(assetPath)) {
const sourcemap = hasSourceMap
? {
[`${assetPath}.map`]: `${assetPath}.map`,
}
: {};
return Object.assign(
map,
{ [assetPath]: assetPath },
sourcemap,
);
}
// hash everything else as normal
const assetHash = hasha.fromFileSync(
assetLocation,
{
algorithm: 'md5',
},
);
const hashedPath = path.join(
path.dirname(assetPath),
assetHash,
path.basename(assetPath),
);
const sourcemap = hasSourceMap
? {
[`${assetPath}.map`]: `${hashedPath}.map`,
}
: {};
return Object.assign(
map,
{ [assetPath]: hashedPath },
sourcemap,
);
}, {});
return Promise.all(
// copy all the built files to their hash locations
Object.keys(assetMap).map((asset) =>
cpFile(
path.resolve(paths.target, asset),
path.resolve(paths.hash, assetMap[asset]),
),
),
)
.then(() => {
// we need unhashed keys for webpack entry bundles so we can refer to them in play templates.
// since they arrived ready-hashed, we need to add some new ones from the hashed ones...
// get the webpack entry bundles
const webpackEntryBundles = Object.keys(
assetMap,
).filter(
(key) =>
webpackRegex.test(key) &&
!webpackChunkRegex.test(key) &&
!sourcemapRegex.test(key),
);
// create a new key for each one and add them them to asset map
return Object.assign(
{},
assetMap,
webpackEntryBundles.reduce(
(map, webpackEntryBundle) =>
Object.assign(map, {
[webpackEntryBundle.replace(
/(javascripts\/)(.+\/)/,
'$1',
)]:
assetMap[
webpackEntryBundle
],
}),
{},
),
webpackEntryBundles.reduce(
(map, webpackEntryBundle) =>
Object.assign(map, {
[webpackEntryBundle.replace(
/(javascripts\/commercial\/)(.+\/)/,
'$1',
)]:
assetMap[
webpackEntryBundle
],
}),
{},
),
);
})
.then(
(
normalisedAssetMap, // save the asset map
) =>
mkdirp(
path.resolve(paths.hash, 'assets'),
).then(() =>
writeFile(
path.resolve(
paths.hash,
'assets',
'assets.map',
),
JSON.stringify(
normalisedAssetMap,
null,
4,
),
),
),
);
},
},
],
{ concurrent: false },
),
};
export default task;