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

Support detect and replace swc's runtime-helpers #50

Open
0xdevalias opened this issue Nov 20, 2023 · 7 comments
Open

Support detect and replace swc's runtime-helpers #50

0xdevalias opened this issue Nov 20, 2023 · 7 comments
Labels
enhancement New feature or request scope: unminify

Comments

@0xdevalias
Copy link

0xdevalias commented Nov 20, 2023

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:

It seems that the issue here is less about smart-rename's handleReactRename not handling useState properly; and more that wakaru needs to add support for swc's 'runtime helper' functions like _sliced_to_array / etc from @swc/helpers; probably in a similar way to how babel's are currently implemented:

This may in part be relevant to the following 'module detection' issue as well:

Originally posted by @0xdevalias in #49 (comment)


See Also

@0xdevalias 0xdevalias changed the title handle swc's runtime-helpers like (_sliced_to_array, etc) in unminify's transformations handle swc's runtime-helpers (@swc/helpers; _sliced_to_array, etc) like babel's in unminify's transformations Nov 20, 2023
@0xdevalias
Copy link
Author

Looking through the rest of the webpack bundle code (Ref) for the 22830 module, we find it in main.js; which after unpacking, becomes module-22830.js:

⇒ npx @wakaru/unpacker main.js -o ./main-unpacked/
# ..snip..

