esbuild-plugin
This commit is contained in:
		
							parent
							
								
									3ab0c1ab67
								
							
						
					
					
						commit
						aeb932e1d0
					
				| @ -25,7 +25,7 @@ bundle with ESBuild for browser use. | ||||
| - ["NodeJS"](#nodejs) explores how to import SheetJS libraries in a script and | ||||
| bundle with ESBuild for NodeJS use. | ||||
| 
 | ||||
| :::info pass | ||||
| :::note pass | ||||
| 
 | ||||
| This demo focuses on integration details with the ESBuild bundler. | ||||
| 
 | ||||
| @ -34,6 +34,13 @@ The tutorial covers SheetJS library usage. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| The [ESBuild section of the Content demo](/docs/demos/static/esbuild) covers | ||||
| loaders. They are ideal for static sites pulling data from sheets at build time. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 October 19 against esbuild `0.19.5` | ||||
|  | ||||
							
								
								
									
										151
									
								
								docz/docs/03-demos/01-frontend/19-bundler/09-browserify.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										151
									
								
								docz/docs/03-demos/01-frontend/19-bundler/09-browserify.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| --- | ||||
| title: Bundling Sheets with Browserify | ||||
| sidebar_label: Browserify | ||||
| pagination_prev: demos/index | ||||
| pagination_next: demos/grid/index | ||||
| sidebar_position: 9 | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| [Browserify](https://browserify.org/) is a module bundler. | ||||
| 
 | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses Browserify and SheetJS to export data. We'll explore how to add | ||||
| SheetJS to a site using Browserify and how to export data to spreadsheets. | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 October 21 against Browserify `17.0.0` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| [The "Frameworks" section](/docs/getting-started/installation/frameworks) covers | ||||
| installation with Yarn and other package managers. | ||||
| 
 | ||||
| After installing the SheetJS module in a Browserify project, `require` | ||||
| expressions can load relevant parts of the library. | ||||
| 
 | ||||
| ```js | ||||
| var XLSX = require("xlsx"); | ||||
| // ... use XLSX ... | ||||
| ``` | ||||
| 
 | ||||
| Browserify can also process `require` expressions in Web Worker scripts. | ||||
| 
 | ||||
| ## Complete Example | ||||
| 
 | ||||
| 0) Initialize a new project: | ||||
| 
 | ||||
| ```bash | ||||
| mkdir sheetjs-browserify | ||||
| cd sheetjs-browserify | ||||
| npm init -y | ||||
| ``` | ||||
| 
 | ||||
| 1) Install the tarball using a package manager: | ||||
| 
 | ||||
| <Tabs groupId="pm"> | ||||
|   <TabItem value="npm" label="npm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="pnpm" label="pnpm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="yarn" label="Yarn" default> | ||||
| <CodeBlock language="bash">{`\ | ||||
| yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| 2) Save the following to `index.js`: | ||||
| 
 | ||||
| ```js title="index.js" | ||||
| // highlight-next-line | ||||
| const { utils, version, writeFileXLSX } = require('xlsx'); | ||||
| 
 | ||||
| document.getElementById("xport").addEventListener("click", function() { | ||||
|   /* fetch JSON data and parse */ | ||||
|   var url = "https://sheetjs.com/data/executive.json"; | ||||
|   fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) { | ||||
| 
 | ||||
|     /* filter for the Presidents */ | ||||
|     var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); }); | ||||
| 
 | ||||
|     /* sort by first presidential term */ | ||||
|     prez.forEach(function(row) { | ||||
|       row.start = row.terms.find(function(term) { | ||||
|         return term.type === "prez"; | ||||
|       }).start | ||||
|     }); | ||||
|     prez.sort(function(l,r) { return l.start.localeCompare(r.start); }); | ||||
| 
 | ||||
|     /* flatten objects */ | ||||
|     var rows = prez.map(function(row) { return { | ||||
|       name: row.name.first + " " + row.name.last, | ||||
|       birthday: row.bio.birthday | ||||
|     }; }); | ||||
| 
 | ||||
|     /* generate worksheet and workbook */ | ||||
|     var worksheet = utils.json_to_sheet(rows); | ||||
|     var workbook = utils.book_new(); | ||||
|     utils.book_append_sheet(workbook, worksheet, "Dates"); | ||||
| 
 | ||||
|     /* fix headers */ | ||||
|     utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" }); | ||||
| 
 | ||||
