babel: [Bug]: Babel removes parenthesis at place where are required.

đź’»

  • Would you like to work on a fix?

How are you using Babel?

Programmatic API (babel.transform, babel.parse)

Input code

import something from 'something';

export function myfunction(bool) {
    return /** @type {SomeType} */ (
        bool ? something.a() : something.b()
    )
}

Configuration file name

No response

Configuration

No response

Current and expected behavior

const something = require('something');

exports.myfunction = myfunction;

function myfunction(bool) {
    return; /** @type {SomeType} */ 
        bool ? something.a() : something.b()
}

Environment

System: OS: Windows 11 10.0.22621 Binaries: Node: 18.6.0 - C:\Program Files\nodejs\node.EXE npm: 8.15.0 - C:\Program Files\nodejs\npm.CMD npmPackages: @babel/cli: ^7.23.0 => 7.23.0 @babel/core: ^7.23.2 => 7.23.2 @babel/plugin-transform-modules-commonjs: ^7.23.3 => 7.23.3 @babel/preset-env: ^7.23.3 => 7.23.3 @babel/traverse: ^7.23.3 => 7.23.3 eslint: ^8.53.0 => 8.53.0 jest: ^29.7.0 => 29.7.0

Possible solution

No response

Additional context

The issue happens when there is /** @type {SomeType} */ in return statement, or in assigment. Causing either in invalid return, or even if code itself works, in output files, TypeScript throws error because of invalid types (as without parenthesis it doesn’t apply type cast on whole expression).

reproduction in comment below:

https://github.com/babel/babel/issues/16111#issuecomment-1825331645

About this issue

  • Original URL
  • State: closed
  • Created 7 months ago
  • Comments: 23 (10 by maintainers)

Most upvoted comments

@ahocevar @kungfooman Thank you for your detailed report! I think there are two problems here.

https://github.com/babel/babel/issues/16111#issuecomment-1825514962 Regression from @babel/generator@7.23.4, I can reproduce it, will fix it.

And https://github.com/bjornharrtell/ol-issue-15358 is related to @babel/parser. <del>I get the error, but even if I downgrade @babel/parser it doesn’t work.🤔<del>

Suddenly it works fine and I’m investigating. 🤦‍♂️

@ahocevar I tried to reproduce the issue and it’s kinda confusing:

mkdir babel_paren
cd babel_paren

Create file test.mjs:

import {parse} from '@babel/parser';
import generator from '@babel/generator';
const generate = generator.default;
const code = `
function getResolutionForValueFunction(power) {
  power = power || 2;
  const maxResolution = this.getConstrainedResolution(this.maxResolution_);
  const minResolution = this.minResolution_;
  const max = Math.log(maxResolution / minResolution) / Math.log(power);
  return (
    /**
     * @param {number} value Value.
     * @return {number} Resolution.
     */
    function (value) {
      const resolution = maxResolution / Math.pow(power, value * max);
      return resolution;
    }
  );
}
`;
const ast = parse(code);
const out = generate(ast,
  {
    compact: true,
    //createParenthesizedExpressions: true,
  }
);
console.log(out);

First run:

rm -rf node_modules package.json package-lock.json && npm i @babel/parser@7.23.3 @babel/generator@7.23.3 && node test.mjs
added 12 packages, and audited 13 packages in 737ms

found 0 vulnerabilities
{
  code: 'function getResolutionForValueFunction(power){power=power||2;const maxResolution=this.getConstrainedResolution(this.maxResolution_);const minResolution=this.minResolution_;const max=Math.log(maxResolution/minResolution)/Math.log(power);return(/**\n' +
    '     * @param {number} value Value.\n' +
    '     * @return {number} Resolution.\n' +
    '     */function(value){const resolution=maxResolution/Math.pow(power,value*max);return resolution;});}',
  decodedMap: undefined,
  __mergedMap: [Getter],
  map: [Getter/Setter],
  rawMappings: [Getter/Setter]
}

Nice parentheses, right?

But now run:

npm i @babel/generator && node test.mjs

Result

up to date, audited 13 packages in 439ms

