diff --git a/docz/docs/03-demos/04-static/01-lume.md b/docz/docs/03-demos/04-static/01-lume.md index afb7cd9..ffab21a 100644 --- a/docz/docs/03-demos/04-static/01-lume.md +++ b/docz/docs/03-demos/04-static/01-lume.md @@ -129,7 +129,7 @@ site.use(sheets({ :::note -This was tested against `lume v1.17.5` on 2023 June 25. +This demo was last tested against `lume v1.17.5` on 2023 June 25. This example uses the Nunjucks template format. Lume plugins support additional template formats, including Markdown and JSX. diff --git a/docz/docs/03-demos/06-desktop/04-tauri.md b/docz/docs/03-demos/06-desktop/04-tauri.md index c2fc0dd..a86821c 100644 --- a/docz/docs/03-demos/06-desktop/04-tauri.md +++ b/docz/docs/03-demos/06-desktop/04-tauri.md @@ -257,9 +257,9 @@ This demo was tested in the following environments: | OS and Version | Arch | Tauri | Date | |:---------------|:-----|:---------|:-----------| -| macOS 13.4.0 | x64 | `v1.4.0` | 2023-06-25 | +| macOS 13.5.1 | x64 | `v1.5.0` | 2023-09-30 | | macOS 13.4.1 | ARM | `v1.4.0` | 2023-06-29 | -| Windows 10 | x64 | `v1.4.1` | 2023-07-30 | +| Windows 10 | x64 | `v1.5.0` | 2023-10-01 | | Windows 11 | ARM | `v1.4.1` | 2023-09-26 | | Linux (HoloOS) | x64 | `v1.4.1` | 2023-07-30 | | Linux (Debian) | ARM | `v1.4.1` | 2023-09-26 | @@ -287,14 +287,14 @@ If required dependencies are installed, the output will show a checkmark next to ``` [✔] Environment - - OS: Mac OS 13.4.0 X64 + - OS: Mac OS 13.5.1 X64 ✔ Xcode Command Line Tools: installed - ✔ rustc: 1.70.0 (90c541806 2023-05-31) - ✔ Cargo: 1.70.0 (ec8a8a0ca 2023-04-25) - ✔ rustup: 1.26.0 (5af9b9484 2023-04-05) + ✔ rustc: 1.72.1 (d5c2e9c34 2023-09-13) + ✔ Cargo: 1.72.1 (103a7ff2e 2023-08-15) + ✔ rustup: 1.26.0+1046 (d4c684485 2023-08-30) ✔ Rust toolchain: stable-x86_64-apple-darwin (default) - - node: 18.16.1 - - npm: 9.5.1 + - node: 16.20.2 + - npm: 8.19.4 ``` :::caution pass @@ -374,8 +374,7 @@ curl -o src/App.vue https://docs.sheetjs.com/tauri/App.vue npm run tauri build ``` -At the end, it will print the path to the generated program. If the program path -is not listed, it is typically found in the `src-tauri/target/release` folder. +At the end, it will print the path to the generated installer. :::info pass @@ -393,9 +392,28 @@ sudo pacman -S openssl ::: -6) Run the program. The following features should be manually verified: +6) Run the program. -- When it is opened, the app will download + + + +```bash +./src-tauri/target/release/SheetJSTauri +``` + + + + +```powershell +.\src-tauri\target\release\SheetJSTauri.exe +``` + + + + +The following features should be manually verified: + +- When it is loaded, the app will download and display the data in a table. - Clicking "Save Data" will show a save dialog. After selecting a path and name, the app will write a file. That file can be opened in a spreadsheet editor. diff --git a/docz/docs/03-demos/09-cloud/01-salesforce.md b/docz/docs/03-demos/09-cloud/01-salesforce.md index 43d34fe..c95b5cd 100644 --- a/docz/docs/03-demos/09-cloud/01-salesforce.md +++ b/docz/docs/03-demos/09-cloud/01-salesforce.md @@ -5,12 +5,20 @@ pagination_next: demos/extensions/index --- import current from '/version.js'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; -Salesforce apps can use third-party libraries in "Lightning Web Components". +[Salesforce](https://www.salesforce.com/) is a suite of cloud-based software +systems for Customer Relationship Management (CRM). "Lightning Web Components" +(LWC) is a robust JavaScript extension platform available to Salesforce apps[^1]. -This demo assumes familiarity with Lightning Web Components. Salesforce has a -[detailed introduction.](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_introduction) +[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing +data from spreadsheets. + +This demo explores the LWC scripting features in Salesforce. We'll explore how +to install SheetJS scripts in Lightning Web Components and build a sample app +for exporting lists to XLSX workbooks. :::caution pass @@ -21,191 +29,72 @@ may require some adjustments. The official documentation should be consulted. :::note -This demo was last tested on 2023 April 09 using Lightning API version `57.0`. +This demo was last tested on 2023 September 30 using Lightning API version `58.0`. ::: -## Getting Started +:::warning Telemetry -This demo was built on a "Developer Edition" account. At the time of writing, an -[account can be created for free.](https://developer.salesforce.com/signup) - -### Create Sample Project and Component - - - -Following the steps in ["Develop in Non-Scratch Orgs"](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_sfdx_deploy): - - +The Salesforce developer tools embed telemetry. It can be disabled by setting +the environment variable `SF_DISABLE_TELEMETRY` to `true` or by running ```bash -## Login -sfdx force:auth:web:login -d -a LWC-Hub - -## Create Sample Project and Component -sfdx force:project:create --projectname SheetForce -cd SheetForce -sfdx force:lightning:component:create --type lwc -n sheetComponent -d force-app/main/default/lwc +npx @salesforce/cli config set disable-telemetry=true --global ``` -By default, the component will not be available to app pages. A few files must -be changed: +::: -`force-app\main\default\lwc\sheetComponent\sheetComponent.html` add some HTML: +## Integration Details -```html title="force-app\main\default\lwc\sheetComponent\sheetComponent.html" - -``` +Lightning Web Components can load scripts stored in static resources. -`force-app\main\default\lwc\sheetComponent\sheetComponent.js-meta.xml` change -`isExposed` from `false` to `true` and add some metadata: - -```xml title="force-app\main\default\lwc\sheetComponent\sheetComponent.js-meta.xml" - - - 57.0 - - true - SheetForce - SheetJS Demo - - lightning__AppPage - - - -``` - -### Deploy Sample Project - -Deploy the project: - -```bash -sfdx force:source:deploy -p force-app -u SALESFORCE@USER.NAME # replace with actual username -``` - -The custom component can be found in Custom Code > Lightning Components. - -![Custom Component](pathname:///files/sfcustcomp.png) - -### Initialize App Page - -Create an "App Page" in the "Lightning App Builder". Instructions are included -in [Hello World in a Scratch Org](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_sfdx_hello_world) - -The following options should be set: -- The "App Page" option should be selected. -- The App Label should be set to "SheetJS Demo". -- The "One Region" layout should be selected. - -Under Custom components, you should see "SheetForce". Click and drag it into -the app builder main view to add it to the page. - -Click "Save" then click "Activate". The following options should be set: -- Click "Change..." next to "Icon" and pick a memorable icon -- Under "Lightning Experience" click "LightningBolt" then "Add page to app" - -Click "Save" to activate the page, then click the left arrow to return to Setup. - -Click the App Launcher and select "Bolt Solutions" then "SheetJS Demo". You -should see a page like - -![Demo](pathname:///files/sfinitial.png) - - -## Adding the Standalone Script +### Installation The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone) can be downloaded and added as a static resource. -Due to Salesforce name restrictions, the script must be renamed to `sheetjs.js` +:::info pass -

1) Download https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js

- -:::warning pass - -**DO NOT "COPY AND PASTE"!** The file should be explicitly downloaded. Copying -and pasting corrupts the source code and the component will fail in subtle ways. - -The easiest approach is to right-click the link and select "Save Link As..." +Due to Salesforce name restrictions, the script must be renamed to `sheetjs.js`. ::: -2) Move the file to the `force-app/main/default/staticresources/` folder and - rename the file to `sheetjs.js`. +### Loading SheetJS -3) Create `force-app/main/default/staticresources/sheetjs.resource-meta.xml`: +Assuming the script was renamed to `sheetjs.js`, the name of the resource will +be `sheetjs`. `async` functions can use `loadScript` to fetch and load the +library. The script will define the variable `XLSX`[^2] -```xml title="force-app/main/default/staticresources/sheetjs.resource-meta.xml" - - - Private - application/javascript - -``` - -4) Deploy the project again: - -```bash -sfdx force:source:deploy -p force-app -u SALESFORCE@USER.NAME # replace with actual username -``` - -:::note pass - -The official documentation recommends adding a static resource with a ZIP file. -That approach is not explored in this demo. - -::: - -Custom Code > Static Resources should now list `sheetjs`: - -![Static Resources](pathname:///files/sfstatic.png) - -### Test the Static Resource - -The script can be loaded from component code with: +It is recommended to load the library in a callback. For example, the following +`@api` method loads the library and exports sample data to a spreadsheet file: ```js -import sheetjs from '@salesforce/resourceUrl/sheetjs'; -``` - -The library includes a version number that can be displayed: - -1) Add a reference in `sheetComponent.js` and expose the `version` property: - -```js title="force-app/main/default/lwc/sheetComponent/sheetComponent.js" -import { LightningElement } from 'lwc'; +import { LightningElement, api } from 'lwc'; import { loadScript } from 'lightning/platformResourceLoader'; -// highlight-next-line import sheetjs from '@salesforce/resourceUrl/sheetjs'; export default class SheetComponent extends LightningElement { - version = "???"; // start with ??? - async connectedCallback() { - // highlight-next-line + @api async download() { await loadScript(this, sheetjs); // load the library // At this point, the library is accessible with the `XLSX` variable - this.version = XLSX.version; + + // Create worksheet + var ws = XLSX.utils.aoa_to_sheet([ + [ "S", "h", "e", "e", "t", "J"," S" ], + [ 5 , 4 , 3 , 3 , 7 , 9 , 5 ] + ]); + + // Create workbook and add worksheet + var wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, ws, "Data"); + + // Export Data + XLSX.writeFile(wb, "SheetForceExport.xlsx"); } } ``` -2) Reference the variable in `sheetComponent.html`: - -```html title="force-app/main/default/lwc/sheetComponent/sheetComponent.html" - -``` - -3) Deploy the project again and re-load the Bolt Solutions "SheetJS Demo" page: - -![Version number](pathname:///files/sfversion.png) - -## Exporting Data from SF Lists +### Exporting Data from SF List :::note pass @@ -214,7 +103,9 @@ There are many different data types and APIs. This demo uses the deprecated ::: -### Steps +Using the LWC Wire Service, components receive data in separate events. Exports +are typically generated in a separate event handler. Component state is normally +used to handle the timing mismatch. #### Getting Account Data @@ -222,6 +113,7 @@ The main method to obtain data is `getListUi` and the key for account data is `ACCOUNT_OBJECT`: ```js +import { LightningElement, wire } from 'lwc'; import { getListUi } from 'lightning/uiListApi'; import ACCOUNT_OBJECT from '@salesforce/schema/Account'; @@ -240,7 +132,7 @@ export default class SheetComponent extends LightningElement { } ``` -#### Generating an Array of Arrays +#### Array of Arrays SheetJS most reliably translates "arrays of arrays", a nested array which directly maps to individual cell addresses. For example: @@ -301,29 +193,447 @@ A suitable SheetJS array of arrays can be constructed by mapping across records: ```js var headers = [ "Name", "Phone" ]; - this.aoa = [headers].concat(data.records.records.map(record => [ + var aoa = [headers].concat(data.records.records.map(record => [ record.fields.Name.value, // Name field record.fields.Phone.value, // Phone field ])); ``` -This is readily exported to a spreadsheet in a callback function: +#### State + +This data is available in a wire service callback, but it is common to export +the data in a separate API event. This flow is handled with a state variable: + +```js +export default class SheetComponent extends LightningElement { + // highlight-next-line + aoa; // will hold data for export + @wire(getListUi, { + objectApiName: ACCOUNT_OBJECT.objectApiName, + listViewApiName: 'AllAccounts' + }) listInfo({ error, data }) { + if (data) { + var headers = [ "Name", "Phone" ]; + // create AOA + var _aoa = [headers].concat(data.records.records.map(record => [ + record.fields.Name.value, // Name field + record.fields.Phone.value, // Phone field + ])); + + // assign to state + // highlight-next-line + this.aoa = _aoa; + } else if (error) console.log(error); + }; +} +``` + +#### Exporting Data + +This is readily exported to a spreadsheet in a callback function. Starting from +the array of arrays, the SheetJS `aoa_to_sheet` method[^3] generates a SheetJS +sheet object[^4]. A workbook object[^5] is created with `book_new`[^6] and the +sheet is added with `book_append_sheet`[^7]. Finally, the SheetJS `writeFile` +method creates a XLSX file and initiates a download[^8]. ```js @api async download() { await loadScript(this, sheetjs); // load the library + // get data from state + // highlight-next-line + var _aoa = this.aoa; + // create workbook var wb = XLSX.utils.book_new(); - var ws = XLSX.utils.aoa_to_sheet(this.aoa); + var ws = XLSX.utils.aoa_to_sheet(_aoa); XLSX.utils.book_append_sheet(wb, ws, "Data"); + // export XLSX.writeFile(wb, "SheetForceExport.xlsx"); }; ``` -### Complete Example +## Complete Example -1) Add a button to `sheetComponent.html` that will call a `download` callback: +:::info pass + +This demo was built on a "Developer Edition" account. At the time of writing, an +[account can be created for free.](https://developer.salesforce.com/signup) + +::: + +0) Create a "Developer Edition" account. Take note of the unique Username + +### Configure Tools + +1) Install [NodeJS LTS](https://nodejs.org/en/download). + +2) Disable telemetry: + +```bash +npx @salesforce/cli config set disable-telemetry=true --global +``` + +3) Confirm the CLI tool works by checking version information: + +```bash +npx @salesforce/cli --version +``` + +:::note pass + +When the demo was last tested, the command printed + +``` +@salesforce/cli/2.10.2 darwin-x64 node-v16.20.2 +``` + +::: + +4) Log into the org from the CLI tool: + +```bash +npx @salesforce/cli org login web +``` + +This will open a web browser. Sign in and authorize the application. + +### Create Project + +5) Create the "SheetForce" sample project with the `project generate` command: + +```bash +npx @salesforce/cli project generate -n SheetForce +``` + +Enter the project directory: + +```bash +cd SheetForce +``` + +6) Create a LWC component with the `lightning generate component` command: + +```bash +npx @salesforce/cli lightning generate component --type lwc -n sheetComponent -d force-app/main/default/lwc +``` + +:::warning pass + +At the time of testing, the CLI tool created components with app version 59. +This version number is not valid for scratch orgs. + +**This is a bug in the Salesforce CLI** + +The workaround is to manually pin version 58 in the JSON and `meta.xml` files. + +::: + +7) Replace `force-app\main\default\lwc\sheetComponent\sheetComponent.html` with +the following template code: + +```html title="force-app\main\default\lwc\sheetComponent\sheetComponent.html" + +``` + +8) Replace `force-app\main\default\lwc\sheetComponent\sheetComponent.js-meta.xml` +with the following XML: + +```xml title="force-app\main\default\lwc\sheetComponent\sheetComponent.js-meta.xml" + + + + 58.0 + true + SheetForce + SheetJS Demo + + lightning__AppPage + + + +``` + +9) Edit `sfdx-version.json` and set the `sourceApiVersion` to `58.0`: + +```json title="sfdx-version.json" + "name": "SheetForce", + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", +// highlight-next-line + "sourceApiVersion": "58.0" +} +``` + +### Deploy Sample Project + +10) Deploy the project from the CLI. You will need the Salesforce unique +Username. For example, if the Username was `SF@USER.NAME`, the command is: + +```bash +npx @salesforce/cli project deploy start -d force-app -o SF@USER.NAME +``` + +11) Find the new component: + + + + +To find the new component in "Lightning Experience" view: + +A) In the Salesforce site, click on the gear icon in the top-right corner of the +page and select "Setup" (Setup for current app). + +B) Type "Custom Code" in the left sidebar search box. Expand "Custom Code", +expand "Lightning Components" and click "Lightning Components". + +:::caution pass + +With certain security settings, Salesforce will show an error: + +> We can't display this page because your browser blocks cross-domain cookies, but you can view this page in Salesforce Classic. + +Click the link to open the page in Salesforce Classic. + +::: + + + + + +A) Click the "Setup" link in the top-right corner of the page. + +B) Type "Lightning" in the left sidebar search box. Expand "Develop", expand +"Lightning Components" and click "Lightning Components". + + + + +The page in Salesforce Classic will look like the screenshot below: + +![Custom Component](pathname:///salesforce/custcomp.png) + +### Initialize App Page + +#### Create App Page + +12) Create an "App Page" in the "Lightning App Builder": + + + + +A) In the Salesforce site, click on the gear icon in the top-right corner of the +page and select "Setup" (Setup for current app). + +B) Type "App Build" in the left sidebar search box. Expand "User Interface" and +click "Lightning App Builder". + +:::caution pass + +With certain security settings, Salesforce will show an error: + +> We can't display this page because your browser blocks cross-domain cookies, but you can view this page in Salesforce Classic. + +Click the link to open the page in Salesforce Classic. + +::: + +C) Click the "New" button. + + + + +A) Click the "Setup" link in the top-right corner of the page. + +B) Type "App Build" in the left search box and select "Lightning App Builder". + +C) Click the "New" button. + + + + +#### App Wizard + +D) Select "App Page" in the left list and click "Next" + +E) Type "SheetJS Demo" in the Label textbox and click "Next" + +F) Select "One Region" in the left list and click "Done" + +#### App Builder + +13) Add the "SheetForce" component to the App Page. + +In the left "Components" sidebar, under the "Custom" section, there should be a +"SheetForce" entry. + +Click and drag "SheetForce" into the "Add Component(s) Here" frame in the app +builder main view to add it to the page. + +Click "Save". + +14) Activate the page. + +When the "Page Saved" modal is displayed, click "Activate". + +The following options should be set: +- Click "Change..." next to "Icon" and pick a memorable icon +- Under "Lightning Experience" click "LightningBolt" then "Add page to app" + +Click "Save" to activate the page. + +15) Open the demo page. + +Click the left arrow in the top-left corner of the page to return to Setup. + +If there is a "Switch to Lightning Experience" at the top, click the link. + +Click the App Launcher (`᎒᎒᎒`) and search for "SheetJS". Under "Items", the new +"SheetJS Demo" will be listed, Click "SheetJS Demo". + +The app will display the "SheetForce demo" text from the component template: + +![Demo](pathname:///salesforce/initial.png) + + +### Add SheetJS + +

16) Download https://cdn.sheetjs.com/xlsx-{current}/package/dist/xlsx.full.min.js

