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

Customisable initial indent levels #11

Closed
4 tasks done
viktor-yakubiv opened this issue Dec 5, 2023 · 19 comments
Closed
4 tasks done

Customisable initial indent levels #11

viktor-yakubiv opened this issue Dec 5, 2023 · 19 comments
Labels
🙅 no/wontfix This is not (enough of) an issue for this project 👎 phase/no Post cannot or will not be acted on

Comments

@viktor-yakubiv
Copy link

viktor-yakubiv commented Dec 5, 2023

Initial checklist

Problem

I want to align all children of head and body at 0-indent-level. Currently, there in an option only to indent it at the first level.

Solution

Replace indentInitial with initialindentLevel that makes an initialIndent by multiplying to the indent option.

Roughly speaking:

lineIndent = initialIndent + level * indent
// where
initialIndent = initialIndentLevel * indent

The default initialIndentLevel is 1 for backward compatibility.
For indentInitial: false, it would be 0;
and -1 for my case.

Alternatives

Can be solved on the level of text roughly with the following command:

rehype ... | sed -E 's/^\t//'
@github-actions github-actions bot added 👋 phase/new Post is being triaged automatically 🤞 phase/open Post is being triaged manually and removed 👋 phase/new Post is being triaged automatically labels Dec 5, 2023
@wooorm
Copy link
Member

wooorm commented Dec 5, 2023

head and body

that sounds like the indentInitial option? Did you read the readme and try that out? How is that different from what you are asking?

@viktor-yakubiv
Copy link
Author

If the proposal seems to be decent, I can implement the solution. This would be the major version bump as the options interface changes.

I don't see an option for extending the current interface, as the negative indent would not be accounted for.

@viktor-yakubiv
Copy link
Author

Let me elaborate with examples.

indentInitial: true (current default)

<!doctype html>
<html>
  <head>
    <title>Hello 👋</title>
  </head>
  <body>
    <h1>Indented by 1 level</h1>
  </body>
</html>

indentInitial: false (initialIndentLevel: 0)

<!doctype html>
<html>
<head>
  <title>Hello 👋</title>
</head>
<body>
  <h1>Indented by 0 level</h1>
</body>
</html>

Desired option: initialIndentLevel: -1

<!doctype html>
<html>
<head>
<title>Hello 👋</title>
</head>
<body>
<h1>Indented by -1 level</h1>
</body>
</html>

or for the sake of sanity:

<!doctype html>
<title>Hello 👋</title>
<h1>Indented by -1 level</h1>

@wooorm
Copy link
Member

wooorm commented Dec 5, 2023

You just want no indents? Why? That doesn’t seem very pretty?
And, your last example, what is sane about it: that seems like https://github.com/rehypejs/rehype-minify/tree/main/packages/rehype-preset-minify

@viktor-yakubiv
Copy link
Author

From one perspective, I agree. It does not look pretty when you write complete HTML. In contrast, I like writing code heavily omitting tags.

When I start with the following template:

<!doctype html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1.0">
<title>Hello 👋</title>

<h1>Blog Title</h1>
<main>
  <!-- imagine patching this with plugins -->
</main>

After processing and reformatting, I get the result like below:

<!doctype html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="width=device-width, initial-scale=1.0">
  <title>Hello 👋</title>
</head>
<body>
  <h1>Blog Title</h1>
  <main><!-- processed code --></main>
Relevant rehype-stringify settings
preferUnquoted: true
omitOptionalTags: true
collapseEmptyAttributes: true

While on the short example, added </head><body> explains the situation (which is a separate topic) and the indentation looks clear, the long page read to the terminal or opened in an editor does not look properly formatted.


I did not see the benefit of using rehype-minify-* as I prefer formatted source code with no obfuscation. Did you want to point to a specific plugin from the preset?

@wooorm
Copy link
Member

wooorm commented Dec 5, 2023

does not look properly formatted.

What doesn’t look proper?

How would it look proper?

Why do you write pretty markup, format it, and then not use optional tags? Fine to write without them, as I do too, but why do you want to read formatted-but-no-optional-tags on a terminal?

