@lichens-innovation/ts-common 1.16.0 → 1.18.0
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.
- package/dist/csv.cjs.map +1 -1
- package/dist/csv.js.map +1 -1
- package/dist/dimensions.utils-BSm9gmWz.d.cts +47 -0
- package/dist/dimensions.utils-BSm9gmWz.d.ts +47 -0
- package/dist/excel.cjs +5 -3
- package/dist/excel.cjs.map +1 -1
- package/dist/excel.js +5 -3
- package/dist/excel.js.map +1 -1
- package/dist/index.cjs +243 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -43
- package/dist/index.d.ts +40 -43
- package/dist/index.js +224 -1
- package/dist/index.js.map +1 -1
- package/dist/mime.cjs +72 -0
- package/dist/mime.cjs.map +1 -1
- package/dist/mime.d.cts +16 -1
- package/dist/mime.d.ts +16 -1
- package/dist/mime.js +66 -1
- package/dist/mime.js.map +1 -1
- package/dist/web.cjs +271 -0
- package/dist/web.cjs.map +1 -1
- package/dist/web.d.cts +65 -2
- package/dist/web.d.ts +65 -2
- package/dist/web.js +249 -1
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
- package/dist/dimensions.utils-BwIBA5op.d.cts +0 -6
- package/dist/dimensions.utils-BwIBA5op.d.ts +0 -6
package/dist/csv.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":["mime","Papa"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":["mime","Papa"],"mappings":";;;;;;;;;;;AAwDO,IAAM,WAAA,GAAc,CAAC,SAAA,KAA8B;AACxD,EAAA,MAAM,QAAA,GAAWA,qBAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kDAAA,EAAqD,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,QAAA;AACT,CAAA;;;AC1DO,IAAM,eAAA,GAAkB,CAAC,GAAA,KAA2D;AACzF,EAAA,MAAM,YAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,eAAA,GAAkB,CAAC,IAAA,KAA4C;AAC1E,EAAA,MAAM,IAAA,GAAiC,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAMC,sBAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA;AAE/D,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA,EAAG,CAAA;AACrD","file":"csv.cjs","sourcesContent":["import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import Papa from \"papaparse\";\nimport { getMimeType } from \"../mime\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport const convertToCsvRow = (row: Record<string, CellValue>): Record<string, string> => {\n const stringRow: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(row)) {\n stringRow[key] = String(value ?? \"\");\n }\n\n return stringRow;\n};\n\nexport const generateCsvBlob = (data: Record<string, CellValue>[]): Blob => {\n const rows: Record<string, string>[] = data.map(convertToCsvRow);\n const csv = Papa.unparse(rows, { header: true, delimiter: \",\" });\n\n return new Blob([csv], { type: getMimeType(\"csv\") });\n};\n"]}
|
package/dist/csv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":[],"mappings":";;;;AAwDO,IAAM,WAAA,GAAc,CAAC,SAAA,KAA8B;AACxD,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kDAAA,EAAqD,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,QAAA;AACT,CAAA;;;AC1DO,IAAM,eAAA,GAAkB,CAAC,GAAA,KAA2D;AACzF,EAAA,MAAM,YAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,eAAA,GAAkB,CAAC,IAAA,KAA4C;AAC1E,EAAA,MAAM,IAAA,GAAiC,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA;AAE/D,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA,EAAG,CAAA;AACrD","file":"csv.js","sourcesContent":["import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import Papa from \"papaparse\";\nimport { getMimeType } from \"../mime\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport const convertToCsvRow = (row: Record<string, CellValue>): Record<string, string> => {\n const stringRow: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(row)) {\n stringRow[key] = String(value ?? \"\");\n }\n\n return stringRow;\n};\n\nexport const generateCsvBlob = (data: Record<string, CellValue>[]): Blob => {\n const rows: Record<string, string>[] = data.map(convertToCsvRow);\n const csv = Papa.unparse(rows, { header: true, delimiter: \",\" });\n\n return new Blob([csv], { type: getMimeType(\"csv\") });\n};\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
interface RgbColor {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
interface RgbaColor extends RgbColor {
|
|
7
|
+
a: number;
|
|
8
|
+
}
|
|
9
|
+
declare const rgbToHex: (r: number, g: number, b: number) => string;
|
|
10
|
+
declare const hexToRgb: (hex: string) => RgbColor;
|
|
11
|
+
declare const rgbaToHex: (color: RgbaColor) => string;
|
|
12
|
+
declare const getOpacityHexValue: (opacity: number) => string;
|
|
13
|
+
declare const rgbaToHexWithAlpha: (color: RgbaColor) => string;
|
|
14
|
+
declare const rgbToString: (color: RgbColor) => string;
|
|
15
|
+
declare const rgbaToString: (color: RgbaColor) => string;
|
|
16
|
+
declare const getLuminance: (r: number, g: number, b: number) => number;
|
|
17
|
+
declare const getContrastTextColor: (hexColor: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Normalized RGB color with values between 0 and 1.
|
|
20
|
+
* Useful for graphics libraries like Three.js.
|
|
21
|
+
*/
|
|
22
|
+
interface NormalizedRgb {
|
|
23
|
+
r: number;
|
|
24
|
+
g: number;
|
|
25
|
+
b: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert a hex color string to normalized RGB values (0-1 range).
|
|
29
|
+
* @param hex - Hex color string (with or without # prefix)
|
|
30
|
+
* @returns Normalized RGB object with values between 0 and 1
|
|
31
|
+
*/
|
|
32
|
+
declare const hexToNormalizedRgb: (hex: string) => NormalizedRgb;
|
|
33
|
+
/**
|
|
34
|
+
* Determine if a color is light or dark based on relative luminance.
|
|
35
|
+
* Useful for choosing contrasting text colors.
|
|
36
|
+
* @param hex - Hex color string
|
|
37
|
+
* @returns True if the color is considered light (luminance > 0.5)
|
|
38
|
+
*/
|
|
39
|
+
declare const isLightColor: (hex: string) => boolean;
|
|
40
|
+
declare const getColorForPercentage: (percent: number) => string;
|
|
41
|
+
|
|
42
|
+
interface Dimensions {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { type Dimensions as D, type NormalizedRgb as N, type RgbColor as R, type RgbaColor as a, rgbaToHex as b, rgbaToHexWithAlpha as c, rgbToString as d, rgbaToString as e, getLuminance as f, getOpacityHexValue as g, hexToRgb as h, getContrastTextColor as i, hexToNormalizedRgb as j, isLightColor as k, getColorForPercentage as l, rgbToHex as r };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
interface RgbColor {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
interface RgbaColor extends RgbColor {
|
|
7
|
+
a: number;
|
|
8
|
+
}
|
|
9
|
+
declare const rgbToHex: (r: number, g: number, b: number) => string;
|
|
10
|
+
declare const hexToRgb: (hex: string) => RgbColor;
|
|
11
|
+
declare const rgbaToHex: (color: RgbaColor) => string;
|
|
12
|
+
declare const getOpacityHexValue: (opacity: number) => string;
|
|
13
|
+
declare const rgbaToHexWithAlpha: (color: RgbaColor) => string;
|
|
14
|
+
declare const rgbToString: (color: RgbColor) => string;
|
|
15
|
+
declare const rgbaToString: (color: RgbaColor) => string;
|
|
16
|
+
declare const getLuminance: (r: number, g: number, b: number) => number;
|
|
17
|
+
declare const getContrastTextColor: (hexColor: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Normalized RGB color with values between 0 and 1.
|
|
20
|
+
* Useful for graphics libraries like Three.js.
|
|
21
|
+
*/
|
|
22
|
+
interface NormalizedRgb {
|
|
23
|
+
r: number;
|
|
24
|
+
g: number;
|
|
25
|
+
b: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert a hex color string to normalized RGB values (0-1 range).
|
|
29
|
+
* @param hex - Hex color string (with or without # prefix)
|
|
30
|
+
* @returns Normalized RGB object with values between 0 and 1
|
|
31
|
+
*/
|
|
32
|
+
declare const hexToNormalizedRgb: (hex: string) => NormalizedRgb;
|
|
33
|
+
/**
|
|
34
|
+
* Determine if a color is light or dark based on relative luminance.
|
|
35
|
+
* Useful for choosing contrasting text colors.
|
|
36
|
+
* @param hex - Hex color string
|
|
37
|
+
* @returns True if the color is considered light (luminance > 0.5)
|
|
38
|
+
*/
|
|
39
|
+
declare const isLightColor: (hex: string) => boolean;
|
|
40
|
+
declare const getColorForPercentage: (percent: number) => string;
|
|
41
|
+
|
|
42
|
+
interface Dimensions {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { type Dimensions as D, type NormalizedRgb as N, type RgbColor as R, type RgbaColor as a, rgbaToHex as b, rgbaToHexWithAlpha as c, rgbToString as d, rgbaToString as e, getLuminance as f, getOpacityHexValue as g, hexToRgb as h, getContrastTextColor as i, hexToNormalizedRgb as j, isLightColor as k, getColorForPercentage as l, rgbToHex as r };
|
package/dist/excel.cjs
CHANGED
|
@@ -77,6 +77,11 @@ var WorksheetBuilder = class {
|
|
|
77
77
|
var rgbToArgb = (rgb) => {
|
|
78
78
|
return { argb: `FF${rgb}` };
|
|
79
79
|
};
|
|
80
|
+
|
|
81
|
+
// src/utils/types.utils.ts
|
|
82
|
+
var isNullish = (value) => value === null || value === void 0;
|
|
83
|
+
|
|
84
|
+
// src/mime/index.ts
|
|
80
85
|
var getMimeType = (extension) => {
|
|
81
86
|
const mimeType = mime__default.default.getType(extension);
|
|
82
87
|
if (!mimeType) {
|
|
@@ -85,9 +90,6 @@ var getMimeType = (extension) => {
|
|
|
85
90
|
return mimeType;
|
|
86
91
|
};
|
|
87
92
|
|
|
88
|
-
// src/utils/types.utils.ts
|
|
89
|
-
var isNullish = (value) => value === null || value === void 0;
|
|
90
|
-
|
|
91
93
|
// src/excel/exceljs.utils.ts
|
|
92
94
|
var sanitizeWorksheetTitle = (title) => {
|
|
93
95
|
const MAX_LENGTH = 31;
|
package/dist/excel.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/mime/index.ts","../src/utils/types.utils.ts","../src/excel/exceljs.utils.ts"],"names":["mime","ExcelJS"],"mappings":"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","file":"excel.cjs","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","import mime from \"mime\";\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/utils/types.utils.ts","../src/mime/index.ts","../src/excel/exceljs.utils.ts"],"names":["mime","ExcelJS"],"mappings":"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","file":"excel.cjs","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
package/dist/excel.js
CHANGED
|
@@ -70,6 +70,11 @@ var WorksheetBuilder = class {
|
|
|
70
70
|
var rgbToArgb = (rgb) => {
|
|
71
71
|
return { argb: `FF${rgb}` };
|
|
72
72
|
};
|
|
73
|
+
|
|
74
|
+
// src/utils/types.utils.ts
|
|
75
|
+
var isNullish = (value) => value === null || value === void 0;
|
|
76
|
+
|
|
77
|
+
// src/mime/index.ts
|
|
73
78
|
var getMimeType = (extension) => {
|
|
74
79
|
const mimeType = mime.getType(extension);
|
|
75
80
|
if (!mimeType) {
|
|
@@ -78,9 +83,6 @@ var getMimeType = (extension) => {
|
|
|
78
83
|
return mimeType;
|
|
79
84
|
};
|
|
80
85
|
|
|
81
|
-
// src/utils/types.utils.ts
|
|
82
|
-
var isNullish = (value) => value === null || value === void 0;
|
|
83
|
-
|
|
84
86
|
// src/excel/exceljs.utils.ts
|
|
85
87
|
var sanitizeWorksheetTitle = (title) => {
|
|
86
88
|
const MAX_LENGTH = 31;
|
package/dist/excel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/mime/index.ts","../src/utils/types.utils.ts","../src/excel/exceljs.utils.ts"],"names":[],"mappings":"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","file":"excel.js","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","import mime from \"mime\";\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/utils/types.utils.ts","../src/mime/index.ts","../src/excel/exceljs.utils.ts"],"names":[],"mappings":"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","file":"excel.js","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|