Skip to content

Support handlebars#3534

Merged
vjeux merged 16 commits intoprettier:masterfrom
robert-j-webb:support-handlebars
Dec 29, 2017
Merged

Support handlebars#3534
vjeux merged 16 commits intoprettier:masterfrom
robert-j-webb:support-handlebars

Conversation

@robert-j-webb
Copy link
Copy Markdown
Contributor

This work is me picking off where @azz left. This is his pr and my starting point: here.

#1882 is the issue related to this.

Feedback is extremely welcome. I'm doing my best and I have a good deal of hours to dedicate to this.

@robert-j-webb robert-j-webb force-pushed the support-handlebars branch 3 times, most recently from 747401e to 9e88e75 Compare December 20, 2017 22:41
@azz azz mentioned this pull request Dec 21, 2017
9 tasks
@robert-j-webb robert-j-webb force-pushed the support-handlebars branch 5 times, most recently from 1006c3b to 758655b Compare December 22, 2017 20:04
@robert-j-webb
Copy link
Copy Markdown
Contributor Author

Hi! This is ready for review. I'd be happy to fix any issues that there are with it. Thank you!

@azz

@robert-j-webb robert-j-webb force-pushed the support-handlebars branch 6 times, most recently from 58e04f4 to 4e74616 Compare December 22, 2017 23:23
@robert-j-webb
Copy link
Copy Markdown
Contributor Author

I've been debugging this for a couple of hours and I'm getting a couple of errors. First of all,
Dynamic requires are not currently supported by rollup-plugin-commonjs

Secondly, ... is not a token. This only happens when the build is node 4. I'm going to try changing the glimmer version to see if I can get something to work.

@azz
Copy link
Copy Markdown
Member

azz commented Dec 23, 2017

Take a look at rollup.parser.config.js to see how to fix dynamic requires.

... isn't supported in Node.js v4. It might be used in Glimmer so you'll need to run it through babel. See build-docs.js for an example of that.

"less",
"scss",
"json",
"glimmer",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just comment this line out and update snapshots for now. When we release it we'll uncomment.


<div {{hello param param param param hashPair=value hashPair=value hashPair=value hashPair=value hashPair=value}}>
Hello
</div> No newline at end of file
Copy link
Copy Markdown
Member

@azz azz Dec 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline here (there's a handful more, could you run through all of them?)

@robert-j-webb
Copy link
Copy Markdown
Contributor Author

I added new lines to the test files and commented out the line you referenced. @azz

@Turbo87
Copy link
Copy Markdown

Turbo87 commented Dec 24, 2017

@robert-j-webb how did you solve the whitespace handling issues that are inherited from HTML?

@robert-j-webb
Copy link
Copy Markdown
Contributor Author

I trim away all white space. At some point, I want to preserve the users’ extra line breaks between elements. However, I scoped this pr to be a much more manageable pretty printer by dealing with that problem later, preferably with lots of community feedback.

@azz
Copy link
Copy Markdown
Member

azz commented Dec 26, 2017

There was a massive refactor in #3536 that caused some conflicts here. Would you be able to solve them?

Go with language-handlebars/parser-glimmer.js

@robert-j-webb
Copy link
Copy Markdown
Contributor Author

Yeah I'll start working on it! I love the plugin approach.

azz and others added 8 commits December 26, 2017 14:53
Polished Support for Sub Expressions
Added test cases for glimmer primitives
Added support for concat statements
Attempted to make element nodes work
Attempted block statements
Element Nodes are OK
Added support for block elements that are not else-if related
Added support for Else/If
Cleaning up
Rebase
Switch node 4 syntax
Update build
@robert-j-webb
Copy link
Copy Markdown
Contributor Author

Ok I got the rebase to work @azz. I think so at least! It's really good that there's a solid plugin system now.

Copy link
Copy Markdown
Contributor

@vjeux vjeux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome, thanks for doing that! As soon as it is green we can merge it and iterate from there.

Go to bed!
{{/if}}
</h1>
<h2>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to preserve the empty line above.

<div class="entry">
<h1>
{{title}}
</h1>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems overkill to always break

{{if goodbye true}}
{{if goodbye false}}
{{if goodbye true}}"
>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it respect the jsx-bracket-same-line option?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it doesn't. I think a ternary in a few places would fix that though. I didn't write any support for options! :(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to support it :)

Adding a new language to prettier works in two phases. The first one is to print everything in a correct way, which you mostly did here. The second one is to make all the things than can be thrown at it look good.

You’re now entering the second phase :) So expect to do a lot of tweaks everywhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected this! I'm really happy to do this part too. I figured that it would need tweaking because a lot of the decisions I made were based off asking a few people in the community and then picking from what is available. I will be in airplane for a few hours next week so I'll have plenty of time to implement whatever is decided upon.

A long enough string to trigger a line break that would prevent wrapping more.
</div>
<div>
A long enough string to trigger a line break that would prevent wrapping more and more.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not wrapping here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is one the tricky ones to decide. You wouldn't break this string up to multiple lines:

const hello = 'A string that is over 80 chars long';

