Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use human-readable variable names instead of $a3b86e0361d88a0cf0c1608dc4320503$$interop$default #6092

Open
fregante opened this issue Apr 8, 2021 · 10 comments

Comments

@fregante
Copy link
Contributor

fregante commented Apr 8, 2021

As discussed in #4687

Also possibly related: #5862

🙋 feature request

Parcel generates long variable names for imported modules, presumably to avoid conflict. Rollup however avoids conflict and still preserves the original variable names where possible: example. Can this be done in Parcel too?

🤔 Expected Behavior

import square from './square.js';
square();

Should generate something like:

function square ( x ) {
	return x * x;
}
square();

😯 Current Behavior

… instead it generates something like:

function $c85f682ae4f0d253760e24f58010a7ef$export$default ( x ) {
	return x * x;
}
$c85f682ae4f0d253760e24f58010a7ef$export$default();

💁 Possible Solution

Rollup’s code might have answers, but I'm not familiar with it.

🔦 Context

My extension has been suddenly rejected by the Chrome Extension Store due to "obfuscation". I'm trying to resolve this issue with them and I'll probably temporarily use variable name mangling as an interim solution, but mangled variable names aren't particularly human-readable either.

Also, generally speaking, readable variable names can help during development, where minification doesn't apply.

@mischnic
Copy link
Member

mischnic commented Apr 8, 2021

I'm curious what the output looks like with Webpack's module concatenation mode...

@fregante
Copy link
Contributor Author

fregante commented Apr 8, 2021

heh, related 2018 closed request webpack/webpack#5691

Input changed to avoid being detected as a noop:

export default function square ( x ) {
	console.log(x * x);
}
❯ webpack --entry ./main.js --mode development --no-devtool
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./square.js":
/*!*******************!*\
  !*** ./square.js ***!
  \*******************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (/* binding */ square)
/* harmony export */ });
function square ( x ) {
	console.log(x * x);
}


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__webpack_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!*****************!*\
  !*** ./main.js ***!
  \*****************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _square_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./square.js */ "./square.js");

(0,_square_js__WEBPACK_IMPORTED_MODULE_0__.default)(1);

})();

/******/ })()
;

However, incredibly, once I let it minify it:

webpack --entry ./main.js --mode production --no-devtool
(()=>{"use strict";console.log(1)})();

@fregante
Copy link
Contributor Author

fregante commented Apr 8, 2021

As a direct comparison:

❯ npx parcel --version
2.0.0-nightly.635+f5962737
❯ npx parcel build main.js --no-source-maps --no-optimize
(function () {
  function $c85f682ae4f0d253760e24f58010a7ef$export$default(x) {
    console.log(x * x);
  }
  $c85f682ae4f0d253760e24f58010a7ef$export$default(1);
})();

Already much better than Webpack 👆 🙌 although the variable names do seem more obfuscated

❯ npx parcel build main.js --no-source-maps
!function(){var o;o=1,console.log(o*o)}();

@danieltroger
Copy link
Contributor

I've also dreamt about this when debugging unminified builds. But parcel's scope hoisting is so awesome that I haven't dared to complain 😁

@mischnic
Copy link
Member

mischnic commented Apr 8, 2021

These two use a module registry at runtime
webpack --entry ./main.js --mode development --no-devtool
parcel build main.js --no-scope-hoist

These two perform module concatentation/scope hoisting:
webpack --entry ./main.js --mode production --no-devtool
parcel build main.js

@devongovett
Copy link
Member

In most cases, this doesn't matter because the code will be run through a minifier later anyway. Variables are only renamed in production builds, so dev builds don't have this problem. I would say the main use case for this is when building libraries, where you aren't running a minifier. So I see this as a different kind of "optimization" (for readability as opposed to size).

I think an optimizer plugin could possibly be built to do this. Source maps should have enough information - each mapping has an optional name property, which is typically used in debuggers to show you the original variable names rather than minified ones, but we could repurpose it to remap the renamed vars back to prettier ones (and dedup along the way).

I don't think it's possible in the current architecture to keep the original names from the beginning, because files are processed in parallel and there's no way to know when a collision might occur.

@kherock
Copy link
Contributor

kherock commented Oct 18, 2021

I think simply moving the hexadecimal portion of the generated name to the end would go a long way to improve stack trace readability. In situations where sourcemaps aren't available, I think the generated names are descriptive enough, it's just the hex portion at the beginning that throws me off as I'm scanning through a stack trace or stepping through code. The real pain is needing to scroll horizontally just to get an idea of what function or variable name is being used.

@AlexVipond
Copy link

I ran into this while bundling TS files into standalone browser scripts that expose functions and variables in global scope (legacy codebase, includes plain JS). When Parcel changes variables names in global scope, it prevents other code from accessing those things.

I switched to the TSC transformer which doesn't change variable names.

@wcjohnson
Copy link

wcjohnson commented Oct 6, 2023

I'm also being impacted by this issue. Stack traces throughout my infrastructure are displaying hashed function names for functions exported from intermediate/bundled-in modules when running Node executables deployed from a Parcel build, e.g.

Connection attempt # 1 failed with error Error: Missing required configuration environment variable: MYSQL_HOST
    at $d0ba45a5474fad9a$export$d812c8e1906863d0 (/tools/clidba/src/value.ts:33:15)

While it is possible to trace this back correctly thanks to the sourcemap, it is often possible for a devops engineer to diagnose an issue instantly when the function names are readable, rather than have to search the codebase. I would propose that the long export hash be modified so at the very least it includes the name of the original exported function.

I ran into this while bundling TS files into standalone browser scripts that expose functions and variables in global scope (legacy codebase, includes plain JS). When Parcel changes variables names in global scope, it prevents other code from accessing those things.

I switched to the TSC transformer which doesn't change variable names.

For the record, switching to the TSC transformer as suggested here may help in some cases but it does not stop Parcel from mangling the names of intermediate ES module exports when building a bundle.

@kungfooman
Copy link

kungfooman commented Dec 19, 2023

In most cases, this doesn't matter because the code will be run through a minifier later anyway. Variables are only renamed in production builds, so dev builds don't have this problem.

Hi @devongovett, what is the current way to create a dev build? I searched around and found:

NODE_ENV=development parcel build <entrypoint> --no-minify

--no-minify results in error: unknown option '--no-minify' and NODE_ENV=development still causes unreadable variable names.

I also tested --no-optimize, to no avail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants