dmnd / dedent Goto Github PK
View Code? Open in Web Editor NEW⬅️ ES6 string tag that strips indentation from multi-line strings.
License: MIT License
⬅️ ES6 string tag that strips indentation from multi-line strings.
License: MIT License
Splitting out the last step from #36: I'd like to onboard most or all of the tooling set up in http://github.com/JoshuaKGoldberg/template-typescript-node-package https://github.com/JoshuaKGoldberg/create-typescript-app.
i tried to used dedent and came accros two issues :
First i had some bug with \` being transformed the wrong way (i can't remember exactly)
Second is if you try for example to add a first blank line or you want to tab all you text you can't.
Here is the short module i ended up writing to overcome both. If you want i could make it a pr.
Code here : https://github.com/ekino/veggies/blob/master/src/extensions/snapshot/dedent.js
tests here : https://github.com/ekino/veggies/blob/master/tests/extensions/snapshot/dedent.test.js
For commodity here is the code
/**
* Extract spaces length
* @param {string} text
* @returns {number} length tab and space before first char
*/
const getSpacesLength = text => {
let length = 0
while (length < text.length) {
const char = text[length]
if (char !== ' ' && char !== '\t') break
length += 1
}
return length
}
/**
* Used to remove indentation from a text. Usefull with multine string in backticks.
*
* Two way to use it : `
* My text
* Another line
* Another line again
* `
* the result text will be :
* "My text
* Another line
* Another line again"
*
* In this case, alignment is done on the length of the first character that is not a space or a tab of all lines
*
* Or
*
* Another way : `
* """
* My text
* Another line
* Another line again
* """
* `
* the result text will be :
* " My text
* Another line
* Another line again"
*
* In this case, alignment is donne on the spaces or tab before """
*
* Warning : First line and last line will always be ignored
*
* @param {string} text
* @return {string}
*/
module.exports = text => {
if (typeof text !== 'string') text = text[0]
let lines = text.split('\n')
if (lines.length < 3) return text
lines = lines.slice(1, lines.length - 1)
let skipLength = getSpacesLength(lines[0])
if (
lines[0].substr(skipLength, 3) === '"""' &&
lines[lines.length - 1].substr(skipLength, 3) === '"""'
) {
lines = lines.slice(1, lines.length - 1)
} else {
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
skipLength = Math.min(skipLength, getSpacesLength(line))
}
}
const resultLines = []
for (let i = 0; i < lines.length; i++) {
resultLines.push(lines[i].substring(skipLength))
}
return resultLines.join('\n')
}
Is this test actually testing the expected behaviour:
it("works with removing same number of spaces", () => {
const dd = require("../dedent");
const result = dd`
first
second
third
`;
expect(result).toBe("first\nsecond\n third");
});
I thought this was a bug, and have a fix for it, but then there's a test for it.
I would have expected the result to be "first\n___second\n______third"
(MD is eating up my whitespace).
Let me know if it is actually a bug and I'll make a PR
Following #50 -> #51: I didn't try to convert macro.js
to TypeScript because I'm not very familiar with https://github.com/kentcdodds/babel-plugin-macros. But in theory it'd be nice for someone who is more familiar to:
macro.d.ts
macro.js
to macro.ts
macro.d.ts
gets recreated, akin to today's build:types
for dedent.js
-> dist/dedent.d.ts
Splitting out from #36: I'm going to convert the main source files in this repo to TypeScript.
I would convert macro.js
but it's not something I'm particularly familiar with. Will allow that to be a followup.
Follow on from #2 - currently dedent
does not treat $
or {
as special when determining what it should do with escaped characters:
dedent`\$` === '\\$'
dedent`\{` === '\\{'
While I've not used macros myself, from what I can tell dedent/macro.js
is only expected to be used when babel-plugin-marcos
is being used and is a special file that is consumed by the plugin (i.e. even if I'm using babel-plugin-macros
, I still myself wouldn't write require('dedent/macros.js')
).
As such, I think it's safe to list it as an optional peer dependency so that folks who are not using the macro don't have to carry around this extra dependency.
main
branch of the repository.babel-plugin-macros being an optional peer
it no longer is an optional peer right now in main
It looks like #60 got undone in #55, which might be a mistake as there's not a good reason to include a babel plugin as a package dependency. Maybe it should be a devDependency if the maintainers aren't willing to move it as a peer dep like the plugin says it should be installed
main
branch of the repository.Just tracking for visibility: I haven't been able to put the ACCESS_TOKEN
or NPM_TOKEN
tokens in place per https://github.com/JoshuaKGoldberg/create-typescript-app/blob/7aa6d6b57e6c84e1ff134bb528a72e0c5ecd61a7/docs/Creation.md. #55 added in tooling that uses them. Until those are added to this repo, the automated contributors and release jobs will fail. I've reached out to try to get access or have them added for me.
No response
main
branch of the repository.A major node version dependency should not be upgraded in a bug fix.
NPM is showing the latest version is 1.5.2
, which requires Node 18
. This is breaking our application since Jest 29.5.0
has this somewhere in the dependency tree only supporting Node 16
.
It looks like that tag was removed from GitHub. A new tag that reverts functionality back to 1.5.1
should be tagged as 1.5.3
.
This pulls together #2, #26, #45, #46:
dedent
is used with template literal strings.
dedent
is used as a function, users aren't expecting that special character escapingI think the "right" (or: least unexpected) way would be to add an option with a not-very-savory name like escapeSpecialCharacters
. The option would default to:
true
when called for a template literal stringfalse
when called as a functionProposal:
Possibly interesting for the future (but I don’t have a use case for it, myself): allow dedent
to be combined with template tag functions:
console.log(dedent(html)`
<div>
Hello!
</div>
`);
However, things would be relatively complex, because you’d want to dedent before passing the input on to the tag function.
npm ERR! code E404
npm ERR! 404 Not Found - GET https://repo.huaweicloud.com/repository/npm/dedent/-/dedent-1.5.1.tgz
npm ERR! 404
npm ERR! 404 'dedent@https://repo.huaweicloud.com/repository/npm/dedent/-/dedent-1.5.1.tgz' is not in this registry.
npm ERR! 404
npm ERR! 404 Note that you can also install from a
npm ERR! 404 tarball, folder, http url, or git url.
Please publish as an ES Module.
If users need CJS, it could be compiled from the standard javascript.
Argument of type 'string' is not assignable to parameter of type 'TemplateStringsArray'.
Means this code in the readme (and the code I wrote 😂) is no longer valid:
const third = dedent(`
Wait! I lied. Dedent can also be used as a function.
`);
The workaround is to use ES6 string tags:
const first = dedent`A string`
Hello, you've recently updated dedent from 0.7.0 to 1.0.2 which means in semver that this new version is major and potentially breaking.
Where can I found your changelog?
Dedent is massively used https://www.npmjs.com/package/dedent
The community will highly benefits from having an easy to find changelog (with a copy within https://github.com/dmnd/dedent/releases).
And also please could you also use GitHub relase -> https://github.com/dmnd/dedent/releases that makes it really easy to find. Specially for developers and maintainers like me that deals with hundreds of packages to updates 🙏
Thanks for your awesome work
When using dedent
in a node ESM environment, a import dedent from 'dedent'
statement will result a {default: dedent}
object.
> const dedent = await import('dedent')
undefined
> dedent`abc`
Uncaught TypeError: dedent is not a function
> dedent('abc')
Uncaught TypeError: dedent is not a function
> dedent
[Module: null prototype] {
__esModule: undefined,
default: <ref *1> [Function: dedent] { default: [Circular *1] }
}
> dedent.default`abc`
'abc'
This issue comes from several reasones:
package.json
a module
filed is specified to dist/dedent.mjs
, but NodeJS doesn't support this fieldmain
field in package.json
references to dist/dedent.js
which is a CommonJS module, NodeJS's ESM then transforms module.exports
into a default exportTo address this, we need a exports
field in package.json
:
{
"exports": {
".": {
"types": "./index.d.ts",
"import": "./dist/dedent.mjs",
"default": "./dist/dedent.js"
}
}
}
This will solve the import issue, but is potentially a breaking change to NodeJS ESM environment, would it worse a major version?
👋 Hi all! I'm going to be helping out with maintenance of the project for a little while. Pinning this issue to explain project context & next steps.
The dedent
package has been one of the more common, popular packages in the JS ecosystem for quite a while. Per dedent
package on npm there are >16 million downloads a week. Quite impressive given that the last published version was 6 years ago.
In the long term, https://github.com/tc39/proposal-string-dedent is mostly going to become the standard way to dedent strings in JavaScript. But even if that were to be ratified today, it'd be some time before all engines run by users supported it. So this dedent
package is here to stay for a while.
Both this project's popularity and the presence of a TC39 proposal are strong indicators that dedenting strings is a common, important user need in JS development!
Here's what I'm going to do with this repository:
I'm going to try to publish a new beta version with changes that have been merged over the last few years. I'll try to tag it as a beta so folks can preview it. Just to be safe. #16
Update July 2nd, 2023: [email protected]
is published under the beta
tag. I'll promote it to stable next week.
Update July 10th, 2023: [email protected]
is published as stable. 🚀
For now, adding any large new features to dedent
is out of scope. This package is going to stay mostly as-is.
Bug fixes will of course be accepted & released.
Please do post feedback if you have any questions or suggestions. Cheers all! ❤️
main
branch of the repository.From https://github.com/dmnd/dedent/actions/runs/7278084706/job/19831511946?pr=84:
Run pnpm build
> [email protected] build /home/runner/work/dedent/dedent
> yarn build:legacy && yarn build:modern && yarn build:types
error This project's package.json defines "packageManager": "yarn@pnpm@8.[7](https://github.com/dmnd/dedent/actions/runs/7278084706/job/19831511946?pr=84#step:4:8).0". However the current global version of Yarn is 1.22.21.
Looks like I missed some references to yarn.
No response
Looks like the module removes all newlines from start and end, but I think it makes more sense to only remove one newline at start and end so that I can add newlines without adding \n
.
// Adds a newline at the end. Currently the newline would be deleted.
dedent`
foo
bar
`
dedent(`
<p>Hello world!</p>\n
`);
becomes <p>Hello world!</p>
without newline in the end.
dedent("\\`") === "`"
Bug in this line https://github.com/dmnd/dedent/blob/master/dedent.js#L17
Hi there. Thanks for the package. It works really well.
One small piece of feedback: as someone who is using this in my own library and not as a template writer, I don't actually have any templates in my own code. Instead, users pass templates to my library. I just need to know that the templates passed to my package methods have been dedented. So, really I just want to call a simple function that returns a new, dedented template.
Here is the function that I'm currently using to do do this:
var dedent = require('dedent');
function dd(str) {
return dedent`${str}`;
}
Then calling dd(template);
will obviously return a dedented version of template. It would be nice if this method was accessible somewhere.
Cheers.
It would be nice to have this at build time instead of runtime, would you be open to a PR adding /macro.js
with glue code for babel-plugin-macros? I'll gladly make that PR 😄
cc @kentcdodds
Hey,
Is it me or doesn't dedent
support dedenting tabs?
Cheers
npm has 0.7.0, but here on github, 0.6.0 is the latest : please just tag the new release.
main
branch of the repository.I installed dedent
with pnpm install dmnd/dedent
Then Typescript can't find the declaration but at runtime (Vite devServer) my strings are dedented:
Also trying to auto-import it with https://github.com/unplugin/unplugin-auto-import configured with
'dedent': [
[`default`, `dedent`],
],
My project is a pnpm monorepo with one tsconfig per package. dedent is a dep of only one of the packages
When I go to node_modules/dedent
, the dist
folder is missing.
When I install the dev dependencies and build it, the dist
folder is created and everything works well
so I run a pnpm remove dedent
then pnpm install dedent
and everythin works well now (removing it first is mandatory)
I open this bug anyway as it may be useful to add an 'install' section to the README to avoid this. I personally didn't know that pnpm would automatically map to github if I added the vendor in the install command.
Started here indentjs/endent#8
More details here graphql-nexus/nexus-prisma#50
The issue is that if \n
shows up in a template string it gets read as a newline.
I realise that string-dedent
is apparently the polyfill (I don't know if its technically official, but it is the library used in the REPL), but I think there is value in mirroring the behaviour laid out in the String.dedent
proposal because:
String.dedent
if/when it landsAgain while string-dedent
is a package that can be installed through npm, unless there is something people really disagree with in the String.dedent
proposal (which I think would be of serious concern anyway), I think there is value is having one of the most popular dedent
libraries match its behaviour more.
At the least the REPL indicates:
\\
to escape an escape (relates to #69)My personal concern is that String.dedent
will come out with these subtle differences that mean I have to relearn how dedenting works - so having dedent
start to mirror String.dedent
more closely would address that.
There are several pull requests and unanswered issues. Is this project dead?
$ node
> dedent = require(".").default
[Function: dedent]
> `a\xa0\tb`
'a \tb'
> dedent`a\xa0b`
'a\\xa0\\tb'
> ⏎
(For people stumbling upon this issue: https://github.com/MartinKolarik/dedent-js might work better for you.)
main
branch of the repository.dedent("\\nu")
should return the string unchanged.
dedent("\\nu")
turns the \\n into a newline, i.e. returns a newline character + u
.
This happens because result.replace(/\\n/g, "\n")
is called regardless of the value of escapeSpecialCharacters
, i.e. regardless of whether dedent is called as a tag on a template or as a function on a string.
An easy fix would be to make the replacement conditional, solving this particular problem. (However, it would still not work for dedent`\\nu`
because here, still the \\n would be replaced 🤷♂️)
not sure if this is a bug or somehow expected behaviour that i don't understand yet, but here's a lil repro:
$ cat test.js
const dedent = require('dedent');
console.log(
dedent`
Foo
${JSON.stringify({ a: 'b' }, null, 2)}
Bar
`,
);
$ node src/test.js
Foo
{
"a": "b"
}
Bar
Observations:
Bar
line is not at the same level of indentation as Foo.thanks!
Hijacked by @JoshuaKGoldberg on September 4th, 2023: see #12 (comment). Now accepting PRs for a dedent({ recursive: true })
option.
const a = dedent`
hello
world
`;
const b = dedent`
foo.
${a}
bar.
`;
results in
foo.
hello
world
bar.
instead of
foo.
hello
world
bar.
This is unfortunate because it would be nice to compose dedented strings.
If you decide to fix this, any test cases should also check that inlined dedents make sense:
const weird = dedent`
foo.
${things.map(x => dedent`
hello
world
`).join('\n')}
bar.
`;
In the (old) current version 0.7.0, dedent
is published with a dist/dedent.js
that sets module.exports
to the dedent
function:
if (typeof module !== "undefined") {
module.exports = dedent;
}
That's meant to be used with older-style Node.js code like:
const dedent = require("dedent");
However, in e13e334, the style was switched over to ES modules style default
export:
exports.default = dedent;
This is a breaking change that just hasn't been published yet. Per this Sourcegraph query for const dedent = require("dedent")
, many users are still using the old style API.
Since I'm trying to keep this package stable (#36), I'm going to use https://www.npmjs.com/package/babel-plugin-add-module-exports to dial back the breaking change. That way folks can import both with the old const dedent = require("dedent")
and the new import dedent from "dedent"
ways.
I would like a "join" option, so that multi-line strings could be both dedented and joined into a single line.
Example:
example = dedent(' ', `
This is a
multiline string
that is joined with spaces
instead of newlines.
`); // "This is a multiline string that is joined with spaces instead of newlines."
A little backstory, I actually have my own dedent
function implemented in several projects, and use it constantly! I found this repo when thinking about publishing my dedent
. I'm glad to see it already exists!
This 'join' feature is something I use quite often, too, so I'm hoping its something I could add to this project.
Let's discuss?
Consider this example
dedent`
test
test2
`
Outputs
test
test2
Regex in line 43 (l.match(/^(\s+)\S+/);
) should probably read \s*
instead of \s+
.
The ES6 default export is a good thing, but hasn't been published yet.
Could you please npm version
& npm publish
?
E.g.:
console.log(dedent`
if foo
br
hr
`)
Cheers
Ref: #67 (comment)
As currently written, this module performs interpolation before
stripping leading whitespace. In particular, whitespace is stripped from
the interpolated components. I think that this is the wrong behavior.
My understanding is that dedent
is supposed to be effectively
syntactic sugar. Writing
function foo() {
const things = dedent`\
lorem ipsum
dolor sit amet
`;
return things;
}
should be completely equivalent to writing
function foo() {
const things = `\
lorem ipsum
dolor sit amet
`;
return things;
}
with the sole benefit that the code indentation doesn’t have to become
wonky just to accommodate the text.
This suggests that
function foo() {
const stuff = "abc\n xyz";
const things = dedent`\
lorem ${stuff}
dolor sit amet
`;
return things;
}
should also be equivalent to
function foo() {
const stuff = "abc\n xyz";
const things = `\
lorem ${stuff}
dolor sit amet
`;
return things;
}
which means that the output should be
lorem abc
xyz
dolor sit amet
but with the current version of dedent
the result is actually
lorem abc
xyz
dolor sit amet
Let me provide a concrete case where this matters. I was using dedent
to server-side render static pages on my site. The code looked roughly
like this:
// lots of context, so this code is indented a bunch...
const page = dedent`\
<!DOCTYPE html>
<html>
<body>
<div id="container">${html}</div>
<script src="${pathToBundle}"></script>
</body>
</html>
`;
This all worked well—I wrote it and forgot about it. But when I added
code blocks to my site, I noticed that they rendered incorrectly: code
like
<pre><code>function foo(bar, baz) {
while (true) {
if (bar()) {
console.log("stuff");
if (baz()) {
console.log(
"even more stuff"
);
}
}
}
}</code></pre>
was being rendered as
<pre><code>function foo(bar, baz) {
while (true) {
if (bar()) {
console.log("stuff");
if (baz()) {
console.log(
"even more stuff"
);
}
}
}
}</code></pre>
in the static HTML. Because pre
sets white-space: pre
, the resulting
code is rendered incorrectly in the browser, too. This was somewhat
difficult to track down!
I ended up replacing this module with a hand-written version that fit my
purposes better. (There was an additional change that I needed to make
to properly render code blocks with line-trailing backslashes.) You can
see that change here:
wchargin/wchargin.github.io@06475d4
Is this something that you’re interested in pulling upstream? I realize
that it is a breaking change, so I’d understand if you’re not
interested.
(Also, 👋 !)
Cross-posting @Haroenv's #65 (comment):
I wonder if instead of an option, the real issue is that $ without a { after it doesn't need to be escaped, right?
@G-Rath - do you have thoughts here?
main
branch of the repository.const d = require('dedent')
const result = d(' first line\n second line'); // has space character at the beginning
// result is 'first line\nsecond line'
Expecting first line and second lines to be dedented
const d = require('dedent')
const result = d(' first line\n second line'); // has space character at the beginning
// result is 'first line\n second line'
Actual behavior is that the first space is stripped and the second line is not dedented
All code was executed and tested on the library version 1.5.1
node.js - v16.15.1
Expected:
dd`\`` === '`'
Actual:
dd`\`` === '\\`'
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.