|     /* calculate column width */ | ||||
|     var max_width = rows.reduce(function(w, r) { return Math.max(w, r.name.length); }, 10); | ||||
|     worksheet["!cols"] = [ { wch: max_width } ]; | ||||
| 
 | ||||
|     /* create an XLSX file and try to save to Presidents.xlsx */ | ||||
|     writeFileXLSX(workbook, "Presidents.xlsx"); | ||||
|   }); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 3) Bundle the scripts: | ||||
| 
 | ||||
| ```bash | ||||
| npx browserify app.js > browserify.js | ||||
| ``` | ||||
| 
 | ||||
| 4) Spin up a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server | ||||
| ``` | ||||
| 
 | ||||
| 5) Create a small HTML page that loads the script.  Save to `index.html`: | ||||
| 
 | ||||
| ```html title="index.html" | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head></head> | ||||
|   <body> | ||||
|     <h1>SheetJS Presidents Demo</h1> | ||||
|     <button id="xport">Click here to export</button> | ||||
|     <script src="./index.min.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| 6) Start a local HTTP server and go to `http://localhost:8080/` | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server . | ||||
| ``` | ||||
| 
 | ||||
| Click on "Click here to export" to generate a file. | ||||
							
								
								
									
										186
									
								
								docz/docs/03-demos/01-frontend/19-bundler/21-swcpack.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										186
									
								
								docz/docs/03-demos/01-frontend/19-bundler/21-swcpack.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,186 @@ | ||||
| --- | ||||
| title: Bundling Sheets with SWC | ||||
| sidebar_label: SWC spack | ||||
| pagination_prev: demos/index | ||||
| pagination_next: demos/grid/index | ||||
| sidebar_position: 21 | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import Tabs from '@theme/Tabs'; | ||||
| import TabItem from '@theme/TabItem'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| SWC[^1] is a JS toolchain. SWC provides `spack` (formally called "swcpack") for | ||||
| bundling scripts. | ||||
| 
 | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses `spack` and SheetJS to export data. We'll explore how to bundle | ||||
| SheetJS in a site using `spack` and how to export data to spreadsheets. | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 October 20 against SWC 1.2.246 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Integration Details | ||||
| 
 | ||||
| [The "Frameworks" section](/docs/getting-started/installation/frameworks) covers | ||||
| installation with Yarn and other package managers. | ||||
| 
 | ||||
| After installing the SheetJS module in a SWC `spack` project, `import` | ||||
| statements can load relevant parts of the library. | ||||
| 
 | ||||
| Projects that import data will use methods such as `read`[^2] to parse workbooks | ||||
| and `sheet_to_json`[^3] to generate usable data from files. As `sheet_to_json` | ||||
| is part of the `utils` object, the required import is: | ||||
| 
 | ||||
| ```js | ||||
| import { read, utils } from 'xlsx'; | ||||
| ``` | ||||
| 
 | ||||
| Projects that export data will use methods such as `json_to_sheet`[^4] to | ||||
| generate worksheets and `writeFile`[^5] to export files. As `json_to_sheet` is | ||||
| part of the `utils` object, the required import is: | ||||
| 
 | ||||
| ```js | ||||
| import { utils, writeFile } from 'xlsx'; | ||||
| ``` | ||||
| 
 | ||||
| :::warning pass | ||||
| 
 | ||||
| When this demo was tested against the latest version, `spack` crashed: | ||||
| 
 | ||||
| ``` | ||||
| thread '<unnamed>' panicked at 'cannot access a scoped thread local variable without calling `set` first', | ||||
| ``` | ||||
| 
 | ||||
| Until the bug is fixed, it is strongly recommended to use `@swc/core@1.2.246`. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Complete Example | ||||
| 
 | ||||
| 0) Initialize a new project: | ||||
| 
 | ||||
| ```bash | ||||
| mkdir sheetjs-spack | ||||
| cd sheetjs-spack | ||||
| npm init -y | ||||
| ``` | ||||
| 
 | ||||
| 1) Install the dependencies using a package manager: | ||||
| 
 | ||||
| <Tabs groupId="pm"> | ||||
|   <TabItem value="npm" label="npm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="pnpm" label="pnpm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="yarn" label="Yarn" default> | ||||
| <CodeBlock language="bash">{`\ | ||||
| yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| The `regenerator-runtime` dependency is used for transpiling `fetch` and is not | ||||
| required if the interface code does not use `fetch` or Promises. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 2) Save the following to `index.js`: | ||||
| 
 | ||||
