+111
-128
@@ -1,89 +0,81 @@ | ||
| var internals = {} | ||
| var moment = require('moment') | ||
| var Papa = require('papaparse') | ||
| var lodash = require('lodash') | ||
| const internals = {} | ||
| const { format: formatDate, parse: parseDate, isValid } = require('date-fns') | ||
| const Papa = require('papaparse') | ||
| // eslint-disable-next-line no-extend-native | ||
| String.prototype.splice = function (idx, rem, str) { | ||
| return this.slice(0, idx) + str + this.slice(idx + Math.abs(rem)) | ||
| // Secure string splice utility function | ||
| const stringSplice = (str, idx, rem, newStr) => { | ||
| return str.slice(0, idx) + newStr + str.slice(idx + Math.abs(rem)) | ||
| } | ||
| var parseCol = function (row, map, format) { | ||
| var r = {} | ||
| lodash.forEach(map, function (i) { | ||
| var v = row.substring(i.start - 1, (i.start + i.width - 1)).trim() | ||
| const parseCol = (row, map, format = 'json') => { | ||
| const r = {} | ||
| for (const { name, width, start, type, ...field } of map) { | ||
| const v = row.substring(start - 1, (start + width - 1)).trim() | ||
| if (v) { | ||
| switch (i.type) { | ||
| case 'date': | ||
| if (i.inputformat) { | ||
| if (moment(v, i.inputformat).isValid()) { | ||
| r[i.name] = moment(v, i.inputformat).format(i.outputformat) | ||
| switch (type) { | ||
| case 'date': { | ||
| try { | ||
| let parsedDate | ||
| if (field.inputformat) { | ||
| // Convert moment format to date-fns format | ||
| const dateFnsInputFormat = field.inputformat.replace(/YYYY/g, 'yyyy').replace(/MM/g, 'MM').replace(/DD/g, 'dd') | ||
| parsedDate = parseDate(v, dateFnsInputFormat, new Date()) | ||
| } else { | ||
| r[i.name] = null | ||
| parsedDate = new Date(v) | ||
| } | ||
| } else { | ||
| if (moment(v).isValid()) { | ||
| r[i.name] = moment(v).format(i.outputformat) | ||
| if (isValid(parsedDate)) { | ||
| const dateFnsOutputFormat = field.outputformat.replace(/YYYY/g, 'yyyy').replace(/MM/g, 'MM').replace(/DD/g, 'dd') | ||
| r[name] = formatDate(parsedDate, dateFnsOutputFormat) | ||
| } else { | ||
| r[i.name] = null | ||
| r[name] = null | ||
| } | ||
| } catch (e) { | ||
| r[name] = null | ||
| } | ||
| break | ||
| case 'float': | ||
| var precision = 2 | ||
| if (i.percision) { // Support incorrect spelling for backward compatibility | ||
| precision = i.percision | ||
| } | ||
| if (i.precision) { | ||
| precision = i.precision | ||
| } | ||
| var symbol = '' | ||
| if (i.symbol && format === 'csv') { | ||
| symbol = i.symbol | ||
| } | ||
| if (lodash.includes(v, '.')) { | ||
| r[i.name] = symbol + parseFloat(v).toFixed(precision) | ||
| } | ||
| case 'float': { | ||
| const precision = field.precision || field.percision || 2 // Support backward compatibility | ||
| const symbol = (field.symbol && format === 'csv') ? field.symbol : '' | ||
| if (v.includes('.')) { | ||
| r[name] = symbol + parseFloat(v).toFixed(precision) | ||
| } else { | ||
| r[i.name] = symbol + parseFloat(v.splice(i.width - precision, 0, '.')).toFixed(precision) | ||
| r[name] = symbol + parseFloat(stringSplice(v, width - precision, 0, '.')).toFixed(precision) | ||
| } | ||
| break | ||
| } | ||
| case 'int': | ||
| r[i.name] = parseInt(v) | ||
| r[name] = parseInt(v) | ||
| break | ||
| case 'bool': | ||
| r[i.name] = false | ||
| if (v === i.tVal) { | ||
| r[i.name] = true | ||
| } | ||
| r[name] = v === field.tVal | ||
| break | ||
| case 'string': | ||
| r[i.name] = v | ||
| r[name] = v | ||
| break | ||
| default: | ||
| r[i.name] = v | ||
| r[name] = v | ||
| } | ||
| } else { | ||
| r[i.name] = null | ||
| r[name] = null | ||
| } | ||
| }) | ||
| } | ||
| return r | ||
| } | ||
| internals.parse = function (specs, input) { | ||
| if (typeof (specs) !== 'object') throw new Error('specs is not an array') | ||
| if (lodash.isEmpty(specs)) throw new Error('specs is empty') | ||
| if (lodash.isEmpty(specs.map)) throw new Error('specs maps is empty') | ||
| if (lodash.isEmpty(specs.options)) throw new Error('specs options is empty') | ||
| if (input === '') throw new Error('input is empty') | ||
| internals.parse = (specs, input) => { | ||
| if (typeof specs !== 'object') throw new Error('specs must be an object') | ||
| if (!specs || Object.keys(specs).length === 0) throw new Error('specs cannot be empty') | ||
| if (!specs.map || specs.map.length === 0) throw new Error('specs.map cannot be empty') | ||
| if (!specs.options || Object.keys(specs.options).length === 0) throw new Error('specs.options cannot be empty') | ||
| if (input === '') throw new Error('input cannot be empty') | ||
| specs = startCheck(specs) | ||
| var arrayOutput = [] | ||
| var objectOutput = {} | ||
| var splitInput = input.replace(/\r\n/g, '\n').split('\n') | ||
| if (splitInput.indexOf('') !== -1) { | ||
| splitInput.splice(splitInput.indexOf(''), 1) | ||
| } | ||
| lodash.forEach(splitInput, function (i, idx) { | ||
| const arrayOutput = [] | ||
| const objectOutput = {} | ||
| const splitInput = input.replace(/\r\n/g, '\n').split('\n').filter(line => line !== '') | ||
| for (let idx = 0; idx < splitInput.length; idx++) { | ||
| const i = splitInput[idx] | ||
| if (i.length === specs.options.fullwidth && !specs.options.levels) { | ||
| if (!lodash.isEmpty(specs.options.skiplines)) { | ||
| if (specs.options.skiplines.indexOf(parseInt(idx) + 1) === -1) { | ||
| if (specs.options.skiplines && specs.options.skiplines.length > 0) { | ||
| if (!specs.options.skiplines.includes(idx + 1)) { | ||
| arrayOutput.push(parseCol(i, specs.map, specs.options.format)) | ||
@@ -95,21 +87,14 @@ } | ||
| } else if (specs.options.levels) { | ||
| var level = lodash.find(specs.options.levels, function (v) { | ||
| if (idx >= v.start && idx <= v.end) { | ||
| return true | ||
| } | ||
| const level = Object.values(specs.options.levels).find(v => idx >= v.start && idx <= v.end) | ||
| const levelKey = Object.keys(specs.options.levels).find(key => { | ||
| const v = specs.options.levels[key] | ||
| return idx >= v.start && idx <= v.end | ||
| }) | ||
| var levelMap = lodash.filter(specs.map, { | ||
| level: lodash.findKey(specs.options.levels, function (v) { | ||
| if (idx >= v.start && idx <= v.end) { | ||
| return true | ||
| } | ||
| }) | ||
| }) | ||
| const levelMap = specs.map.filter(item => item.level === levelKey) | ||
| if (i.length === level.fullwidth) { | ||
| // eslint-disable-next-line no-prototype-builtins | ||
| if (!objectOutput.hasOwnProperty(level.nickname)) { | ||
| if (!(level.nickname in objectOutput)) { | ||
| objectOutput[level.nickname] = [] | ||
| } | ||
| if (specs.options.skiplines !== null) { | ||
| if (specs.options.skiplines.indexOf(parseInt(idx) + 1) === -1) { | ||
| if (specs.options.skiplines && specs.options.skiplines.length > 0) { | ||
| if (!specs.options.skiplines.includes(idx + 1)) { | ||
| objectOutput[level.nickname].push(parseCol(i, levelMap, specs.options.format)) | ||
@@ -121,8 +106,8 @@ } | ||
| } else { | ||
| throw new Error('Row #' + (parseInt(idx) + 1) + ' does not match fullwidth') | ||
| throw new Error(`Row #${idx + 1} does not match fullwidth`) | ||
| } | ||
| } else { | ||
| throw new Error('Row #' + (parseInt(idx) + 1) + ' does not match fullwidth') | ||
| throw new Error(`Row #${idx + 1} does not match fullwidth`) | ||
| } | ||
| }) | ||
| } | ||
| switch (specs.options.format) { | ||
@@ -142,29 +127,27 @@ case 'csv': | ||
| internals.unparse = function (specs, input, levels) { | ||
| var output = [] | ||
| if (typeof (specs) !== 'object') throw new Error('specs is not an array') | ||
| if (lodash.isEmpty(specs)) throw new Error('specs is empty') | ||
| if (input === '') throw new Error('input is empty') | ||
| var counter = 0 | ||
| internals.unparse = (specs, input, levels) => { | ||
| let output = '' | ||
| if (typeof specs !== 'object') throw new Error('specs must be an object') | ||
| if (!specs || specs.length === 0) throw new Error('specs cannot be empty') | ||
| if (input === '') throw new Error('input cannot be empty') | ||
| let counter = 0 | ||
| if (levels) { | ||
| var rowCount = 0 | ||
| lodash.forEach(levels, function (l) { | ||
| var inputByLevel = input[l] | ||
| rowCount = rowCount + inputByLevel.length | ||
| }) | ||
| lodash.forEach(levels, function (l) { | ||
| var inputByLevel = input[l] | ||
| var specsByLevel = lodash.filter(specs, { | ||
| level: l | ||
| }) | ||
| lodash.forEach(inputByLevel, function (inp) { | ||
| lodash.forEach(specsByLevel, function (spec) { | ||
| var value = String(inp[spec.name]) | ||
| let rowCount = 0 | ||
| for (const l of levels) { | ||
| const inputByLevel = input[l] | ||
| rowCount += inputByLevel.length | ||
| } | ||
| for (const l of levels) { | ||
| const inputByLevel = input[l] | ||
| const specsByLevel = specs.filter(spec => spec.level === l) | ||
| for (const inp of inputByLevel) { | ||
| for (const spec of specsByLevel) { | ||
| let value = String(inp[spec.name]) | ||
| value = preprocessCheck(spec, value, inp) | ||
| var valueLength = value.length | ||
| const valueLength = value.length | ||
| if (spec.width - value.length >= 0) { | ||
| for (var i = 1; i <= spec.width - valueLength; i++) { | ||
| var symbol = spec.padding_symbol ? spec.padding_symbol : ' ' | ||
| if (symbol.length > 1) throw new Error('padding_symbol can not have length > 1') | ||
| for (let i = 1; i <= spec.width - valueLength; i++) { | ||
| const symbol = spec.padding_symbol || ' ' | ||
| if (symbol.length > 1) throw new Error('padding_symbol cannot have length > 1') | ||
| switch (spec.padding_position) { | ||
@@ -182,25 +165,25 @@ case 'start': | ||
| } | ||
| output = output + value.substring(0, spec.width) | ||
| output += value.substring(0, spec.width) | ||
| } | ||
| }) | ||
| counter = counter + 1 | ||
| } | ||
| counter++ | ||
| if (rowCount !== counter) { | ||
| output = output + '\n' | ||
| output += '\n' | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
| } | ||
| return output | ||
| } else { | ||
| for (var row in input) { | ||
| for (var spec in specs) { | ||
| var value = input[row][specs[spec].name] | ||
| var defaultValue = lodash.defaultTo(specs[spec].default, '') | ||
| value = lodash.defaultTo(value, defaultValue) | ||
| for (const row in input) { | ||
| for (const spec in specs) { | ||
| let value = input[row][specs[spec].name] | ||
| const defaultValue = specs[spec].default != null ? specs[spec].default : '' | ||
| value = value != null ? value : defaultValue | ||
| value = String(value) | ||
| value = preprocessCheck(specs[spec], value, input[row]) | ||
| var valueLength = value.length | ||
| const valueLength = value.length | ||
| if (specs[spec].width - value.length >= 0) { | ||
| for (var i = 1; i <= specs[spec].width - valueLength; i++) { | ||
| var symbol = specs[spec].padding_symbol ? specs[spec].padding_symbol : ' ' | ||
| if (symbol.length > 1) throw new Error('padding_symbol can not have length > 1') | ||
| for (let i = 1; i <= specs[spec].width - valueLength; i++) { | ||
| const symbol = specs[spec].padding_symbol || ' ' | ||
| if (symbol.length > 1) throw new Error('padding_symbol cannot have length > 1') | ||
| switch (specs[spec].padding_position) { | ||
@@ -219,7 +202,7 @@ case 'start': | ||
| } | ||
| output = output + value.substring(0, specs[spec].width) | ||
| output += value.substring(0, specs[spec].width) | ||
| } | ||
| counter = counter + 1 | ||
| counter++ | ||
| if (input.length !== counter) { | ||
| output = output + '\n' | ||
| output += '\n' | ||
| } | ||
@@ -231,16 +214,16 @@ } | ||
| const preprocessCheck = (spec, value, row) => { | ||
| return (spec.preprocess) ? spec.preprocess(value, row) : value | ||
| } | ||
| const preprocessCheck = (spec, value, row) => spec.preprocess ? spec.preprocess(value, row) : value | ||
| const startCheck = (specs) => { | ||
| let nextStart = 1 | ||
| specs.map = specs.map.map(col => { | ||
| if (!col.start) col.start = nextStart | ||
| nextStart = col.start + col.width | ||
| return col | ||
| }) | ||
| return specs | ||
| return { | ||
| ...specs, | ||
| map: specs.map.map(col => { | ||
| const start = col.start || nextStart | ||
| nextStart = start + col.width | ||
| return { ...col, start } | ||
| }) | ||
| } | ||
| } | ||
| module.exports = internals |
+3
-4
| { | ||
| "name": "fixy", | ||
| "version": "1.3.3", | ||
| "version": "2.0.0", | ||
| "description": "A Fixed Width Input Parser", | ||
@@ -20,5 +20,4 @@ "main": "index.js", | ||
| "dependencies": { | ||
| "lodash": "4.17.21", | ||
| "moment": "2.29.4", | ||
| "papaparse": "5.2.0" | ||
| "date-fns": "^2.30.0", | ||
| "papaparse": "^5.4.1" | ||
| }, | ||
@@ -25,0 +24,0 @@ "devDependencies": { |
+1
-3
| [](https://badge.fury.io/js/fixy) | ||
| [](https://david-dm.org/SteveyPugs/fixy) | ||
| [](https://david-dm.org/SteveyPugs/fixy#info=devDependencies) | ||
| [](https://travis-ci.org/SteveyPugs/fixy) | ||
| [](https://app.travis-ci.com/SteveyPugs/fixy) | ||
@@ -6,0 +4,0 @@ # Fixy |
2
-33.33%33294
-0.62%875
-1.91%251
-0.79%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated