I’m working on a TypeScript project where I’m using Parcel v2.x for bundling, and I want to remove the hash from the output filenames (particularly from index.runtime.js).
I’ve been using the parcel-namer-hashless plugin, and while it successfully removes the hash from most of my files (like favicon.ico, esp32.js, etc.), I’m still encountering issues with the index.runtime.js file. They continue to have a hash in their filenames, despite my efforts to configure the plugin to remove them.
Here’s what I’ve tried so far:
Configured parcel-namer-hashless in package.json:
{
"name": "typescript",
"version": "1.0.0",
"description": "This an example of using esptool-js with parcel and typescript",
"source": "src/index.html",
"scripts": {
"genDocs": "cd ../.. && npm run genDocs && mkdir -p examples/typescript/dist && cp -r docs examples/typescript/dist && cd examples/typescript",
"dev": "npm run clean && npm run genDocs && parcel src/index.html",
"build": "npm run clean && npm run genDocs && parcel build src/index.html --no-optimize --public-url ./",
"clean": "rimraf dist .parcel-cache",
"test": "echo \"Error: no test specified\" && exit 1",
"postbuild": "mv dist/index.runtime.*.js dist/index.runtime.js"
},
"parcelIgnore": [
"./docs/.+"
],
"author": "",
"license": "ISC",
"devDependencies": {
"parcel": "^2.8.3",
"parcel-namer-hashless": "^1.0.6",
"parcel-resolver-ignore": "^2.1.5",
"rimraf": "^4.1.2",
"typescript": "^4.9.4",
"web-serial-polyfill": "^1.0.15"
},
"parcel-namer-hashless": {
"include": [
".js$",
".ico$"
]
}
}
Configured the .parcelrc file:
{
"extends": "@parcel/config-default",
"resolvers": [
"parcel-resolver-ignore",
"..."
],
"namers": [
"parcel-namer-hashless"
]
}
Cleared the Parcel cache and rebuilt multiple times.
Despite these efforts, index.runtime.js still havs hashes appended to them. The build output looks like this:
dist/index.runtime.298534a0.js
All other files are correctly renamed without hashes.
I also tried adding a post-build script to manually rename the files, but I’d prefer to have a cleaner solution within Parcel if possible. My Questions:
How can I ensure that index.runtime.js are generated without hashes using Parcel and parcel-namer-hashless? Is there a better way to configure Parcel or the parcel-namer-hashless plugin to handle runtime files? Is there a specific reason why Parcel insists on adding a hash to runtime files, and how can this behavior be overridden?
Any advice or suggestions would be greatly appreciated. Thanks in advance!
I also tried it with a custom-namer.js with the same output:
const { Namer } = require('@parcel/plugin');
module.exports = new Namer({
async name({ bundle }) {
const entryAsset = bundle.getMainEntry();
// Handle the main HTML or JS entry point (index)
if (entryAsset && entryAsset.filePath.includes('index')) {
// Check if it's a runtime file (Parcel auto-generates runtime bundles)
if (bundle.type === 'js' && !bundle.isInline && bundle.needsStableName && bundle.isSplittable === false) {
// Ensure the runtime script is named without a hash
return `index.runtime.js`;
}
// For regular 'index.js' or 'index.html'
return `index.${bundle.type}`;
}
// For all other entry points, use the original file name without hash
if (entryAsset) {
const originalName = entryAsset.filePath.split('/').pop().split('.')[0]; // Get original name without extension
return `${originalName}.${bundle.type}`;
}
// Fallback for other dynamically generated chunks or assets
const id = bundle.id.slice(0, 8); // Shorten bundle ID to 8 characters for uniqueness
return `bundle-${id}.${bundle.type}`;
}
});
I think there is no way to custom the *.runtime.*.js
file name.
There are comments referencing this behavior in applyRuntimes.js.
As you can see, the file name is replaced with .runtime.${hashReference}.${bundle.type}
.
function nameRuntimeBundle(
bundle: InternalBundle,
siblingBundle: InternalBundle,
) {
// We don't run custom namers on runtime bundles as the runtime assumes that they are
// located at the same nesting level as their owning bundle. Custom naming could
// be added in future as long as the custom name is validated.
let {hashReference} = bundle;
let name = nullthrows(siblingBundle.name)
// Remove the existing hash from standard file patterns
// e.g. 'main.[hash].js' -> 'main.js' or 'main~[hash].js' -> 'main.js'
.replace(new RegExp(`[\\.~\\-_]?${siblingBundle.hashReference}`), '')
// Ensure the file ends with 'runtime.[hash].js'
.replace(`.${bundle.type}`, `.runtime.${hashReference}.${bundle.type}`);
bundle.name = name;
bundle.displayName = name.replace(hashReference, '[hash]');
}
After the custom Namer
's name
function is applied to the file names, the *.runtime.*.js
file is created, and this nameRuntimeBundle()
function is then executed. This means it is impossible to change the name using a Namer
.
(The parcel-namer-hashless
package also uses Namer
, so it produces the same result.)
You can add the following configuration in package.json
to avoid generating *.runtime.*.js
by skipping the above code (and its related code):
"@parcel/runtime-js": {
"splitManifestThreshold": 1000000 // default is 100,000 bytes
},
If the bundle size exceeds the threshold, it will attach priority: parallel
. Parallel loading is likely required to resolve dependencies using the *.runtime.*.js
file (I think that's probably the case).
Reference: https://github.com/parcel-bundler/parcel/discussions/9490