next-asset-module

This commit is contained in:
SheetJS 2023-05-26 18:50:23 -04:00
parent 0acbc990f0
commit ae138cc327
15 changed files with 571 additions and 151 deletions

@ -7,13 +7,12 @@ sidebar_custom_props:
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
# NodeJS
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Tarballs are available on <https://cdn.sheetjs.com>.
<p><a href={`https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}>https://cdn.sheetjs.com/xlsx-{current}/xlsx-{current}.tgz</a> is the URL for version {current}</p>
@ -158,3 +157,38 @@ XLSX.stream.set_readable(Readable);
import * as cpexcel from 'xlsx/dist/cpexcel.full.mjs';
XLSX.set_cptable(cpexcel);
```
#### NextJS
:::warning
`fs` cannot be imported from the top level in NextJS pages. This will not work:
```js
/* it is safe to import the library from the top level */
import { readFile, utils, set_fs } from 'xlsx';
/* it is not safe to import 'fs' from the top level ! */
// highlight-next-line
import * as fs from 'fs'; // this import will fail
set_fs(fs);
```
:::
`fs` should be loaded with a dynamic import within a lifecycle function:
```js title="index.js"
/* it is safe to import the library from the top level */
import { readFile, utils, set_fs } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
export async function getServerSideProps() {
// highlight-next-line
set_fs(await import("fs")); // dynamically import 'fs' in `getServerSideProps`
const wb = readFile(join(cwd(), "public", "sheetjs.xlsx"));
// ...
}
```
The [NextJS demo](/docs/demos/static/nextjs) includes complete examples.

@ -5,61 +5,36 @@ pagination_next: demos/mobile/index
---
import current from '/version.js';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
:::note
This was tested against `next v13.1.1` on 2023 January 14.
This was tested against `next v13.4.4` on 2023 May 26.
:::
:::info
The [NodeJS module](/docs/getting-started/installation/nodejs) can be imported
from pages or loaded in Webpack loaders.
At a high level, there are two ways to pull spreadsheet data into NextJS apps:
loading an asset module or performing the file read operations from the NextJS
lifecycle. At the time of writing, NextJS does not offer an out-of-the-box
asset module solution, so this demo focuses on raw operations. NextJS does not
watch the spreadsheets, so `next dev` hot reloading will not work!
:::warning
[`import`](/docs/getting-started/installation/nodejs#esm-import) does not load
NodeJS native modules. The Installation section includes a note on dynamic
import of `fs` within lifecycle methods.
:::
The general strategy with NextJS apps is to generate HTML snippets or data from
the lifecycle functions and reference them in the template.
NextJS best practices have evolved over time, but there are three key parts:
HTML output can be generated using `XLSX.utils.sheet_to_html` and inserted into
the document using the `dangerouslySetInnerHTML` attribute:
1) [Loading Data](#loading-data): NextJS can read files in lifecycle methods OR
custom Webpack loaders can create asset modules.
```jsx
export default function Index({html, type}) { return (
// ...
// highlight-next-line
<div dangerouslySetInnerHTML={{ __html: html }} />
// ...
); }
```
2) [Lifecycle Methods](#nextjs-strategies): NextJS includes strategies for
static pages (`getStaticProps`) as well as dynamic pages (`getServerSideProps`).
:::warning Reading and writing files during the build process
`fs` cannot be statically imported from the top level in NextJS pages. The
dynamic import must happen within a lifecycle function. For example:
```js
/* it is safe to import the library from the top level */
import { readFile, utils, set_fs } from 'xlsx';
/* it is not safe to import 'fs' from the top level ! */
// import * as fs from 'fs'; // this will fail
import { join } from 'path';
import { cwd } from 'process';
export async function getServerSideProps() {
// highlight-next-line
set_fs(await import("fs")); // dynamically import 'fs' when needed
const wb = readFile(join(cwd(), "public", "sheetjs.xlsx")); // works
// ...
}
```
:::
3) [Data Presentation](#data-presentation): Pages use React and JSX.
:::caution Next 13+ and SWC
@ -75,6 +50,154 @@ module.exports = {
:::
## Loading Data
At a high level, there are two ways to pull spreadsheet data into NextJS apps:
loading an asset module or performing the file read operations from the NextJS
lifecycle methods.
Asset modules are appropriate for static sites when the file names are known in
advance. Performing file read operations in lifecycle methods is more flexible
but does not support live reloading.
### Asset Module
:::caution
When the demo was last tested, Turbopack did not support true raw loaders. For
development use, the normal `npx next dev` should be used.
:::
The following diagram depicts the workbook waltz:
```mermaid
flowchart LR
file[(workbook\nfile)]
subgraph SheetJS operations
base64(base64\nstring)
aoo(array of\nobjects)
end
html{{HTML\nTABLE}}
file --> |base64-loader.js\ncustom plugin| base64
base64 --> |page\nlifecycle method| aoo
aoo --> |page\nIndex method| html
```
In this flow, it is strongly recommended to make a loader return a Base64 string:
```js title="base64-loader.js"
function loader(content) {
/* since `loader.raw` is true, `content` is a Buffer */
return `export default '${content.toString("base64")}'`;
}
/* ensure the function receives a Buffer */
loader.raw = true;
module.exports = loader;
```
The webpack configuration is controlled in `next.config.js`:
```js title="next.config.js"
module.exports = {
webpack: (config) => {
// highlight-start
/* add to the webpack config module.rules array */
config.module.rules.push({
/* `test` matches file extensions */
test: /\.(numbers|xls|xlsx|xlsb)/,
/* use the loader script */
use: [ { loader: './base64-loader' } ]
});
// highlight-end
return config;
}
};
```
Module alias directories can be defined in `jsconfig.json` or `tsconfig.json`:
```json title="jsconfig.json"
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
// highlight-next-line
"@/*": ["*"]
}
}
}
```
Pages can import the files directly. It is strongly recommended to store files
in a `data` folder. This example uses `getStaticProps` to parse `sheetjs.xlsx`:
```jsx title="index.js"
import { read, utils } from 'xlsx';
// highlight-next-line
import base64 from '@/data/sheetjs.xlsx';
export async function getStaticProps() {
/* parse base64 data */
// highlight-next-line
const wb = read(base64, { type: "base64" });
return { props: {
/* generate array of objects from the first sheet */
data: utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]])
} };
}
```
### Raw Operations
Files can be read using `readFile` in lifecycle methods. The `cwd` method from
the `process` module will point to the root of the project.
The following diagram depicts the workbook waltz:
```mermaid
flowchart LR
file[(workbook\nfile)]
subgraph SheetJS operations
buffer(NodeJS\nBuffer)
aoo(array of\nobjects)
end
html{{HTML\nTABLE}}
file --> |page\nlifecycle method| buffer
buffer --> |page\nlifecycle method| aoo
aoo --> |page\nIndex method| html
```
This example reads the file `sheetjs.xlsx` in the `data` folder in the project:
```js
import { readFile, utils, set_fs } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
export async function getServerSideProps() {
// highlight-start
set_fs(await import("fs")); // dynamically import 'fs' when needed
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
const wb = readFile(filename);
// highlight-end
/* generate and return the html from the first worksheet */
const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
return { props: { data } };
}
```
:::warning Reading and writing files during the build process
As the NextJS workaround is non-traditional, it bears repeating:
`fs` cannot be statically imported from the top level in NextJS pages. The
dynamic import must happen within a lifecycle function.
:::
## NextJS Strategies
NextJS currently provides 3 strategies:
@ -86,14 +209,17 @@ NextJS currently provides 3 strategies:
### Static Site Generation
When using `getStaticProps`, the file will be read once during build time.
This example reads `sheetjs.xlsx` from the `data` folder:
<Tabs groupId="data">
<TabItem value="asset" label="Asset Module">
```js
import { readFile, set_fs, utils } from 'xlsx';
import { read, utils } from 'xlsx';
import base64 from '@/data/sheetjs.xlsx';
export async function getStaticProps() {
/* read file */
set_fs(await import("fs"));
const wb = readFile(path_to_file)
const wb = read(base64, { type: "base64" });
/* generate and return the html from the first worksheet */
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
@ -101,6 +227,28 @@ export async function getStaticProps() {
};
```
</TabItem>
<TabItem value="raw" label="Raw Operations">
```js
import { readFile, set_fs, utils } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
export async function getStaticProps() {
set_fs(await import("fs"));
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
const wb = readFile(filename);
/* generate and return the html from the first worksheet */
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
return { props: { html } };
};
```
</TabItem>
</Tabs>
### Dynamic Routes
Typically a static site with dynamic routes has an endpoint `/sheets/[id]` that
@ -108,10 +256,35 @@ implements both `getStaticPaths` and `getStaticProps`.
- `getStaticPaths` should return an array of worksheet indices:
<Tabs groupId="data">
<TabItem value="asset" label="Asset Module">
```js
import { read } from 'xlsx';
import base64 from '@/data/sheetjs.xlsx';
export async function getStaticPaths() {
/* read file */
const wb = read(base64, { type: "base64" });
/* generate an array of objects that will be used for generating pages */
const paths = wb.SheetNames.map((name, idx) => ({ params: { id: idx.toString() } }));
return { paths, fallback: false };
};
```
</TabItem>
<TabItem value="raw" label="Raw Operations">
```js
import { readFile, set_fs } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
export async function getStaticPaths() {
/* read file */
set_fs(await import("fs"));
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
const wb = readFile(path);
/* generate an array of objects that will be used for generating pages */
@ -120,6 +293,9 @@ export async function getStaticPaths() {
};
```
</TabItem>
</Tabs>
:::note
For a pure static site, `fallback` must be set to `false`!
@ -128,10 +304,36 @@ For a pure static site, `fallback` must be set to `false`!
- `getStaticProps` will generate the actual HTML for each page:
<Tabs groupId="data">
<TabItem value="asset" label="Asset Module">
```js
import { read, utils } from 'xlsx';
import base64 from '@/data/sheetjs.xlsx';
export async function getStaticProps(ctx) {
/* read file */
const wb = read(base64, { type: "base64" });
/* get the corresponding worksheet and generate HTML */
const ws = wb.Sheets[wb.SheetNames[ctx.params.id]]; // id from getStaticPaths
const html = utils.sheet_to_html(ws);
return { props: { html } };
};
```
</TabItem>
<TabItem value="raw" label="Raw Operations">
```js
import { readFile, set_fs, utils } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
export async function getStaticProps(ctx) {
/* read file */
set_fs(await import("fs"));
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
const wb = readFile(path);
/* get the corresponding worksheet and generate HTML */
@ -141,6 +343,9 @@ export async function getStaticProps(ctx) {
};
```
</TabItem>
</Tabs>
### Server-Side Rendering
:::caution Do not use on a static site
@ -156,13 +361,24 @@ changes frequently and a static site is undesirable.
When using `getServerSideProps`, the file will be read on each request.
<Tabs groupId="data">
<TabItem value="asset" label="Asset Module">
:::caution Consider using a static strategy
When using asset modules, the file names and file paths are processed during the
build step. The content is fixed. In this situation, a static approach such as
`getStaticProps` is strongly recommended.
:::
```js
import { readFile, set_fs, utils } from 'xlsx';
import { read } from 'xlsx';
import base64 from '@/data/sheetjs.xlsx';
export async function getServerSideProps() {
/* read file */
set_fs(await import("fs"));
const wb = readFile(path_to_file);
const wb = read(base64, { type: "base64" });
/* generate and return the html from the first worksheet */
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
@ -170,24 +386,119 @@ export async function getServerSideProps() {
};
```
</TabItem>
<TabItem value="raw" label="Raw Operations">
```js
import { readFile, set_fs, utils } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
export async function getServerSideProps() {
/* read file */
set_fs(await import("fs"));
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
const wb = readFile(path);
/* generate and return the html from the first worksheet */
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
return { props: { html } };
};
```
</TabItem>
</Tabs>
## Data Presentation
[The React demo](/docs/demos/frontend/react) compares common approaches.
### HTML
HTML output can be generated using `XLSX.utils.sheet_to_html` and inserted into
the document using the `dangerouslySetInnerHTML` attribute:
```mermaid
flowchart LR
subgraph SheetJS operations
data(File\nData)
code{{HTML\nTABLE}}
end
html{{Rendered\nPage}}
data --> |lifecycle\nsheet_to_html| code
code --> |Index\ninnerHTML| html
```
```jsx
export default function Index({html, type}) { return (
<div dangerouslySetInnerHTML={{ __html: html }} />
); }
```
### Arrays of Objects
Arrays of objects can be generated using `XLSX.utils.sheet_to_json` and inserted
into the document using standard JSX:
```mermaid
flowchart LR
subgraph SheetJS operations
data(File\nData)
aoo(array of\nobjects)
end
html{{Rendered\nPage}}
data --> |lifecycle\nsheet_to_json| aoo
aoo --> |Index\nReact + JSX| html
```
```jsx
export default function Index({aoo, type}) { return (
<table><thead><tr key={0}><th>Name</th><th>Index</th></tr></thead><tbody>
// highlight-start
{aoo.map(row => ( <tr>
<td>{row.Name}</td>
<td>{row.Index}</td>
</tr>))}
// highlight-end
</tbody></table>
); }
```
## Demo
:::note
This demo showcases the following SheetJS + NextJS flows:
| Page | Loading Data | Lifecycle Method | SheetJS API |
|:----------------------|:-------------|:---------------------|:----------------|
| `/getStaticProps` | asset module | `getStaticProps` | `sheet_to_json` |
| `/sheets/[id]` | asset module | `getStaticPaths` | `sheet_to_html` |
| `/getServerSideProps` | lifecycle | `getServerSideProps` | `sheet_to_html` |
:::
### Initial Setup
0) Disable NextJS telemetry:
```js
npx next@13.1.1 telemetry disable
npx next@13.4.4 telemetry disable
```
Confirm it is disabled by running
```js
npx next@13.1.1 telemetry status
npx next@13.4.4 telemetry status
```
1) Set up folder structure. At the end, a `pages` folder with a `sheets`
subfolder must be created. On Linux or MacOS or WSL:
```bash
mkdir sheetjs-next
cd sheetjs-next
mkdir -p pages/sheets/
```
@ -201,13 +512,30 @@ curl -LO https://docs.sheetjs.com/next/sheetjs.xlsx
3) Install dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.1.1`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.4.4`}
</CodeBlock>
4) Download test scripts:
4) Download NextJS config scripts and place in the root folder:
- [`base64-loader.js`](pathname:///next/base64-loader.js)
- [`jsconfig.json`](pathname:///next/jsconfig.json)
- [`next.config.js`](pathname:///next/next.config.js)
- [`styles.css`](pathname:///next/styles.css)
On Linux or MacOS or WSL:
```bash
curl -LO https://docs.sheetjs.com/next/base64-loader.js
curl -LO https://docs.sheetjs.com/next/jsconfig.json
curl -LO https://docs.sheetjs.com/next/next.config.js
curl -LO https://docs.sheetjs.com/next/styles.css
```
5) Download test scripts:
Download and place the following scripts in the `pages` subfolder:
- [`_app.js`](pathname:///next/_app.js)
- [`index.js`](pathname:///next/index.js)
- [`getServerSideProps.js`](pathname:///next/getServerSideProps.js)
- [`getStaticPaths.js`](pathname:///next/getStaticPaths.js)
@ -227,6 +555,7 @@ On Linux or MacOS or WSL:
```bash
cd pages
curl -LO https://docs.sheetjs.com/next/_app.js
curl -LO https://docs.sheetjs.com/next/index.js
curl -LO https://docs.sheetjs.com/next/getServerSideProps.js
curl -LO https://docs.sheetjs.com/next/getStaticPaths.js
@ -236,10 +565,12 @@ curl -LOg 'https://docs.sheetjs.com/next/[id].js'
cd ../..
```
5) Test the deployment:
### Testing
6) Test the deployment:
```bash
npx next@13.1.1
npx next@13.4.4
```
Open a web browser and access:
@ -247,43 +578,50 @@ Open a web browser and access:
- `http://localhost:3000` landing page
- `http://localhost:3000/getStaticProps` shows data from the first sheet
- `http://localhost:3000/getServerSideProps` shows data from the first sheet
- `http://localhost:3000/getStaticPaths` shows a list (3 sheets)
- `http://localhost:3000/getStaticPaths` shows a list (2 sheets)
The individual worksheets are available at
- `http://localhost:3000/sheets/0`
- `http://localhost:3000/sheets/1`
- `http://localhost:3000/sheets/2`
6) Stop the server and run a production build:
7) While the development server is running, open the `/getStaticProps` page and
open `sheetjs.xlsx` with a spreadsheet editor. In the editor, add a row to the
bottom of the "Indices" worksheet.
After saving the file, the website should refresh with the new row.
### Production Build
8) Stop the server and run a production build:
```bash
npx next@13.1.1 build
npx next@13.4.4 build
```
The final output will show a list of the routes and types:
```
Route (pages) Size First Load JS
┌ ○ / 541 B 77.4 kB
├ ○ /404 181 B 73.7 kB
├ λ /getServerSideProps 594 B 77.4 kB
├ ● /getStaticPaths 2.56 kB 79.4 kB
├ ● /getStaticProps 591 B 77.4 kB
└ ● /sheets/[id] (447 ms) 569 B 77.4 kB
┌ ○ / 563 B 74.4 kB
├ /_app 0 B 73.9 kB
├ ○ /404 182 B 74.1 kB
├ λ /getServerSideProps 522 B 74.4 kB
├ ● /getStaticPaths 2.89 kB 76.8 kB
├ ● /getStaticProps 586 B 74.5 kB
└ ● /sheets/[id] 522 B 74.4 kB
├ /sheets/0
├ /sheets/1
└ /sheets/2
└ /sheets/1
```
As explained in the summary, the `/getStaticPaths` and `/getStaticProps` routes
are completely static. 3 `/sheets/#` pages were generated, corresponding to 3
are completely static. 2 `/sheets/#` pages were generated, corresponding to 2
worksheets in the file. `/getServerSideProps` is server-rendered.
7) Try to build a static site:
9) Try to build a static site:
```bash
npx next@13.1.1 export
npx next@13.4.4 export
```
:::note The static export will fail!
@ -293,34 +631,38 @@ is still server-rendered.
:::
8) Delete `pages/getServerSideProps.js` and rebuild:
### Static Site
10) Delete `pages/getServerSideProps.js` and rebuild:
```bash
rm -f pages/getServerSideProps.js
npx next@13.1.1 build
npx next@13.4.4 build
```
Inspecting the output, there should be no lines with the `λ` symbol:
```
Route (pages) Size First Load JS
┌ ○ / 541 B 77.4 kB
├ ○ /404 181 B 73.7 kB
├ ● /getStaticPaths 2.56 kB 79.4 kB
├ ● /getStaticProps 591 B 77.4 kB
└ ● /sheets/[id] (459 ms) 569 B 77.4 kB
┌ ○ / 563 B 74.4 kB
├ /_app 0 B 73.9 kB
├ ○ /404 182 B 74.1 kB
├ ● /getStaticPaths 2.89 kB 76.8 kB
├ ● /getStaticProps 586 B 74.5 kB
└ ● /sheets/[id] 522 B 74.4 kB
├ /sheets/0
├ /sheets/1
└ /sheets/2
└ /sheets/1
```
9) Generate the static site:
11) Generate the static site:
```bash
npx next@13.1.1 export
npx next@13.4.4 export
```
The static site will be written to the `out` subfolder, which can be hosted with
The static site will be written to the `out` subfolder
12) Serve the static site:
```bash
npx http-server out

@ -236,7 +236,8 @@ sequenceDiagram
## Bindings
Duktape is easily embeddable. Bindings exist for many languages.
Bindings exist for many languages. As these bindings require "native" code, they
may not work on every platform.
### Perl

@ -263,7 +263,8 @@ file `sheetjsw.xlsb` will be created. That file can be opened with Excel.
## Bindings
V8 is easily embeddable. Bindings exist for many languages.
Bindings exist for many languages. As these bindings require "native" code, they
may not work on every platform.
### Rust

@ -1,28 +1,25 @@
import Head from 'next/head';
import { readFile, set_fs, utils } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
import { read, utils } from 'xlsx';
import base64 from "@/sheetjs.xlsx";
export default function Index({type, html, name}) { return ( <div>
export default function Index({type, html, name}) { return ( <>
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{`SheetJS Next.JS ${type} Demo`}</title>
<style jsx>{`body, #app { height: 100%; };`}</style>
</Head>
<pre>
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
This demo reads from /sheetjs.xlsx<br/><br/>
<b>{name}</b>
<div dangerouslySetInnerHTML={{ __html: html }} />
</pre>
</div> ); }
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
<p>
This demo reads from <code>/sheetjs.xlsx</code><br/><br/>
Sheet name: <b>{name}</b><br/>
</p>
<div dangerouslySetInnerHTML={{ __html: html }} />
</> ); }
let cache = [];
export async function getStaticProps(ctx) {
if(!cache || !cache.length) {
set_fs(await import("fs"));
const wb = readFile(join(cwd(), "sheetjs.xlsx"));
const wb = read(base64, {type: "base64"});
cache = wb.SheetNames.map((name) => ({ name, sheet: wb.Sheets[name] }));
}
const entry = cache[ctx.params.id];
@ -37,11 +34,10 @@ export async function getStaticProps(ctx) {
}
export async function getStaticPaths() {
set_fs(await import("fs"));
const wb = readFile(join(cwd(), "sheetjs.xlsx"));
const wb = read(base64, {type: "base64"});
cache = wb.SheetNames.map((name) => ({ name, sheet: wb.Sheets[name] }));
return {
paths: wb.SheetNames.map((name, idx) => ({ params: { id: idx.toString() } })),
paths: wb.SheetNames.map((_, idx) => ({ params: { id: idx.toString() } })),
fallback: false,
};
}

5
docz/static/next/_app.js Normal file

@ -0,0 +1,5 @@
import '../styles.css'
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

@ -0,0 +1,7 @@
function loader(content) {
/* since `loader.raw` is true, `content` is a Buffer */
return `export default '${content.toString("base64")}'`;
}
/* ensure the function receives a Buffer */
loader.raw = true;
module.exports = loader;

@ -7,14 +7,13 @@ export default function Index({type, html}) { return ( <div>
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{`SheetJS Next.JS ${type} Demo`}</title>
<style jsx>{`body, #app { height: 100%; };`}</style>
</Head>
<pre>
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
<p>
This demo reads from /sheetjs.xlsx<br/><br/>
It generates HTML from the first sheet.<br/><br/>
<div dangerouslySetInnerHTML={{ __html: html }} />
</pre>
</p>
<div dangerouslySetInnerHTML={{ __html: html }} />
</div> ); }
export async function getServerSideProps() {

@ -1,30 +1,28 @@
import Head from 'next/head';
import Link from "next/link";
import { readFile, set_fs, utils } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
import { read } from 'xlsx';
import base64 from "@/sheetjs.xlsx";
export default function Index({type, snames}) { return ( <div>
export default function Index({type, snames}) { return ( <>
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{`SheetJS Next.JS ${type} Demo`}</title>
<style jsx>{`body, #app { height: 100%; };`}</style>
</Head>
<pre>
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
This demo reads from /sheetjs.xlsx<br/><br/>
Each worksheet maps to a path:<br/><br/>
<ul>
{snames.map((sname, idx) => (<li key={idx}>
<Link href="/sheets/[id]" as={`/sheets/${idx}`}>{`Sheet index=${idx} name="${sname}"`}</Link>
</li>))}
</ul>
</pre>
</div> ); }
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
<p>
This demo reads from <code>/sheetjs.xlsx</code><br/><br/>
Each worksheet maps to a path:<br/>
</p>
<table><thead><tr key={0}><th>Sheet Name</th><th>URL</th></tr></thead><tbody>
{snames.map((sname, idx) => (<tr key={idx+1}>
<td><Link href="/sheets/[id]" as={`/sheets/${idx}`}>{sname}</Link></td>
<td><code>/sheets/{idx}</code></td>
</tr>))}
</tbody></table>
</> ); }
export async function getStaticProps() {
set_fs(await import("fs"));
const wb = readFile(join(cwd(), "sheetjs.xlsx"))
const wb = read(base64, {type: "base64"});
return {
props: {
type: "getStaticPaths",

@ -1,29 +1,31 @@
import Head from 'next/head';
import { readFile, set_fs, utils } from 'xlsx';
import { join } from 'path';
import { cwd } from 'process';
import { read, utils } from 'xlsx';
import base64 from "@/sheetjs.xlsx";
export default function Index({type, html}) { return ( <div>
export default function Index({type, aoo}) { return ( <>
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
<title>{`SheetJS Next.JS ${type} Demo`}</title>
<style jsx>{`body, #app { height: 100%; };`}</style>
</Head>
<pre>
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
This demo reads from /sheetjs.xlsx<br/><br/>
It generates HTML from the first sheet.<br/><br/>
<div dangerouslySetInnerHTML={{ __html: html }} />
</pre>
</div> ); }
<h3>{`SheetJS Next.JS ${type} Demo`}</h3>
<p>
This demo reads from <code>/sheetjs.xlsx</code><br/><br/>
It generates objects from the first sheet.<br/><br/>
</p>
<table><thead><tr key={0}><th>Name</th><th>Index</th></tr></thead><tbody>
{aoo.map((row, R) => ( <tr key={R+1}>
<td>{row.Name}</td>
<td>{row.Index}</td>
</tr>))}
</tbody></table>
</> ); }
export async function getStaticProps() {
set_fs(await import("fs"));
const wb = readFile(join(cwd(), "sheetjs.xlsx"))
const wb = read(base64, {type: "base64"});
return {
props: {
type: "getStaticProps",
html: utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]),
aoo: utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]),
},
}
}

@ -1,16 +1,26 @@
import Head from 'next/head';
export default function Index() { return ( <div>
export default function Index() { return ( <>
<Head>
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
<title>SheetJS Next.JS Demo</title>
<style jsx>{`body, #app { height: 100%; };`}</style>
</Head>
<pre>
<h3>SheetJS Next.JS Demo</h3>
This demo reads from /sheetjs.xlsx<br/><br/>
- <a href="/getStaticProps">getStaticProps</a><br/><br/>
- <a href="/getServerSideProps">getServerSideProps</a><br/><br/>
- <a href="/getStaticPaths">getStaticPaths</a><br/>
</pre>
</div> ); }
<h3>SheetJS Next.JS Demo</h3>
<p>
This demo reads from <code>/sheetjs.xlsx</code><br/>
</p>
<table><thead><tr><th>Route</th><th>NextJS Strategy</th></tr></thead><tbody>
<tr>
<td><a href="/getStaticProps"><code>/getStaticProps</code></a></td>
<td><code>getStaticProps</code></td>
</tr>
<tr>
<td><a href="/getServerSideProps"><code>/getServerSideProps</code></a></td>
<td><code>getServerSideProps</code></td>
</tr>
<tr>
<td><a href="/getStaticPaths"><code>/getStaticPaths</code></a></td>
<td><code>getStaticPaths</code></td>
</tr>
</tbody></table>
</> ); }

@ -0,0 +1,8 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["*"]
}
}
}

@ -0,0 +1,12 @@
module.exports = {
webpack: (config) => {
/* add to the webpack config module.rules array */
config.module.rules.push({
/* `test` matches file extensions */
test: /\.(numbers|xls|xlsx|xlsb)/,
/* use the loader script */
use: [ { loader: './base64-loader' } ]
});
return config;
}
};

Binary file not shown.

@ -0,0 +1,5 @@
body, #app {
height: 100%;
font-size: 16px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}