| ```js title="index.js" | ||||
| import { utils, version, writeFileXLSX } from 'xlsx'; | ||||
| 
 | ||||
| document.getElementById("xport").addEventListener("click", async() => { | ||||
| /* fetch JSON data and parse */ | ||||
| const url = "https://sheetjs.com/data/executive.json"; | ||||
| const raw_data = await (await fetch(url)).json(); | ||||
| 
 | ||||
| /* filter for the Presidents */ | ||||
| const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez")); | ||||
| 
 | ||||
| /* flatten objects */ | ||||
| const rows = prez.map(row => ({ | ||||
|   name: row.name.first + " " + row.name.last, | ||||
|   birthday: row.bio.birthday | ||||
| })); | ||||
| 
 | ||||
| /* generate worksheet and workbook */ | ||||
| const worksheet = utils.json_to_sheet(rows); | ||||
| const workbook = utils.book_new(); | ||||
| utils.book_append_sheet(workbook, worksheet, "Dates"); | ||||
| 
 | ||||
| /* fix headers */ | ||||
| utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" }); | ||||
| 
 | ||||
| /* calculate column width */ | ||||
| const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10); | ||||
| worksheet["!cols"] = [ { wch: max_width } ]; | ||||
| 
 | ||||
| /* create an XLSX file and try to save to Presidents.xlsx */ | ||||
| writeFileXLSX(workbook, "Presidents.xlsx"); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 3) Create an `spack.config.js` config file: | ||||
| 
 | ||||
| ```js title="spack.config.js" | ||||
| module.exports = ({ | ||||
|   entry: { | ||||
|     'web': __dirname + '/index.js', | ||||
|   }, | ||||
|   output: { | ||||
|     path: __dirname + '/lib' | ||||
|   }, | ||||
|   module: {}, | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 4) Build for production: | ||||
| 
 | ||||
| ```bash | ||||
| npx spack | ||||
| ``` | ||||
| 
 | ||||
| This command will create the script `lib/web.js` | ||||
| 
 | ||||
| 5) Create a small HTML page that loads the generated script: | ||||
| 
 | ||||
| ```html title="index.html" | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head></head> | ||||
|   <body> | ||||
|     <h1>SheetJS Presidents Demo</h1> | ||||
|     <button id="xport">Click here to export</button> | ||||
|     <script src="lib/web.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| 6) Start a local HTTP server, then go to `http://localhost:8080/` | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server | ||||
| ``` | ||||
| 
 | ||||
| Click on "Click here to export" to generate a file. | ||||
| 
 | ||||
| [^1]: See ["Bundling Configuration"](https://swc.rs/docs/configuration/bundling) in the SWC documentation for more details. | ||||
| [^2]: See [`read` in "Reading Files"](/docs/api/parse-options) | ||||
| [^3]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) | ||||
| [^4]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input) | ||||
| [^5]: See [`writeFile` in "Writing Files"](/docs/api/write-options) | ||||
| @ -35,84 +35,9 @@ The following tools are covered in separate pages: | ||||
|   </li>); | ||||
| })}</ul> | ||||
| 
 | ||||
| ## Browserify | ||||
| 
 | ||||
| `browserify` is compatible with the library and should "just work" with the | ||||
| `require` form in a main page or in a web worker: | ||||
| 
 | ||||
| ```js | ||||
| var XLSX = require("xlsx"); | ||||
| // ... use XLSX ... | ||||
| ``` | ||||
| 
 | ||||
| [After installing the NodeJS module](/docs/getting-started/installation/nodejs), | ||||
| bundling is straightforward: | ||||
| 
 | ||||
| ```bash | ||||
| browserify app.js > browserify.js | ||||
| uglifyjs browserify.js > browserify.min.js | ||||
| ``` | ||||
| 
 | ||||
| Web Worker scripts can be bundled using the same approach. | ||||
| 
 | ||||
| <details><summary><b>Complete Example</b> (click to show)</summary> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 May 07 against Browserify `17.0.0` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 1) Install the tarball using a package manager: | ||||
| 
 | ||||
| <Tabs groupId="pm"> | ||||
|   <TabItem value="npm" label="npm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="pnpm" label="pnpm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="yarn" label="Yarn" default> | ||||
| <CodeBlock language="bash">{`\ | ||||
| yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| 2) Download the following files: | ||||
| 
 | ||||
