+554
@@ -0,0 +1,554 @@
|
||||
import { dedent } from '..';
|
||||
|
||||
function tag(strings: TemplateStringsArray, ...values: number[]) {
|
||||
let string = strings[0];
|
||||
|
||||
values.forEach((value, i) => {
|
||||
string += 2 * value + strings[i + 1];
|
||||
});
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
describe('dedent tag', () => {
|
||||
it('should work with empty string', () => {
|
||||
expect(dedent``).toEqual('');
|
||||
});
|
||||
|
||||
it('should work with tabs', () => {
|
||||
expect(dedent`Line #1
|
||||
Line #2
|
||||
Line #3`).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(dedent`Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}`).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(dedent`${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line`).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it('should work with spaces', () => {
|
||||
expect(dedent`Line #1
|
||||
Line #2
|
||||
Line #3`).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(dedent`Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}`).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(dedent`${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line`).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it('should remove leading/trailing line break', () => {
|
||||
expect(
|
||||
dedent`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`,
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`,
|
||||
).toEqual('Line #1\n\tLine #2\n\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`,
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`,
|
||||
).toEqual('Line #1\n\tLine #2\n\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`,
|
||||
).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it('should not remove more than one leading/trailing line break', () => {
|
||||
expect(
|
||||
dedent`
|
||||
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
|
||||
`,
|
||||
).toEqual('\nLine #1\nLine #2\nLine #3\n');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
|
||||
`,
|
||||
).toEqual('\nLine #1\nLine #2\nLine #3\n');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
|
||||
`,
|
||||
).toEqual('\n1. line #1\n2. line #2\n3. line\n');
|
||||
});
|
||||
|
||||
it('should remove the same number of tabs/spaces from each line', () => {
|
||||
expect(
|
||||
dedent`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`,
|
||||
).toEqual('Line #1\n\tLine #2\n\t\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`,
|
||||
).toEqual('Line #1\n\tLine #2\n\t\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`,
|
||||
).toEqual('1. line #1\n\t2. line #2\n\t\t3. line');
|
||||
});
|
||||
|
||||
it("should ignore the last line if it doesn't contain anything else than whitespace", () => {
|
||||
expect(
|
||||
(() => {
|
||||
return dedent`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`;
|
||||
})(),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
(() => {
|
||||
return dedent`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`;
|
||||
})(),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
(() => {
|
||||
return dedent`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`;
|
||||
})(),
|
||||
).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it("should process escape sequences", () => {
|
||||
expect(
|
||||
(() => {
|
||||
return dedent`
|
||||
\${not interpolated}
|
||||
\`
|
||||
`;
|
||||
})(),
|
||||
).toEqual('${not interpolated}\n`');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dedent() function', () => {
|
||||
it('should work with tabs', () => {
|
||||
expect(
|
||||
dedent(`Line #1
|
||||
Line #2
|
||||
Line #3`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line`),
|
||||
).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it('should work with spaces', () => {
|
||||
expect(
|
||||
dedent(`Line #1
|
||||
Line #2
|
||||
Line #3`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line`),
|
||||
).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it('should remove leading/trailing line break', () => {
|
||||
expect(
|
||||
dedent(`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`),
|
||||
).toEqual('Line #1\n\tLine #2\n\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`),
|
||||
).toEqual('Line #1\n\tLine #2\n\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`),
|
||||
).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it('should not remove more than one leading/trailing line break', () => {
|
||||
expect(
|
||||
dedent(`
|
||||
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
|
||||
`),
|
||||
).toEqual('\nLine #1\nLine #2\nLine #3\n');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
|
||||
`),
|
||||
).toEqual('\nLine #1\nLine #2\nLine #3\n');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
|
||||
`),
|
||||
).toEqual('\n1. line #1\n2. line #2\n3. line\n');
|
||||
});
|
||||
|
||||
it('should remove the same number of tabs/spaces from each line', () => {
|
||||
expect(
|
||||
dedent(`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`),
|
||||
).toEqual('Line #1\n\tLine #2\n\t\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`),
|
||||
).toEqual('Line #1\n\tLine #2\n\t\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent(`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`),
|
||||
).toEqual('1. line #1\n\t2. line #2\n\t\t3. line');
|
||||
});
|
||||
|
||||
it("should ignore the last line if it doesn't contain anything else than whitespace", () => {
|
||||
expect(
|
||||
(() => {
|
||||
return dedent(`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`);
|
||||
})(),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
(() => {
|
||||
return dedent(`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`);
|
||||
})(),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
(() => {
|
||||
return dedent(`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`);
|
||||
})(),
|
||||
).toEqual('1. line #1\n2. line #2\n3. line');
|
||||
});
|
||||
|
||||
it("should process escape sequences", () => {
|
||||
expect(
|
||||
dedent(`
|
||||
\${not interpolated}
|
||||
\`
|
||||
`),
|
||||
).toEqual('${not interpolated}\n`');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dedent() function with custom tag', () => {
|
||||
it('should work with tabs', () => {
|
||||
expect(
|
||||
dedent(tag`Line #1
|
||||
Line #2
|
||||
Line #3`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(tag`Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}`),
|
||||
).toEqual('Line #2\nLine #4\nLine #6');
|
||||
|
||||
expect(
|
||||
dedent(tag`${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line`),
|
||||
).toEqual('2. line #2\n4. line #4\n6. line');
|
||||
});
|
||||
|
||||
it('should work with spaces', () => {
|
||||
expect(
|
||||
dedent(tag`Line #1
|
||||
Line #2
|
||||
Line #3`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(tag`Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}`),
|
||||
).toEqual('Line #2\nLine #4\nLine #6');
|
||||
|
||||
expect(
|
||||
dedent(tag`${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line`),
|
||||
).toEqual('2. line #2\n4. line #4\n6. line');
|
||||
});
|
||||
|
||||
it('should remove leading/trailing line break', () => {
|
||||
expect(
|
||||
dedent(tag`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`),
|
||||
).toEqual('Line #1\n\tLine #2\n\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`),
|
||||
).toEqual('Line #2\nLine #4\nLine #6');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`),
|
||||
).toEqual('Line #2\n\tLine #4\n\tLine #6');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`),
|
||||
).toEqual('2. line #2\n4. line #4\n6. line');
|
||||
});
|
||||
|
||||
it('should not remove more than one leading/trailing line break', () => {
|
||||
expect(
|
||||
dedent(tag`
|
||||
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
|
||||
`),
|
||||
).toEqual('\nLine #1\nLine #2\nLine #3\n');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
|
||||
`),
|
||||
).toEqual('\nLine #2\nLine #4\nLine #6\n');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
|
||||
`),
|
||||
).toEqual('\n2. line #2\n4. line #4\n6. line\n');
|
||||
});
|
||||
|
||||
it('should remove the same number of tabs/spaces from each line', () => {
|
||||
expect(
|
||||
dedent(tag`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`),
|
||||
).toEqual('Line #1\n\tLine #2\n\t\tLine #3');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`),
|
||||
).toEqual('Line #2\n\tLine #4\n\t\tLine #6');
|
||||
|
||||
expect(
|
||||
dedent(tag`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`),
|
||||
).toEqual('2. line #2\n\t4. line #4\n\t\t6. line');
|
||||
});
|
||||
|
||||
it("should ignore the last line if it doesn't contain anything else than whitespace", () => {
|
||||
expect(
|
||||
(() => {
|
||||
return dedent(tag`
|
||||
Line #1
|
||||
Line #2
|
||||
Line #3
|
||||
`);
|
||||
})(),
|
||||
).toEqual('Line #1\nLine #2\nLine #3');
|
||||
|
||||
expect(
|
||||
(() => {
|
||||
return dedent(tag`
|
||||
Line #${1}
|
||||
Line #${2}
|
||||
Line #${3}
|
||||
`);
|
||||
})(),
|
||||
).toEqual('Line #2\nLine #4\nLine #6');
|
||||
|
||||
expect(
|
||||
(() => {
|
||||
return dedent(tag`
|
||||
${1}. line #${1}
|
||||
${2}. line #${2}
|
||||
${3}. line
|
||||
`);
|
||||
})(),
|
||||
).toEqual('2. line #2\n4. line #4\n6. line');
|
||||
});
|
||||
|
||||
it("should process escape sequences", () => {
|
||||
expect(
|
||||
dedent(tag`
|
||||
\${not interpolated}
|
||||
\`
|
||||
`),
|
||||
).toEqual('${not interpolated}\n`');
|
||||
});
|
||||
});
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { dedent } from '..';
|
||||
|
||||
describe('Issue 21', () => {
|
||||
it('should dedent nested dedents correctly', () => {
|
||||
const fieldDocs = dedent`
|
||||
* a
|
||||
* b
|
||||
* c
|
||||
`
|
||||
|
||||
const a = dedent`
|
||||
/**
|
||||
${fieldIntro()}
|
||||
*
|
||||
${fieldDocs}
|
||||
*
|
||||
${fieldExample()}
|
||||
*/
|
||||
`
|
||||
|
||||
function fieldIntro() {
|
||||
return dedent`
|
||||
* 0
|
||||
`
|
||||
}
|
||||
function fieldExample() {
|
||||
return dedent`
|
||||
* d
|
||||
`
|
||||
}
|
||||
|
||||
const expected = `/**
|
||||
* 0
|
||||
*
|
||||
* a
|
||||
* b
|
||||
* c
|
||||
*
|
||||
* d
|
||||
*/`
|
||||
|
||||
expect(a).toEqual(expected);
|
||||
});
|
||||
|
||||
/**
|
||||
* Could not find a way to handle this but is an edge case we'd like to solve eventually
|
||||
*/
|
||||
it.skip('should handle function calls of nested dedents correctly', () => {
|
||||
const fieldDocs = dedent(`
|
||||
* a
|
||||
* b
|
||||
* c
|
||||
`)
|
||||
|
||||
const a = dedent(`
|
||||
/**
|
||||
${fieldIntro()}
|
||||
*
|
||||
${fieldDocs}
|
||||
*
|
||||
${fieldExample()}
|
||||
*/
|
||||
`)
|
||||
|
||||
function fieldIntro() {
|
||||
return dedent(`
|
||||
* 0
|
||||
`)
|
||||
}
|
||||
function fieldExample() {
|
||||
return dedent(`
|
||||
* d
|
||||
`)
|
||||
}
|
||||
|
||||
const expected = `/**
|
||||
* 0
|
||||
*
|
||||
* a
|
||||
* b
|
||||
* c
|
||||
*
|
||||
* d
|
||||
*/`
|
||||
|
||||
expect(a).toEqual(expected);
|
||||
});
|
||||
});
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
export function dedent(
|
||||
templ: TemplateStringsArray | string,
|
||||
...values: unknown[]
|
||||
): string {
|
||||
let strings = Array.from(typeof templ === 'string' ? [templ] : templ);
|
||||
|
||||
// 1. Remove trailing whitespace.
|
||||
strings[strings.length - 1] = strings[strings.length - 1].replace(
|
||||
/\r?\n([\t ]*)$/,
|
||||
'',
|
||||
);
|
||||
|
||||
// 2. Find all line breaks to determine the highest common indentation level.
|
||||
const indentLengths = strings.reduce((arr, str) => {
|
||||
const matches = str.match(/\n([\t ]+|(?!\s).)/g);
|
||||
if (matches) {
|
||||
return arr.concat(
|
||||
matches.map((match) => match.match(/[\t ]/g)?.length ?? 0),
|
||||
);
|
||||
}
|
||||
return arr;
|
||||
}, <number[]>[]);
|
||||
|
||||
// 3. Remove the common indentation from all strings.
|
||||
if (indentLengths.length) {
|
||||
const pattern = new RegExp(`\n[\t ]{${Math.min(...indentLengths)}}`, 'g');
|
||||
|
||||
strings = strings.map((str) => str.replace(pattern, '\n'));
|
||||
}
|
||||
|
||||
// 4. Remove leading whitespace.
|
||||
strings[0] = strings[0].replace(/^\r?\n/, '');
|
||||
|
||||
// 5. Perform interpolation.
|
||||
let string = strings[0];
|
||||
|
||||
values.forEach((value, i) => {
|
||||
// 5.1 Read current indentation level
|
||||
const endentations = string.match(/(?:^|\n)( *)$/)
|
||||
const endentation = endentations ? endentations[1] : ''
|
||||
let indentedValue = value
|
||||
// 5.2 Add indentation to values with multiline strings
|
||||
if (typeof value === 'string' && value.includes('\n')) {
|
||||
indentedValue = String(value)
|
||||
.split('\n')
|
||||
.map((str, i) => {
|
||||
return i === 0 ? str : `${endentation}${str}`
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
string += indentedValue + strings[i + 1];
|
||||
});
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
export default dedent;
|
||||
Reference in New Issue
Block a user