@cj-tech-master/excelts 3.0.1-canary.20251230172852.9dca08f → 3.0.1-canary.20251230173853.c3c79fa

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.
@@ -15,6 +15,7 @@ exports.isSyncValidate = isSyncValidate;
15
15
  exports.parseCsv = parseCsv;
16
16
  exports.formatCsv = formatCsv;
17
17
  exports.parseCsvStream = parseCsvStream;
18
+ const csv_number_1 = require("./csv-number");
18
19
  // =============================================================================
19
20
  // Helper Functions
20
21
  // =============================================================================
@@ -376,7 +377,7 @@ function parseCsv(input, options = {}) {
376
377
  * Format data as a CSV string
377
378
  */
378
379
  function formatCsv(data, options = {}) {
379
- const { delimiter = ",", quote: quoteOption = '"', escape: escapeOption, rowDelimiter = "\n", alwaysQuote = false, quoteColumns = false, quoteHeaders = false, headers, writeHeaders: writeHeadersOption, writeBOM = false, includeEndRowDelimiter = false, alwaysWriteHeaders = false, transform } = options;
380
+ const { delimiter = ",", quote: quoteOption = '"', escape: escapeOption, rowDelimiter = "\n", alwaysQuote = false, quoteColumns = false, quoteHeaders = false, headers, writeHeaders: writeHeadersOption, writeBOM = false, includeEndRowDelimiter = false, alwaysWriteHeaders = false, transform, decimalSeparator = "." } = options;
380
381
  // Determine if headers should be written (default: true when headers is provided)
381
382
  const shouldWriteHeaders = writeHeadersOption ?? true;
382
383
  // If quote is false or null, disable quoting entirely
@@ -403,7 +404,9 @@ function formatCsv(data, options = {}) {
403
404
  if (value === null || value === undefined) {
404
405
  return "";
405
406
  }
406
- const str = String(value);
407
+ const str = typeof value === "number"
408
+ ? (0, csv_number_1.formatNumberForCsv)(value, decimalSeparator)
409
+ : String(value);
407
410
  // If quoting is disabled, return raw string
408
411
  if (!quoteEnabled) {
409
412
  return str;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatNumberForCsv = formatNumberForCsv;
4
+ exports.parseNumberFromCsv = parseNumberFromCsv;
5
+ function formatNumberForCsv(value, decimalSeparator) {
6
+ if (decimalSeparator !== ",") {
7
+ return String(value);
8
+ }
9
+ // Keep JS numeric string form but replace the decimal point.
10
+ // Use split/join for broad runtime compatibility.
11
+ return String(value).split(".").join(",");
12
+ }
13
+ function parseNumberFromCsv(value, decimalSeparator) {
14
+ if (decimalSeparator !== ",") {
15
+ return Number(value);
16
+ }
17
+ const trimmed = value.trim();
18
+ // Minimal locale support: treat a single comma as the decimal separator.
19
+ // Common EU CSV uses delimiter ';' and decimal ',' (e.g. 12,34).
20
+ if (/^-?\d+(,\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
21
+ return Number(trimmed.replace(",", "."));
22
+ }
23
+ return Number(value);
24
+ }
@@ -12,6 +12,7 @@ exports.createCsvParserStream = createCsvParserStream;
12
12
  exports.createCsvFormatterStream = createCsvFormatterStream;
13
13
  const stream_1 = require("stream");
14
14
  const csv_core_1 = require("./csv-core");
15
+ const csv_number_1 = require("./csv-number");
15
16
  /**
16
17
  * Transform stream that parses CSV data row by row
17
18
  *
@@ -441,6 +442,7 @@ class CsvFormatterStream extends stream_1.Transform {
441
442
  this.delimiter = options.delimiter ?? ",";
442
443
  this.rowDelimiter = options.rowDelimiter ?? "\n";
443
444
  this.alwaysQuote = options.alwaysQuote ?? false;
445
+ this.decimalSeparator = options.decimalSeparator ?? ".";
444
446
  // writeHeaders defaults to true when headers is provided
445
447
  this.shouldWriteHeaders = options.writeHeaders ?? true;
446
448
  if (Array.isArray(options.headers)) {
@@ -593,7 +595,7 @@ class CsvFormatterStream extends stream_1.Transform {
593
595
  if (value === null || value === undefined) {
594
596
  return "";
595
597
  }
596
- const str = String(value);
598
+ const str = typeof value === "number" ? (0, csv_number_1.formatNumberForCsv)(value, this.decimalSeparator) : String(value);
597
599
  if (!this.quoteEnabled) {
598
600
  return str;
599
601
  }
@@ -13,6 +13,7 @@ exports.parseCsvToWorksheet = parseCsvToWorksheet;
13
13
  exports.formatWorksheetToCsv = formatWorksheetToCsv;
14
14
  const datetime_1 = require("../utils/datetime");
15
15
  const csv_core_1 = require("./csv-core");
16
+ const csv_number_1 = require("./csv-number");
16
17
  // Special Excel values mapping
17
18
  const SpecialValues = {
18
19
  true: true,
@@ -28,14 +29,15 @@ const SpecialValues = {
28
29
  /**
29
30
  * Create the default value mapper for CSV parsing
30
31
  */
31
- function createDefaultValueMapper(dateFormats) {
32
+ function createDefaultValueMapper(dateFormats, parserOptions) {
32
33
  const dateParser = datetime_1.DateParser.create(dateFormats);
34
+ const decimalSeparator = parserOptions?.decimalSeparator ?? ".";
33
35
  return function mapValue(datum) {
34
36
  if (datum === "") {
35
37
  return null;
36
38
  }
37
39
  // Try to parse as number
38
- const datumNumber = Number(datum);
40
+ const datumNumber = typeof datum === "string" ? (0, csv_number_1.parseNumberFromCsv)(datum, decimalSeparator) : Number(datum);
39
41
  if (!Number.isNaN(datumNumber) && datumNumber !== Infinity) {
40
42
  return datumNumber;
41
43
  }
@@ -95,12 +97,14 @@ function parseCsvToWorksheet(content, workbook, options = {}) {
95
97
  "YYYY-MM-DD[T]HH:mm:ss",
96
98
  "YYYY-MM-DD"
97
99
  ];
98
- const map = options.map || createDefaultValueMapper(dateFormats);
100
+ // If using the default mapper, pass parserOptions for locale-aware number parsing.
101
+ // (Custom map overrides this behavior.)
102
+ const effectiveMap = options.map || createDefaultValueMapper(dateFormats, options.parserOptions);
99
103
  // Parse CSV
100
104
  const rows = (0, csv_core_1.parseCsv)(content, options.parserOptions);
101
105
  // Add rows to worksheet
102
106
  for (const row of rows) {
103
- worksheet.addRow(row.map(map));
107
+ worksheet.addRow(row.map(effectiveMap));
104
108
  }
105
109
  return worksheet;
106
110
  }
@@ -89,7 +89,7 @@ class CSV {
89
89
  const worksheet = this.workbook.addWorksheet(options?.sheetName);
90
90
  const dateFormats = options?.dateFormats ??
91
91
  ["YYYY-MM-DD[T]HH:mm:ssZ", "YYYY-MM-DD[T]HH:mm:ss", "MM-DD-YYYY", "YYYY-MM-DD"];
92
- const map = options?.map || (0, csv_base_1.createDefaultValueMapper)(dateFormats);
92
+ const map = options?.map || (0, csv_base_1.createDefaultValueMapper)(dateFormats, options?.parserOptions);
93
93
  const parser = new csv_stream_1.CsvParserStream(options?.parserOptions);
94
94
  return new Promise((resolve, reject) => {
95
95
  stream.pipe(parser);
@@ -235,7 +235,7 @@ class CSV {
235
235
  const worksheet = this.workbook.addWorksheet(options?.sheetName);
236
236
  const dateFormats = options?.dateFormats ??
237
237
  ["YYYY-MM-DD[T]HH:mm:ssZ", "YYYY-MM-DD[T]HH:mm:ss", "MM-DD-YYYY", "YYYY-MM-DD"];
238
- const map = options?.map || (0, csv_base_1.createDefaultValueMapper)(dateFormats);
238
+ const map = options?.map || (0, csv_base_1.createDefaultValueMapper)(dateFormats, options?.parserOptions);
239
239
  const parser = new csv_stream_1.CsvParserStream(options?.parserOptions);
240
240
  parser.on("data", (row) => {
241
241
  worksheet.addRow(row.map(map));
@@ -8,6 +8,7 @@
8
8
  *
9
9
  * @see https://tools.ietf.org/html/rfc4180
10
10
  */
11
+ import { formatNumberForCsv } from "./csv-number.js";
11
12
  // =============================================================================
12
13
  // Helper Functions
13
14
  // =============================================================================
@@ -369,7 +370,7 @@ export function parseCsv(input, options = {}) {
369
370
  * Format data as a CSV string
370
371
  */
371
372
  export function formatCsv(data, options = {}) {
372
- const { delimiter = ",", quote: quoteOption = '"', escape: escapeOption, rowDelimiter = "\n", alwaysQuote = false, quoteColumns = false, quoteHeaders = false, headers, writeHeaders: writeHeadersOption, writeBOM = false, includeEndRowDelimiter = false, alwaysWriteHeaders = false, transform } = options;
373
+ const { delimiter = ",", quote: quoteOption = '"', escape: escapeOption, rowDelimiter = "\n", alwaysQuote = false, quoteColumns = false, quoteHeaders = false, headers, writeHeaders: writeHeadersOption, writeBOM = false, includeEndRowDelimiter = false, alwaysWriteHeaders = false, transform, decimalSeparator = "." } = options;
373
374
  // Determine if headers should be written (default: true when headers is provided)
374
375
  const shouldWriteHeaders = writeHeadersOption ?? true;
375
376
  // If quote is false or null, disable quoting entirely
@@ -396,7 +397,9 @@ export function formatCsv(data, options = {}) {
396
397
  if (value === null || value === undefined) {
397
398
  return "";
398
399
  }
399
- const str = String(value);
400
+ const str = typeof value === "number"
401
+ ? formatNumberForCsv(value, decimalSeparator)
402
+ : String(value);
400
403
  // If quoting is disabled, return raw string
401
404
  if (!quoteEnabled) {
402
405
  return str;
@@ -0,0 +1,20 @@
1
+ export function formatNumberForCsv(value, decimalSeparator) {
2
+ if (decimalSeparator !== ",") {
3
+ return String(value);
4
+ }
5
+ // Keep JS numeric string form but replace the decimal point.
6
+ // Use split/join for broad runtime compatibility.
7
+ return String(value).split(".").join(",");
8
+ }
9
+ export function parseNumberFromCsv(value, decimalSeparator) {
10
+ if (decimalSeparator !== ",") {
11
+ return Number(value);
12
+ }
13
+ const trimmed = value.trim();
14
+ // Minimal locale support: treat a single comma as the decimal separator.
15
+ // Common EU CSV uses delimiter ';' and decimal ',' (e.g. 12,34).
16
+ if (/^-?\d+(,\d+)?([eE][+-]?\d+)?$/.test(trimmed)) {
17
+ return Number(trimmed.replace(",", "."));
18
+ }
19
+ return Number(value);
20
+ }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { Transform } from "stream";
8
8
  import { isSyncTransform, isSyncValidate } from "./csv-core.js";
9
+ import { formatNumberForCsv } from "./csv-number.js";
9
10
  /**
10
11
  * Transform stream that parses CSV data row by row
11
12
  *
@@ -434,6 +435,7 @@ export class CsvFormatterStream extends Transform {
434
435
  this.delimiter = options.delimiter ?? ",";
435
436
  this.rowDelimiter = options.rowDelimiter ?? "\n";
436
437
  this.alwaysQuote = options.alwaysQuote ?? false;
438
+ this.decimalSeparator = options.decimalSeparator ?? ".";
437
439
  // writeHeaders defaults to true when headers is provided
438
440
  this.shouldWriteHeaders = options.writeHeaders ?? true;
439
441
  if (Array.isArray(options.headers)) {
@@ -586,7 +588,7 @@ export class CsvFormatterStream extends Transform {
586
588
  if (value === null || value === undefined) {
587
589
  return "";
588
590
  }
589
- const str = String(value);
591
+ const str = typeof value === "number" ? formatNumberForCsv(value, this.decimalSeparator) : String(value);
590
592
  if (!this.quoteEnabled) {
591
593
  return str;
592
594
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { DateParser, DateFormatter } from "../utils/datetime.js";
8
8
  import { parseCsv, formatCsv } from "./csv-core.js";
9
+ import { parseNumberFromCsv } from "./csv-number.js";
9
10
  // Special Excel values mapping
10
11
  const SpecialValues = {
11
12
  true: true,
@@ -21,14 +22,15 @@ const SpecialValues = {
21
22
  /**
22
23
  * Create the default value mapper for CSV parsing
23
24
  */
24
- export function createDefaultValueMapper(dateFormats) {
25
+ export function createDefaultValueMapper(dateFormats, parserOptions) {
25
26
  const dateParser = DateParser.create(dateFormats);
27
+ const decimalSeparator = parserOptions?.decimalSeparator ?? ".";
26
28
  return function mapValue(datum) {
27
29
  if (datum === "") {
28
30
  return null;
29
31
  }
30
32
  // Try to parse as number
31
- const datumNumber = Number(datum);
33
+ const datumNumber = typeof datum === "string" ? parseNumberFromCsv(datum, decimalSeparator) : Number(datum);
32
34
  if (!Number.isNaN(datumNumber) && datumNumber !== Infinity) {
33
35
  return datumNumber;
34
36
  }
@@ -88,12 +90,14 @@ export function parseCsvToWorksheet(content, workbook, options = {}) {
88
90
  "YYYY-MM-DD[T]HH:mm:ss",
89
91
  "YYYY-MM-DD"
90
92
  ];
91
- const map = options.map || createDefaultValueMapper(dateFormats);
93
+ // If using the default mapper, pass parserOptions for locale-aware number parsing.
94
+ // (Custom map overrides this behavior.)
95
+ const effectiveMap = options.map || createDefaultValueMapper(dateFormats, options.parserOptions);
92
96
  // Parse CSV
93
97
  const rows = parseCsv(content, options.parserOptions);
94
98
  // Add rows to worksheet
95
99
  for (const row of rows) {
96
- worksheet.addRow(row.map(map));
100
+ worksheet.addRow(row.map(effectiveMap));
97
101
  }
98
102
  return worksheet;
99
103
  }
@@ -51,7 +51,7 @@ class CSV {
51
51
  const worksheet = this.workbook.addWorksheet(options?.sheetName);
52
52
  const dateFormats = options?.dateFormats ??
53
53
  ["YYYY-MM-DD[T]HH:mm:ssZ", "YYYY-MM-DD[T]HH:mm:ss", "MM-DD-YYYY", "YYYY-MM-DD"];
54
- const map = options?.map || createDefaultValueMapper(dateFormats);
54
+ const map = options?.map || createDefaultValueMapper(dateFormats, options?.parserOptions);
55
55
  const parser = new CsvParserStream(options?.parserOptions);
56
56
  return new Promise((resolve, reject) => {
57
57
  stream.pipe(parser);
@@ -197,7 +197,7 @@ class CSV {
197
197
  const worksheet = this.workbook.addWorksheet(options?.sheetName);
198
198
  const dateFormats = options?.dateFormats ??
199
199
  ["YYYY-MM-DD[T]HH:mm:ssZ", "YYYY-MM-DD[T]HH:mm:ss", "MM-DD-YYYY", "YYYY-MM-DD"];
200
- const map = options?.map || createDefaultValueMapper(dateFormats);
200
+ const map = options?.map || createDefaultValueMapper(dateFormats, options?.parserOptions);
201
201
  const parser = new CsvParserStream(options?.parserOptions);
202
202
  parser.on("data", (row) => {
203
203
  worksheet.addRow(row.map(map));
@@ -58,6 +58,13 @@ export interface CsvParseOptions {
58
58
  renameHeaders?: boolean;
59
59
  /** Comment character - lines starting with this are ignored */
60
60
  comment?: string;
61
+ /**
62
+ * Decimal separator used when parsing numbers from CSV (default: ".").
63
+ *
64
+ * Note: core CSV parsing returns strings; this option is intended for higher-level
65
+ * consumers (e.g. the default value mapper in csv.base) that convert strings to numbers.
66
+ */
67
+ decimalSeparator?: "." | ",";
61
68
  /** Maximum number of data rows to parse (excluding header) */
62
69
  maxRows?: number;
63
70
  /** Number of lines to skip at the beginning (before header detection) */
@@ -129,6 +136,11 @@ export interface CsvFormatOptions {
129
136
  escape?: string | false | null;
130
137
  /** Row delimiter (default: "\n") */
131
138
  rowDelimiter?: string;
139
+ /**
140
+ * Decimal separator used when formatting numbers to CSV (default: ".").
141
+ * For European-style CSV, this is commonly "," (often together with delimiter ";").
142
+ */
143
+ decimalSeparator?: "." | ",";
132
144
  /** Always quote all fields (default: false, only quote when necessary) */
133
145
  alwaysQuote?: boolean;
134
146
  /** Quote specific columns by name or index */
@@ -0,0 +1,3 @@
1
+ export type DecimalSeparator = "." | ",";
2
+ export declare function formatNumberForCsv(value: number, decimalSeparator: DecimalSeparator): string;
3
+ export declare function parseNumberFromCsv(value: string, decimalSeparator: DecimalSeparator): number;
@@ -83,6 +83,7 @@ export declare class CsvFormatterStream extends Transform {
83
83
  private rowDelimiter;
84
84
  private quoteEnabled;
85
85
  private alwaysQuote;
86
+ private decimalSeparator;
86
87
  private headerWritten;
87
88
  private headers;
88
89
  private shouldWriteHeaders;
@@ -45,7 +45,7 @@ export interface CsvWriteOptions {
45
45
  /**
46
46
  * Create the default value mapper for CSV parsing
47
47
  */
48
- export declare function createDefaultValueMapper(dateFormats: readonly DateFormat[]): (datum: any) => any;
48
+ export declare function createDefaultValueMapper(dateFormats: readonly DateFormat[], parserOptions?: Pick<CsvParseOptions, "decimalSeparator">): (datum: any) => any;
49
49
  /**
50
50
  * Create the default value mapper for CSV writing
51
51
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cj-tech-master/excelts",
3
- "version": "3.0.1-canary.20251230172852.9dca08f",
3
+ "version": "3.0.1-canary.20251230173853.c3c79fa",
4
4
  "description": "TypeScript Excel Workbook Manager - Read and Write xlsx and csv Files.",
5
5
  "type": "module",
6
6
  "publishConfig": {