| - [`app.js`](pathname:///browserify/app.js) | ||||
| - [`index.html`](pathname:///browserify/index.html) | ||||
| - [`xlsxworker.js`](pathname:///browserify/xlsxworker.js) | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://docs.sheetjs.com/browserify/app.js | ||||
| curl -LO https://docs.sheetjs.com/browserify/index.html | ||||
| curl -LO https://docs.sheetjs.com/browserify/xlsxworker.js | ||||
| ``` | ||||
| 
 | ||||
| 3) Bundle the scripts: | ||||
| 
 | ||||
| ```bash | ||||
| npx browserify app.js > browserify.js | ||||
| npx browserify xlsxworker.js > worker.js | ||||
| ``` | ||||
| 
 | ||||
| 4) Spin up a local web server: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server | ||||
| ``` | ||||
| 
 | ||||
| 5) Access the site `http://localhost:8080/` and use the file input element to | ||||
| select a spreadsheet. | ||||
| 
 | ||||
| </details> | ||||
| #### Browserify | ||||
| 
 | ||||
| **[The exposition has been moved to a separate page.](/docs/demos/frontend/bundler/browserify)** | ||||
| 
 | ||||
| ## Bun | ||||
| 
 | ||||
| @ -312,7 +237,7 @@ click the "Click to Export!" button to generate a file. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## RequireJS | ||||
| #### RequireJS | ||||
| 
 | ||||
| **[The exposition has been moved to a separate page.](/docs/demos/frontend/bundler/requirejs)** | ||||
| 
 | ||||
| @ -405,7 +330,6 @@ npx rollup index.js --plugin @rollup/plugin-node-resolve --file bundle.js --form | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 5) Start a local HTTP server, then go to `http://localhost:8080/` | ||||
| 
 | ||||
| ```bash | ||||
| @ -521,135 +445,9 @@ Click on "Click here to export" to generate a file. | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| ## SWC | ||||
| #### SWC | ||||
| 
 | ||||
| SWC provides `spack` for bundling scripts. | ||||
| 
 | ||||
| :::warning pass | ||||
| 
 | ||||
| When this demo was last tested, there was a bug affecting 1.2.247 and 1.3 . It | ||||
| is strongly recommended to use `@swc/core@1.2.245` until the bug is fixed. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| <details><summary><b>Complete Example</b> (click to show)</summary> | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 May 07 against SWC 1.2.246 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 1) Install the dependencies using a package manager: | ||||
| 
 | ||||
| <Tabs groupId="pm"> | ||||
|   <TabItem value="npm" label="npm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="pnpm" label="pnpm"> | ||||
| <CodeBlock language="bash">{`\ | ||||
| pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
|   <TabItem value="yarn" label="Yarn" default> | ||||
| <CodeBlock language="bash">{`\ | ||||
| yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 | ||||
| </CodeBlock> | ||||
|   </TabItem> | ||||
| </Tabs> | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| The `regenerator-runtime` dependency is used for transpiling `fetch` and is not | ||||
| required if the interface code does not use `fetch` or Promises. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| 2) Save the following to `index.js`: | ||||
| 
 | ||||
| ```js title="index.js" | ||||
| import { utils, version, writeFileXLSX } from 'xlsx'; | ||||
| 
 | ||||
| document.getElementById("xport").addEventListener("click", async() => { | ||||
| /* fetch JSON data and parse */ | ||||
| const url = "https://sheetjs.com/data/executive.json"; | ||||
| const raw_data = await (await fetch(url)).json(); | ||||
| 
 | ||||
| /* filter for the Presidents */ | ||||
| const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez")); | ||||
| 
 | ||||
| /* flatten objects */ | ||||
| const rows = prez.map(row => ({ | ||||
|   name: row.name.first + " " + row.name.last, | ||||
|   birthday: row.bio.birthday | ||||
| })); | ||||
| 
 | ||||
| /* generate worksheet and workbook */ | ||||
| const worksheet = utils.json_to_sheet(rows); | ||||
| const workbook = utils.book_new(); | ||||
| utils.book_append_sheet(workbook, worksheet, "Dates"); | ||||
| 
 | ||||
| /* fix headers */ | ||||
| utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" }); | ||||
| 
 | ||||
| /* calculate column width */ | ||||
| const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10); | ||||
| worksheet["!cols"] = [ { wch: max_width } ]; | ||||
| 
 | ||||
| /* create an XLSX file and try to save to Presidents.xlsx */ | ||||
| writeFileXLSX(workbook, "Presidents.xlsx"); | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 3) Create an `spack.config.js` config file: | ||||
| 
 | ||||
| ```js title="spack.config.js" | ||||
| const { config } = require('@swc/core/spack'); | ||||
| 
 | ||||
| module.exports = ({ | ||||
|   entry: { | ||||
|     'web': __dirname + '/index.js', | ||||
|   }, | ||||
|   output: { | ||||
|     path: __dirname + '/lib' | ||||
|   }, | ||||
|   module: {}, | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 4) Build for production: | ||||
| 
 | ||||
| ```bash | ||||
| npx spack | ||||
| ``` | ||||
| 
 | ||||
| This command will create the script `lib/web.js` | ||||
| 
 | ||||
| 5) Create a small HTML page that loads the generated script: | ||||
| 
 | ||||
| ```html title="index.html" | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head></head> | ||||
|   <body> | ||||
|     <h1>SheetJS Presidents Demo</h1> | ||||
|     <button id="xport">Click here to export</button> | ||||
|     <script src="lib/web.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| 6) Start a local HTTP server, then go to `http://localhost:8080/` | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server | ||||
| ``` | ||||
| 
 | ||||
| Click on "Click here to export" to generate a file. | ||||
| 
 | ||||
| </details> | ||||
| **[The exposition has been moved to a separate page.](/docs/demos/frontend/bundler/swcpack)** | ||||
| 
 | ||||
| #### SystemJS | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										361
									
								
								docz/docs/03-demos/04-static/04-esbuild.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										361
									
								
								docz/docs/03-demos/04-static/04-esbuild.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,361 @@ | ||||
| --- | ||||
| title: Building Sheets with ESBuild | ||||
| sidebar_label: ESBuild | ||||
| pagination_prev: demos/net/index | ||||
| pagination_next: demos/mobile/index | ||||
| sidebar_custom_props: | ||||
|   type: bundler | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| import CodeBlock from '@theme/CodeBlock'; | ||||
| 
 | ||||
| [ESBuild](https://esbuild.github.io/) is a modern build tool for generating | ||||
| static sites. It has a robust JavaScript-powered plugin system[^1] | ||||
| 
 | ||||
| [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing | ||||
| data from spreadsheets. | ||||
| 
 | ||||
| This demo uses ESBuild and SheetJS to pull data from a spreadsheet and display | ||||
| the content in an HTML table. We'll explore how to load SheetJS in a ESBuild | ||||
| loader and generate data for use in webpages. | ||||
| 
 | ||||
| The ["Demo"](#demo) creates a complete website powered by a XLSX spreadsheet. | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| This demo covers static asset imports. For processing files in the browser, the | ||||
| ["Bundlers" demo](/docs/demos/frontend/bundler/esbuild) includes an example of | ||||
| importing the SheetJS library in a browser script. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## ESBuild Loader | ||||
| 
 | ||||
| ESBuild supports custom loader plugins. The loader receives an absolute path to | ||||
| the spreadsheet on the filesystem. | ||||
| 
 | ||||
| The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be | ||||
| imported from ESBuild loader plugins. | ||||
| 
 | ||||
| :::info pass | ||||
| 
 | ||||
| ESBuild loader plugins use ECMAScript Modules. The plugin ultimately receives | ||||
| raw paths to files. [`fs`](/docs/getting-started/installation/nodejs#esm-import) | ||||
| must be manually imported: | ||||
| 
 | ||||
| ```js | ||||
| import * as XLSX from 'xlsx'; | ||||
| 
 | ||||
| /* load 'fs' for readFile and writeFile support */ | ||||
| import * as fs from 'fs'; | ||||
| XLSX.set_fs(fs); | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| The following diagram depicts the workbook waltz: | ||||
| 
 | ||||
| ```mermaid | ||||
| flowchart LR | ||||
|   subgraph ESBuild Custom Plugin in build.mjs | ||||
|     file[(workbook\nfile)] | ||||
|     wb(((SheetJS\nWorkbook))) | ||||
|     aoo(array of\nobjects) | ||||
|   end | ||||
|   html{{HTML\nTABLE}} | ||||
|   file --> |`readFile`\n\n| wb | ||||
|   wb --> |`sheet_to_json`\n\n| aoo | ||||
|   aoo --> |app.js\nfrontend code| html | ||||
| ``` | ||||
| 
 | ||||
| ### ESBuild Config | ||||
| 
 | ||||
| Plugins can be referenced in the `plugins` array of the build config object: | ||||
| 
 | ||||
| ```js title="build.mjs (structure)" | ||||
| import * as esbuild from 'esbuild' | ||||
| 
 | ||||
| // highlight-next-line | ||||
| let sheetjsPlugin = { | ||||
|   name: 'sheetjs', | ||||
|   setup(build) { | ||||
|     // ... | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| await esbuild.build({ | ||||
|   entryPoints: ['app.js'], | ||||
|   bundle: true, | ||||
|   outfile: 'out.js', | ||||
|   // highlight-next-line | ||||
|   plugins: [sheetjsPlugin], | ||||
| }) | ||||
| ``` | ||||
| 
 | ||||
| ### Registering File Extensions | ||||
| 
 | ||||
| The `setup` method receives the build options. Handlers for custom files should | ||||
| be added using `build.onLoad`. | ||||
| 
 | ||||
| The first argument to `onLoad` is a configuration object. The `filter` property | ||||
| is expected to be a regular expression. The following regular expression matches | ||||
| NUMBERS, XLSX, XLS, and XLSB files: | ||||
| 
 | ||||
| ```js | ||||
|     const EXTS = /.(numbers|xlsx|xls|xlsb)$/; | ||||
| ``` | ||||
| 
 | ||||
| The second argument to `onLoad` is a callback that receives an arguments object. | ||||
| The `path` property of the object is the absolute path to the file. | ||||
| 
 | ||||
| ```js | ||||
|   setup(build) { | ||||
|     build.onLoad({ filter: EXTS }, (args) => { | ||||
|       const path = args.path; | ||||
|       // ... | ||||
|     }); | ||||
|   }, | ||||
| ``` | ||||
| 
 | ||||
| ### SheetJS Operations | ||||
| 
 | ||||
| The SheetJS `readFile` method[^2] will directly read the file on the filesystem. | ||||
| The return value is a SheetJS workbook object[^3]. | ||||
| 
 | ||||
| The loader in this demo will parse the workbook, pull the first worksheet, and | ||||
| generate an array of row objects using the `sheet_to_json` method[^4]. | ||||
| 
 | ||||
| :::caution pass | ||||
| 
 | ||||
| JSON does not natively support Dates! `JSON.stringify` will generate strings. | ||||
| 
 | ||||
| Through a clever workaround, it is possible to encode dates separately and | ||||
| recover the Date objects in the generated code module. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ```js | ||||
| import * as XLSX from 'xlsx'; | ||||
| import * as fs from 'fs'; | ||||
| XLSX.set_fs(fs); | ||||
| 
 | ||||
| /* plugin */ | ||||
| let sheetjsPlugin = { | ||||
|   name: 'sheetjs', | ||||
|   setup(build) { | ||||
|     /* match NUMBERS, XLSX, XLS, and XLSB files */ | ||||
|     const EXTS = /.(numbers|xlsx|xls|xlsb)$/; | ||||
| 
 | ||||
|     /* this method will be called once for each referenced file */ | ||||
|     build.onLoad({ filter: EXTS }, (args) => { | ||||
|       /* parse file from filesystem */ | ||||
|       const wb = XLSX.readFile(args.path); | ||||
|       /* get first worksheet */ | ||||
|       const ws = wb.Sheets[wb.SheetNames[0]]; | ||||
| 
 | ||||
|       /* workaround for JSON limitation */ | ||||
|       Date.prototype.toJSON2 = Date.prototype.toJSON; | ||||
|       Date.prototype.toJSON = function() { return {d:this.toISOString()}; }; | ||||
| 
 | ||||
|       /* generate row objects */ | ||||
|       const data = XLSX.utils.sheet_to_json(ws); | ||||
| 
 | ||||
|       /* generate final module code */ | ||||
|       const res = JSON.stringify(data); | ||||
|       Date.prototype.toJSON = Date.prototype.toJSON2; | ||||
|       const contents = `const data = ${res}; | ||||
| data.forEach(row => { | ||||
|   Object.keys(row).forEach(k => { | ||||
|     if(row[k]?.d) row[k] = new Date(row[k].d); | ||||
|   }) | ||||
| }); | ||||
| export default data;` | ||||
|       return { contents, loader: 'js' }; | ||||
|     }); | ||||
|   }, | ||||
| }; | ||||
| ``` | ||||
| 
 | ||||
| ### Asset Imports | ||||
| 
 | ||||
| Spreadsheets can be imported using the plugin.  Assuming `pres.xlsx` is stored | ||||
| in the same folder as the script, `./pres.xlsx` will be a data module: | ||||
| 
 | ||||
| ```js title="src/index.js" | ||||
| import data from './pres.xlsx'; | ||||
| /* `data` is an array of objects from data/pres.xlsx */ | ||||
| 
 | ||||
| const elt = document.createElement('div'); | ||||
| elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" + | ||||
|   data.map((row) => `<tr> | ||||
|     <td>${row.Name}</td> | ||||
|     <td>${row.Index}</td> | ||||
|   </tr>`).join("") + | ||||
| "</table>"; | ||||
| document.body.appendChild(elt); | ||||
| ``` | ||||
| 
 | ||||
| ## Demo | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 October 21 against ESBuild 0.19.5 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### Initial Setup | ||||
| 
 | ||||
| 0) Create a new skeleton project: | ||||
| 
 | ||||
| ```bash | ||||
| mkdir sheetjs-esb | ||||
| cd sheetjs-esb | ||||
| npm init -y | ||||
| npm i --save esbuild@0.19.5 | ||||
| ``` | ||||
| 
 | ||||
| 1) Install the SheetJS NodeJS module: | ||||
| 
 | ||||
