* @property {'string'|'number'|'bigint'|'date'} [type='string'] The JavaScript data type to attempt to cast this column to. If
* the data cannot be unmangled, the data passes through as a `string`. The default value is `'string'`.
* @property {string} [displayName=name] Optional `string` value to indicate the desired header name in the output CSV.
- * @property {string} [defaultValue] Optional `string` value to use to fill empty CSV fields. If no value is provided, the
+ * @property {string} [defaultValue=''] Optional `string` value to use to fill empty CSV fields. If no value is provided, the
* field value will be `''` (empty string) in the output CSV.
*/
this.#passthroughEmptyRows = options.passthroughEmptyRows ?? false;
this.#typeCastOnly = options.typeCastOnly ?? false;
- for (const { name, type, displayName = name, defaultValue = null } of headers) {
+ for (const { name, type, displayName = name, defaultValue = '' } of headers) {
let normalizedType = type?.toLowerCase() ?? 'string';
if (!['string', 'number', 'bigint', 'date'].includes(normalizedType)) {
console.warn(`Type "${normalizedType}" is not supported, defaulting to string.`);
/** @type {Array<CSVNormalizerRow>} */
const out = [];
for (const { name, type, displayName, defaultValue, index } of this.#columns) {
- let value = row[index];
+ let value = row[index] ?? '';
const emptyField = value === '' ? true : false;
switch (type) {
case 'string': break;
case 'bigint': value = this.#typeCastOnly ? BigInt(value).toString() : CSVNormalizer.fixExcelBigInt(value); break;
case 'date': value = this.#typeCastOnly ? new Date(value).toISOString() : CSVNormalizer.fixExcelDate(value); break;
}
- if (emptyField && defaultValue !== null) {
+ if (emptyField) {
value = defaultValue;
}
out.push({ name, displayName, value, emptyField });
['', '', 's3']
]));
});
+ test('handles uneven CSV', { concurrency: true }, async () => {
+ await createCSVMockStream([
+ ['common', 'maybe1', 'maybe2', 'maybe3'],
+ ['c'],
+ ['c', 'm'],
+ ['c', 'm', 'm'],
+ ['c', 'm', 'm', 'm']
+ ])
+ .pipeThrough(new CSVNormalizer([
+ { name: 'common', type: 'string' },
+ { name: 'maybe1', type: 'string' },
+ { name: 'maybe2', type: 'string' },
+ { name: 'maybe3', type: 'string' },
+ ]))
+ .pipeThrough(new CSVDenormalizer())
+ .pipeTo(csvStreamEqualWritable([
+ ['common', 'maybe1', 'maybe2', 'maybe3'],
+ ['c', '', '', ''],
+ ['c', 'm', '', ''],
+ ['c', 'm', 'm', ''],
+ ['c', 'm', 'm', 'm'],
+ ]));
+ });
test('defaults to "string" type', { concurrency: true }, async () => {
await createCSVMockStream([
['columnA', 'columnB'],