Webpack Bundling Trouble #3077

Closed
opened 2024-02-20 23:04:57 +00:00 by gwkline · 2 comments

Hi there! I've been using this package to handle parsing XLSX files, so first off thank you for building this!

I am running into an issue regarding bundle size that I haven't been able to resolve. Was wondering if this was expected or if I need to adjust my approach. I am currently using this in a React/NextJS codebase, with Webpack as the bundler, and Bun as the package manager. The package itself takes up about 1mb in the client bundle, and each of the edge bundles being created, which is not ideal. XLSX is only being used when parsing files obviously.

Let's say I have some component like so:

import { parse } from "@/lib/parse"
export default function FileUpload() {
    const handleFileUpload(file: File) => {
        const { result, errored } = await parse(file)
        ....
    }

    ...
}

and parse looks something like this:

import { read, utils } from "xlsx";
import type { WorkBook } from "xlsx";

export async function parse(file: ArrayBuffer) {
	const workbook = read(file, { type: "buffer", cellDates: true });
	const result = await parseBlaster(workbook);
	return result;
}

No matter what I've tried, the XLSX is always present in the client bundle. I have tried initializing XLSX as such:

const workbook = (await import("xlsx")).read(file, { type: "buffer", cellDates: true });

I've tried a util to import and export xlsx:

import { read, utils } from "xlsx";
import type { WorkBook } from "xlsx";
export {read, utils, WorkBook}

and use like so:

const workbook = (await import("../../lib/xlsx")).read(file, { type: "buffer", cellDates: true });

But no luck there either. Any suggestions? Thanks!

Hi there! I've been using this package to handle parsing XLSX files, so first off thank you for building this! I am running into an issue regarding bundle size that I haven't been able to resolve. Was wondering if this was expected or if I need to adjust my approach. I am currently using this in a React/NextJS codebase, with Webpack as the bundler, and Bun as the package manager. The package itself takes up about 1mb in the client bundle, and each of the edge bundles being created, which is not ideal. XLSX is only being used when parsing files obviously. Let's say I have some component like so: ``` import { parse } from "@/lib/parse" export default function FileUpload() { const handleFileUpload(file: File) => { const { result, errored } = await parse(file) .... } ... } ``` and parse looks something like this: ``` import { read, utils } from "xlsx"; import type { WorkBook } from "xlsx"; export async function parse(file: ArrayBuffer) { const workbook = read(file, { type: "buffer", cellDates: true }); const result = await parseBlaster(workbook); return result; } ``` No matter what I've tried, the XLSX is always present in the client bundle. I have tried initializing XLSX as such: ``` const workbook = (await import("xlsx")).read(file, { type: "buffer", cellDates: true }); ``` I've tried a util to import and export xlsx: ``` import { read, utils } from "xlsx"; import type { WorkBook } from "xlsx"; export {read, utils, WorkBook} ``` and use like so: ``` const workbook = (await import("../../lib/xlsx")).read(file, { type: "buffer", cellDates: true }); ``` But no luck there either. Any suggestions? Thanks!
Owner

https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-external-libraries suggests you can await import from a relevant callback. We'll add a new NextJS example (the current one focuses on server-side processing).

  1. Set up initial project:
npx create-next-app@latest sheetjs-i3077 --ts --no-eslint --no-tailwind --no-src-dir --no-app --import-alias "@/*"
cd sheetjs-i3077
npm i --save https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz
npm run build
  1. Replace pages/index.tsx with the following:
import { useState } from "react";

export default function Home() {
  const [__html, setHTML] = useState("");

  return (
    <>
      <input type="file" onChange={async(e) => {
        const XLSX = await import("xlsx");
        if(!e.target?.files?.[0]) return;
        const wb = XLSX.read(await e.target.files[0].arrayBuffer());
        const ws = wb.Sheets[wb.SheetNames[0]];
        setHTML(XLSX.utils.sheet_to_html(ws));
      }}/>
      <p dangerouslySetInnerHTML={{__html}}/>
    </>
  );
}
  1. Build the app:
npm run build

In local testing, the route table looked like:

Route (pages)                             Size     First Load JS
┌ ○ /                                     510 B          79.3 kB
├   /_app                                 0 B            78.8 kB
├ ○ /404                                  181 B            79 kB
└ λ /api/hello                            0 B            78.8 kB
+ First Load JS shared by all             79.5 kB
  ├ chunks/framework-5429a50ba5373c56.js  45.2 kB
  ├ chunks/main-930135e47dff83e9.js       31.8 kB
  └ other shared chunks (total)           2.59 kB

○  (Static)   prerendered as static content
λ  (Dynamic)  server-rendered on demand using Node.js
  1. Run the local server:
npm run start -- -p 54337
  1. Open a browser to http://localhost:54337/

Checking the network tab, the library is not loaded initially:

i3077-firstload.png

After using the file input element, the library is loaded in a separate chunk:

i3077-input.png

https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-external-libraries suggests you can `await import` from a relevant callback. We'll add a new NextJS example (the current one focuses on server-side processing). 0) Set up initial project: ```bash npx create-next-app@latest sheetjs-i3077 --ts --no-eslint --no-tailwind --no-src-dir --no-app --import-alias "@/*" cd sheetjs-i3077 npm i --save https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz npm run build ``` 1) Replace `pages/index.tsx` with the following: ```tsx import { useState } from "react"; export default function Home() { const [__html, setHTML] = useState(""); return ( <> <input type="file" onChange={async(e) => { const XLSX = await import("xlsx"); if(!e.target?.files?.[0]) return; const wb = XLSX.read(await e.target.files[0].arrayBuffer()); const ws = wb.Sheets[wb.SheetNames[0]]; setHTML(XLSX.utils.sheet_to_html(ws)); }}/> <p dangerouslySetInnerHTML={{__html}}/> </> ); } ``` 2) Build the app: ```bash npm run build ``` In local testing, the route table looked like: ``` Route (pages) Size First Load JS ┌ ○ / 510 B 79.3 kB ├ /_app 0 B 78.8 kB ├ ○ /404 181 B 79 kB └ λ /api/hello 0 B 78.8 kB + First Load JS shared by all 79.5 kB ├ chunks/framework-5429a50ba5373c56.js 45.2 kB ├ chunks/main-930135e47dff83e9.js 31.8 kB └ other shared chunks (total) 2.59 kB ○ (Static) prerendered as static content λ (Dynamic) server-rendered on demand using Node.js ``` 3) Run the local server: ```bash npm run start -- -p 54337 ``` 4) Open a browser to http://localhost:54337/ Checking the network tab, the library is not loaded initially: ![i3077-firstload.png](/attachments/57ac7126-fcb4-443d-abca-974349e6a686) After using the file input element, the library is loaded in a separate chunk: ![i3077-input.png](/attachments/f9950c79-8036-431b-a646-f9e673f3d838)
Author

Got it! It seems like that indeed worked, thank you!

Got it! It seems like that indeed worked, thank you!
Sign in to join this conversation.
No Milestone
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sheetjs/sheetjs#3077
No description provided.