| <CodeBlock language="bash">{`\ | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} | ||||
| </CodeBlock> | ||||
| 
 | ||||
| 2) Save the following to `index.html`: | ||||
| 
 | ||||
| ```html title="index.html" | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title>SheetJS + ESBuild</title> | ||||
|   </head> | ||||
|   <body> | ||||
|    <script src="out.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| 3) Save the following to `app.js`: | ||||
| 
 | ||||
| ```js title="app.js" | ||||
| import data from './pres.numbers' | ||||
| const elt = document.createElement('div'); | ||||
| elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" + | ||||
|   data.map((row) => `<tr> | ||||
|     <td>${row.Name}</td> | ||||
|     <td>${row.Index}</td> | ||||
|   </tr>`).join("") + | ||||
| "</table>"; | ||||
| document.body.appendChild(elt); | ||||
| ``` | ||||
| 
 | ||||
| 4) Save the following to `build.mjs`: | ||||
| 
 | ||||
| ```js title="build.mjs" | ||||
| import * as esbuild from 'esbuild' | ||||
| import * as XLSX from 'xlsx'; | ||||
| import * as fs from 'fs'; | ||||
| XLSX.set_fs(fs); | ||||
| 
 | ||||
| let sheetjsPlugin = { | ||||
|   name: 'sheetjs', | ||||
|   setup(build) { | ||||
|     /* match NUMBERS, XLSX, XLS, and XLSB files */ | ||||
|     const EXTS = /.(numbers|xlsx|xls|xlsb)$/; | ||||
| 
 | ||||
|     /* this method will be called once for each referenced file */ | ||||
|     build.onLoad({ filter: EXTS }, (args) => { | ||||
|       /* parse file from filesystem */ | ||||
|       const wb = XLSX.readFile(args.path); | ||||
|       /* get first worksheet */ | ||||
|       const ws = wb.Sheets[wb.SheetNames[0]]; | ||||
| 
 | ||||
|       /* workaround for JSON limitation */ | ||||
|       Date.prototype.toJSON2 = Date.prototype.toJSON; | ||||
|       Date.prototype.toJSON = function() { return {d:this.toISOString()}; }; | ||||
| 
 | ||||
|       /* generate row objects */ | ||||
|       const data = XLSX.utils.sheet_to_json(ws); | ||||
| 
 | ||||
|       /* generate final module code */ | ||||
|       const res = JSON.stringify(data); | ||||
|       Date.prototype.toJSON = Date.prototype.toJSON2; | ||||
|       const contents = `const data = ${res}; | ||||
| data.forEach(row => { | ||||
|   Object.keys(row).forEach(k => { | ||||
|     if(row[k]?.d) row[k] = new Date(row[k].d); | ||||
|   }) | ||||
| }); | ||||
| export default data;` | ||||
|       return { contents, loader: 'js' }; | ||||
|     }); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| await esbuild.build({ | ||||
|   entryPoints: ['app.js'], | ||||
|   bundle: true, | ||||
|   outfile: 'out.js', | ||||
|   plugins: [sheetjsPlugin], | ||||
| }); | ||||
| ``` | ||||
| 
 | ||||
