Skip to content

Commit

Permalink
Normalize newlines
Browse files Browse the repository at this point in the history
  • Loading branch information
Nam Hoang Le committed Apr 25, 2021
1 parent ea61184 commit 3db99bd
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 69 deletions.
22 changes: 3 additions & 19 deletions src/drawTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,9 @@ import drawRow from './drawRow';
import type {
TableConfig, Row,
} from './types/internal';

/**
* Group the array into sub-arrays by sizes.
*
* @example
* groupBySizes(['a', 'b', 'c', 'd', 'e'], [2, 1, 2]) = [ ['a', 'b'], ['c'], ['d', 'e'] ]
*/

const groupBySizes = <T>(array: T[], sizes: number[]): T[][] => {
let startIndex = 0;

return sizes.map((size) => {
const group = array.slice(startIndex, startIndex + size);

startIndex += size;

return group;
});
};
import {
groupBySizes,
} from './utils';

export default (rows: Row[], columnWidths: number[], rowHeights: number[], config: TableConfig): string => {
const {
Expand Down
16 changes: 4 additions & 12 deletions src/makeConfig.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import cloneDeep from 'lodash.clonedeep';
import calculateMaximumColumnWidthIndex from './calculateMaximumColumnWidthIndex';
import getBorderCharacters from './getBorderCharacters';
import type {
ColumnUserConfig, Indexable,
BorderUserConfig, BorderConfig, TableUserConfig,
TableUserConfig,
} from './types/api';
import type {
ColumnConfig, Row, TableConfig,
} from './types/internal';
import {
makeBorder,
} from './utils';
import validateConfig from './validateConfig';

/**
* Merges user provided border characters with the default border ("honeywell") characters.
*/
const makeBorder = (border: BorderUserConfig | undefined): BorderConfig => {
return {
...getBorderCharacters('honeywell'),
...border,
};
};

/**
* Creates a configuration for every column using default
* values for the missing configuration properties.
Expand Down
17 changes: 3 additions & 14 deletions src/makeStreamConfig.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
import cloneDeep from 'lodash.clonedeep';
import getBorderCharacters from './getBorderCharacters';
import type {
ColumnUserConfig,
Indexable,
StreamUserConfig,
BorderUserConfig,
BorderConfig,
} from './types/api';
import type {
ColumnConfig,
StreamConfig,
} from './types/internal';
import {
makeBorder,
} from './utils';
import validateConfig from './validateConfig';

/**
* Merges user provided border characters with the default border ("honeywell") characters.
*
*/
const makeBorder = (border?: BorderUserConfig): BorderConfig => {
return {
...getBorderCharacters('honeywell'),
...border,
};
};

/**
* Creates a configuration for every column using default
* values for the missing configuration properties.
Expand Down
7 changes: 6 additions & 1 deletion src/stringifyTableData.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import type {
Row,
} from './types/internal';
import {
normalizeString,
} from './utils';

export default (rows: unknown[][]): Row[] => {
return rows.map((cells) => {
return cells.map(String);
return cells.map((cell) => {
return normalizeString(String(cell));
});
});
};
70 changes: 70 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import slice from 'slice-ansi';
import stripAnsi from 'strip-ansi';
import getBorderCharacters from './getBorderCharacters';
import type {
BorderConfig, BorderUserConfig,
} from './types/api';

/**
* Converts Windows-style newline to Unix-style
*
* @internal
*/
export const normalizeString = (input: string): string => {
return input.replace(/\r\n/g, '\n');
};

/**
* Splits ansi string by newlines
*
* @internal
*/
export const splitAnsi = (input: string): string[] => {
const lengths = stripAnsi(input).split('\n').map(({length}) => {
return length;
});

const result: string[] = [];
let startIndex = 0;

lengths.forEach((length) => {
result.push(length === 0 ? '' : slice(input, startIndex, startIndex + length));

// Plus 1 for the newline character itself
startIndex += length + 1;
});

return result;
};

/**
* Merges user provided border characters with the default border ("honeywell") characters.
*
* @internal
*/
export const makeBorder = (border: BorderUserConfig | undefined): BorderConfig => {
return {
...getBorderCharacters('honeywell'),
...border,
};
};

/**
* Groups the array into sub-arrays by sizes.
*
* @internal
* @example
* groupBySizes(['a', 'b', 'c', 'd', 'e'], [2, 1, 2]) = [ ['a', 'b'], ['c'], ['d', 'e'] ]
*/

export const groupBySizes = <T>(array: T[], sizes: number[]): T[][] => {
let startIndex = 0;

return sizes.map((size) => {
const group = array.slice(startIndex, startIndex + size);

startIndex += size;

return group;
});
};
6 changes: 5 additions & 1 deletion src/validateTableData.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
normalizeString,
} from './utils';

export default (rows: unknown[][]): void => {
if (!Array.isArray(rows)) {
throw new TypeError('Table data must be an array.');
Expand All @@ -24,7 +28,7 @@ export default (rows: unknown[][]): void => {

for (const cell of row) {
// eslint-disable-next-line no-control-regex
if (/[\u0001-\u0006\u0008\u0009\u000B-\u001A]/.test(cell)) {
if (/[\u0001-\u0006\u0008\u0009\u000B-\u001A]/.test(normalizeString(String(cell)))) {
throw new Error('Table data must not contain control characters.');
}
}
Expand Down
23 changes: 3 additions & 20 deletions src/wrapCell.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,9 @@
import slice from 'slice-ansi';
import stripAnsi from 'strip-ansi';
import {
splitAnsi,
} from './utils';
import wrapString from './wrapString';
import wrapWord from './wrapWord';

const splitAnsi = (input: string) => {
const lengths = stripAnsi(input).split('\n').map(({length}) => {
return length;
});

const result: string[] = [];
let startIndex = 0;

lengths.forEach((length) => {
result.push(length === 0 ? '' : slice(input, startIndex, startIndex + length));

// Plus 1 for the newline character itself
startIndex += length + 1;
});

return result;
};

/**
* Wrap a single cell value into a list of lines
*
Expand Down
29 changes: 27 additions & 2 deletions test/validateTableData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import {
expect,
} from 'chai';
import {
table,
} from '../src';
import validateTableData from '../src/validateTableData';

describe('validateTableData', () => {
Expand Down Expand Up @@ -52,7 +55,27 @@ describe('validateTableData', () => {

context('cell data contains newlines', () => {
it('does not throw', () => {
validateTableData([['ab\nc']]);
expect(() => {
validateTableData([['ab\nc']]);
}).to.not.throw();
});
});

context('cell data contains Windows-style newlines', () => {
it('does not throw and replaces by Unix-style newline', () => {
expect(() => {
validateTableData([['ab\r\nc']]);
}).to.not.throw();

expect(table([['ab\r\nc']])).to.equal('╔════╗\n║ ab ║\n║ c ║\n╚════╝\n');
});
});

context('cell data contains carriage return only', () => {
it('throws an error', () => {
expect(() => {
validateTableData([['ab\rc']]);
}).to.throw(Error, 'Table data must not contain control characters.');
});
});

Expand All @@ -79,7 +102,9 @@ describe('validateTableData', () => {
].join('');

it('does not throw', () => {
validateTableData([[link]]);
expect(() => {
validateTableData([[link]]);
}).to.not.throw();
});
});

Expand Down

0 comments on commit 3db99bd

Please sign in to comment.