+ +:::warning pass + +**DO NOT "COPY AND PASTE"!** The file should be explicitly downloaded. Copying +and pasting corrupts the source code and the component will fail in subtle ways. + +The easiest approach is to right-click the link and select "Save Link As..." + +::: + +The following command can be run in PowerShell or `bash`: + +{`\ +curl -o xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`} + + + +17) Move the file to the `force-app/main/default/staticresources/` folder and +rename the file to `sheetjs.js`. + +If the file was downloaded from the previous command, `mv` can move and rename: + +```bash +mv xlsx.full.min.js force-app/main/default/staticresources/sheetjs.js +``` + +18) Create `force-app/main/default/staticresources/sheetjs.resource-meta.xml` +(`sheetjs.resource-meta.xml` in the folder from step 2) with the following XML: + +```xml title="force-app/main/default/staticresources/sheetjs.resource-meta.xml" + + + Private + application/javascript + +``` + +19) Deploy the project again. Replace `SF@USER.NAME` with the unique Username: + +```bash +npx @salesforce/cli project deploy start -d force-app -o SF@USER.NAME +``` + +20) Look for the static resource: + + + + +A) In the Salesforce site, click on the gear icon in the top-right corner of the +page and select "Setup" (Setup for current app). + +B) Type "Static" in the left sidebar search box. Click "Static Resources" + +:::caution pass + +With certain security settings, Salesforce will show an error: + +> We can't display this page because your browser blocks cross-domain cookies, but you can view this page in Salesforce Classic. + +Click the link to open the page in Salesforce Classic. + +::: + + + + +A) Click the "Setup" link in the top-right corner of the page. + +B) Type "Static" in the left sidebar search box. Click "Static Resources" + + + + +The page in Salesforce Classic will look like the screenshot below: + +![Static Resources](pathname:///salesforce/static.png) + +### Test the Static Resource + +21) Replace `force-app/main/default/lwc/sheetComponent/sheetComponent.js` with +the following script: + +```js title="force-app/main/default/lwc/sheetComponent/sheetComponent.js" +import { LightningElement } from 'lwc'; +import { loadScript } from 'lightning/platformResourceLoader'; +import sheetjs from '@salesforce/resourceUrl/sheetjs'; + +export default class SheetComponent extends LightningElement { + version = "???"; // start with ??? + async connectedCallback() { + await loadScript(this, sheetjs); // load the library + // At this point, the library is accessible with the `XLSX` variable + // highlight-next-line + this.version = XLSX.version; + } +} +``` + +This component exposes a `version` property based on the SheetJS version. + +22) Replace `force-app/main/default/lwc/sheetComponent/sheetComponent.html` with +the following template: + +```html title="force-app/main/default/lwc/sheetComponent/sheetComponent.html" + +``` + +This template references the `version` property. + +23) Deploy the project again. Replace `SF@USER.NAME` with the unique Username: + +```bash +npx @salesforce/cli project deploy start -d force-app -o SF@USER.NAME +``` + +24) Reload the "SheetJS Demo" page. The page should display the SheetJS version: + +![Version number](pathname:///salesforce/version.png) + +:::info pass + +It may take a few minutes for Salesforce to refresh. If the demo still shows the +original "SheetForce demo" text after refreshing, close and reopen the demo app. + +::: + +### Export Data from SF Lists + +25) Add a button to `sheetComponent.html` that will call a `download` callback: ```html title="force-app/main/default/lwc/sheetComponent/sheetComponent.html" ``` -2) Replace `sheetComponent.js` with the following: +26) Replace `sheetComponent.js` with the following: ```js title="force-app/main/default/lwc/sheetComponent/sheetComponent.js" import { LightningElement, wire, api } from 'lwc'; @@ -373,13 +683,21 @@ export default class SheetComponent extends LightningElement { } ``` -3) Re-deploy and refresh the app page: +27) Deploy the project again. Replace `SF@USER.NAME` with the unique Username: -![SF Export Button](pathname:///files/sfexport.png) +```bash +npx @salesforce/cli project deploy start -d force-app -o SF@USER.NAME +``` -The simple export has all of the data: +28) Reload the "SheetJS Demo" page. The page should include a button for export: -![Excel Export](pathname:///files/sfxlexport.png) +![SF Export Button](pathname:///salesforce/export.png) + +29) Click the "Click to Export!" button. The app will attempt to download a file. + +The simple export includes all of the data: + +![Excel Export](pathname:///salesforce/xl.png) :::tip pass @@ -387,3 +705,12 @@ The simple export has all of the data: cell styling, automatic column width calculations, and frozen rows. ::: + +[^1]: It is strongly recommended to review the [detailed introduction in the Salesforce documentation](https://developer.salesforce.com/docs/platform/lwc/guide/get-started-introduction.html) +[^2]: The `XLSX` variable is the main global for the SheetJS library. It exposes methods as described in ["API Reference"](/docs/api/) +[^3]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input) +[^4]: See ["Sheet Objects"](/docs/csf/sheet) +[^5]: See ["Workbook Object"](/docs/csf/book) +[^6]: See [`book_new` in "Utilities"](/docs/api/utilities/wb) +[^7]: See [`book_append_sheet` in "Utilities"](/docs/api/utilities/wb) +[^8]: See [`writeFile` in "Writing Files"](/docs/api/write-options) \ No newline at end of file diff --git a/docz/docs/03-demos/09-cloud/11-aws.md b/docz/docs/03-demos/09-cloud/11-aws.md index c100c10..01bb6be 100644 --- a/docz/docs/03-demos/09-cloud/11-aws.md +++ b/docz/docs/03-demos/09-cloud/11-aws.md @@ -7,8 +7,22 @@ pagination_next: demos/extensions/index import current from '/version.js'; import CodeBlock from '@theme/CodeBlock'; -AWS is a Cloud Services platform which includes traditional virtual machine -support, "Serverless Functions", cloud storage and much more. +[Amazon Web Services](https://aws.amazon.com/) (AWS) is a Cloud Services +platform which includes traditional virtual machine support, "Serverless +Functions" and cloud storage. + +[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing +data from spreadsheets. + +This demo explores two key AWS offerings: + +- ["Lambda Functions"](#lambda-functions) ("Lambda") explores the serverless + computing offering. The demo creates a JavaScript function that can process + user-submitted files and generate spreadsheets. + +- ["S3 Storage"](#s3-storage) explores the cloud storage ("S3") offering. The + demo uses the NodeJS connection library to read spreadsheets from S3 and write + spreadsheets to a S3 bucket. :::caution pass @@ -17,30 +31,86 @@ will be available in the future. ::: -This demo focuses on two key offerings: cloud storage ("S3") and the -"Serverless Function" platform ("Lambda"). - -The [NodeJS Module](/docs/getting-started/installation/nodejs) can be shipped in -a bundled Lambda function. - :::note -This was tested on 2023 April 24. +This demo was last tested on 2023 October 01. ::: -## AWS Lambda Functions +## Lambda Functions + +AWS offers the NodeJS runtime for JavaScript serverless function.[^1] + +The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be +required in Lambda functions. When deploying, the entire `node_modules` folder +can be added to the ZIP package. + +:::note pass In this demo, the "Function URL" (automatic API Gateway management) features are used. Older deployments required special "Binary Media Types" to handle formats like XLSX. At the time of testing, the configuration was not required. +::: + +:::info pass + +Node.js runtime can use `x86_64` or `arm64` CPU architectures. SheetJS libraries +work on both platforms in Linux, Windows, and macOS operating systems. + +::: + ### Reading Data -In the Lambda handler method, the `event.body` attribute is a Base64-encoded -string. The `busboy` body parser can accept a decoded body. +In the Lambda handler, the `event.body` attribute is a Base64-encoded string +representing the HTTP request form data. This body must be parsed. -
Code Sample (click to hide) +#### Processing Form Bodies + +The `busboy` body parser[^2] is battle-tested in NodeJS deployments. + +`busboy` fires a `'file'` event for every file in the form body. The callback +receives a NodeJS stream that should be collected into a Buffer: + +```js +/* accumulate the files manually */ +var files = {}; +bb.on('file', function(fieldname, file, filename) { + /* concatenate the individual data buffers */ + var buffers = []; + file.on('data', function(data) { buffers.push(data); }); + file.on('end', function() { files[fieldname] = Buffer.concat(buffers); }); +}); +``` + +`busboy` fires a `'finish'` event when the body parsing is finished. Callbacks +can assume every file in the form body has been stored in NodeJS Buffer objects. + +#### Processing NodeJS Buffers + +The SheetJS `read` method[^3] can read the Buffer objects and generate SheetJS +workbook objects[^4] which can be processed with other API functions. + +For example, a handler can use `sheet_to_csv`[^5] to generate CSV text: + +```js +/* on the finish event, all of the fields and files are ready */ +bb.on('finish', function() { + /* grab the first file */ + var f = files["upload"]; + if(!f) callback(new Error("Must submit a file for processing!")); + + /* f[0] is a buffer */ + // highlight-next-line + var wb = XLSX.read(f[0]); + + /* grab first worksheet and convert to CSV */ + var ws = wb.Sheets[wb.SheetNames[0]]; + callback(null, { statusCode: 200, body: XLSX.utils.sheet_to_csv(ws) }); +}); +``` + +
Complete Code Sample (click to show) This example takes the first uploaded file submitted with the key `upload`, parses the file and returns the CSV content of the first worksheet. @@ -92,12 +162,38 @@ exports.handler = function(event, context, callback) { ### Writing Data -For safely transmitting binary data, the `base64` type should be used. Lambda -callback response `isBase64Encoded` property forces a binary download. +For safely transmitting binary data, Base64 strings should be used. -
Code Sample (click to hide) +The SheetJS `write` method[^6] with the option `type: "base64"` will generate +Base64-encoded strings. -This example generates a sample workbook and writes to a XLSX workbook. +```js +/* sample SheetJS workbook object */ +var wb = XLSX.read("S,h,e,e,t,J,S\n5,4,3,3,7,9,5", {type: "binary"}); +/* write to XLSX file in Base64 encoding */ +var b64 = XLSX.write(wb, { type: "base64", bookType: "xlsx" }); +``` + +The Lambda callback response function accepts options. Setting `isBase64Encoded` +to `true` will ensure the callback handler decodes the data. To ensure browsers +will try to download the response, the `Content-Disposition` header must be set: + +```js +callback(null, { + statusCode: 200, + /* Base64-encoded file */ + isBase64Encoded: true, + body: b64, + headers: { + /* Browsers will treat the response as the file SheetJSLambda.xlsx */ + "Content-Disposition": 'attachment; filename="SheetJSLambda.xlsx"' + } +}); +``` + +
Complete Code Sample (click to show) + +This example creates a sample workbook object and sends the file in the response: ```js var XLSX = require('xlsx'); @@ -123,11 +219,18 @@ exports.handler = function(event, context, callback) {
-### Demo +### Lambda Demo -
Complete Example (click to hide) +:::note pass -0) Review the quick start for JavaScript on AWS +At the time of writing, the AWS Free Tier included an allowance of 1 million +free requests per month and 400 thousand GB-seconds of compute resources. + +::: + +0) If you do not have an account, create a new AWS free tier account[^7]. + +#### Create Project ZIP 1) Create a new folder and download [`index.js`](pathname:///aws/index.js): @@ -150,28 +253,67 @@ npm i https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz busboy`} yes | zip -c ../SheetJSLambda.zip -r . ``` -4) In the web interface for AWS Lambda, create a new Function with the options: +#### Lambda Setup -- Select "Author from scratch" (default choice when last verified) -- "Function Name": SheetJSLambda -- "Runtime": "Node.js" (select the version in the "Latest supported" block) -- Advanced Settings: - + check "Enable function URL" - + Auth type: NONE +4) Sign into the [AWS Management Console](https://aws.amazon.com/console/) with +a root user account. + +5) Type "Lambda" in the top search box and click Lambda (under Services). + +6) Open "Functions" in the left sidebar. + +If the left sidebar is not open, click the `≡` icon in the left edge of the page. + +7) Click the "Create function" button in the main panel. + +8) Select the following options: + +- In the top list, select "Author from scratch" (default choice) + +- Type a memorable "Function Name" ("SheetJSLambda" when last tested) + +- In the "Runtime" dropdown, look for the "Latest supported" section and select + "Node.js" ("Node.js 18.x" when last tested) + +- Expand "Advanced Settings" and check "Enable function URL". This will display + a few sub-options: + + "Auth type" select "NONE" (disable IAM authentication) + Check "Configure cross-origin resource sharing (CORS)" -5) In the Interface, click "Upload from" and select ".zip file". Click the -"Upload" button in the modal, select SheetJSLambda.zip, and click "Save". +9) Click "Create function" to create the function. +#### Upload Code + +10) In the Interface, scroll down and select the "Code" tab. + +11) Click the "Upload from" dropdown and select ".zip file". + +12) Click the "Upload" button in the modal. With the file picker, select the +`SheetJSLambda.zip` file created in step 3. Click "Save". + +:::note pass When the demo was last tested, the ZIP was small enough that the Lambda code editor will load the package. -6) Enable external access to the function. +::: -Under Configuration > Function URL, click "Edit" and ensure that Auth type is -set to NONE. If it is not, select NONE and click Save. +13) In the code editor, double-click `index.js` and confirm the code editor +displays JavaScript code. + +#### External Access + +14) Click "Configuration" in the tab list. + +15) In the sidebar below the tab list, select "Function URL" and click "Edit". + +16) Set the "Auth type" to "NONE" and click Save. The page will redirect to the +Function properties. + +17) Select the "Configuration" tab and select "Permissions" in the left sidebar. + +18) Scroll down to "Resource-based policy statements" and ensure that +`FunctionURLAllowPublicAccess` is listed. -Under Configuration > Permissions, scroll down to "Resource-based policy". If no policy statements are defined, select "Add Permission" with the options: - Select "Function URL" at the top @@ -182,110 +324,260 @@ If no policy statements are defined, select "Add Permission" with the options: Click "Save" and a new Policy statement should be created. -7) Find the Function URL (It is in the "Function Overview" section). +#### Lambda Testing -Try to access that URL in a web browser and the site will try to download -`SheetJSLambda.xlsx`. Save and open the file to confirm it is valid. +19) Find the Function URL (It is in the "Function Overview" section). -To test parsing, download and make a POST -request to the public function URL (change `FUNCTION_URL` in the command): +20) Try to access the function URL in a web browser. + +The site will attempt to download `SheetJSLambda.xlsx`. Save and open the file +to confirm it is valid. + +21) Download and make a POST request to the +public function URL. + +This can be tested on the command line. Change `FUNCTION_URL` in the commands: ```bash +curl -LO https://sheetjs.com/pres.numbers curl -X POST -F "upload=@pres.numbers" FUNCTION_URL ``` -The result should be a CSV output of the first sheet. - -
+The terminal will display CSV output of the first sheet. ## S3 Storage -The main module for S3 and all AWS services is `aws-sdk`. +The main NodeJS module for S3 and all AWS services is `aws-sdk`[^8]. + +The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be +required in NodeJS scripts. + +### Connecting to S3 + +The `AWS` module includes a function `S3` that performs the connection. Access +keys for an IAM user[^9] must be used: + +```js +/* credentials */ +var accessKeyId = "...", secretAccessKey = "...""; + +/* file location */ +var Bucket = "...", Key = "pres.numbers"; + +/* connect to s3 account */ +var AWS = require('aws-sdk'); +var s3 = new AWS.S3({ + apiVersion: '2006-03-01', + credentials: { accessKeyId, secretAccessKey } +}); + +``` ### Reading Data +#### Fetching Files from S3 + The `s3#getObject` method returns an object with a `createReadStream` method. -Buffers can be concatenated and passed to `XLSX.read`. +`createReadStream` returns a NodeJS stream: -
Demo (click to hide) +```js +/* open stream to the file */ +var stream = s3.getObject({ Bucket: Bucket, Key: Key }).createReadStream(); +``` -This sample fetches a buffer from S3 and parses the workbook. +#### Concatenating NodeJS Streams -1) Save the following script to `SheetJSReadFromS3.js`: +Buffers can be concatenated from the stream into one unified Buffer object: -```js title="SheetJSReadFromS3.js" -var XLSX = require("xlsx"); -var AWS = require('aws-sdk'); - -/* replace these constants */ -var accessKeyId = ""; -var secretAccessKey = ""; -var Bucket = ""; - -var Key = "pres.numbers"; - -/* Get stream */ -var s3 = new AWS.S3({ - apiVersion: '2006-03-01', - credentials: { - accessKeyId: accessKeyId, - secretAccessKey: secretAccessKey - } -}); -var f = s3.getObject({ Bucket: Bucket, Key: Key }).createReadStream(); - -/* collect data */ +```js +/* array of buffers */ var bufs = []; -f.on('data', function(data) { bufs.push(data); }); -f.on('end', function() { - /* concatenate and parse */ - var wb = XLSX.read(Buffer.concat(bufs)); - console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])); +/* add each data chunk to the array */ +stream.on('data', function(data) { bufs.push(data); }); +/* the callback will be called after all of the data is collected */ +stream.on('end', function() { + /* concatenate */ + var buf = Buffer.concat(bufs); + + /* AT THIS POINT, `buf` is a NodeJS Buffer */ }); ``` -2) Create a new bucket (or get the name of an existing bucket). +#### Parsing NodeJS Buffers -3) Download +The SheetJS `read` method[^10] can read the final object and generate SheetJS +workbook objects[^11] which can be processed with other API functions. -In the S3 site, open the bucket and click "Upload". In the Upload page, click -and drag the `pres.numbers` file into the browser window and click "Upload". +For example, a callback can use `sheet_to_csv`[^12] to generate CSV text: -4) Edit `SheetJSReadFromS3.js` and replace the marked constants: +```js +stream.on('end', function() { + /* concatenate */ + var buf = Buffer.concat(bufs); -- `accessKeyId`: access key for the AWS account -- `secretAccessKey`: secret access key for the AWS account -- `Bucket`: name of the bucket + /* parse */ + var wb = XLSX.read(Buffer.concat(bufs)); -5) Run the script: - -```bash -node SheetJSReadFromS3.js + /* generate CSV from first worksheet */ + var first_ws = wb.Sheets[wb.SheetNames[0]]; + var csv = XLSX.utils.sheet_to_csv(first_ws); + console.log(csv); +}); ``` -The program will display the data in CSV format. - -
- ### Writing Data -`S3#upload` directly accepts a Buffer. +The SheetJS `write` method[^13] with the option `type: "buffer"` will generate +NodeJS Buffers. `S3#upload` directly accepts these Buffer objects. -
Demo (click to hide) +This example creates a sample workbook object, generates XLSX file data in a +NodeJS Buffer, and uploads the data to S3: + +```js +/* generate sample workbook */ +var wb = XLSX.read("S,h,e,e,t,J,S\n5,4,3,3,7,9,5", {type: "binary"}); + +/* write to XLSX file in a NodeJS Buffer */ +var Body = XLSX.write(wb, {type: "buffer", bookType: "xlsx"}); + +/* upload buffer */ +s3.upload({ Bucket, Key, Body }, function(err, data) { + if(err) throw err; + console.log("Uploaded to " + data.Location); +}); +``` + +### S3 Demo + +:::note pass + +At the time of writing, the AWS Free Tier included 5GB of S3 storage with 20,000 +Get requests and 2000 Put requests per month. + +::: + +This sample fetches a buffer from S3 and parses the workbook. + +0) If you do not have an account, create a new AWS free tier account[^14]. + +#### Create S3 Bucket + +1) Sign into the [AWS Management Console](https://aws.amazon.com/console/) with +a root user account. + +2) Type "S3" in the top search box and click S3 (under Services). + +3) Open "Buckets" in the left sidebar. + +If the left sidebar is not open, click the `≡` icon in the left edge of the page. + +4) Click the "Create bucket" button in the main panel. + +5) Select the following options: + +- Type a memorable "Bucket Name" ("sheetjsbouquet" when last tested) + +- In the "Object Ownership" section, select "ACLs disabled" + +- Check "Block *all* public access" + +- Look for the "Bucket Versioning" section and select "Disable" + +6) Click "Create bucket" to create the bucket. + +#### Create IAM User + +7) Type "IAM" in the top search box and click IAM (under Services). + +8) Open "Users" in the left sidebar. + +If the left sidebar is not open, click the `≡` icon in the left edge of the page. + +9) Click the "Create user" button in the main panel. + +10) In step 1, type a memorable "Bucket Name" ("sheetjs-user" when last tested). +Click "Next". + +11) In step 2, click "Next" + +12) In step 3, click "Create user" to create the user. + +#### Add Permissions + +13) Click the new user name in the Users table. + +14) Select the "Permissions" tab + +15) Click the "Add permissions" dropdown and select "Add permissions". + +16) Select "Attach policies directly". + +17) In the "Permissions policies" section, search for "AmazonS3FullAccess". +There should be one entry. + +18) Check the checkbox next to "AmazonS3FullAccess" and click the "Next" button. + +19) In the "Review" screen, click "Add permissions" + +#### Generate Keys + +20) Click "Security credentials", then click "Create access key". + +21) Select the "Local code" option. Check "I understand the above recommendation +and want to proceed to create an access key." and click "Next" + +22) Click "Create Access Key" and click "Download .csv file" in the next screen. + +In the generated CSV: + +- Cell A2 is the "Access key ID" (`accessKeyId` in the AWS API) +- Cell B2 is the "Secret access key" (`secretAccessKey` in the AWS API) + +#### Set up Project + +23) Create a new NodeJS project: + +```bash +mkdir SheetJSS3 +cd SheetJSS3 +npm init -y +``` + +24) Install dependencies: + +{`\ +mkdir -p node_modules +npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz aws-sdk@2.1467.0`} + + +#### Write Test + +:::note pass This sample creates a simple workbook, generates a NodeJS buffer, and uploads the buffer to S3. -1) Save the following script to `SheetJSWriteToS3.js`: +``` + | A | B | C | D | E | F | G | +---+---|---|---|---|---|---|---| + 1 | S | h | e | e | t | J | S | + 2 | 5 | 4 | 3 | 3 | 7 | 9 | 5 | +``` + +::: + +25) Save the following script to `SheetJSWriteToS3.js`: ```js title="SheetJSWriteToS3.js" var XLSX = require("xlsx"); var AWS = require('aws-sdk'); /* replace these constants */ +// highlight-start var accessKeyId = ""; var secretAccessKey = ""; var Bucket = ""; +// highlight-end var Key = "test.xlsx"; @@ -308,21 +600,94 @@ s3.upload({ Bucket: Bucket, Key: Key, Body: Body }, function(err, data) { }); ``` -2) Create a new bucket (or get the name of an existing bucket). - -3) Edit `SheetJSWriteToS3.js` and replace the marked constants: +26) Edit `SheetJSWriteToS3.js` and replace the highlighted lines: - `accessKeyId`: access key for the AWS account - `secretAccessKey`: secret access key for the AWS account - `Bucket`: name of the bucket -4) Run the script: +The keys are found in the CSV from step 22. The Bucket is the name from step 5. + +27) Run the script: ```bash node SheetJSWriteToS3.js ``` -5) In the S3 site, select the bucket and download the object named `test.xlsx`. -Open the file in a spreadsheet editor. +This file will be stored with the object name `test.xlsx`. It can be manually +downloaded from the S3 web interface. -
\ No newline at end of file +#### Read Test + +This sample will download and process the test file from "Write Test". + +28) Save the following script to `SheetJSReadFromS3.js`: + +```js title="SheetJSReadFromS3.js" +var XLSX = require("xlsx"); +var AWS = require('aws-sdk'); + +/* replace these constants */ +// highlight-start +var accessKeyId = ""; +var secretAccessKey = ""; +var Bucket = ""; +// highlight-end + +var Key = "test.xlsx"; + +/* Get stream */ +var s3 = new AWS.S3({ + apiVersion: '2006-03-01', + credentials: { + accessKeyId: accessKeyId, + secretAccessKey: secretAccessKey + } +}); +var f = s3.getObject({ Bucket: Bucket, Key: Key }).createReadStream(); + +/* collect data */ +var bufs = []; +f.on('data', function(data) { bufs.push(data); }); +f.on('end', function() { + /* concatenate and parse */ + var wb = XLSX.read(Buffer.concat(bufs)); + console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])); +}); +``` + +29) Edit `SheetJSReadFromS3.js` and replace the highlighted lines: + +- `accessKeyId`: access key for the AWS account +- `secretAccessKey`: secret access key for the AWS account +- `Bucket`: name of the bucket + +The keys are found in the CSV from Step 22. The Bucket is the name from Step 5. + +30) Run the script: + +```bash +node SheetJSReadFromS3.js +``` + +The program will display the data in CSV format. + +``` +S,h,e,e,t,J,S +5,4,3,3,7,9,5 +``` + +[^1]: See ["Building Lambda functions with Node.js"](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html) in the AWS documentation +[^2]: The `busboy` module is distributed [on the public NPM registry](https://npm.im/busboy) +[^3]: See [`read` in "Reading Files"](/docs/api/parse-options) +[^4]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details. +[^5]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output) +[^6]: See [`write` in "Writing Files"](/docs/api/write-options) +[^7]: Registering for a free account [on the AWS Free Tier](https://aws.amazon.com/free/) requires a valid phone number and a valid credit card. +[^8]: The `aws-sdk` module is distributed [on the public NPM registry](https://npm.im/aws-sdk) +[^9]: See ["Managing access keys for IAM users"](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) in the AWS documentation +[^10]: See [`read` in "Reading Files"](/docs/api/parse-options) +[^11]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details. +[^12]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output) +[^13]: See [`write` in "Writing Files"](/docs/api/write-options) +[^14]: Registering for a free account [on the AWS Free Tier](https://aws.amazon.com/free/) requires a valid phone number and a valid credit card. \ No newline at end of file diff --git a/docz/docs/03-demos/09-cloud/12-azure.md b/docz/docs/03-demos/09-cloud/12-azure.md index 2ac2a24..bf71e7b 100644 --- a/docz/docs/03-demos/09-cloud/12-azure.md +++ b/docz/docs/03-demos/09-cloud/12-azure.md @@ -22,7 +22,7 @@ and the "Serverless Function" platform ("Azure Functions"). :::note -This was tested on 2023 April 29. +This demo was last tested on 2023 April 29. ::: diff --git a/docz/docs/09-miscellany/05-contributing.md b/docz/docs/09-miscellany/05-contributing.md index 622c272..e939e1c 100644 --- a/docz/docs/09-miscellany/05-contributing.md +++ b/docz/docs/09-miscellany/05-contributing.md @@ -41,8 +41,8 @@ These instructions were tested on the following platforms: |:------------------------------|:-----------| | Linux (Steam Deck Holo x64) | 2023-09-22 | | Linux (Ubuntu 18 AArch64) | 2023-09-07 | -| MacOS 10.13 (x64) | 2023-04-04 | -| MacOS 13.0 (ARM64) | 2023-04-13 | +| MacOS 10.13.6 (x64) | 2023-09-30 | +| MacOS 13.6 (ARM64) | 2023-09-30 | | Windows 10 (x64) + WSL Ubuntu | 2023-07-23 | | Windows 11 (x64) + WSL Ubuntu | 2023-08-31 | | Windows 11 (ARM) + WSL Ubuntu | 2023-09-18 | @@ -59,7 +59,7 @@ tests will pass in Windows XP with NodeJS 5.10.0. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - + A) Ensure WSL ("WSL 2" in Windows 10) and the Ubuntu distribution are installed. @@ -97,7 +97,7 @@ C) Install NodeJS :::info pass -When this was last tested, the script showed a deprecation notice. +In the most recent test, the script showed a deprecation notice. **The script worked as expected.** @@ -164,10 +164,12 @@ sudo apt-get install -y unzip -A) Run `git`. If Xcode or the command-line tools are not installed, you will be -asked to install. Click "Install" and run through the steps. +A) Open a terminal window and run `git`. -B) Open a terminal window and install Homebrew: +If Xcode or the command-line tools are not installed, you will be asked to +install. Click "Install" and run through the steps. + +B) Open a terminal window and install the Homebrew package manager: ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" @@ -185,7 +187,7 @@ To confirm analytics are disabled, run brew analytics state ``` -It should print `Analytics are disabled.` +The message should state that analytics are disabled or destroyed. D) Install Mercurial and Subversion: @@ -193,15 +195,21 @@ D) Install Mercurial and Subversion: brew install mercurial subversion ``` -E) Install NodeJS +E) Install NodeJS. :::note pass [The official NodeJS site](https://nodejs.org/en/download/) provides installers for "LTS" and "Current" releases. The "LTS" version should be installed. -Older versions of macOS are not compatible with newer versions of NodeJS. In -local testing, macOS 10.13 required NodeJS version `12.22.12` +**Older versions of macOS are not compatible with newer versions of NodeJS.** + +In local testing, macOS 10.13 required NodeJS version `12.22.12`: + +```bash +curl -LO https://nodejs.org/download/release/v12.22.12/node-v12.22.12.pkg +open node-v12.22.12.pkg +``` ::: @@ -289,6 +297,28 @@ git clone https://git.sheetjs.com/sheetjs/sheetjs cd sheetjs ``` +:::note pass + +On older platforms, the clone may fail due to SSL certificate problems: + +```bash +fatal: unable to access 'https://git.sheetjs.com/sheetjs/sheetjs/': SSL certificate problem: certificate has expired +``` + +The simplest workaround is to disable SSL verification: + +```bash +git config --global http.sslVerify false +``` + +**It is strongly recommended to re-enable SSL verification after cloning**: + +```bash +git config --global http.sslVerify true +``` + +::: + 1) Install NodeJS modules for building the scripts: ```bash @@ -334,6 +364,27 @@ make test_misc git checkout -- . ``` +:::info pass + +In some tests on older releases of macOS, the build failed with an error: + +```bash +ReferenceError: n is not defined +``` + +The first error in the call stack points to `dist/xlsx.zahl.js`. + +Older versions of macOS `sed` are known to misinterpret newline characters. The +workaround is to upgrade to a newer version of `sed`. On macOS: + +```bash +brew install gnu-sed +echo 'export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"' >> ~/.profile +. ~/.profile +``` + +::: + ### Reproduce official builds 5) Run `git log` and search for the commit that matches a particular release @@ -374,21 +425,71 @@ make dist The local checksum for the browser script can be computed with: + + + ```bash -$ md5sum dist/xlsx.full.min.js -0b2f539797f92d35c6394274818f2c22 dist/xlsx.full.min.js +md5sum dist/xlsx.full.min.js ``` + + + +```bash +md5 dist/xlsx.full.min.js +``` + + + + +```bash +md5sum dist/xlsx.full.min.js +``` + + + + + The checksum for the CDN version can be computed with: + + + ```bash -$ curl -L https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js | md5sum - -0b2f539797f92d35c6394274818f2c22 - +curl -L https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js | md5sum - +``` + + + + +```bash +curl -k -L https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js | md5 +``` + + + + +```bash +curl -L https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js | md5sum - +``` + + + + +When the demo was last tested on macOS, against version `0.20.0`: + +```bash +$ md5 dist/xlsx.full.min.js +# highlight-next-line +MD5 (dist/xlsx.full.min.js) = 0b2f539797f92d35c6394274818f2c22 +$ curl -k -L https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js | md5 +# highlight-next-line +0b2f539797f92d35c6394274818f2c22 ``` The two hashes should match. -9) To return to the HEAD commit, run +9) Return to the `HEAD` commit: ```bash git checkout master diff --git a/docz/static/files/sfexport.png b/docz/static/files/sfexport.png deleted file mode 100644 index 974cdd3..0000000 Binary files a/docz/static/files/sfexport.png and /dev/null differ diff --git a/docz/static/files/sfversion.png b/docz/static/files/sfversion.png deleted file mode 100644 index 282d308..0000000 Binary files a/docz/static/files/sfversion.png and /dev/null differ diff --git a/docz/static/files/sfcustcomp.png b/docz/static/salesforce/custcomp.png similarity index 100% rename from docz/static/files/sfcustcomp.png rename to docz/static/salesforce/custcomp.png diff --git a/docz/static/salesforce/export.png b/docz/static/salesforce/export.png new file mode 100644 index 0000000..748d620 Binary files /dev/null and b/docz/static/salesforce/export.png differ diff --git a/docz/static/files/sfinitial.png b/docz/static/salesforce/initial.png similarity index 100% rename from docz/static/files/sfinitial.png rename to docz/static/salesforce/initial.png diff --git a/docz/static/files/sfstatic.png b/docz/static/salesforce/static.png similarity index 100% rename from docz/static/files/sfstatic.png rename to docz/static/salesforce/static.png diff --git a/docz/static/salesforce/version.png b/docz/static/salesforce/version.png new file mode 100644 index 0000000..2b83a09 Binary files /dev/null and b/docz/static/salesforce/version.png differ diff --git a/docz/static/files/sfxlexport.png b/docz/static/salesforce/xl.png similarity index 100% rename from docz/static/files/sfxlexport.png rename to docz/static/salesforce/xl.png