| 5) Download <https://sheetjs.com/pres.numbers> and save to the project folder: | ||||
| 
 | ||||
| ```bash | ||||
| curl -LO https://sheetjs.com/pres.numbers | ||||
| ``` | ||||
| 
 | ||||
| ### Static Site Test | ||||
| 
 | ||||
| 6) Build the site: | ||||
| 
 | ||||
| ```bash | ||||
| node build.mjs | ||||
| ``` | ||||
| 
 | ||||
| The final script will be saved to `out.js` | ||||
| 
 | ||||
| 7) Start a local web server to host the project folder: | ||||
| 
 | ||||
| ```bash | ||||
| npx http-server . | ||||
| ``` | ||||
| 
 | ||||
| The command will print a list of URLs. | ||||
| 
 | ||||
| 8) Open one of the URLs printed in the previous step (`http://localhost:8080`) | ||||
| and confirm that the same data is displayed. | ||||
| 
 | ||||
| To verify that the data was added to the page, append `out.js` to the URL | ||||
| (`http://localhost:8080/out.js`) and view the source.  The source will include | ||||
| president names.  It will not include SheetJS library references! | ||||
| 
 | ||||
| In the last test, the generated source looked like the following snippet | ||||
| 
 | ||||
| ```js title="out.js" | ||||
| (() => { | ||||
|   // pres.numbers | ||||
|   var data = [{ "Name": "Bill Clinton", "Index": 42 }, /* ... more data */]; | ||||
|   data.forEach((row) => { | ||||
|     Object.keys(row).forEach((k) => { | ||||
|       if (row[k]?.d) | ||||
|         row[k] = new Date(row[k].d); | ||||
|     }); | ||||
|   }); | ||||
|   var pres_default = data; | ||||
| 
 | ||||
|   // app.js | ||||
|   var elt = document.createElement("div"); | ||||
|   elt.innerHTML = "<table><tr><th>Name</th><th>Index</th></tr>" + pres_default.map((row) => `<tr> | ||||
|     <td>${row.Name}</td> | ||||
|     <td>${row.Index}</td> | ||||
|   </tr>`).join("") + "</table>"; | ||||
|   document.body.appendChild(elt); | ||||
| })(); | ||||
| ``` | ||||
| 
 | ||||
