Skip to content

Commit

Permalink
feat(strings): Add stripIndent utility
Browse files Browse the repository at this point in the history
  • Loading branch information
MorevM committed Mar 24, 2024
1 parent 485c20f commit f8bee7f
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/strings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export * from './quote/quote';
export * from './random-string/random-string';
export * from './romanize/romanize';
export * from './snake-case/snake-case';
export * from './strip-indent/strip-indent';
export * from './unquote/unquote';
56 changes: 56 additions & 0 deletions src/strings/strip-indent/strip-indent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { stripIndent } from './strip-indent';

describe('strip-indent', () => {
it('Returns the string as is if there is no extra indentation and leading/trailing linebreaks', () => {
expect(stripIndent('')).toBe('');
expect(stripIndent('foo')).toBe('foo');
expect(stripIndent('foo\n\t\t\tbar\n\nbar')).toBe('foo\n\t\t\tbar\n\nbar');
});

it('Removes leading/trailing linebreaks and lines containing only space\tab characters by default', () => {
expect(stripIndent('\nfoo\n')).toBe('foo');
expect(stripIndent('\n\t \nfoo\n\t\t \t\t\n')).toBe('foo');
});

it('Preserves leading linebreaks and lines containing only space\tab characters using `trimLeadingSpacings: false`', () => {
expect(stripIndent('\nfoo\n', { trimLeadingSpacings: false })).toBe('\nfoo');
expect(stripIndent('\n\t \nfoo\n\t\t \t\t\n', { trimLeadingSpacings: false })).toBe('\n\t \nfoo');
});

it('Preserves trailing linebreaks and lines containing only space\tab characters using `trimTrailingSpacings: false`', () => {
expect(stripIndent('\nfoo\n', { trimTrailingSpacings: false })).toBe('foo\n');
expect(stripIndent('\n\t \nfoo\n\t\t \t\t\n', { trimTrailingSpacings: false })).toBe('foo\n\t\t \t\t\n');
});

it('Removes extra indentation and linebreaks', () => {
expect(stripIndent(`
<div>
<div></div>
</div>
`)).toBe('<div>\n\t<div></div>\n</div>');
});

it('Removes extra indentation and linebreaks considering extra spacings', () => {
expect(stripIndent(`
<div>
<div></div>
<div></div>
<div></div>
</div>
`)).toBe('<div>\n\t<div></div>\n\t <div></div>\n\t\t\t<div></div>\n</div>');
});

it('Removes only extra indentation using `trim[X]Spacings: false`', () => {
expect(stripIndent(`
<div>
<div></div>
</div>
`, { trimLeadingSpacings: false, trimTrailingSpacings: false })).toBe('\n\n<div>\n\t<div></div>\n</div>\n\n\t\t');
});
});
58 changes: 58 additions & 0 deletions src/strings/strip-indent/strip-indent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { mergeObjects } from '../../objects/merge-objects/merge-objects';

const _getMinimalIndent = (input: string) => {
const match = input.match(/^[\t ]*(?=\S)/gm);
if (!match) return 0;

return match.reduce((minimalIndentation, spacings) =>
Math.min(minimalIndentation, spacings.length), Infinity);
};


type Options = {
/**
* Whether to remove leading linebreaks and spacings (before the first non-space character)
*
* @default true
*/
trimLeadingSpacings: boolean;

/**
* Whether to remove trailing linebreaks and spacings (after the last non-space character)
*
* @default true
*/
trimTrailingSpacings: boolean;
};

const DEFAULTS: Options = {
trimLeadingSpacings: true,
trimTrailingSpacings: true,
};

const _applyOptions = (input: string, options: Options) => {
options.trimLeadingSpacings && (input = input.replace(/^\n\s*(?=\S)/, ''));
options.trimTrailingSpacings && (input = input.replace(/\n\s*$/, ''));

return input;
};

/**
* Removes an extra indentation of the string. \
* Useful to work with template literal strings.
*
* @param input String to remove an extra indentation.
* @param userOptions Extra options.
*
* @returns The string with minimal required indentation.
*/
export const stripIndent = (input: string, userOptions?: Partial<Options>) => {
const options = mergeObjects(DEFAULTS, userOptions) as Required<Options>;

const minIndent = _getMinimalIndent(input);
if (minIndent === 0) return _applyOptions(input, options);

const regex = new RegExp(`^[\t ]{${minIndent}}`, 'gm');

return _applyOptions(input, options).replace(regex, '');
};

0 comments on commit f8bee7f

Please sign in to comment.