Skip to content

Commit

Permalink
Replacing any with unknown and adding a few assertions. Dropping impl…
Browse files Browse the repository at this point in the history
…icit any.
  • Loading branch information
nathanb committed Dec 6, 2024
1 parent c1d1702 commit f64908d
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 32 deletions.
13 changes: 11 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/json-csv-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@iwsio/json-csv-core",
"version": "1.1.7",
"version": "1.2.0-alpha.1",
"description": "Easily convert JSON to CSV. This is a core library built for browser and node to support buffered conversion to CSV.",
"keywords": [
"json",
Expand Down
50 changes: 28 additions & 22 deletions packages/json-csv-core/src/exporter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ExportOptions, FieldList } from './types.js'
import { ExportOptions, Field, FieldList } from './types.js'

export function checkOptions(opts?: Partial<ExportOptions>) {
const options: Partial<ExportOptions> = opts == null ? {} : { ...opts }
Expand All @@ -11,7 +11,7 @@ export function checkOptions(opts?: Partial<ExportOptions>) {
/**
* Main entry point. Convert a buffered array of data to a CSV string.
*/
export function buffered(data: Record<string, any>[], opts: Partial<ExportOptions>) {
export function buffered(data: Record<string, unknown>[], opts: Partial<ExportOptions>) {
const options = checkOptions(opts)
let output = ''
let writtenHeader = false
Expand All @@ -31,7 +31,7 @@ export function buffered(data: Record<string, any>[], opts: Partial<ExportOption
return output
}

export function prepValue(text: string, forceQuoted: boolean, fieldSeparator: string): any {
export function prepValue(text: string, forceQuoted: boolean, fieldSeparator: string): unknown {
if (text == null) text = ''
const quoted = forceQuoted || text.indexOf('"') >= 0 || text.indexOf(fieldSeparator) >= 0 || text.indexOf('\n') >= 0
let result = text.replace(/"/g, '""')
Expand All @@ -40,31 +40,32 @@ export function prepValue(text: string, forceQuoted: boolean, fieldSeparator: st
}

export function getHeaderRow(fields: FieldList, fieldSeparator: string) {
const fieldKeys = Object.keys(fields)
let header = fieldKeys.reduce((line, fieldKey) => {
const field = fields[fieldKey]
let header = ''
for (let ix = 0; ix < fields.length; ix++) {
const field = fields[ix]
const label = field.label || field.name
if (line === 'START') {
line = ''
} else {
line += fieldSeparator
if (ix > 0) {
header += fieldSeparator
}
line += prepValue(label, field.quoted, fieldSeparator)
return line
}, 'START')
header += prepValue(label, field.quoted, fieldSeparator)
}
header += '\r\n'
return header
}

export function getBodyRow(data: Record<string, any> | undefined | null, fields: FieldList, fieldSeparator: string): string {
const reducer = (line, field) => {
const assertString = (value: unknown): value is string => {
return typeof value === 'string'
}

export function getBodyRow(data: Record<string, unknown> | undefined | null, fields: FieldList, fieldSeparator: string): string {
const reducer = (line: string, field: Field) => {
if (line === 'START') {
line = ''
} else {
line += fieldSeparator
}
let val = getValue(data, field.name)
// vinicioslc support to OR || operator allowing multiples names to the same column
// support to OR || operator allowing multiples names to the same column
// the code will use the last non null and non empty value
if (field.name.includes('||')) {
// by default column is empty
Expand All @@ -75,7 +76,7 @@ export function getBodyRow(data: Record<string, any> | undefined | null, fields:
// get value and associate
const fieldVal = getValue(data, field)
// remove whitespaces and check if non null before assign
if (val != null && fieldVal.trim().length > 0 && fieldVal.trim() !== '') {
if (val != null && assertString(fieldVal) && fieldVal.trim().length > 0 && fieldVal.trim() !== '') {
val = fieldVal
}
// do this for every field
Expand All @@ -84,7 +85,7 @@ export function getBodyRow(data: Record<string, any> | undefined | null, fields:

if (typeof field.transform === 'function') {
val = field.transform(val)
} else if (typeof field.filter === 'function') {
} else if (typeof field.filter === 'function') { // backward compatibility
val = field.filter(val)
}
if (typeof val !== 'undefined' && val !== null) {
Expand All @@ -99,24 +100,29 @@ export function getBodyRow(data: Record<string, any> | undefined | null, fields:
return row
}

export function getValue(data: Record<string, any>, keyPath: string): any {
export function getValue(data: Record<string, unknown>, keyPath: string): unknown {
const keys = keyPath.split('.')
if (keys.length > 0) return getValueIx(data, keys, 0)
return ''
}

export function getValueIx(data: Record<string, any> | undefined | null, keys: string[], ix: number): any {
const assertObject = (value: unknown): value is Record<string, unknown> => {
if (typeof value === 'object') return true
return false
}

export function getValueIx(data: Record<string, unknown> | undefined | null, keys: string[], ix: number): unknown {
if (data == null) return ''

// for filtered fields using the whole row as a source.
// `this` is a keyword here; hoping not to conflict with existing fields.
if (keys[0] === 'this') return data

const val = data[keys[ix]]
if (typeof val === 'undefined') return ''
if (val == null) return ''

// walk the dot-notation recursively to get the remaining values.
if ((keys.length - 1) > ix) return getValueIx(val, keys, ix + 1)
if ((keys.length - 1) > ix && assertObject(val)) return getValueIx(val, keys, ix + 1)

return val
}
Expand Down
9 changes: 8 additions & 1 deletion packages/json-csv-core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
export type Field = {
name: string
label?: string
transform?: (source: any) => string
transform?: (source: unknown) => string
/**
* @deprecated Please use `transform` instead
* @param source unknown value to be transformed
* @returns string transformed value
*/
filter?: (source: unknown) => string
quoted?: boolean
}

export type FieldList = Field[]
Expand Down
2 changes: 1 addition & 1 deletion packages/json-csv-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@iwsio/json-csv-node",
"version": "6.1.7",
"version": "6.2.0-alpha.1",
"description": "ESM/CJS module that easily converts JSON to CSV. This package supports streaming and buffered conversion to CSV.",
"homepage": "https://github.com/IWSLLC/json-csv",
"author": "Nathan Bridgewater <[email protected]>",
Expand Down
10 changes: 5 additions & 5 deletions packages/json-csv-node/src/string-writer.cts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ import { StringDecoder } from 'string_decoder'
* See: https://nodejs.org/docs/latest-v14.x/api/stream.html#stream_decoding_buffers_in_a_writable_stream
*/
export default class StringWriter extends Writable {
_decoder: any
_decoder: StringDecoder
data: any
constructor(options?: WritableOptions) {
super(options)
this._decoder = new StringDecoder(options?.defaultEncoding)
this._decoder = new StringDecoder(options?.defaultEncoding ?? 'utf8')
this.data = ''
}

_write(chunk, encoding, callback) {
if (encoding === 'buffer') {
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void) {
if (encoding != null) {
chunk = this._decoder.write(chunk)
}
this.data += chunk
callback()
}

_final(callback) {
_final(callback: (error?: Error | null) => void) {
this.data += this._decoder.end()
callback()
}
Expand Down
1 change: 1 addition & 0 deletions tsconfig-base.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"lib": ["ES6"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noImplicitAny": true,
"resolveJsonModule": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
Expand Down

0 comments on commit f64908d

Please sign in to comment.