@teselagen/file-utils 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,45 +1,40 @@
1
- /* Copyright (C) 2018 TeselaGen Biotechnology, Inc. */
1
+ // packages/file-utils/src/file-utils.js
2
2
  import { camelCase, flatMap, remove, startsWith, snakeCase } from "lodash";
3
3
  import { loadAsync } from "jszip";
4
- import Promise from "bluebird";
4
+ import Promise2 from "bluebird";
5
5
  import { parse, unparse } from "papaparse";
6
-
7
- const logDebug = (...args) => {
6
+ var logDebug = (...args) => {
8
7
  if (process.env.DEBUG_CSV_PARSING) {
9
- // eslint-disable-next-line no-console
10
8
  console.log(...args);
11
9
  }
12
10
  };
13
-
14
- export const allowedCsvFileTypes = [".csv", ".txt", ".xlsx"];
15
-
16
- export const isZipFile = file => {
11
+ var allowedCsvFileTypes = [".csv", ".txt", ".xlsx"];
12
+ var isZipFile = (file) => {
17
13
  const type = file.mimetype || file.type;
18
14
  return type === "application/zip" || type === "application/x-zip-compressed";
19
15
  };
20
-
21
- export const getExt = file => file.name.split(".").pop();
22
- export const isExcelFile = file => getExt(file) === "xlsx";
23
- export const isCsvFile = file => getExt(file) === "csv";
24
- export const isTextFile = file => ["text", "txt"].includes(getExt(file));
25
-
26
- export const isCsvOrExcelFile = file => isCsvFile(file) || isExcelFile(file);
27
-
28
- export const extractZipFiles = async allFiles => {
29
- if (!Array.isArray(allFiles)) allFiles = [allFiles];
30
- // make a copy so we don't mutate the form value
16
+ var getExt = (file) => file.name.split(".").pop();
17
+ var isExcelFile = (file) => getExt(file) === "xlsx";
18
+ var isCsvFile = (file) => getExt(file) === "csv";
19
+ var isTextFile = (file) => ["text", "txt"].includes(getExt(file));
20
+ var isCsvOrExcelFile = (file) => isCsvFile(file) || isExcelFile(file);
21
+ var extractZipFiles = async (allFiles) => {
22
+ if (!Array.isArray(allFiles))
23
+ allFiles = [allFiles];
31
24
  allFiles = [...allFiles];
32
25
  const zipFiles = remove(allFiles, isZipFile);
33
- if (!zipFiles.length) return allFiles;
26
+ if (!zipFiles.length)
27
+ return allFiles;
34
28
  const zipFilesArray = Array.isArray(zipFiles) ? zipFiles : [zipFiles];
35
- const parsedZips = await Promise.map(zipFilesArray, file =>
36
- loadAsync(file instanceof Blob ? file : file.originFileObj)
29
+ const parsedZips = await Promise2.map(
30
+ zipFilesArray,
31
+ (file) => loadAsync(file instanceof Blob ? file : file.originFileObj)
37
32
  );
38
- const zippedFiles = flatMap(parsedZips, zip =>
39
- Object.keys(zip.files).map(key => zip.files[key])
33
+ const zippedFiles = flatMap(
34
+ parsedZips,
35
+ (zip) => Object.keys(zip.files).map((key) => zip.files[key])
40
36
  );
41
- const unzippedFiles = await Promise.map(zippedFiles, file => {
42
- // converts the compressed file to a string of its contents
37
+ const unzippedFiles = await Promise2.map(zippedFiles, (file) => {
43
38
  return file.async("blob").then(function(fileData) {
44
39
  const newFileObj = new File([fileData], file.name);
45
40
  return {
@@ -52,38 +47,32 @@ export const extractZipFiles = async allFiles => {
52
47
  if (unzippedFiles.length) {
53
48
  return allFiles.concat(
54
49
  unzippedFiles.filter(
55
- ({ name, originFileObj }) =>
56
- !name.includes("__MACOSX") &&
57
- !name.includes(".DS_Store") &&
58
- originFileObj.size !== 0
50
+ ({ name, originFileObj }) => !name.includes("__MACOSX") && !name.includes(".DS_Store") && originFileObj.size !== 0
59
51
  )
60
52
  );
61
53
  } else {
62
54
  return allFiles;
63
55
  }
64
56
  };
65
-
66
- const defaultCsvParserOptions = {
57
+ var defaultCsvParserOptions = {
67
58
  header: true,
68
59
  skipEmptyLines: "greedy",
69
60
  trimHeaders: true
70
61
  };
71
- export const setupCsvParserOptions = (parserOptions = {}) => {
62
+ var setupCsvParserOptions = (parserOptions = {}) => {
72
63
  const {
73
64
  camelCaseHeaders = false,
74
65
  lowerCaseHeaders = false,
75
66
  ...rest
76
67
  } = parserOptions;
77
-
78
68
  const papaParseOpts = { ...rest };
79
69
  if (camelCaseHeaders) {
80
70
  logDebug("[CSV-PARSER] camelCasing headers");
81
- papaParseOpts.transformHeader = header => {
71
+ papaParseOpts.transformHeader = (header) => {
82
72
  let transHeader = header;
83
73
  if (!startsWith(header.trim(), "ext-")) {
84
74
  transHeader = camelCase(header);
85
75
  }
86
-
87
76
  if (transHeader) {
88
77
  logDebug(
89
78
  `[CSV-PARSER] Transformed header from: ${header} to: ${transHeader}`
@@ -92,16 +81,14 @@ export const setupCsvParserOptions = (parserOptions = {}) => {
92
81
  } else {
93
82
  logDebug(`[CSV-PARSER] Not transforming header: ${header}`);
94
83
  }
95
-
96
84
  return transHeader;
97
85
  };
98
86
  } else if (lowerCaseHeaders) {
99
- papaParseOpts.transformHeader = header => {
87
+ papaParseOpts.transformHeader = (header) => {
100
88
  let transHeader = header;
101
89
  if (!startsWith(header, "ext-")) {
102
90
  transHeader = header.toLowerCase();
103
91
  }
104
-
105
92
  if (transHeader) {
106
93
  logDebug(
107
94
  `[CSV-PARSER] Transformed header from: ${header} to: ${transHeader}`
@@ -110,35 +97,30 @@ export const setupCsvParserOptions = (parserOptions = {}) => {
110
97
  } else {
111
98
  logDebug(`[CSV-PARSER] Not transforming header: ${header}`);
112
99
  }
113
-
114
100
  return transHeader;
115
101
  };
116
102
  }
117
-
118
103
  return papaParseOpts;
119
104
  };
120
-
121
- const normalizeCsvHeaderHelper = h => snakeCase(h.toUpperCase()).toUpperCase();
122
-
123
- export function normalizeCsvHeader(header) {
105
+ var normalizeCsvHeaderHelper = (h) => snakeCase(h.toUpperCase()).toUpperCase();
106
+ function normalizeCsvHeader(header) {
124
107
  if (header.startsWith("ext-") || header.startsWith("EXT-")) {
125
108
  return header;
126
109
  }
127
110
  return normalizeCsvHeaderHelper(header);
128
111
  }
129
-
130
- export const parseCsvFile = (csvFile, parserOptions = {}) => {
131
- return new Promise((resolve, reject) => {
112
+ var parseCsvFile = (csvFile, parserOptions = {}) => {
113
+ return new Promise2((resolve, reject) => {
132
114
  const opts = {
133
115
  ...defaultCsvParserOptions,
134
116
  ...setupCsvParserOptions(parserOptions),
135
- complete: results => {
117
+ complete: (results) => {
136
118
  if (results && results.errors && results.errors.length) {
137
119
  return reject("Error in csv: " + JSON.stringify(results.errors));
138
120
  }
139
121
  resolve(results);
140
122
  },
141
- error: error => {
123
+ error: (error) => {
142
124
  reject(error);
143
125
  }
144
126
  };
@@ -146,28 +128,11 @@ export const parseCsvFile = (csvFile, parserOptions = {}) => {
146
128
  parse(csvFile.originFileObj, opts);
147
129
  });
148
130
  };
149
-
150
- /**
151
- * Gets a properly formatted json object into s csv string
152
- * https://www.papaparse.com/docs#json-to-csv
153
- * const options = {
154
- quotes: false, //or array of booleans
155
- quoteChar: '"',
156
- escapeChar: '"',
157
- delimiter: ",",
158
- header: true,
159
- newline: "\r\n",
160
- skipEmptyLines: false, //other option is 'greedy', meaning skip delimiters, quotes, and whitespace.
161
- columns: null //or array of strings
162
- }
163
- * @returns csv as string
164
- */
165
- export const jsonToCsv = (jsonData, options = {}) => {
131
+ var jsonToCsv = (jsonData, options = {}) => {
166
132
  const csv = unparse(jsonData, options);
167
133
  return csv;
168
134
  };
169
-
170
- export const parseCsvString = (csvString, parserOptions = {}) => {
135
+ var parseCsvString = (csvString, parserOptions = {}) => {
171
136
  const opts = {
172
137
  ...defaultCsvParserOptions,
173
138
  ...setupCsvParserOptions(parserOptions)
@@ -175,27 +140,25 @@ export const parseCsvString = (csvString, parserOptions = {}) => {
175
140
  logDebug(`[CSV-PARSER] parseCsvString opts:`, opts);
176
141
  return parse(csvString, opts);
177
142
  };
178
-
179
- export async function parseCsvOrExcelFile(
180
- fileOrFiles,
181
- { csvParserOptions } = {}
182
- ) {
143
+ async function parseCsvOrExcelFile(fileOrFiles, { csvParserOptions } = {}) {
183
144
  let csvFile, excelFile, txtFile;
184
145
  if (Array.isArray(fileOrFiles)) {
185
146
  csvFile = fileOrFiles.find(isCsvFile);
186
147
  excelFile = fileOrFiles.find(isExcelFile);
187
148
  txtFile = fileOrFiles.find(isTextFile);
188
149
  } else {
189
- if (isExcelFile(fileOrFiles)) excelFile = fileOrFiles;
190
- else if (isCsvFile(fileOrFiles)) csvFile = fileOrFiles;
191
- else if (isTextFile(fileOrFiles)) txtFile = fileOrFiles;
150
+ if (isExcelFile(fileOrFiles))
151
+ excelFile = fileOrFiles;
152
+ else if (isCsvFile(fileOrFiles))
153
+ csvFile = fileOrFiles;
154
+ else if (isTextFile(fileOrFiles))
155
+ txtFile = fileOrFiles;
192
156
  }
193
157
  if (!csvFile && !excelFile && !txtFile) {
194
158
  throw new Error("No csv or excel files found");
195
159
  }
196
-
197
- if (!csvFile && !excelFile) csvFile = txtFile;
198
-
160
+ if (!csvFile && !excelFile)
161
+ csvFile = txtFile;
199
162
  if (!csvFile && excelFile && window.parseExcelToCsv) {
200
163
  csvFile = await window.parseExcelToCsv(
201
164
  excelFile.originFileObj || excelFile
@@ -210,13 +173,8 @@ export async function parseCsvOrExcelFile(
210
173
  parsedCsv.originalFile = csvFile;
211
174
  return parsedCsv;
212
175
  }
213
-
214
- export const validateCSVRequiredHeaders = (
215
- fields,
216
- requiredHeaders,
217
- filename
218
- ) => {
219
- const missingRequiredHeaders = requiredHeaders.filter(field => {
176
+ var validateCSVRequiredHeaders = (fields, requiredHeaders, filename) => {
177
+ const missingRequiredHeaders = requiredHeaders.filter((field) => {
220
178
  return !fields.includes(field);
221
179
  });
222
180
  if (missingRequiredHeaders.length) {
@@ -226,91 +184,64 @@ export const validateCSVRequiredHeaders = (
226
184
  )})`;
227
185
  }
228
186
  };
229
-
230
- export const validateCSVRow = (row, requiredHeaders, index) => {
231
- const missingRequiredFields = requiredHeaders.filter(field => !row[field]);
187
+ var validateCSVRow = (row, requiredHeaders, index) => {
188
+ const missingRequiredFields = requiredHeaders.filter((field) => !row[field]);
232
189
  if (missingRequiredFields.length) {
233
190
  if (missingRequiredFields.length === 1) {
234
- return `Row ${index + 1} is missing the required field "${
235
- missingRequiredFields[0]
236
- }"`;
191
+ return `Row ${index + 1} is missing the required field "${missingRequiredFields[0]}"`;
237
192
  } else {
238
- return `Row ${index +
239
- 1} is missing these required fields: ${missingRequiredFields.join(
193
+ return `Row ${index + 1} is missing these required fields: ${missingRequiredFields.join(
240
194
  ", "
241
195
  )}`;
242
196
  }
243
197
  }
244
198
  };
245
-
246
- export const cleanCommaSeparatedCell = cellData =>
247
- (cellData || "")
248
- .split(",")
249
- .map(n => n.trim())
250
- .filter(n => n);
251
-
252
- /**
253
- * Because the csv rows might not have the same header keys in some cases (extended properties)
254
- * this function will make sure that each row will have all headers so that the export
255
- * does not drop fields
256
- * @param {*} rows
257
- */
258
- export const cleanCsvExport = rows => {
199
+ var cleanCommaSeparatedCell = (cellData) => (cellData || "").split(",").map((n) => n.trim()).filter((n) => n);
200
+ var cleanCsvExport = (rows) => {
259
201
  const allHeaders = [];
260
- rows.forEach(row => {
261
- Object.keys(row).forEach(header => {
202
+ rows.forEach((row) => {
203
+ Object.keys(row).forEach((header) => {
262
204
  if (!allHeaders.includes(header)) {
263
205
  allHeaders.push(header);
264
206
  }
265
207
  });
266
208
  });
267
- rows.forEach(row => {
268
- allHeaders.forEach(header => {
209
+ rows.forEach((row) => {
210
+ allHeaders.forEach((header) => {
269
211
  row[header] = row[header] || "";
270
212
  });
271
213
  });
272
214
  return rows;
273
215
  };
274
-
275
- export const filterFilesInZip = async (file, accepted) => {
216
+ var filterFilesInZip = async (file, accepted) => {
276
217
  const zipExtracted = await extractZipFiles(file);
277
218
  const acceptedFiles = [];
278
219
  for (const extFile of zipExtracted) {
279
220
  const extension = "." + getExt(extFile);
280
- if (accepted.some(ext => ext === extension)) {
221
+ if (accepted.some((ext) => ext === extension)) {
281
222
  acceptedFiles.push(extFile);
282
223
  }
283
224
  }
284
-
285
225
  if (acceptedFiles.length && acceptedFiles.length < zipExtracted.length)
286
226
  window.toastr.warning("Some files don't have the proper file extension.");
287
-
288
227
  if (!acceptedFiles.length)
289
228
  window.toastr.warning("No files with the proper extension were found.");
290
-
291
229
  return acceptedFiles;
292
230
  };
293
-
294
- export function removeExt(filename) {
231
+ function removeExt(filename) {
295
232
  if (filename && filename.includes(".")) {
296
- return filename
297
- .split(".")
298
- .slice(0, -1)
299
- .join(".");
233
+ return filename.split(".").slice(0, -1).join(".");
300
234
  } else {
301
235
  return filename;
302
236
  }
303
237
  }
304
-
305
- export async function uploadAndProcessFiles(files = []) {
306
- if (!files.length) return null;
307
-
238
+ async function uploadAndProcessFiles(files = []) {
239
+ if (!files.length)
240
+ return null;
308
241
  const formData = new FormData();
309
242
  files.forEach(({ originFileObj }) => formData.append("file", originFileObj));
310
-
311
243
  const response = await window.api.post("/user_uploads/", formData);
312
-
313
- return response.data.map(d => ({
244
+ return response.data.map((d) => ({
314
245
  encoding: d.encoding,
315
246
  mimetype: d.mimetype,
316
247
  originalname: d.originalname,
@@ -318,8 +249,7 @@ export async function uploadAndProcessFiles(files = []) {
318
249
  size: d.size
319
250
  }));
320
251
  }
321
-
322
- export async function encodeFilesForRequest(files) {
252
+ async function encodeFilesForRequest(files) {
323
253
  const encodedFiles = [];
324
254
  for (const file of files) {
325
255
  const encoded = await fileToBase64(file.originalFileObj);
@@ -332,16 +262,36 @@ export async function encodeFilesForRequest(files) {
332
262
  }
333
263
  return encodedFiles;
334
264
  }
335
-
336
- const fileToBase64 = file => {
337
- return new Promise(resolve => {
265
+ var fileToBase64 = (file) => {
266
+ return new Promise2((resolve) => {
338
267
  const reader = new FileReader();
339
- // Read file content on file loaded event
340
268
  reader.onload = function(event) {
341
269
  resolve(event.target.result);
342
270
  };
343
-
344
- // Convert data to base64
345
271
  reader.readAsDataURL(file);
346
272
  });
347
273
  };
274
+ export {
275
+ allowedCsvFileTypes,
276
+ cleanCommaSeparatedCell,
277
+ cleanCsvExport,
278
+ encodeFilesForRequest,
279
+ extractZipFiles,
280
+ filterFilesInZip,
281
+ getExt,
282
+ isCsvFile,
283
+ isCsvOrExcelFile,
284
+ isExcelFile,
285
+ isTextFile,
286
+ isZipFile,
287
+ jsonToCsv,
288
+ normalizeCsvHeader,
289
+ parseCsvFile,
290
+ parseCsvOrExcelFile,
291
+ parseCsvString,
292
+ removeExt,
293
+ setupCsvParserOptions,
294
+ uploadAndProcessFiles,
295
+ validateCSVRequiredHeaders,
296
+ validateCSVRow
297
+ };
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@teselagen/file-utils",
3
- "version": "0.2.1",
4
- "type": "commonjs",
3
+ "version": "0.2.2",
4
+ "type": "module",
5
5
  "dependencies": {
6
6
  "bluebird": "^3.7.2",
7
7
  "jszip": "^3.10.1",
8
8
  "lodash": "^4.17.21",
9
9
  "papaparse": "^5.4.1"
10
- }
10
+ },
11
+ "module": "index.js",
12
+ "main": "index.js"
11
13
  }
package/.eslintrc.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "extends": ["../../.eslintrc.json"],
3
- "ignorePatterns": ["!**/*"],
4
- "overrides": [
5
- {
6
- "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
- "rules": {}
8
- },
9
- {
10
- "files": ["*.ts", "*.tsx"],
11
- "rules": {}
12
- },
13
- {
14
- "files": ["*.js", "*.jsx"],
15
- "rules": {}
16
- }
17
- ]
18
- }
package/jest.config.js DELETED
@@ -1,10 +0,0 @@
1
- /* eslint-disable */
2
- module.exports = {
3
- displayName: 'file-utils',
4
- preset: '../../jest.preset.js',
5
- transform: {
6
- '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
7
- },
8
- moduleFileExtensions: ['ts', 'js', 'html'],
9
- coverageDirectory: '../../coverage/packages/file-utils',
10
- };
package/project.json DELETED
@@ -1,51 +0,0 @@
1
- {
2
- "name": "file-utils",
3
- "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
- "sourceRoot": "packages/file-utils/src",
5
- "projectType": "library",
6
- "targets": {
7
- "version": {
8
- "executor": "@jscutlery/semver:version",
9
- "options": {
10
- "preset": "conventional"
11
- }
12
- },
13
- "build": {
14
- "executor": "@nx/esbuild:esbuild",
15
- "outputs": ["{options.outputPath}"],
16
- "options": {
17
- "outputPath": "dist/packages/file-utils",
18
- "main": "packages/file-utils/src/index.js",
19
- "tsConfig": "packages/file-utils/tsconfig.lib.json",
20
- "assets": ["packages/file-utils/*.md"],
21
- "generatePackageJson": true
22
- }
23
- },
24
- "publish": {
25
- "command": "node tools/scripts/publish.mjs file-utils {args.ver} {args.tag}",
26
- "dependsOn": ["build"]
27
- },
28
- "lint": {
29
- "executor": "@nx/linter:eslint",
30
- "outputs": ["{options.outputFile}"],
31
- "options": {
32
- "lintFilePatterns": ["packages/file-utils/**/*.js"]
33
- }
34
- },
35
- "test": {
36
- "executor": "@nx/jest:jest",
37
- "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
38
- "options": {
39
- "jestConfig": "packages/file-utils/jest.config.js",
40
- "passWithNoTests": true
41
- },
42
- "configurations": {
43
- "ci": {
44
- "ci": true,
45
- "codeCoverage": true
46
- }
47
- }
48
- }
49
- },
50
- "tags": []
51
- }
@@ -1,6 +0,0 @@
1
- import { fileUtils } from './file-utils';
2
- describe('fileUtils', () => {
3
- it('should work', () => {
4
- expect(fileUtils()).toEqual('file-utils');
5
- });
6
- });
package/src/index.js DELETED
@@ -1 +0,0 @@
1
- export * from './file-utils';
package/tsconfig.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "module": "commonjs",
5
- "allowJs": true,
6
- "forceConsistentCasingInFileNames": true,
7
- "strict": true,
8
- "noImplicitOverride": true,
9
- "noPropertyAccessFromIndexSignature": true,
10
- "noImplicitReturns": true,
11
- "noFallthroughCasesInSwitch": true
12
- },
13
- "files": [],
14
- "include": [],
15
- "references": [
16
- {
17
- "path": "./tsconfig.lib.json"
18
- },
19
- {
20
- "path": "./tsconfig.spec.json"
21
- }
22
- ]
23
- }
package/tsconfig.lib.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../dist/out-tsc",
5
- "declaration": true,
6
- "types": ["node"]
7
- },
8
- "include": ["src/**/*.ts", "src/**/*.js"],
9
- "exclude": [
10
- "jest.config.ts",
11
- "src/**/*.spec.ts",
12
- "src/**/*.test.ts",
13
- "src/**/*.spec.js",
14
- "src/**/*.test.js"
15
- ]
16
- }
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../dist/out-tsc",
5
- "module": "commonjs",
6
- "types": ["jest", "node"]
7
- },
8
- "include": [
9
- "jest.config.ts",
10
- "src/**/*.test.ts",
11
- "src/**/*.spec.ts",
12
- "src/**/*.d.ts"
13
- ]
14
- }