I prefer formatted source code with no obfuscation.

Making things small or large are both formatting. rehype-minify-whitespace for example is used in both projects. It minifies whitespace. It sounds like you might want that.

@viktor-yakubiv
Copy link
Author

Why do you write pretty markup, format it, and then not use optional tags?

I can make up a linting case for this. Imagine, you want to write HTML, then have some minor data patching like automatic wrapping dates following the ISO format into a <time> element, before committing the file to the repository. This could be done with lint-staged and rehype-cli.

My case is almost like that. Similarly to your personal website, I generate HTML pages from different sources using custom plugins and templates. Yet, I want the final result to look like it was written by hand: be concise and readable (not minified) and be easy to validate manually.


Making things small or large are both formatting.

To make myself sound more accurate, I can use prettified instead.
I will remember this broader context of the word though. Thank you for noting.

@wooorm
Copy link
Member

wooorm commented Dec 5, 2023

AST transforms and formatters are not for small changes.

If you want to lint and do small changes, such as that example, write your own plugins, using the positional info on nodes, and splice the original source document.

If that is your goal, these tools will never be what you want.

Never indenting elements will not help you in that direction.

With a more relaxed goal, concise and readable could mean to include the default of omitOptionalTags: false, I’m pretty sure everything will be fine.

How would it look proper?

I still wonder how you’d prefer a document to look

@viktor-yakubiv
Copy link
Author

Sorry, I missed to answer the question about the look. I expect it to remain in the same way as it was before processing:

Input (copied from above)

<!doctype html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1.0">
<title>Hello 👋</title>

<h1>Blog Title</h1>
<main>
  <!-- imagine patching this with plugins -->
</main>

Output

<!doctype html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1.0">
<title>Hello 👋</title>

<h1>Blog Title</h1>
<main><!-- processed code --></main>

(comment indentation is out of scope for this example and is only illustrative)

@viktor-yakubiv
Copy link
Author

AST transforms and formatters are not for small changes.

In my case, the changes are not small. I cannot go without the formatter.

For example, let's take a photo gallery page. It is generated by listing every file from a directory, ordered by date of update from git history. Every image entry is generated using hastscript that produces no spacing between elements.

To make the processing output look pretty, I need a formatter. Yet, it does not achieve 100% of the desired look.

@wooorm
Copy link
Member

wooorm commented Dec 5, 2023

Right, your actual use case needs a code generator.
And you want it pretty, so a formatter.

That other use case where you only change some attributes of some elements, doesn’t need those.

Yet, it does not achieve 100% of the desired look.

There are two different projects:

  • rehype-stringify, which is the final step that does not change meaning (whitespace can be meaningful), which has options that are useful for minification, such as not using tags when they are not needed.
  • rehype-format, which changes whitespace (which can be meaningful), and assumes that you will not minify, and so not omit optional tags with rehype-stringify.

The output you want cannot be made.

rehype-stringify cannot omit <body> if it starts with whitespace. </head> cannot be omitted if it is followed by whitespace.
This project injects line endings, whether indented or not, between blocks, and those two are blocks, which are whitespace.

<!-- imagine patching this with plugins -->

As I mentioned before, you can access the AST, and use its positional info.
Find the main element, take its start and end offset, generate the main element, and inject it between those two indexes, if you never want to change anything outside main.

@viktor-yakubiv
Copy link
Author

What you suggest, does make sense. I will look into ways to reformat only particular elements.

As far as I am aware, there is no hast-util-format that would format particular nodes. To create this function, I would need to extract the transformer from rehype-format by calling the attacher and then transform a subtree with that. There might be some edge cases with leading and trailing whitespace.

Would it be useful to have hast-util-format though?

How do you feel about initalIndent suggestion despite it does not seem to solve my actual case?

@wooorm
Copy link
Member

wooorm commented Dec 5, 2023

You don’t need utilities to do that. unified, and this plugin, run on fragments too. Not only on whole documents.

You’re using hastscript etc already, right?
So you have some variable tree, which is a tree.
Now, use hast-util-select to find your main, stuff it in main.
Then you do const newMain = await unified().use(rehypeFormat).run(main) to get a formatted tree.

