Skip to content

Commit

Permalink
Treat text as unix lines
Browse files Browse the repository at this point in the history
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')`
  • Loading branch information
jvasseur committed Nov 16, 2022
1 parent ef0b033 commit 2f39c5c
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 20 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ indent`
<value>
${name}
</value>
`).join('\n')}
`).join('')}
</test>
`
```
Expand All @@ -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

Expand Down
14 changes: 7 additions & 7 deletions index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,35 @@ 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', () => {
expect(indent`
<test>
${"test\nvalue"}
</test>
`).toBe('<test>\n test\n value\n</test>');
`).toBe('<test>\n test\n value\n</test>\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', () => {
Expand All @@ -43,7 +43,7 @@ test('Nested indent test', () => {
<value>
${name}
</value>
`).join('\n')}
`).join('')}
</test>
`).toBe(`<test>\n <value>\n First\n </value>\n <value>\n Second\n </value>\n</test>`);
`).toBe(`<test>\n <value>\n First\n </value>\n <value>\n Second\n </value>\n</test>\n`);
})
37 changes: 25 additions & 12 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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;

0 comments on commit 2f39c5c

Please sign in to comment.