I could take text and break on space, but I think that's against what I would expect from prettier.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend looking at what prettier outputs for jsx and try to be as close as possible. This has been battle tested over time and it hasn’t been a big source of issues.

A long enough string to trigger a line break that would prevent wrapping more and more.
</div>
<div>
{{#block}}{{hello}}{{/block}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the heuristics you chose. Sometimes it is in one line, sometimes it breaks, for things that look similar.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that HTML tags would break as frequently as block elements, but I remember running into some situations where that would cause some very ugly formatting.

Perhaps block elements should also always break? What would be best?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a small example for JSX: Playground link

Input:

<div>
  {a}
</div>;
<div>{a}</div>;
<div a>{a}</div>;
<div a b>{a}</div>;
<div>{a}{b}</div>;

Output:

<div>{a}</div>;
<div>{a}</div>;
<div a>{a}</div>;
<div a b>
  {a}
</div>;
<div>
  {a}
  {b}
</div>;

It always break if there's >2 attributes or >2 values and inlines otherwise.

This is a small example but you should look at the JSX source code and see all the heuristics it does to make it look good.

"less",
"scss",
"json",
// "glimmer",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would name all those "handlebars". Except for JavaScript, we started naming the parser after the language it parses and not the actual name of the parser. This is easier for people to figure out which to use. It would be nice to rename it to --language at some point but we'll see.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that renaming from glimmer to handlebars would make sense. It's just that at this moment, glimmer doesn't support partial statements, which handlebars does support. So it's kind of a specific caveat that they're different.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if there's a list of the differences between the two?

Copy link
Copy Markdown
Contributor Author

@robert-j-webb robert-j-webb Dec 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I'll ask the maintainers of the project. @mmun

Copy link
Copy Markdown

@mmun mmun Dec 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handlebars is a plain text templating language. In practice it is often used as an HTML templating language, but it is also commonly used for templating configuration files (toml, ini, yaml, etc).

Glimmer is only used for templating HTML/DOM. There are a couple differences from vanilla HTML:

  • The parser will hard error if non-void tags aren't balanced. It also only attempts to handle "content" HTML, e.g. it doesn't correctly handle <script> parsing. I don't think we plan to change that any time soon.
  • There are a few extensions we've added to HTML that let us differentiate HTML attributes from component arguments:
<MyComponent class="red" @data={{myData}}>

You can also pass blocks as arguments (I believe they are commonly called render props in React)

<MyComponent>
  <@header=>This is a header!</@header>
  <@header=>This is a footer!</@header>
</MyComponent>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mmun isn't it technically "HTMLBars" instead of "Glimmer" with "Glimmer" just implementing a parser for "HTMLBars"?

Copy link
Copy Markdown

@mmun mmun Dec 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Turbo87 It's true that that is how we launched it but it was always for lack of a better name. You won't find references to HTMLBars in the guides/API docs anymore. The Glimmer brand now encompasses a DOM templating language, a runtime VM, and an experimental application framework (http://glimmerjs.com)

Copy link
Copy Markdown

@mmun mmun Dec 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vjeux Let me start by saying thank you very much for working on prettier and for reviewing this PR! And thanks to @robert-j-webb for working on it!

I completely agree with you that we should minimize confusion with the configuration. Taking a step back away from Glimmer for a second, what configuration would you propose to differentiate between .txt.hbs, .md.hbs, .html.hbs, .toml.hbs, etc? Each variant would need different formatting for the non-Handlebars parts. It's also plausible to me that the stuff inside the curlies ({{ ... }}) might be slightly formatted differently depending on the variant as well.

I think that answering this question may shed some light on how to deal with Glimmer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not super familiar with handlebars but the philosophy we followed with prettier so far is to take use cases one by one and make them really good. So my recommendation would be to make it work perfectly for Ember and then we can figure out how to handle more use cases. My guy feeling is that there’s a big market for people eager to get prettier to work for the html variant and low interest in the other variants right now.


</body>
</html>
`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The html parser/output seems to be completely broken :p

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's not great. I could fix (it's very similar to handlebars) it but I wanted to scope my PR around just getting handlebars working because it's already so much code.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, don’t worry about the html printer

@vjeux vjeux merged commit 833666a into prettier:master Dec 29, 2017
@vjeux
Copy link
Copy Markdown
Contributor

vjeux commented Dec 29, 2017

I just want to be explicit about what the next steps are.

Prettier has been successful because it doesn’t just prints code but because it prints it extremely well. In order to achieve this level of quality, we need to add a bunch of complexity in the printer. The good news is that we have already done a lot of similar work for JSX so you should steal as much as possible. This way it has already been battle tested and we know it works well, and if people switch between React and Ember projects using prettier, they will already be familiar with the way code is formatted.

In order to make sure that things are well formatted, we want to run it in as many codebases as possible and go through the diffs and try to make all the outputs look as good as possible as well as running —debug-check to make sure that we’re printing correct code.

Once it’s in a really good shape, then we can release it to everyone. At this point it’s likely going to get a lot of people to try it out and give good feedback that needs to be acted on. After a while it’ll stabilize and it’ll just work and save people a bunch of time :)

I hope that it makes sense. Let me know if you have any questions.

@robert-j-webb
Copy link
Copy Markdown
Contributor Author

I absolutely understand! I'll look more into how JSX is doing things and try to mirror it. I don't know if it will be possible to reuse the JSX code itself, but I'll look into that. I'm excited to make this work extremely well!

@vjeux
Copy link
Copy Markdown
Contributor

vjeux commented Dec 29, 2017

Awesome! It’s going to be so good :)

For reusing code, I wouldn’t spend too much time, if it’s easy then do it but otherwise it’s not a big deal. It’s better for the behavior to be similar.

@duailibe
Copy link
Copy Markdown
Collaborator

@robert-j-webb A while ago @karl tried to come up with a intermediate representation that JSX, Handlebars and others would translate to and then apply the rules to that representation, if you're interested

@chriskrycho
Copy link
Copy Markdown

Quick question as a very interested consumer: is the easiest way to try this out just installing master? I figure it may be useful to give feedback from our reasonably large codebase (3650 LoC of .hbs per cloc).

@ikatyang
Copy link
Copy Markdown
Member

ikatyang commented Jan 5, 2018

@chriskrycho

yarn add prettier/prettier
yarn prettier **/*.hbs --parser glimmer

@SimenB
Copy link
Copy Markdown
Contributor

SimenB commented Jan 5, 2018

I tried running this at work (it's mustache and not handlebars, but close enough!), and the first issue I hit (as it failed on like 75% of all our templates) is that it doesn't support the old else (or not, no idea what it's called) syntax.

$ echo '{{^something}}{{/something}}' | node_modules/.bin/prettier --parser glimmer
[error] stdin: TypeError: Cannot read property 'blockParams' of undefined
[error]     at TokenizerEventHandlers.Program (/Users/simbekkh/repos/mfinn/node_modules/@glimmer/syntax/dist/commonjs/es5/lib/parser/handlebars-node-visitors.js:67:61)
[error]     at TokenizerEventHandlers.BlockStatement (/Users/simbekkh/repos/mfinn/node_modules/@glimmer/syntax/dist/commonjs/es5/lib/parser/handlebars-node-visitors.js:100:28)
[error]     at TokenizerEventHandlers.acceptNode (/Users/simbekkh/repos/mfinn/node_modules/@glimmer/syntax/dist/commonjs/es5/lib/parser.js:64:31)
[error]     at TokenizerEventHandlers.Program (/Users/simbekkh/repos/mfinn/node_modules/@glimmer/syntax/dist/commonjs/es5/lib/parser/handlebars-node-visitors.js:75:18)
[error]     at TokenizerEventHandlers.acceptNode (/Users/simbekkh/repos/mfinn/node_modules/@glimmer/syntax/dist/commonjs/es5/lib/parser.js:64:31)
[error]     at preprocess (/Users/simbekkh/repos/mfinn/node_modules/@glimmer/syntax/dist/commonjs/es5/lib/parser/tokenizer-event-handlers.js:336:61)
[error]     at Object.parse (/Users/simbekkh/repos/mfinn/node_modules/prettier/src/language-handlebars/parser-glimmer.js:27:12)
[error]     at Object.parse (/Users/simbekkh/repos/mfinn/node_modules/prettier/src/main/parser.js:61:19)
[error]     at formatWithCursor (/Users/simbekkh/repos/mfinn/node_modules/prettier/index.js:100:22)
[error]     at Object.formatWithCursor (/Users/simbekkh/repos/mfinn/node_modules/prettier/index.js:389:12)
[error]     at format (/Users/simbekkh/repos/mfinn/node_modules/prettier/src/cli/util.js:154:19)
[error]     at thirdParty.getStream.then.input (/Users/simbekkh/repos/mfinn/node_modules/prettier/src/cli/util.js:258:19)
[error]     at <anonymous>
[error]     at process._tickCallback (internal/process/next_tick.js:188:7)

This works on http://tryhandlebarsjs.com/.

image

@SimenB
Copy link
Copy Markdown
Contributor

SimenB commented Jan 5, 2018

It also doesn't support partials, which is sad :( (glimmerjs/glimmer-vm#397)

Another thing is that the column in the syntax errors are 0, and should probably be ignored in the code frames (just point to the line).

image

Yet another issue is that it says that Error: A block may only be used inside an HTML element or another block. for valid stuff, like:

<input {{#checked}}checked{{/checked}}>

This also works just fine in http://tryhandlebarsjs.com/

That's all I've got for now. Not sure which, if any, are fixable.

If this is aimed solely at ember, I suppose all of these are ok? 🤷‍♂️ I've never touched ember before, but I've used both handlebars and mustache

@mmun
Copy link
Copy Markdown

mmun commented Jan 5, 2018

@SimenB Thanks for the report! Yeah, we don't intend to support cases like <input {{#checked}}checked{{/checked}}> in Glimmer. Same for partials, decorators, etc. They don't make sense in Glimmer's component model.

@SimenB SimenB mentioned this pull request Jan 9, 2018
@peterjmag peterjmag mentioned this pull request Nov 28, 2018
@lock lock bot added the locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting. label Jan 18, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Jan 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants