benjamn / recast

JavaScript syntax tree transformer, nondestructive pretty-printer, and automatic source map generator
MIT License
5.01k stars 350 forks source link

Printing parenthesized JSX nodes #211

Open aaronjensen opened 9 years ago

aaronjensen commented 9 years ago

Is it currently possible to print JSX nodes like so:

var x = (
  <div>
    {test}
  </div>
);

They seem to always print like:

var x = (<div>
  {test}
</div>);

http://felix-kling.de/esprima_ast_explorer/#/984sneJJn2

Thanks!

aaronjensen commented 9 years ago

Probably related: https://github.com/benjamn/recast/issues/147

milahu commented 2 years ago

same here

works for assign breaks for return

function App(props) {
  //const div = ( // good
  return ( // bad
    <div className="app">
      hello {props.name}
    </div>
  );
  //return div;
}

but only when i transform the AST

good result: assign

function App(props) {
  const div = (
    <div class="app">
      hello {props.name}
    </div>
  );
  return div;
}

bad result: return

function App(props) {
  return (
    (<div class="app">hello{props.name}
    </div>)
  );
}

note: also whitespace in hello {props.name} is lost

demo

```js const putout = require("putout") // good: assign var source = ` function App(props) { const div = (
hello {props.name}
); return div; } `; // bad: return var source = ` function App(props) { return (
hello {props.name}
); } `; console.log("input:\n" + source) /* based on https://github.com/coderaiser/putout/blob/master/packages/plugin-react-router/lib/convert-switch-to-routes/index.js https://github.com/coderaiser/putout/blob/master/packages/plugin-react-hooks/lib/apply-short-fragment/index.js */ const myPlugin = { rules: { 'jsx-classname-to-class': { report: () => `Use class instead of className attribute`, include: () => [ 'JSXOpeningElement', ], fix: (path) => { const attr = path.node.attributes.find(attr => attr.name.name == "className") attr.name.name = "class" }, filter: (path) => { const hasClassName = !!path.node.attributes.find(attr => attr.name.name == "className") const hasClass = !!path.node.attributes.find(attr => attr.name.name == "class") return hasClassName && !hasClass }, }, } } const res = putout(source, { isTS: true, isJSX: true, //sourceFileName: 'input.tsx', processors: [ //'typescript', // @putout/processor-typescript type checking for TypeScript code ], plugins: [ //'typescript', // @putout/plugin-typescript transform TypeScript code ['my-plugin', myPlugin], ], rules: { // default: all rules are on //"my-plugin/jsx-classname-to-class": "on", } }); console.log("output:\n" + res.code) ```

bug seems to be in

https://github.com/benjamn/recast/blob/f41dd8b53fe25839b0866fec5dad4232c6ed21b8/lib/printer.ts#L1305-L1322

child.type = "JSXText"
child.value = "\n      hello "
line 1314: "hello"

from

  return (
    <div class="app">
      hello {props.name}
    </div>
  );

the whitespace before </div>

child.value: "\n    "
line 1316: "\n"

https://github.com/benjamn/recast/blob/f41dd8b53fe25839b0866fec5dad4232c6ed21b8/lib/printer.ts#L1326

openingLines = '    <div class="app">'
childLines = "hello{props.name}\n"
closingLines = "</div>"

in the "good" case, this code is not reached instead, print seems to use the fast path

i guess the slow path is caused by adding parens at return ( ... ) but we already have parens around that expression

when parens are missing

return <div>
  asdf
</div>

then it should create a parens-block, indented by 2 spaces

return (
  <div>
    asdf
  </div>
)
milahu commented 2 years ago

nevermind. im moving on to eslint as its more popular