You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add an option to recursively dedent nested template literal strings #12
Hmm, I can see why you'd want this behavior... but also it's a bit counter-intuitive to how the package is positioned right now IMO. Today, it's used as a "dedent everything in this string" utility - not a "dedent this string, and still allow indents in some contents".
Perhaps this can become an opt-in? A couple of API starting ideas..
Perhaps this can become an opt-in? A couple of API starting ideas..
...
Thoughts, everyone?
@JoshuaKGoldberg thanks for pointing me here! To provide some feedback on this - I use this package a lot (I write a lot of cli tooling in particular that spits out formatted error messages to users) and 10/10 times this is the default behavior i'm looking for. That said, I understand and totally respect it's a question of scope and not wanting to fundamentally change (in a backwards incompatible way) the behaviour of this significantly depended-on library!
With that in mind, I feel like it's worthy enough of being a top level namespace thing (rather than buried as an option), and so I like what you initially suggested the most:
I don't personally love this, the $$ example is neat enough but ddedent or anything else i can think of just looks weird and non obvious. but maybe if you could come up with a catchy alias it could be nice.
3rd option (which could go alongside as an alias of the first option)
import { recursive as dedent } from 'dedent';
console.log(dedent`...`);
The dedent.recursive option is definitely appealing. A similar idea came up with escapeSpecialCharacters too, like dedent.escapeSpecialCharacters. The one issue is that we'd then have to content with a whole bunch of potential options there. Would dedent.escapeSpecialCharacters.recursive need to work too?
For now, let's add it in as an opt-in option: dedent({ recursive: true }).
changed the title [-]Nested dedents does not work as expected[/-][+]Add an option to recursively dedent nested template literal strings[/+]on Sep 5, 2023
Heh, hey @JoshuaKGoldberg ! Good to see you here! Sorry I missed this before.
FWIW, it's not my expected behavior to have the library dedent most things, except child strings. I consider the current behavior a bug, not a separate feature, though I may be missing something.
I wouldn't want to have to use an option for a library like this – more specifically, I wouldn't want to have to police that everyone in my codebase always calls the recursive version.
I could maybe be okay with import dedent from 'dedent/recursive', but really I'd much rather this just be the default behavior (though I'll admit it may require a major version bump).
For now, personally, I'd probably simply stick with ts-dedent which has this behavior out of the box (as well as TS types) if a recursive option were added.
The one issue is that we'd then have to content with a whole bunch of potential options there. Would dedent.escapeSpecialCharacters.recursive need to work too?
For now, let's add it in as an opt-in option: dedent({ recursive: true }).
@JoshuaKGoldberg I've implemented this locally and found that the name recursive might be confusing.
It would be better called alignValues since it only needs to align each interpolated value's indentation to match its starting line's indentation. With this clear functionality in mind, it feels natural to me that it should be enabled by default. Take a look at this branch if you're interested.
If we ever need to pass the parent options to nested dedent calls, that should be addressed with a separate option.
With this clear functionality in mind, it feels natural to me that it should be enabled by default
tl;dr: this would be a breaking change and we can't do that
Longer thoughts for transparency: If we were writing a new library this year, I would absolutely agree with that. The proposed behavior makes a lot of sense as a default. But dedent is over a decade old and has roughly 40 million downloads a week. Any small change to behavior to a package with this much widespread adoption comes with a real risk of breaking existing users. The most important thing for the project is to keep things running smoothly for existing users.
Given that this behavior is not technically a bug, just a preference (albeit one we all differ from the default behavior on), it wouldn't be appropriate to release it in a minor version. That'd have to be a breaking change for a new major version. Which we totally could do if it was warranted! I don't think this one behavior preference warrants a new major, but if enough requests come in, that could be something to discuss separately.
Activity
dralletje commentedon Sep 19, 2017
I needed exactly the same today, so #13 was born.
It now adds the indention of the line you interpolate on to every line in the interpolated string.
What would your preferred behaviour for the last example be?
In my code now, that would result (when you have 3 items in the
thingsarray) in thisrattrayalex-stripe commentedon Sep 19, 2017
Awesome! Will discuss expected behavior in the PR
ZhouHansen commentedon Dec 11, 2017
bertho-zero commentedon May 1, 2018
ZhouHansen commentedon May 21, 2018
bertho-zero commentedon May 21, 2018
dralletje commentedon May 22, 2018
ZhouHansen commentedon May 22, 2018
d07RiV commentedon Jun 1, 2018
dralletje commentedon Jun 6, 2018
rattrayalex commentedon Mar 29, 2022
JoshuaKGoldberg commentedon May 22, 2023
Hmm, I can see why you'd want this behavior... but also it's a bit counter-intuitive to how the package is positioned right now IMO. Today, it's used as a "dedent everything in this string" utility - not a "dedent this string, and still allow indents in some contents".
Perhaps this can become an opt-in? A couple of API starting ideas..
Thoughts, everyone?
bertho-zero commentedon May 22, 2023
magicmark commentedon Sep 4, 2023
@JoshuaKGoldberg thanks for pointing me here! To provide some feedback on this - I use this package a lot (I write a lot of cli tooling in particular that spits out formatted error messages to users) and 10/10 times this is the default behavior i'm looking for. That said, I understand and totally respect it's a question of scope and not wanting to fundamentally change (in a backwards incompatible way) the behaviour of this significantly depended-on library!
With that in mind, I feel like it's worthy enough of being a top level namespace thing (rather than buried as an option), and so I like what you initially suggested the most:
fwiw in terms of API ideas, here's some prior art for a similar thing in execa: https://github.com/sindresorhus/execa#shared-options - so you'd suggest something like:
I don't personally love this, the
$$example is neat enough butddedentor anything else i can think of just looks weird and non obvious. but maybe if you could come up with a catchy alias it could be nice.3rd option (which could go alongside as an alias of the first option)
JoshuaKGoldberg commentedon Sep 5, 2023
The
dedent.recursiveoption is definitely appealing. A similar idea came up withescapeSpecialCharacterstoo, likededent.escapeSpecialCharacters. The one issue is that we'd then have to content with a whole bunch of potential options there. Woulddedent.escapeSpecialCharacters.recursiveneed to work too?For now, let's add it in as an opt-in option:
dedent({ recursive: true }).[-]Nested dedents does not work as expected[/-][+]Add an option to recursively dedent nested template literal strings[/+]rattrayalex commentedon Sep 5, 2023
Heh, hey @JoshuaKGoldberg ! Good to see you here! Sorry I missed this before.
FWIW, it's not my expected behavior to have the library dedent most things, except child strings. I consider the current behavior a bug, not a separate feature, though I may be missing something.
I wouldn't want to have to use an option for a library like this – more specifically, I wouldn't want to have to police that everyone in my codebase always calls the recursive version.
I could maybe be okay with
import dedent from 'dedent/recursive', but really I'd much rather this just be the default behavior (though I'll admit it may require a major version bump).For now, personally, I'd probably simply stick with
ts-dedentwhich has this behavior out of the box (as well as TS types) if arecursiveoption were added.Just my 2c!
PaperStrike commentedon Aug 4, 2025
@JoshuaKGoldberg I've implemented this locally and found that the name
recursivemight be confusing.It would be better called
alignValuessince it only needs to align each interpolated value's indentation to match its starting line's indentation. With this clear functionality in mind, it feels natural to me that it should be enabled by default. Take a look at this branch if you're interested.If we ever need to pass the parent options to nested dedent calls, that should be addressed with a separate option.
JoshuaKGoldberg commentedon Aug 5, 2025
👍 makes sense, I like that name. Nice!
tl;dr: this would be a breaking change and we can't do that
Longer thoughts for transparency: If we were writing a new library this year, I would absolutely agree with that. The proposed behavior makes a lot of sense as a default. But
dedentis over a decade old and has roughly 40 million downloads a week. Any small change to behavior to a package with this much widespread adoption comes with a real risk of breaking existing users. The most important thing for the project is to keep things running smoothly for existing users.Given that this behavior is not technically a bug, just a preference (albeit one we all differ from the default behavior on), it wouldn't be appropriate to release it in a minor version. That'd have to be a breaking change for a new major version. Which we totally could do if it was warranted! I don't think this one behavior preference warrants a new major, but if enough requests come in, that could be something to discuss separately.
feat: add alignValues option (#102)
github-actions commentedon Sep 3, 2025
🎉 This is included in version v1.7.0 🎉
The release is available on:
Cheers! 📦🚀