javascriptnode.jstypescriptnpmdeployment

How to Remove Hash from index.js and index.runtime.js in Parcel with parcel-namer-hashless?


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}`;
  }
});


Solution

  • 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