From 2f39c5c9620de20579852764457f2666d13e5a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vasseur?= Date: Mon, 29 Aug 2022 17:24:56 +0200 Subject: [PATCH] Treat text as unix lines Treat the text as unix lines (sequence of characters newline terminated). This means the string returned by the template will be newline-terminated. Newlines after an expression are removed instead to still get the previous beaviour. To prevent unexpected behavior we add a newline to placeholder values if not present. This mostly result in the following behavior changes: - Strings produced by the tag are newline-terminated - When nesting indent tags, arrays need to be joined with `join('')` instead of `join('\n')` --- README.md | 3 ++- index.test.ts | 14 +++++++------- index.ts | 37 +++++++++++++++++++++++++------------ 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 48d0263..8a944cc 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ indent` ${name} - `).join('\n')} + `).join('')} ` ``` @@ -40,6 +40,7 @@ this package makes a few opiniated choices of how it will interpret the code indentation: - There must be a new line directly following the start of the template - The closing tag should be on a line containing only indentation +- It returns newline-terminated strings. ## How it works diff --git a/index.test.ts b/index.test.ts index e8edb9b..7facf3e 100644 --- a/index.test.ts +++ b/index.test.ts @@ -3,13 +3,13 @@ import indent from '.'; test('It correctly drops template indentation', () => { expect(indent` Test ${"value"} - `).toBe('Test value'); + `).toBe('Test value\n'); }); test('It works when there is no placeholders', () => { expect(indent` Test value - `).toBe('Test value'); + `).toBe('Test value\n'); }); test('It reindent placeholders', () => { @@ -17,21 +17,21 @@ test('It reindent placeholders', () => { ${"test\nvalue"} - `).toBe('\n test\n value\n'); + `).toBe('\n test\n value\n\n'); }); test('It correctly detect indentation when first line is empty', () => { expect(indent` Test - `).toBe('\nTest'); + `).toBe('\nTest\n'); }); test('It correctly detect indentation when last line is empty', () => { expect(indent` Test - `).toBe('Test\n'); + `).toBe('Test\n\n'); }); test('Nested indent test', () => { @@ -43,7 +43,7 @@ test('Nested indent test', () => { ${name} - `).join('\n')} + `).join('')} - `).toBe(`\n \n First\n \n \n Second\n \n`); + `).toBe(`\n \n First\n \n \n Second\n \n\n`); }) diff --git a/index.ts b/index.ts index c8dc437..76f691c 100644 --- a/index.ts +++ b/index.ts @@ -8,8 +8,8 @@ const indentTag = (strings: TemplateStringsArray, ... expressions: any[]) => { parts[0] = parts[0].slice(1); - // Remove last line - parts[strings.length - 1] = parts[parts.length - 1].replace(/\n[ \t]*$/, ''); + // Remove terminating indentation + parts[strings.length - 1] = parts[parts.length - 1].replace(/\n[ \t]*$/, '\n'); // Dedent using indent from first part const indent = parts[0].match(/^\n*([ \t]*)/)![1]; @@ -21,21 +21,34 @@ const indentTag = (strings: TemplateStringsArray, ... expressions: any[]) => { // Ensure expressions are strings const values = expressions.map((expression) => String(expression)); - // Reindent values - const indented = values.map((value, index) => { - const part = parts[index]; + // Treat expression + for (let i= 0; i < values.length; i++) { + const prev = parts[i]; + const next = parts[i + 1]; - const match = part.match(/\n( *)$/); + const match = prev.match(/\n( *)$/); - // Only indent replacement if there is nothing before it on the line - if (!match) { - return value; + // Only tread expressions that are preceded by only indentation and followed by a newline. + if (!match || !next.startsWith('\n')) { + continue; } - return value.replace(/\n/g, match[0]); - }) + const indentation = match[1]; - return parts.flatMap((part, index) => [part, indented[index]]).join(''); + // Fix previous and next parts + parts[i] = parts[i].slice(0, -indentation.length); + parts[i + 1] = parts[i + 1].slice(1); + + // Fix value end of line if needed + if (!values[i].endsWith('\n')) { + values[i] = `${values[i]}\n`; + } + + // Indent value + values[i] = values[i].replace(/(.*)\n/g, `${indentation}$1\n`); + } + + return parts.flatMap((part, index) => [part, values[index]]).join(''); } export default indentTag;