⇒ npx @wakaru/unminify ./main-unpacked/* -o ./main-unminified
# ..snip..
module-22830.js (full source)

Unpacked:

"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,
};

Originally posted by @0xdevalias in #49 (comment)


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)

Unpacked:

"use strict";;
var n = require(14770);
function o(e, t) {
  if (e) {
    if ("string" == typeof e) return (0, n.F)(e, t);
    var r = Object.prototype.toString.call(e).slice(8, -1);
    if (
      ("Object" === r && e.constructor && (r = e.constructor.name),
      "Map" === r || "Set" === r)
    )
      return Array.from(r);
    if (
      "Arguments" === r ||
      /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)
    )
      return (0, n.F)(e, t);
  }
}

module.exports = {
    N: o
};

Unminified:

const { F } = require(14770);

function o(e, t) {
  if (e) {
    if (typeof e == "string") {
      return F(e, t);
    }
    let r = Object.prototype.toString.call(e).slice(8, -1);

    if (r === "Object" && e.constructor) {
      r = e.constructor.name;
    }

    if (r === "Map" || r === "Set") {
      return Array.from(r);
    }

    if (
      r === "Arguments" ||
      /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)
    ) {
      return F(e, t);
    }
  }
}

export default {
  N: o,
};

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)

Unpacked:

"use strict";;
function n(e, t) {
  (null == t || t > e.length) && (t = e.length);
  for (var r = 0, n = Array(t); r < t; r++) n[r] = e[r];
  return n;
}

module.exports = {
    F: n
};

Unminified:

function n(e, t) {
  if (t == null || t > e.length) {
    t = e.length;
  }

  for (var r = 0, n = Array(t); r < t; r++) {
    n[r] = e[r];
  }
  return n;
}

export default {
  F: n,
};

@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 };

@0xdevalias
Copy link
Author

0xdevalias commented Nov 20, 2023

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:

  • https://swc.rs/docs/configuration/compilation#jscexternalhelpers
    • 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 from node_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.

@0xdevalias
Copy link
Author

Looking at module-10604.js, we can see that it's a React component:

module-10604.js (full source)

Unpacked:

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:

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 (
    <div
      className={Z$0(
        "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",
        className
      )}
    >
      <label
        htmlFor={name}
        className="block text-xs font-medium text-gray-900 dark:text-gray-100"
      >
        {displayName}
      </label>
      <div className={Z$0(displayName && "mt-1", "relative")}>
        <input
          {..._$1(
            {
              ref: t,
              type: type,
              name: name,
              id: name,
              className: Z$0(
                "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",
                icon && "pr-6"
              ),
              placeholder: placeholder,
              onBlur: k,
              onChange: T,
              onKeyDown: N,
              autoComplete: autoComplete,
              autoFocus: autoFocus,
            },
            S
          )}
        />
        {icon && <F onClick={onInputIconClick}>{<d.ZP icon={icon} />}</F>}
      </div>
    </div>
  );
});

var F = u.Z.button(c());

Originally posted by @0xdevalias in #49 (comment)

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)

Unpacked:

"use strict";;
;
var n = require(96237);
function o(e) {
  for (var t = 1; t < arguments.length; t++) {
    var r = null != arguments[t] ? arguments[t] : {},
      o = Object.keys(r);
    "function" == typeof Object.getOwnPropertySymbols &&
      (o = o.concat(
        Object.getOwnPropertySymbols(r).filter(function (e) {
          return Object.getOwnPropertyDescriptor(r, e).enumerable;
        })
      )),
      o.forEach(function (t) {
        (0, n.j)(e, t, r[t]);
      });
  }
  return e;
}

module.exports = {
    _: o,
    _object_spread: o
};

Unminified:

const { j } = require(96237);

function o(e) {
  for (let t = 1; t < arguments.length; t++) {
    const r = arguments[t] != null ? arguments[t] : {};
    let o = Object.keys(r);

    if (typeof Object.getOwnPropertySymbols == "function") {
      o = o.concat(
        Object.getOwnPropertySymbols(r).filter(
          (e) => Object.getOwnPropertyDescriptor(r, e).enumerable
        )
      );
    }

    o.forEach((t) => {
      j(e, t, r[t]);
    });
  }
  return e;
}

export default {
  _: o,
  _object_spread: o,
};

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)

Unpacked:

"use strict";;
function Y(U, B) {
  return (
    B || (B = U.slice(0)),
    Object.freeze(
      Object.defineProperties(U, { raw: { value: Object.freeze(B) } })
    )
  );
}

module.exports = {
    _: Y
};

Unminified:

function Y(U, B) {
  if (!B) {
    B = U.slice(0);
  }

  return Object.freeze(
    Object.defineProperties(U, { raw: { value: Object.freeze(B) } })
  );
}

export default {
  _: Y,
};

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)

Unpacked:

"use strict";;
function Y(U) {
  var B,
    G,
    V = "";
  if ("string" == typeof U || "number" == typeof U) V += U;
  else if ("object" == typeof U) {
    if (Array.isArray(U))
      for (B = 0; B < U.length; B++)
        U[B] && (G = Y(U[B])) && (V && (V += " "), (V += G));
    else for (B in U) U[B] && (V && (V += " "), (V += B));
  }
  return V;
}
function V() {
  for (var U, B, G = 0, V = ""; G < arguments.length; )
    (U = arguments[G++]) && (B = Y(U)) && (V && (V += " "), (V += B));
  return V;
}
exports.Z = V;

Unminified:

function Y(U) {
  let B;
  let G;
  let V = "";
  if (typeof U == "string" || typeof U == "number") {
    V += U;
  } else if (typeof U == "object") {
    if (Array.isArray(U)) {
      for (B = 0; B < U.length; B++) {
        if (U[B] && (G = Y(U[B]))) {
          V && (V += " ");
          V += G;
        }
      }
    } else {
      for (B in U) {
        if (U[B]) {
          V && (V += " ");
          V += B;
        }
      }
    }
  }
  return V;
}

export function Z() {
  let U;
  let B;
  for (var G = 0, V = ""; G < arguments.length; ) {
    if ((U = arguments[G++]) && (B = Y(U))) {
      V && (V += " ");
      V += B;
    }
  }
  return Z;
}

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

@0xdevalias
Copy link
Author

main.js

module-71209.js appears to be @swc/helpers _object_spread_props

module-71209.js (full source)

Unpacked:

"use strict";;
function n(e, t) {
  return (
    (t = null != t ? t : {}),
    Object.getOwnPropertyDescriptors
      ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t))
      : (function (e, t) {
          var r = Object.keys(e);
          if (Object.getOwnPropertySymbols) {
            var n = Object.getOwnPropertySymbols(e);
            r.push.apply(r, n);
          }
          return r;
        })(Object(t)).forEach(function (r) {
          Object.defineProperty(
            e,
            r,
            Object.getOwnPropertyDescriptor(t, r)
          );
        }),
    e
  );
}
;

module.exports = {
    _: n,
    _object_spread_props: n
};

Unminified:

function n(e, t) {
  t = t != null ? t : {};

  if (Object.getOwnPropertyDescriptors) {
    Object.defineProperties(e, Object.getOwnPropertyDescriptors(t));
  } else {
    ((e, t) => {
      const r = Object.keys(e);
      if (Object.getOwnPropertySymbols) {
        const n = Object.getOwnPropertySymbols(e);
        r.push.apply(r, n);
      }
      return r;
    })(Object(t)).forEach((r) => {
      Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
    });
  }

  return e;
}

export default {
  _: n,
  _object_spread_props: n,
};

module-70216.js appears to be @swc/helpers _object_without_properties

module-70216.js (full source)

Unpacked:

"use strict";;
function n(e, t) {
  if (null == e) return {};
  var r,
    n,
    o = (function (e, t) {
      if (null == e) return {};
      var r,
        n,
        o = {},
        a = Object.keys(e);
      for (n = 0; n < a.length; n++)
        (r = a[n]), t.indexOf(r) >= 0 || (o[r] = e[r]);
      return o;
    })(e, t);
  if (Object.getOwnPropertySymbols) {
    var a = Object.getOwnPropertySymbols(e);
    for (n = 0; n < a.length; n++)
      (r = a[n]),
        !(t.indexOf(r) >= 0) &&
          Object.prototype.propertyIsEnumerable.call(e, r) &&
          (o[r] = e[r]);
  }
  return o;
}
;

module.exports = {
    _: n,
    _object_without_properties: n
};

Unminified:

function n(e, t) {
  if (e == null) {
    return {};
  }
  let r;
  let n;

  const o = ((e, t) => {
    if (e == null) {
      return {};
    }
    let r;
    let n;
    const o = {};
    const a = Object.keys(e);
    for (n = 0; n < a.length; n++) {
      r = a[n];

      if (!t.includes(r)) {
        o[r] = e[r];
      }
    }
    return o;
  })(e, t);

  if (Object.getOwnPropertySymbols) {
    const a = Object.getOwnPropertySymbols(e);
    for (n = 0; n < a.length; n++) {
      r = a[n];

      if (!t.includes(r) && Object.prototype.propertyIsEnumerable.call(e, r)) {
        o[r] = e[r];
      }
    }
  }
  return o;
}

export default {
  _: n,
  _object_without_properties: n,
};

@0xdevalias
Copy link
Author

0xdevalias commented Nov 20, 2023

The full list of @swc/helpers is here:

Full list
  • _apply_decorated_descriptor
  • _apply_decs_2203_r
  • _array_like_to_array
  • _array_with_holes
  • _array_without_holes
  • _assert_this_initialized
  • _async_generator
  • _async_generator_delegate
  • _async_iterator
  • _async_to_generator
  • _await_async_generator
  • _await_value
  • _check_private_redeclaration
  • _class_apply_descriptor_destructure
  • _class_apply_descriptor_get
  • _class_apply_descriptor_set
  • _class_apply_descriptor_update
  • _class_call_check
  • _class_check_private_static_access
  • _class_check_private_static_field_descriptor
  • _class_extract_field_descriptor
  • _class_name_tdz_error
  • _class_private_field_destructure
  • _class_private_field_get
  • _class_private_field_init
  • _class_private_field_loose_base
  • _class_private_field_loose_key
  • _class_private_field_set
  • _class_private_field_update
  • _class_private_method_get
  • _class_private_method_init
  • _class_private_method_set
  • _class_static_private_field_destructure
  • _class_static_private_field_spec_get
  • _class_static_private_field_spec_set
  • _class_static_private_field_update
  • _class_static_private_method_get
  • _construct
  • _create_class
  • _create_for_of_iterator_helper_loose
  • _create_super
  • _decorate
  • _defaults
  • _define_enumerable_properties
  • _define_property
  • _dispose
  • _export_star
  • _extends
  • _get
  • _get_prototype_of
  • _inherits
  • _inherits_loose
  • _initializer_define_property
  • _initializer_warning_helper
  • _instanceof
  • _interop_require_default
  • _interop_require_wildcard
  • _is_native_function
  • _is_native_reflect_construct
  • _iterable_to_array
  • _iterable_to_array_limit
  • _iterable_to_array_limit_loose
  • _jsx
  • _new_arrow_check
  • _non_iterable_rest
  • _non_iterable_spread
  • _object_destructuring_empty
  • _object_spread
  • _object_spread_props
  • _object_without_properties
  • _object_without_properties_loose
  • _possible_constructor_return
  • _read_only_error
  • _set
  • _set_prototype_of
  • _skip_first_generator_next
  • _sliced_to_array
  • _sliced_to_array_loose
  • _super_prop_base
  • _tagged_template_literal
  • _tagged_template_literal_loose
  • _throw
  • _to_array
  • _to_consumable_array
  • _to_primitive
  • _to_property_key
  • _ts_decorate
  • _ts_generator
  • _ts_metadata
  • _ts_param
  • _ts_values
  • _type_of
  • _unsupported_iterable_to_array
  • _update
  • _using
  • _wrap_async_generator
  • _wrap_native_super
  • _write_only_error

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:

@0xdevalias
Copy link
Author

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 another swc 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:

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:

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

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

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

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 #40 (comment)

@pionxzh pionxzh added the enhancement New feature or request label Nov 21, 2023
@pionxzh pionxzh changed the title handle swc's runtime-helpers (@swc/helpers; _sliced_to_array, etc) like babel's in unminify's transformations Support detect and replace swc's runtime-helpers Nov 21, 2023
@0xdevalias
Copy link
Author

0xdevalias commented Nov 23, 2023

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:

Also:

Originally posted by @0xdevalias in #58 (comment)

——

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 #58 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request scope: unminify
Projects
None yet
Development

No branches or pull requests

2 participants