found 0 vulnerabilities
{
  code: 'function getResolutionForValueFunction(power){power=power||2;const maxResolution=this.getConstrainedResolution(this.maxResolution_);const minResolution=this.minResolution_;const max=Math.log(maxResolution/minResolution)/Math.log(power);return/**\n' +
    '     * @param {number} value Value.\n' +
    '     * @return {number} Resolution.\n' +
    '     */function(value){const resolution=maxResolution/Math.pow(power,value*max);return resolution;};}',
  decodedMap: undefined,
  __mergedMap: [Getter],
  map: [Getter/Setter],
  rawMappings: [Getter/Setter]
}

Parentheses are gone! đź‘€

This only happens when using {compact: true} as generate option.

This appears to be cause of https://github.com/openlayers/openlayers/issues/15358. A reproduction can be found at https://github.com/bjornharrtell/ol-issue-15358.

Looks like this is a regression in @babel/parser. Downgrading to @babel/parser@7.19.0 (by manually editing the lockfile) makes the problem go away:

diff --git a/package-lock.json b/package-lock.json
index c84d63d..3f3903c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -115,7 +115,7 @@
         "@babel/helper-compilation-targets": "^7.22.15",
         "@babel/helper-module-transforms": "^7.23.3",
         "@babel/helpers": "^7.23.2",
-        "@babel/parser": "^7.23.3",
+        "@babel/parser": "^7.19.0",
         "@babel/template": "^7.22.15",
         "@babel/traverse": "^7.23.3",
         "@babel/types": "^7.23.3",
@@ -500,7 +500,7 @@
       }
     },
     "node_modules/@babel/parser": {
-      "version": "7.23.4",
+      "version": "7.19.0",
       "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz",
       "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==",
       "dev": true,
@@ -1849,7 +1849,7 @@
       "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.22.13",
-        "@babel/parser": "^7.22.15",
+        "@babel/parser": "^7.19.0",
         "@babel/types": "^7.22.15"
       },
       "engines": {
@@ -1868,7 +1868,7 @@
         "@babel/helper-function-name": "^7.23.0",
         "@babel/helper-hoist-variables": "^7.22.5",
         "@babel/helper-split-export-declaration": "^7.22.6",
-        "@babel/parser": "^7.23.4",
+        "@babel/parser": "^7.19.0",
         "@babel/types": "^7.23.4",
         "debug": "^4.1.0",
         "globals": "^11.1.0"

I’m releasing a patch right now that might fix this issue

Confirmed @babel/generator@7.23.5 resolves the issue and a lockfile regen/deps cleanup will bring that for me as transitive dependency.

FYI for anyone who might have started seeing the below error in their react application over the last week. This bug is the cause and it is fixed in @babel/generator@7.23.5

Error: Minified React error #152; visit https://reactjs.org/docs/error-decoder.html?invariant=152&args[]=f

Confirmed that downgrading to @babel/generator@7.23.0 resolves https://github.com/bjornharrtell/ol-issue-15358. Note that downgrading to @babel/generator@7.23.3 does not appear to be enough.

@liuxingbaoyu Thank you, I mean integrated into the PR build, because some plugins or options could manipulate the AST (e.g. createParenthesizedExpressions)

Unfortunately there is currently no easy way.

Too bad, I would also like a “Show AST” option to quickly judge what’s going on.

Checking tests in PR #16104 and it really seems it will fix the issue.

Try a single-line type cast comment, it doesn’t work: REPL

Output:

function test(bool = false) {
  return /** @type {number} */bool ? 1 : 2;
}

Isn’t this because createParenthesizedExpressions is false by default (for whatever reason, AST vs. CST story)?

I wanted to test this in REPL, but I can’t find a way to set {createParenthesizedExpressions: true}. Is there any way to set parse options in REPL?

No, even this is unexpected when creatingParenthesizedExpressions: false. This is different in AST.

I wanted to test this in REPL, but I can’t find a way to set {createParenthesizedExpressions: true}. Is there any way to set parse options in REPL?

Unfortunately there is currently no easy way.