It should work. If not let me know.

A rehype-format alternative utility could be nice but as you say, you can call rehypeFormat() to get it.

How do you feel about initalIndent suggestion despite it does not seem to solve my actual case?

I don’t see a good reason for someone to use this but never indent anything. Or to pass -3. I don’t think they give pretty results, and that’s what this project is for. So without a good use case and reason, it sounds like a new feature that nobody needs.

Your root question, to format “pretty” but omit optional tags, still seems vague. It is not clear to me how it would work. It would be a different API than initialIndent. It would have to require users to pass that option to rehype-stringify. It would need to be aware which tags could be omitted. It’s complex.

@Nedomas
Copy link

Nedomas commented Apr 3, 2024

Hi @wooorm @viktor-yakubiv , I also have a need for 0 indent everywhere. My use case is that Shopify rich text editor has 0 indent for some reason and I need to load their generated html into my own editor in a separate app running lexical.dev. If my own editor reformats it with anything other than 0 indent, it will be already dirty on load.

Here’s an example of what Shopify editor outputs (you can see that even ol/li aren’t indented):

<p>As the owner of Cool Barber Supplies, I've seen firsthand the importance of having the right tools and equipment in a barbershop. Running a successful barbershop is no easy feat, and having the right barber supplies can make all the difference. In this blog post, I'll share with you the 5 must-have barber supplies that every barbershop should have.</p>
<h1>1. High-Quality Clippers and Trimmers</h1>
<p>The backbone of any barbershop is the clippers and trimmers.</p>
<ol>
<li>These tools are used for everything from precision fades</li>
<li>to clean-up trims, and they need to be reliable</li>
<li>powerful, and easy to use.</li>
</ol>

Thanks for all the great work!

@viktor-yakubiv
Copy link
Author

@Nedomas, as @wooorm already recommended to me, the best way might be to directly adjust the position after formatting. I assume, this should be something as straightforwars in your case as:

const positionFixPlugin = () => (tree) => {
  visit(tree, node => {
    position.start.column = 0
    position.end.column = 0
  })
}

I have not checked the code. You may need to add a simple test to adjust only (specific) elements (like isElement, check hast-util-is-element).

For the reference, have a look at unist-util-visit.


Also, you may consider removing any kind of position at all, and receive a generated HTML output, with no spacing at all, or apply rehype-minify that was recommended previously by Titus, if having no spaces is an option for you.

@wooorm
Copy link
Member

wooorm commented Apr 4, 2024

it will be already dirty on load.

This sounds like an XY problem. You use two tools. Both tools format many things differently. One thing is indent. If you “solve” indent, you will still have 100s of other things that are formatted differently.

You have some root problem that isn’t about indent. It’s about “dirty”.

wooorm added a commit to syntax-tree/hast-util-format that referenced this issue Sep 19, 2024
@wooorm
Copy link
Member

wooorm commented Sep 19, 2024

Closing as what the OP wants—no indent ever—can be done now. By passing indent: '' or indent: 0.

I don’t think it’s a good idea, to support what was asked here (initialIndentLevel).
But, as strings and numbers are documented to be allowed, I do think it makes sense that the empty string, or zero, are handled as intended.

In this thread, there was also sometimes a discussion about a “root question“ for “no changed at all”.
That is not what this project does, either. This project is about changing things.

There was also a side question about hast-util-minify-whitespace/hast-util-format, those now exist.

@wooorm wooorm closed this as completed Sep 19, 2024
@wooorm wooorm added the 🙅 no/wontfix This is not (enough of) an issue for this project label Sep 19, 2024

This comment has been minimized.

@github-actions github-actions bot added 👎 phase/no Post cannot or will not be acted on and removed 🤞 phase/open Post is being triaged manually labels Sep 19, 2024
@viktor-yakubiv
Copy link
Author

@wooorm thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🙅 no/wontfix This is not (enough of) an issue for this project 👎 phase/no Post cannot or will not be acted on
Development

No branches or pull requests

3 participants