| [^1]: See ["Plugins"](https://esbuild.github.io/plugins/) in the ESBuild documentation. | ||||
| [^2]: See [`readFile` in "Reading Files"](/docs/api/parse-options) | ||||
| [^3]: See ["Workbook Object"](/docs/csf/book) | ||||
| [^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output) | ||||
| @ -125,7 +125,7 @@ This demo was tested in the following deployments: | ||||
| | V8 Version    | Platform     | OS Version    | Compiler         | Date       | | ||||
| |:--------------|:-------------|:--------------|:-----------------|:-----------| | ||||
| | `11.8.82`     | `darwin-x64` | macOS 13.5.1  | `clang 14.0.3`   | 2023-08-26 | | ||||
| | `11.8.82`     | `darwin-arm` | macOS 13.5.1  | `clang 14.0.3`   | 2023-08-26 | | ||||
| | `12.0.175`    | `darwin-arm` | macOS 14.0    | `clang 15.0.0`   | 2023-10-20 | | ||||
| | `11.8.82`     | `win10-x64`  | Windows 10    | `CL 19.37.32822` | 2023-08-26 | | ||||
| | `12.0.72`     | `linux-x64`  | HoloOS 3.4.11 | `gcc 12.2.0`     | 2023-10-11 | | ||||
| | `11.8.82`     | `linux-arm`  | Debian 11     | `gcc 10.2.1`     | 2023-09-26 | | ||||
|  | ||||
| @ -115,7 +115,7 @@ This demo was tested in the following deployments: | ||||
| | Architecture | Date       | | ||||
| |:-------------|:-----------| | ||||
| | `darwin-x64` | 2023-08-31 | | ||||
| | `darwin-arm` | 2023-07-05 | | ||||
| | `darwin-arm` | 2023-10-20 | | ||||
| | `win10-x64`  | 2023-08-31 | | ||||
| | `win11-arm`  | 2023-09-26 | | ||||
| | `linux-x64`  | 2023-10-11 | | ||||
|  | ||||
| @ -134,6 +134,8 @@ This demo was tested in the following deployments: | ||||
| | `darwin-x64` | `a588e49` | 2023-09-22 | | ||||
| | `linux-x64`  | `a588e49` | 2023-10-11 | | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| :::note pass | ||||
| 
 | ||||
| While applications should link against the official libraries, the standalone tool | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user