@cristianmpx/react-import-sheet-headless 1.0.0 → 1.0.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.
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @cristianmpx/react-import-sheet-headless
|
|
2
2
|
|
|
3
3
|
Headless **React** library for **importing** and validating **Excel**/CSV sheet data. No built-in table UI—you bring your own components; the library provides the logic, **Web Worker** speed, and bulk validation.
|
|
4
4
|
|
|
@@ -7,11 +7,78 @@ Headless **React** library for **importing** and validating **Excel**/CSV sheet
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @
|
|
10
|
+
npm install @cristianmpx/react-import-sheet-headless
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
**Peer dependencies:** React 18+ (`react` and `react-dom`).
|
|
14
14
|
|
|
15
|
+
### ⚙️ Framework Setup
|
|
16
|
+
|
|
17
|
+
**Important:** Web Workers require specific configuration depending on your framework. See [FRAMEWORK-SETUP.md](./FRAMEWORK-SETUP.md) for detailed instructions.
|
|
18
|
+
|
|
19
|
+
**Quick config:**
|
|
20
|
+
|
|
21
|
+
<details>
|
|
22
|
+
<summary><strong>Vite + React</strong></summary>
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// vite.config.ts
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
optimizeDeps: {
|
|
28
|
+
exclude: ['@cristianmpx/react-import-sheet-headless'],
|
|
29
|
+
},
|
|
30
|
+
worker: {
|
|
31
|
+
format: 'es',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
</details>
|
|
37
|
+
|
|
38
|
+
<details>
|
|
39
|
+
<summary><strong>Next.js</strong></summary>
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// next.config.js
|
|
43
|
+
module.exports = {
|
|
44
|
+
webpack: (config, { isServer }) => {
|
|
45
|
+
if (!isServer) {
|
|
46
|
+
config.module.rules.push({
|
|
47
|
+
test: /\.worker\.js$/,
|
|
48
|
+
use: { loader: 'worker-loader' },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Important:** Use `dynamic` import with `ssr: false`:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const Importer = dynamic(() => import('./Importer'), { ssr: false });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
</details>
|
|
63
|
+
|
|
64
|
+
<details>
|
|
65
|
+
<summary><strong>Storybook</strong></summary>
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// .storybook/main.ts
|
|
69
|
+
async viteFinal(config) {
|
|
70
|
+
config.optimizeDeps = {
|
|
71
|
+
exclude: ['@cristianmpx/react-import-sheet-headless'],
|
|
72
|
+
};
|
|
73
|
+
config.worker = { format: 'es' };
|
|
74
|
+
return config;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
</details>
|
|
79
|
+
|
|
80
|
+
See [FRAMEWORK-SETUP.md](./FRAMEWORK-SETUP.md) for CRA, Remix, and Webpack configurations.
|
|
81
|
+
|
|
15
82
|
## Why Headless
|
|
16
83
|
|
|
17
84
|
Bring your own components; we provide the logic, Web Worker performance, and bulk validation. No prebuilt `<Table />`—you own the UI and the UX.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function n(t){if(t==null||t==="")return null;if(typeof t=="string"||typeof t=="number"||typeof t=="boolean")return t;if(t instanceof Date&&!Number.isNaN(t.getTime()))return t.toISOString();if(typeof t=="object"&&t!==null&&"getTime"in t&&typeof t.getTime=="function"){let e=t.getTime();return Number.isNaN(e)?String(t):new Date(e).toISOString()}return typeof t=="number"&&Number.isFinite(t)?t:String(t)}export{n as a};
|
|
2
|
+
//# sourceMappingURL=chunk-MIVE7CP7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/parser/engines/normalize-cell.ts"],"sourcesContent":["import type { RawSheetCellValue } from '../../../../types/raw-sheet.js';\n\nexport function toRawSheetCellValue(value: unknown): RawSheetCellValue {\n if (value === null || value === undefined || value === '') return null;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')\n return value;\n // Convert Date to ISO string so Comlink can serialize across worker boundary (Date has Symbol.toPrimitive)\n if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();\n if (\n typeof value === 'object' &&\n value !== null &&\n 'getTime' in value &&\n typeof (value as Date).getTime === 'function'\n ) {\n const ms = (value as Date).getTime();\n return Number.isNaN(ms) ? String(value) : new Date(ms).toISOString();\n }\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n return String(value);\n}\n"],"mappings":"AAEO,SAASA,EAAoBC,EAAmC,CACrE,GAAIA,GAAU,MAA+BA,IAAU,GAAI,OAAO,KAClE,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,UAAY,OAAOA,GAAU,UAC7E,OAAOA,EAET,GAAIA,aAAiB,MAAQ,CAAC,OAAO,MAAMA,EAAM,QAAQ,CAAC,EAAG,OAAOA,EAAM,YAAY,EACtF,GACE,OAAOA,GAAU,UACjBA,IAAU,MACV,YAAaA,GACb,OAAQA,EAAe,SAAY,WACnC,CACA,IAAMC,EAAMD,EAAe,QAAQ,EACnC,OAAO,OAAO,MAAMC,CAAE,EAAI,OAAOD,CAAK,EAAI,IAAI,KAAKC,CAAE,EAAE,YAAY,CACrE,CACA,OAAI,OAAOD,GAAU,UAAY,OAAO,SAASA,CAAK,EAAUA,EACzD,OAAOA,CAAK,CACrB","names":["toRawSheetCellValue","value","ms"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as d}from"./chunk-MIVE7CP7.js";import g from"papaparse";var C="UTF-8";async function P(a,t){if(t&&t.toUpperCase()!=="UTF-8"){let s=await a.arrayBuffer();return new TextDecoder(t).decode(s)}return a.text()}async function T(a,t,s,o,r={}){let c=await P(a,r.encodingOverride),l=r.encodingOverride??C,m={header:!0,delimiter:r.delimiterOverride,delimitersToGuess:[",",";"],preview:r.maxRows??void 0},e=g.parse(c,m),u=e.meta.delimiter??r.delimiterOverride??",",n=e.meta.fields??(e.data[0]?Object.keys(e.data[0]):[]),w=(e.data??[]).map((p,f)=>{let h=n.map(i=>({key:i,value:d(p?.[i])}));return{index:f,cells:h}}),R={name:o,filesize:t,documentHash:s,headersCount:n.length,rowsCount:e.data?.length??0,headers:n,rows:w};return{sheets:{[o]:R},parserMeta:{encoding:l,delimiter:u}}}export{T as parseCsv};
|
|
2
|
+
//# sourceMappingURL=csv-parser-3IOLGOEK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/parser/engines/csv-parser.ts"],"sourcesContent":["import Papa from 'papaparse';\nimport type { ParseOptions } from '../types/index.js';\nimport { toRawSheetCellValue } from './normalize-cell.js';\nimport { RawParseResult, RawSheetRow, RawSheetCell, RawSheet } from '../../../types/raw-sheet.js';\n\nconst DEFAULT_ENCODING = 'UTF-8';\n\nasync function blobToText(blob: Blob, encodingOverride?: string): Promise<string> {\n if (encodingOverride && encodingOverride.toUpperCase() !== 'UTF-8') {\n const buffer = await blob.arrayBuffer();\n return new TextDecoder(encodingOverride).decode(buffer);\n }\n return blob.text();\n}\n\nexport async function parseCsv(\n blob: Blob,\n filesize: number,\n documentHash: string,\n sheetName: string,\n options: ParseOptions = {},\n): Promise<RawParseResult> {\n const text = await blobToText(blob, options.encodingOverride);\n const encoding = options.encodingOverride ?? DEFAULT_ENCODING;\n\n const parseConfig: Papa.ParseConfig<Record<string, unknown>> = {\n header: true,\n delimiter: options.delimiterOverride,\n delimitersToGuess: [',', ';'],\n preview: options.maxRows ?? undefined,\n };\n const result = Papa.parse<Record<string, unknown>>(text, parseConfig);\n const delimiter = result.meta.delimiter ?? options.delimiterOverride ?? ',';\n\n const headers = result.meta.fields ?? (result.data[0] ? Object.keys(result.data[0] as object) : []);\n const rows: RawSheetRow[] = (result.data ?? []).map((rowObj, i) => {\n const cells: RawSheetCell[] = headers.map((key) => ({\n key,\n value: toRawSheetCellValue((rowObj as Record<string, unknown>)?.[key]),\n }));\n return { index: i, cells };\n });\n\n const sheet: RawSheet = {\n name: sheetName,\n filesize,\n documentHash,\n headersCount: headers.length,\n rowsCount: result.data?.length ?? 0,\n headers,\n rows,\n };\n\n return {\n sheets: { [sheetName]: sheet },\n parserMeta: { encoding, delimiter },\n };\n}\n"],"mappings":"wCAAA,OAAOA,MAAU,YAKjB,IAAMC,EAAmB,QAEzB,eAAeC,EAAWC,EAAYC,EAA4C,CAChF,GAAIA,GAAoBA,EAAiB,YAAY,IAAM,QAAS,CAClE,IAAMC,EAAS,MAAMF,EAAK,YAAY,EACtC,OAAO,IAAI,YAAYC,CAAgB,EAAE,OAAOC,CAAM,CACxD,CACA,OAAOF,EAAK,KAAK,CACnB,CAEA,eAAsBG,EACpBH,EACAI,EACAC,EACAC,EACAC,EAAwB,CAAC,EACA,CACzB,IAAMC,EAAO,MAAMT,EAAWC,EAAMO,EAAQ,gBAAgB,EACtDE,EAAWF,EAAQ,kBAAoBT,EAEvCY,EAAyD,CAC7D,OAAQ,GACR,UAAWH,EAAQ,kBACnB,kBAAmB,CAAC,IAAK,GAAG,EAC5B,QAASA,EAAQ,SAAW,MAC9B,EACMI,EAASC,EAAK,MAA+BJ,EAAME,CAAW,EAC9DG,EAAYF,EAAO,KAAK,WAAaJ,EAAQ,mBAAqB,IAElEO,EAAUH,EAAO,KAAK,SAAWA,EAAO,KAAK,CAAC,EAAI,OAAO,KAAKA,EAAO,KAAK,CAAC,CAAW,EAAI,CAAC,GAC3FI,GAAuBJ,EAAO,MAAQ,CAAC,GAAG,IAAI,CAACK,EAAQC,IAAM,CACjE,IAAMC,EAAwBJ,EAAQ,IAAKK,IAAS,CAClD,IAAAA,EACA,MAAOC,EAAqBJ,IAAqCG,CAAG,CAAC,CACvE,EAAE,EACF,MAAO,CAAE,MAAOF,EAAG,MAAAC,CAAM,CAC3B,CAAC,EAEKG,EAAkB,CACtB,KAAMf,EACN,SAAAF,EACA,aAAAC,EACA,aAAcS,EAAQ,OACtB,UAAWH,EAAO,MAAM,QAAU,EAClC,QAAAG,EACA,KAAAC,CACF,EAEA,MAAO,CACL,OAAQ,CAAE,CAACT,CAAS,EAAGe,CAAM,EAC7B,WAAY,CAAE,SAAAZ,EAAU,UAAAI,CAAU,CACpC,CACF","names":["Papa","DEFAULT_ENCODING","blobToText","blob","encodingOverride","buffer","parseCsv","filesize","documentHash","sheetName","options","text","encoding","parseConfig","result","Papa","delimiter","headers","rows","rowObj","i","cells","key","toRawSheetCellValue","sheet"]}
|
package/dist/parser.worker.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as R}from"./chunk-
|
|
1
|
+
import{a as R}from"./chunk-MIVE7CP7.js";import*as k from"comlink";import{sha256 as U}from"js-sha256";var y=256*1024;async function g(e){let t=U.create(),s=0;for(;s<e.size;){let n=await e.slice(s,s+y).arrayBuffer();t.update(new Uint8Array(n)),s+=y}return t.hex()}import*as p from"xlsx";function P(e,t,s,r,n={}){let a=p.read(e,{type:"array",cellDates:!0}),i=n.maxRows,l={};for(let u of a.SheetNames){let d=a.Sheets[u];if(!d)continue;let m=p.utils.sheet_to_json(d,{header:1,defval:null});if(m.length===0){l[u]={name:r??u,filesize:t,documentHash:s,headersCount:0,headers:[],rowsCount:0,rows:[]};continue}let f=m[0].map(o=>o!=null?String(o):""),c=m.slice(1),O=i!=null?Math.min(i,c.length):c.length,x=[];for(let o=0;o<O;o++){let B=c[o],X=f.map((j,E)=>{let L=B?.[E];return{key:j,value:R(L)}});x.push({index:o,cells:X})}l[u]={name:r??u,filesize:t,documentHash:s,headersCount:f.length,rowsCount:c.length,headers:f,rows:x}}return{sheets:l}}var C=["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application/vnd.ms-excel","application/vnd.oasis.opendocument.spreadsheet"],S="text/csv";function H(e,t){let s=t.fileName??e.name;if(s){let n=s.match(/\.([a-z0-9]+)$/i);if(n)return n[1].toLowerCase()}let r=e.type?.toLowerCase();return r===S?"csv":C.some(n=>r?.startsWith(n)||r===n)?r.includes("spreadsheetml")?"xlsx":"xls":null}function M(e,t){return e==="csv"||t.type===S}function N(e,t){return e==="xlsx"||e==="xls"||e==="ods"?!0:C.some(s=>t.type===s||t.type?.startsWith(s))}async function w(e,t={}){let s=e.size,r=await g(e),n=t.engine==="auto"||t.engine===null||t.engine===void 0?null:t.engine,a=H(e,t);if(n==="csv"||n===null&&M(a,e)){let{parseCsv:i}=await import("./csv-parser-3IOLGOEK.js"),l=t.fileName??e.name??"Sheet1";return i(e,s,r,l,t)}if(n==="xlsx"||n===null&&N(a,e)){let i=await e.arrayBuffer();return P(i,s,r,void 0,t)}throw n!==null?new Error(`Unsupported engine: ${n}. Use 'xlsx', 'csv', or omit for automatic detection.`):new Error(`Unsupported file type: ${a??e.type??"unknown"}. Use .xlsx, .xls, .ods or .csv.`)}var h=null,v={},_={async load(e,t={}){h=e,v={...t};let s={...t,maxRows:t.maxRows??10},r=await w(e,s);return structuredClone(r)},async parseAll(e){if(!h)throw new Error("No file loaded. Call load(blob, options) first.");let t={...v,maxRows:void 0};e&&e({phase:"parsing",localPercent:0});let s=await w(h,t);return e&&e({phase:"parsing",localPercent:100}),structuredClone(s)}};k.expose(_);
|
|
2
2
|
//# sourceMappingURL=parser.worker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/parser/worker/parser.worker.ts","../src/core/parser/hash.ts","../src/core/parser/engines/xlsx-parser.ts","../src/core/parser/adapter.ts"],"sourcesContent":["import * as Comlink from 'comlink';\nimport type { RawParseResult } from '../../../types/raw-sheet.js';\nimport type { ParseOptions } from '../types/index.js';\nimport { parseSheet } from '../adapter.js';\n\ntype ProgressCallback = (detail: { phase?: string; localPercent?: number; currentRow?: number; totalRows?: number }) => void;\n\nlet storedBlob: Blob | null = null;\nlet storedOptions: ParseOptions = {};\n\nconst api = {\n async load(blob: Blob, options: ParseOptions = {}): Promise<RawParseResult> {\n storedBlob = blob;\n storedOptions = { ...options };\n const previewOptions: ParseOptions = { ...options, maxRows: options.maxRows ?? 10 };\n const result = await parseSheet(blob, previewOptions);\n return structuredClone(result);\n },\n\n async parseAll(onProgress?: ProgressCallback): Promise<RawParseResult> {\n if (!storedBlob) throw new Error('No file loaded. Call load(blob, options) first.');\n const fullOptions: ParseOptions = { ...storedOptions, maxRows: undefined };\n if (onProgress) onProgress({ phase: 'parsing', localPercent: 0 });\n const result = await parseSheet(storedBlob, fullOptions);\n if (onProgress) onProgress({ phase: 'parsing', localPercent: 100 });\n return structuredClone(result);\n },\n};\n\nComlink.expose(api);\n","import { sha256 } from 'js-sha256';\n\nconst CHUNK_SIZE = 256 * 1024;\n\nexport async function streamHashHex(blob: Blob): Promise<string> {\n const hasher = sha256.create();\n let offset = 0;\n while (offset < blob.size) {\n const chunk = blob.slice(offset, offset + CHUNK_SIZE);\n const buffer = await chunk.arrayBuffer();\n hasher.update(new Uint8Array(buffer));\n offset += CHUNK_SIZE;\n }\n return hasher.hex();\n}\n","import * as XLSX from 'xlsx';\nimport type { RawParseResult, RawSheet, RawSheetRow, RawSheetCell } from '../../../../types/raw-sheet.js';\nimport type { ParseOptions } from '../types/index.js';\nimport { toRawSheetCellValue } from './normalize-cell.js';\n\nexport function parseXlsx(\n arrayBuffer: ArrayBuffer,\n filesize: number,\n documentHash: string,\n sheetNameOverride?: string,\n options: ParseOptions = {},\n): RawParseResult {\n const workbook = XLSX.read(arrayBuffer, { type: 'array', cellDates: true });\n const maxRows = options.maxRows;\n const sheets: Record<string, RawSheet> = {};\n\n for (const name of workbook.SheetNames) {\n const sheet = workbook.Sheets[name];\n if (!sheet) continue;\n const rowsArray = XLSX.utils.sheet_to_json<unknown[]>(sheet, { header: 1, defval: null });\n if (rowsArray.length === 0) {\n sheets[name] = {\n name: sheetNameOverride ?? name,\n filesize,\n documentHash,\n headersCount: 0,\n headers: [],\n rowsCount: 0,\n rows: [],\n };\n continue;\n }\n const headerRow = rowsArray[0] as unknown[];\n const headers = headerRow.map((h) => (h != null ? String(h) : ''));\n const dataRows = rowsArray.slice(1);\n const rowsToTake = maxRows != null ? Math.min(maxRows, dataRows.length) : dataRows.length;\n const rows: RawSheetRow[] = [];\n\n for (let i = 0; i < rowsToTake; i++) {\n const rowValues = dataRows[i] as unknown[] | undefined;\n const cells: RawSheetCell[] = headers.map((key, colIndex) => {\n const raw = rowValues?.[colIndex];\n return { key, value: toRawSheetCellValue(raw) };\n });\n rows.push({ index: i, cells });\n }\n\n sheets[name] = {\n name: sheetNameOverride ?? name,\n filesize,\n documentHash,\n headersCount: headers.length,\n rowsCount: dataRows.length,\n headers,\n rows,\n };\n }\n\n return { sheets };\n}\n","import type { RawParseResult } from '../../types/raw-sheet.js';\nimport type { ParseOptions } from './types/index.js';\nimport { streamHashHex } from './hash.js';\nimport { parseXlsx } from './engines/xlsx-parser.js';\n\nconst XLSX_MIMES = [\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-excel',\n 'application/vnd.oasis.opendocument.spreadsheet',\n];\nconst CSV_MIME = 'text/csv';\n\nfunction getExtension(blob: Blob, options: ParseOptions): string | null {\n const name = options.fileName ?? (blob as File & { name?: string }).name;\n if (name) {\n const match = name.match(/\\.([a-z0-9]+)$/i);\n if (match) return match[1].toLowerCase();\n }\n const type = blob.type?.toLowerCase();\n if (type === CSV_MIME) return 'csv';\n if (XLSX_MIMES.some((m) => type?.startsWith(m) || type === m)) return type.includes('spreadsheetml') ? 'xlsx' : 'xls';\n return null;\n}\n\nfunction isCsv(ext: string | null, blob: Blob): boolean {\n return ext === 'csv' || blob.type === CSV_MIME;\n}\n\nfunction isXlsx(ext: string | null, blob: Blob): boolean {\n if (ext === 'xlsx' || ext === 'xls' || ext === 'ods') return true;\n return XLSX_MIMES.some((m) => blob.type === m || blob.type?.startsWith(m));\n}\n\nexport async function parseSheet(blob: Blob, options: ParseOptions = {}): Promise<RawParseResult> {\n const filesize = blob.size;\n const documentHash = await streamHashHex(blob);\n const engine = options.engine === 'auto' || options.engine === null || options.engine === undefined ? null : options.engine;\n const ext = getExtension(blob, options);\n\n if (engine === 'csv' || (engine === null && isCsv(ext, blob))) {\n const { parseCsv } = await import('./engines/csv-parser.js');\n const sheetName = options.fileName ?? (blob as File & { name?: string }).name ?? 'Sheet1';\n return parseCsv(blob, filesize, documentHash, sheetName, options);\n }\n\n if (engine === 'xlsx' || (engine === null && isXlsx(ext, blob))) {\n const buffer = await blob.arrayBuffer();\n return parseXlsx(buffer, filesize, documentHash, undefined, options);\n }\n\n if (engine !== null) {\n throw new Error(`Unsupported engine: ${engine}. Use 'xlsx', 'csv', or omit for automatic detection.`);\n }\n\n throw new Error(`Unsupported file type: ${ext ?? blob.type ?? 'unknown'}. Use .xlsx, .xls, .ods or .csv.`);\n}\n"],"mappings":"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","names":["Comlink","sha256","CHUNK_SIZE","streamHashHex","blob","hasher","offset","buffer","XLSX","parseXlsx","arrayBuffer","filesize","documentHash","sheetNameOverride","options","workbook","maxRows","sheets","name","sheet","rowsArray","headers","h","dataRows","rowsToTake","rows","i","rowValues","cells","key","colIndex","raw","toRawSheetCellValue","XLSX_MIMES","CSV_MIME","getExtension","blob","options","name","match","type","m","isCsv","ext","isXlsx","parseSheet","filesize","documentHash","streamHashHex","engine","parseCsv","sheetName","buffer","parseXlsx","storedBlob","storedOptions","api","blob","options","previewOptions","result","parseSheet","onProgress","fullOptions"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/parser/worker/parser.worker.ts","../src/core/parser/hash.ts","../src/core/parser/engines/xlsx-parser.ts","../src/core/parser/adapter.ts"],"sourcesContent":["import * as Comlink from 'comlink';\nimport type { RawParseResult } from '../../../types/raw-sheet.js';\nimport type { ParseOptions } from '../types/index.js';\nimport { parseSheet } from '../adapter.js';\n\ntype ProgressCallback = (detail: {\n phase?: string;\n localPercent?: number;\n currentRow?: number;\n totalRows?: number;\n}) => void;\n\nlet storedBlob: Blob | null = null;\nlet storedOptions: ParseOptions = {};\n\nconst api = {\n async load(blob: Blob, options: ParseOptions = {}): Promise<RawParseResult> {\n storedBlob = blob;\n storedOptions = { ...options };\n const previewOptions: ParseOptions = { ...options, maxRows: options.maxRows ?? 10 };\n const result = await parseSheet(blob, previewOptions);\n return structuredClone(result);\n },\n\n async parseAll(onProgress?: ProgressCallback): Promise<RawParseResult> {\n if (!storedBlob) throw new Error('No file loaded. Call load(blob, options) first.');\n const fullOptions: ParseOptions = { ...storedOptions, maxRows: undefined };\n if (onProgress) onProgress({ phase: 'parsing', localPercent: 0 });\n const result = await parseSheet(storedBlob, fullOptions);\n if (onProgress) onProgress({ phase: 'parsing', localPercent: 100 });\n return structuredClone(result);\n },\n};\n\nComlink.expose(api);\n","import { sha256 } from 'js-sha256';\n\nconst CHUNK_SIZE = 256 * 1024;\n\nexport async function streamHashHex(blob: Blob): Promise<string> {\n const hasher = sha256.create();\n let offset = 0;\n while (offset < blob.size) {\n const chunk = blob.slice(offset, offset + CHUNK_SIZE);\n const buffer = await chunk.arrayBuffer();\n hasher.update(new Uint8Array(buffer));\n offset += CHUNK_SIZE;\n }\n return hasher.hex();\n}\n","import * as XLSX from 'xlsx';\nimport type { RawParseResult, RawSheet, RawSheetRow, RawSheetCell } from '../../../../types/raw-sheet.js';\nimport type { ParseOptions } from '../types/index.js';\nimport { toRawSheetCellValue } from './normalize-cell.js';\n\nexport function parseXlsx(\n arrayBuffer: ArrayBuffer,\n filesize: number,\n documentHash: string,\n sheetNameOverride?: string,\n options: ParseOptions = {},\n): RawParseResult {\n const workbook = XLSX.read(arrayBuffer, { type: 'array', cellDates: true });\n const maxRows = options.maxRows;\n const sheets: Record<string, RawSheet> = {};\n\n for (const name of workbook.SheetNames) {\n const sheet = workbook.Sheets[name];\n if (!sheet) continue;\n const rowsArray = XLSX.utils.sheet_to_json<unknown[]>(sheet, { header: 1, defval: null });\n if (rowsArray.length === 0) {\n sheets[name] = {\n name: sheetNameOverride ?? name,\n filesize,\n documentHash,\n headersCount: 0,\n headers: [],\n rowsCount: 0,\n rows: [],\n };\n continue;\n }\n const headerRow = rowsArray[0] as unknown[];\n const headers = headerRow.map((h) => (h != null ? String(h) : ''));\n const dataRows = rowsArray.slice(1);\n const rowsToTake = maxRows != null ? Math.min(maxRows, dataRows.length) : dataRows.length;\n const rows: RawSheetRow[] = [];\n\n for (let i = 0; i < rowsToTake; i++) {\n const rowValues = dataRows[i] as unknown[] | undefined;\n const cells: RawSheetCell[] = headers.map((key, colIndex) => {\n const raw = rowValues?.[colIndex];\n return { key, value: toRawSheetCellValue(raw) };\n });\n rows.push({ index: i, cells });\n }\n\n sheets[name] = {\n name: sheetNameOverride ?? name,\n filesize,\n documentHash,\n headersCount: headers.length,\n rowsCount: dataRows.length,\n headers,\n rows,\n };\n }\n\n return { sheets };\n}\n","import type { RawParseResult } from '../../types/raw-sheet.js';\nimport type { ParseOptions } from './types/index.js';\nimport { streamHashHex } from './hash.js';\nimport { parseXlsx } from './engines/xlsx-parser.js';\n\nconst XLSX_MIMES = [\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.ms-excel',\n 'application/vnd.oasis.opendocument.spreadsheet',\n];\nconst CSV_MIME = 'text/csv';\n\nfunction getExtension(blob: Blob, options: ParseOptions): string | null {\n const name = options.fileName ?? (blob as File & { name?: string }).name;\n if (name) {\n const match = name.match(/\\.([a-z0-9]+)$/i);\n if (match) return match[1].toLowerCase();\n }\n const type = blob.type?.toLowerCase();\n if (type === CSV_MIME) return 'csv';\n if (XLSX_MIMES.some((m) => type?.startsWith(m) || type === m)) return type.includes('spreadsheetml') ? 'xlsx' : 'xls';\n return null;\n}\n\nfunction isCsv(ext: string | null, blob: Blob): boolean {\n return ext === 'csv' || blob.type === CSV_MIME;\n}\n\nfunction isXlsx(ext: string | null, blob: Blob): boolean {\n if (ext === 'xlsx' || ext === 'xls' || ext === 'ods') return true;\n return XLSX_MIMES.some((m) => blob.type === m || blob.type?.startsWith(m));\n}\n\nexport async function parseSheet(blob: Blob, options: ParseOptions = {}): Promise<RawParseResult> {\n const filesize = blob.size;\n const documentHash = await streamHashHex(blob);\n const engine = options.engine === 'auto' || options.engine === null || options.engine === undefined ? null : options.engine;\n const ext = getExtension(blob, options);\n\n if (engine === 'csv' || (engine === null && isCsv(ext, blob))) {\n const { parseCsv } = await import('./engines/csv-parser.js');\n const sheetName = options.fileName ?? (blob as File & { name?: string }).name ?? 'Sheet1';\n return parseCsv(blob, filesize, documentHash, sheetName, options);\n }\n\n if (engine === 'xlsx' || (engine === null && isXlsx(ext, blob))) {\n const buffer = await blob.arrayBuffer();\n return parseXlsx(buffer, filesize, documentHash, undefined, options);\n }\n\n if (engine !== null) {\n throw new Error(`Unsupported engine: ${engine}. Use 'xlsx', 'csv', or omit for automatic detection.`);\n }\n\n throw new Error(`Unsupported file type: ${ext ?? blob.type ?? 'unknown'}. Use .xlsx, .xls, .ods or .csv.`);\n}\n"],"mappings":"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","names":["Comlink","sha256","CHUNK_SIZE","streamHashHex","blob","hasher","offset","buffer","XLSX","parseXlsx","arrayBuffer","filesize","documentHash","sheetNameOverride","options","workbook","maxRows","sheets","name","sheet","rowsArray","headers","h","dataRows","rowsToTake","rows","i","rowValues","cells","key","colIndex","raw","toRawSheetCellValue","XLSX_MIMES","CSV_MIME","getExtension","blob","options","name","match","type","m","isCsv","ext","isXlsx","parseSheet","filesize","documentHash","streamHashHex","engine","parseCsv","sheetName","buffer","parseXlsx","storedBlob","storedOptions","api","blob","options","previewOptions","result","parseSheet","onProgress","fullOptions"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cristianmpx/react-import-sheet-headless",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Headless component for React Import Sheet",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
"lint": "eslint .",
|
|
24
24
|
"publish": "npm run test && npm run build && npm publish --access public",
|
|
25
25
|
"prepare": "husky",
|
|
26
|
-
"commit": "cz"
|
|
26
|
+
"commit": "cz",
|
|
27
|
+
"verify": "node scripts/verify-package.js",
|
|
28
|
+
"diagnose": "node scripts/diagnose-installation.js",
|
|
29
|
+
"prebuild": "node scripts/verify-package.js || true",
|
|
30
|
+
"postbuild": "node scripts/verify-package.js"
|
|
27
31
|
},
|
|
28
32
|
"config": {
|
|
29
33
|
"commitizen": {
|
|
@@ -64,10 +68,23 @@
|
|
|
64
68
|
"react",
|
|
65
69
|
"import",
|
|
66
70
|
"sheet",
|
|
67
|
-
"headless"
|
|
71
|
+
"headless",
|
|
72
|
+
"csv",
|
|
73
|
+
"excel",
|
|
74
|
+
"xlsx",
|
|
75
|
+
"validation",
|
|
76
|
+
"web-workers"
|
|
68
77
|
],
|
|
69
78
|
"author": "Cristian Marin",
|
|
70
79
|
"license": "MIT",
|
|
80
|
+
"repository": {
|
|
81
|
+
"type": "git",
|
|
82
|
+
"url": "https://github.com/cristianm-developer/react-import-sheet-headless.git"
|
|
83
|
+
},
|
|
84
|
+
"bugs": {
|
|
85
|
+
"url": "https://github.com/cristianm-developer/react-import-sheet-headless/issues"
|
|
86
|
+
},
|
|
87
|
+
"homepage": "https://github.com/cristianm-developer/react-import-sheet-headless#readme",
|
|
71
88
|
"lint-staged": {
|
|
72
89
|
"src/**/*.{ts,tsx,js,jsx}": [
|
|
73
90
|
"eslint --fix",
|