Open 0xdevalias opened 12 months ago
Looking through the rest of the webpack bundle code (Ref) for the
22830
module, we find it inmain.js
; which after unpacking, becomesmodule-22830.js
:⇒ npx @wakaru/unpacker main.js -o ./main-unpacked/ # ..snip.. ⇒ npx @wakaru/unminify ./main-unpacked/* -o ./main-unminified # ..snip..
Unpacked: ```js "use strict";; ; var n = require(59378); function o(e, t) { return ( (function (e) { if (Array.isArray(e)) return e; })(e) || (function (e, t) { var r, n, o = null == e ? null : ("undefined" != typeof Symbol && e[Symbol.iterator]) || e["@@iterator"]; if (null != o) { var a = [], i = !0, u = !1; try { for ( o = o.call(e); !(i = (r = o.next()).done) && (a.push(r.value), !t || a.length !== t); i = !0 ); } catch (e) { (u = !0), (n = e); } finally { try { i || null == o.return || o.return(); } finally { if (u) throw n; } } return a; } })(e, t) || (0, n.N)(e, t) || (function () { throw TypeError( "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." ); })() ); } module.exports = { _: o, _sliced_to_array: o }; ``` Unminified: ``` const { N } = require(59378); function o(e, t) { return ( ((e) => { if (Array.isArray(e)) { return e; } })(e) || ((e, t) => { let r; let n; let o = e == null ? null : (typeof Symbol != "undefined" && e[Symbol.iterator]) || e["@@iterator"]; if (o != null) { const a = []; let i = true; let u = false; try { for ( o = o.call(e); !(i = (r = o.next()).done) && (a.push(r.value), !t || a.length !== t); i = true ) {} } catch (e) { u = true; n = e; } finally { try { if (!i && o.return != null) { o.return(); } } finally { if (u) { throw n; } } } return a; } })(e, t) || N(e, t) || (() => { throw TypeError( "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." ); })() ); } export default { _: o, _sliced_to_array: o, }; ```
module-22830.js
(full source)Originally posted by @0xdevalias in https://github.com/pionxzh/wakaru/issues/49#issuecomment-1818812059
The above code (from the previous issue) imported from var n = require(59378);
, which was also found in main.js
, in module-59378.js
:
module-59378.js
(full source)Based on the regex (/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(
), this appears to be @swc/helper
's _unsupported_iterable_to_array
:
function _unsupported_iterable_to_array(o, minLen) {
if (!o) return;
if (typeof o === "string") return _array_like_to_array(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
}
Which then implies that the above webpacked code can be 're-symbolised' as follows:
o(e, t)
-> _unsupported_iterable_to_array(o, minLen)
F(e, t)
-> _array_like_to_array(o, minLen)
r
-> n
export default { N: o };
-> export default { _unsupported_iterable_to_array };
Since in the above code, F
appears to be _array_like_to_array
, then we can follow the const { F } = require(14770);
import and imply that module-14770.js
(also extracted from main.js
) defines it.
module-14770.js
(full source)@swc/helper
's _array_like_to_array
looks like this; which appears to align with the webpacked code in module-14770.js
:
function _array_like_to_array(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
return arr2;
}
Which implies that the webpacked code can be 're-symbolised' as follows:
n(e, t)
-> _array_like_to_array(arr, len)
for (var r = 0, n = Array(t); r < t; r++) { n[r] = e[r]; }
-> for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
return n;
-> return arr2;
export default { F: n };
-> export default { _array_like_to_array };
The seemingly full list of @swc/helpers
helpers is availabe at:
And more about the swc
compilation setting that controls whether helpers are inlined or made external is available here:
jsc.externalHelpers
: The output code may depend on helper functions to support the target environment. By default, a helper function is inlined into the output files where it is required.You can use helpers from an external module by enabling
externalHelpers
and the helpers code will be imported by the output files fromnode_modules/@swc/helpers
.While bundling, this option will greatly reduce your file size.
You must add
@swc/helpers
as a dependency in addition to@swc/core
.
Looking at module-10604.js
, we can see that it's a React component:
Unpacked: ```js var r = require(39324), a = require(22830), i = require(4337), o = require(35250), s = require(19841), l = require(70079), u = require(34303), d = require(38317); function c() { var e = (0, i._)(["absolute right-0 top-1/2 -translate-y-1/2"]); return ( (c = function () { return e; }), e ); } exports.Z = l.forwardRef(function (e, t) { var n = e.name, i = e.placeholder, u = e.type, c = e.displayName, h = e.onChange, g = e.onBlur, m = e.value, p = e.saveOnBlur, v = e.icon, x = e.onInputIconClick, b = e.className, y = e.autoComplete, w = e.autoFocus, j = e.onPressEnter, _ = (0, a._)((0, l.useState)(m), 2), C = _[0], M = _[1], k = (0, l.useCallback)( function (e) { null == g || g(e), p && M(e.target.value); }, [g, p] ), T = (0, l.useCallback)( function (e) { null == h || h(e), p && M(e.target.value); }, [h, p] ), N = (0, l.useCallback)( function (e) { "Enter" === e.key && j && (e.preventDefault(), j()); }, [j] ); (0, l.useEffect)( function () { M(m); }, [m] ); var S = (0, r._)({}, p ? {} : { value: m }, p ? { value: C } : {}); return (0, o.jsxs)("div", { className: (0, s.Z)("rounded-md border border-gray-300 px-3 py-2 shadow-sm focus-within:border-indigo-600 focus-within:ring-1 focus-within:ring-indigo-600 dark:bg-gray-700", b), children: [(0, o.jsx)("label", { htmlFor: n, className: "block text-xs font-medium text-gray-900 dark:text-gray-100", children: c }), (0, o.jsxs)("div", { className: (0, s.Z)(c && "mt-1", "relative"), children: [(0, o.jsx)("input", (0, r._)({ ref: t, type: u, name: n, id: n, className: (0, s.Z)("block w-full border-0 p-0 text-gray-900 placeholder-gray-500 outline-none focus:ring-0 dark:bg-gray-700 dark:text-gray-100 sm:text-sm", v && "pr-6"), placeholder: i, onBlur: k, onChange: T, onKeyDown: N, autoComplete: y, autoFocus: w }, S)), v && (0, o.jsx)(f, { onClick: x, children: (0, o.jsx)(d.ZP, { icon: v }) })] })] }); }); var f = u.Z.button(c()); ``` Unminified: ```js const { _: _$1 } = require(39324); const { _: _$0 } = require(22830); const { _ } = require(4337); const { jsxs, jsx } = require(35250); const { Z: Z$0 } = require(19841); const l = require(70079); const { useState, useCallback, useEffect } = l; const u = require(34303); const d = require(38317); function c() { const e = _(["absolute right-0 top-1/2 -translate-y-1/2"]); c = () => e; return e; } export const Z = l.forwardRef((e, t) => { const { name, placeholder, type, displayName, onChange, onBlur, value, saveOnBlur, icon, onInputIconClick, className, autoComplete, autoFocus, onPressEnter, } = e; const [C, M] = _$0(useState(value), 2); const k = useCallback( (e) => { if (onBlur != null) { onBlur(e); } if (saveOnBlur) { M(e.target.value); } }, [onBlur, saveOnBlur] ); const T = useCallback( (e) => { if (onChange != null) { onChange(e); } if (saveOnBlur) { M(e.target.value); } }, [onChange, saveOnBlur] ); const N = useCallback( (e) => { if (e.key === "Enter" && onPressEnter) { e.preventDefault(); onPressEnter(); } }, [onPressEnter] ); useEffect(() => { M(value); }, [value]); const S = _$1( {}, saveOnBlur ? {} : { value: value }, saveOnBlur ? { value: C } : {} ); return (
module-10604.js
(full source)); }); var F = u.Z.button(c()); ```{icon &&{ }} Originally posted by @0xdevalias in https://github.com/pionxzh/wakaru/issues/49#issuecomment-1818812059
Looking at the imports of the unminified source, we see:
const { _: _$1 } = require(39324);
const { _: _$0 } = require(22830);
const { _ } = require(4337);
const { jsxs, jsx } = require(35250);
const { Z: Z$0 } = require(19841);
const l = require(70079);
const { useState, useCallback, useEffect } = l;
const u = require(34303);
const d = require(38317);
Searching our webpacked code (Ref), we find these modules defined in the following locations:
main.js
39324
: module-39324.js
22830
: module-22830.js
pages/_app.js
4337
: module-4337.js
19841
: module-19841.js
34303
: module-34303.js
38317
: module-38317.js
framework.js
35250
: module-35250.js
{ jsxs, jsx }
70079
: module-70079.js
{ useState, useCallback, useEffect }
Which we can unpack into their individual module files as follows:
⇒ cd ./unpacked/_next/static/chunks
⇒ npx @wakaru/unpacker main.js -o ./main-unpacked/
Generated 98 modules from main.js to main-unpacked (2,949.1ms)
⇒ npx @wakaru/unminify ./main-unpacked/* -o ./main-unminified
# ..snip..
⇒ npx @wakaru/unpacker pages/_app.js -o ./pages/_app-unpacked/
Generated 213 modules from pages/_app.js to pages/_app-unpacked (24,049ms)
⇒ npx @wakaru/unminify ./pages/_app-unpacked/* -o ./pages/_app-unminified
# ..snip..
⇒ npx @wakaru/unpacker framework.js -o ./framework-unpacked/
Generated 9 modules from framework.js to framework-unpacked (3,800.3ms)
⇒ npx @wakaru/unminify ./framework-unpacked/* -o ./framework-unminified
# ..snip..
framework.js
framework.js
seems to mostly be React related.
module-35250.js
is just module.exports = require(82875);
, and module-82875.js
is:
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
module-70079.js
is just module.exports = require(99504);
, and module-99504.js
is:
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
main.js
module-39324.js
appears to be @swc/helpers
_object_spread
module-39324.js
(full source)Which would imply that the const { j } = require(96237);
import would be import { _define_property } from "./_define_property.js";
(which, looking at the webpacked code, module-96237.js
does appear to be):
module-22830.js
we already figured out in another issue is @swc/helpers
_sliced_to_array
:
pages/_app.js
module-4337.js
appears to be @swc/helpers
_tagged_template_literal
module-4337.js
(full source)Not sure what module-19841.js
is exactly.. but it seems to export a .Z
that is used in a lot of JSX className
fields.. so it's probably something like classnames
/ clsx
/ tw-merge
or similar maybe?
Potentially related:
module-19841.js
(full source)module-34303
is explored in depth in the following issue, and appears to be related to styled-components
/ Tailwind-Styled-Component
/ similar libs:
module-38317.js
looks like it might be app specific code containing a bunch of svg
's in JSX
format, etc.
It imports from some more libraries:
const { _: _$1 } = require(39324);
const { _: _$0 } = require(71209);
const { _ } = require(70216);
const { jsx, jsxs } = require(35250);
const { Z: Z$0 } = require(19841);
With the unique ones we haven't yet looked at being:
main.js
71209
: module-71209.js
70216
: module-70216.js
module-71209.js
appears to be @swc/helpers
_object_spread_props
module-71209.js
(full source)module-70216.js
appears to be @swc/helpers
_object_without_properties
module-70216.js
(full source)The full list of @swc/helpers
is here:
Searching my webpacked code (Ref) for functions that have a similar format to the helpers:
⇒ find . -type f -exec grep -E '_[[:alnum:]_]+\s*:\s*function' {} +
# ..snip..
# These don't seem to be @swc/helpers related?
./653.js: __assign: function () {
./653.js: __asyncDelegator: function () {
./653.js: __asyncGenerator: function () {
./653.js: __asyncValues: function () {
./653.js: __await: function () {
./653.js: __awaiter: function () {
./653.js: __classPrivateFieldGet: function () {
./653.js: __classPrivateFieldIn: function () {
./653.js: __classPrivateFieldSet: function () {
./653.js: __createBinding: function () {
./653.js: __decorate: function () {
./653.js: __exportStar: function () {
./653.js: __extends: function () {
./653.js: __generator: function () {
./653.js: __importDefault: function () {
./653.js: __importStar: function () {
./653.js: __makeTemplateObject: function () {
./653.js: __metadata: function () {
./653.js: __param: function () {
./653.js: __read: function () {
./653.js: __rest: function () {
./653.js: __spread: function () {
./653.js: __spreadArray: function () {
./653.js: __spreadArrays: function () {
./653.js: __values: function () {
# ..snip..
# These don't seem to be @swc/helpers related?
./1f110208.js: __parse: function (e, t) {
./1f110208.js: __renderToHTMLTree: function (e, t) {
./1f110208.js: __setFontMetrics: function (e, t) {
./1f110208.js: __defineMacro: function (e, t) {
# ..snip..
# These do seem to be @swc/helpers related
./main.js: _async_to_generator: function () {
./main.js: _class_call_check: function () {
./main.js: _construct: function () {
./main.js: _create_class: function () {
./main.js: _create_super: function () {
./main.js: _inherits: function () {
./main.js: _interop_require_default: function () {
./main.js: _interop_require_wildcard: function () {
./main.js: _object_spread: function () {
./main.js: _object_spread_props: function () {
./main.js: _object_without_properties: function () {
./main.js: _sliced_to_array: function () {
./main.js: _to_consumable_array: function () {
./main.js: _ts_generator: function () {
./main.js: _type_of: function () {
./main.js: _wrap_native_super: function () {
# ..snip..
# These don't seem to be @swc/helpers related?
./pages/payments/success.js: __assign: function () {
./pages/payments/success.js: __asyncDelegator: function () {
./pages/payments/success.js: __asyncGenerator: function () {
./pages/payments/success.js: __asyncValues: function () {
./pages/payments/success.js: __await: function () {
./pages/payments/success.js: __awaiter: function () {
./pages/payments/success.js: __classPrivateFieldGet: function () {
./pages/payments/success.js: __classPrivateFieldIn: function () {
./pages/payments/success.js: __classPrivateFieldSet: function () {
./pages/payments/success.js: __createBinding: function () {
./pages/payments/success.js: __decorate: function () {
./pages/payments/success.js: __exportStar: function () {
./pages/payments/success.js: __extends: function () {
./pages/payments/success.js: __generator: function () {
./pages/payments/success.js: __importDefault: function () {
./pages/payments/success.js: __importStar: function () {
./pages/payments/success.js: __makeTemplateObject: function () {
./pages/payments/success.js: __metadata: function () {
./pages/payments/success.js: __param: function () {
./pages/payments/success.js: __read: function () {
./pages/payments/success.js: __rest: function () {
./pages/payments/success.js: __spread: function () {
./pages/payments/success.js: __spreadArray: function () {
./pages/payments/success.js: __spreadArrays: function () {
./pages/payments/success.js: __values: function () {
# ..snip..
For the __
helpers that aren't @swc/helpers
related, I did a GitHub code search:
And it looks like they are probably related to microsoft/tslib
:
Runtime library for TypeScript helpers.
The following relates to @swc/helpers
_tagged_template_literal
+ self memoisation pattern that it uses for tagged template literals:
Smart-Rename for 'function replaces self' memoisation pattern
We could potentially detect memoisation patterns like the following, and rename the function something more useful:
function p() { var e = (0, r._)([ "\n absolute w-4 h-4 rounded-full text-[10px] text-white flex justify-center items-center right-0 top-[20px] -mr-2 border border-white\n ", "\n ", "\n", ]); return ( (p = function () { return e; }), e ); }
@pionxzh I just had another idea about this, based on some of my deep diving into
@swc/helpers
tonight.. and I think this is actually anotherswc
related transpilation; related to template literals.Using the
swc
playground:If I pass in some code like this:
const foo = bar` staticOne staticTwo ${dynamicOne} ${dynamicTwo} staticThree ${dynamicThree} `
It transpiles to this:
function _tagged_template_literal(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } function _templateObject() { var data = _tagged_template_literal([ "\n staticOne\n staticTwo\n ", "\n ", "\n staticThree\n ", "\n" ]); _templateObject = function _templateObject() { return data; }; return data; } var foo = bar(_templateObject(), dynamicOne, dynamicTwo, dynamicThree);
The
_tagged_template_literal
function comes from@swc/helpers
:Whereas the
_templateObject
is generated from our input data; and seems to follow the same 'self memoising function' pattern that I identified earlier in the webpacked code.Looking at the signature for tagged template functions:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
We can see that they take a
strings
param (represented by the memoised_templateObject
), followed by a param for each dynamic expression; which we can see is what happens on the final line:var foo = bar(_templateObject(), dynamicOne, dynamicTwo, dynamicThree);
Based on that, we could 're-symbolise' that webpacked code as:
function _templateObjectP() { var data = _tagged_template_literal([ "\n absolute w-4 h-4 rounded-full text-[10px] text-white flex justify-center items-center right-0 top-[20px] -mr-2 border border-white\n ", "\n ", "\n", ]); _templateObjectP = function _templateObject() { return data; }; return data; } y = d.Z.span( _templateObjectP(), function (e) { return "warning" === e.$type && "bg-orange-500 text-white"; }, function (e) { return "danger" === e.$type && "bg-red-500 text-white"; } ),
Which we could then 'normalise' back to the original tagged template literal syntax as:
y = d.Z.span` absolute w-4 h-4 rounded-full text-[10px] text-white flex justify-center items-center right-0 top-[20px] -mr-2 border border-white function (e) { return "warning" === e.$type && "bg-orange-500 text-white"; } function (e) { return "danger" === e.$type && "bg-red-500 text-white"; } `;
Or even simplify it further to just:
y = d.Z.span` absolute w-4 h-4 rounded-full text-[10px] text-white flex justify-center items-center right-0 top-[20px] -mr-2 border border-white (e) => "warning" === e.$type && "bg-orange-500 text-white" (e) => "danger" === e.$type && "bg-red-500 text-white" `;
ChatGPT Explanation
> The provided code snippet demonstrates how a tagged template literal is transpiled by SWC, a JavaScript/TypeScript compiler. Let's break down how it works: > > ### Tagged Template Literals in JavaScript > > Before diving into the transpiled code, it's important to understand what tagged template literals are in JavaScript. They allow you to parse template literals with a function. The syntax looks like this: > > ```javascript > const result = tagFunction`string text ${expression} string text`; > ``` > > In this syntax, `tagFunction` is a function that gets called with the parts of the template literal. > > ### Transpiled Code Breakdown > > #### The `_tagged_template_literal` Function > > ```javascript > function _tagged_template_literal(strings, raw) { > if (!raw) { > raw = strings.slice(0); > } > return Object.freeze(Object.defineProperties(strings, { > raw: { > value: Object.freeze(raw) > } > })); > } > ``` > > - **Purpose**: This function is used to process the template strings and their raw representations. > - **Parameters**: > - `strings`: An array of string values from the template literal. > - `raw`: The raw string representations (as if backslashes in the strings were not processed as escape characters). > - **Process**: > - If `raw` is not provided, it creates a copy of `strings`. > - It then freezes the `strings` array and adds a `raw` property to it, containing the frozen `raw` array. > - **Freezing**: `Object.freeze` is used to make sure that the arrays cannot be altered, which is important for template literals to maintain their integrity. > > #### The `_templateObject` Function > > ```javascript > function _templateObject() { > var data = _tagged_template_literal([ > "\n staticOne\n staticTwo\n ", > "\n ", > "\n staticThree\n ", > "\n" > ]); > _templateObject = function _templateObject() { > return data; > }; > return data; > } > ``` > > - **Purpose**: This function initializes the template strings and caches them. > - **Process**: > - It calls `_tagged_template_literal` with the static parts of the template literal. > - The function is then overridden to return the cached `data` on subsequent calls. This ensures that the template strings are only processed once and then reused, which improves performance. > > #### Usage in the Template Tag > > ```javascript > var foo = bar(_templateObject(), dynamicOne, dynamicTwo, dynamicThree); > ``` > > - `bar` is the tag function. > - `_templateObject()` returns the processed and frozen template strings. > - `dynamicOne`, `dynamicTwo`, and `dynamicThree` are the dynamic expressions passed to the tag function. > > ### Summary > > The transpilation process converts a tagged template literal into a form where the template strings are processed and frozen for immutability and performance. The tag function then receives these processed strings and any dynamic values as arguments. This approach maintains the integrity of template literals while optimizing them for repeated use.Originally posted by @0xdevalias in https://github.com/pionxzh/wakaru/issues/40#issuecomment-1819170475
Helper functions like
__extends
or_inherits
are generated as inline, not imported as modules:How can we detect them?
While I don't know the specific answer to that off the top of my head, here are the relevant issues/references related to it:
- Babel:
swc
: #50- TypeScript: #55
Also:
- https://github.com/pionxzh/wakaru/tree/main/packages/unminify#wakaruunminify
It covered most of patterns that are used by the following tools:
- Terser (Check the Terser Progress)
- Babel (Check the Babel Progress)
- SWC (Check the SWC Progress)
- TypeScript
- https://github.com/pionxzh/wakaru/tree/main/packages/unminify/src/transformations/runtime-helpers
41
Originally posted by @0xdevalias in https://github.com/pionxzh/wakaru/issues/58#issuecomment-1823773298
——
Helper functions like
__extends
or_inherits
are generated as inline, not imported as modules:How can we detect them?
Based on what I know, helpers can be inlined or extracted. Currently, the module scanning is based on regex matching (see babel reference) which also works for inlined helpers.
These examples have been updated to external-helper mode.
Originally posted by @pionxzh in https://github.com/pionxzh/wakaru/issues/58#issuecomment-1823777900
Spinning this out into a new issue so it doesn't get lost among the old one, but follow through to the original comment for more of the deep dive/evidence that lead to this being figured out:
See Also