icyrock.com
HomePureScript experiments - jszip
2017-Jun-26 20:10
JSZip is a pure Javascript library for working with zip files. Here's how it can be used from Purescript.
First, create and initialize a Purescript project and install libraries we will need:
1 2 3 4 5 6 | $ mkdir purescript-jszip $ cd purescript-jszip $ pulp init $ bower install --save purescript-aff-promise purescript-node-fs-aff $ npm init -y $ npm install --save jszip |
Then create a Javascript part of the foreign interface in src/Jsz.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | const toJson = function (obj) { return JSON.stringify(obj, null , 2) } exports.jsZipToJson = toJson exports.zipObjectToJson = toJson exports.file = function (jsZip) { return function (name) { return function () { return jsZip.file(name) } } } exports.loadAsyncPromise = function (jsZip) { return function (data) { return jsZip.loadAsync(data) } } exports.newJsZip = function () { const JsZip = require( 'jszip' ) return new JsZip() } exports.asyncPromise = function (zipObject) { return zipObject.async( 'nodebuffer' ) } |
The defined functions are:
- jsZipToJson and zipObjectToJson are helper functions to stringify these two types, used for Show instance
- file is a function that will select a file from the zip by name
- loadAsyncPromise would return a promise of a zip file loaded from the given buffer
- newJsZip returns a fresh JSZip object
- asyncPromise returns a promise of a ZipObject's contents, in the form of a node's buffer object
After that, create the Purescript counterpart, src/Jsz.purs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | module Jsz where import Prelude import Control . Monad . Aff (Aff) import Control . Monad . Eff ( Eff ) import Control . Promise as Promise import Control . Promise (Promise) import Node . Buffer (Buffer) foreign import data JsZip :: Type foreign import jsZipToJson :: JsZip - > String instance jsZipShow :: Show JsZip where show = jsZipToJson foreign import data ZipObject :: Type foreign import zipObjectToJson :: ZipObject - > String instance zipObjectShow :: Show ZipObject where show = zipObjectToJson foreign import file :: forall eff . JsZip - > String - > Eff eff ZipObject foreign import loadAsyncPromise :: JsZip - > Buffer - > Promise JsZip loadAsync :: forall eff . JsZip - > Buffer - > Aff eff JsZip loadAsync jz buf = Promise . toAff $ loadAsyncPromise jz buf foreign import newJsZip :: forall eff . Eff eff JsZip foreign import asyncPromise :: ZipObject - > Promise Buffer async :: forall eff . ZipObject - > Aff eff Buffer async zo = Promise . toAff $ asyncPromise zo |
Here we see the same functions repeated as foreign imports. On top of that, we have:
- Data definitions for JsZip and ZipObject and their Show instances
- Helper functions loadAsync and async, which convert Promise to Aff, to make it easier to use from the rest of the Purescript code
Finally, create a sample example in src/Main.purs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | module Main where import Prelude import Control . Monad . Aff (runAff) import Control . Monad . Eff ( Eff ) import Control . Monad . Eff . Class (liftEff) import Control . Monad . Eff . Console (CONSOLE, log , logShow) import Control . Monad . Eff . Exception (EXCEPTION) import Jsz (async, file, loadAsync, newJsZip) import Node . Buffer (BUFFER, toString) import Node . Encoding (Encoding( .. )) import Node . FS (FS) import Node . FS . Aff ( readFile ) main :: forall eff . Eff ( buffer :: BUFFER , console :: CONSOLE , exception :: EXCEPTION , fs :: FS | eff) Unit main = void $ runAff logShow logShow do bufZip < - readFile "var/sample.zip" jz1 < - liftEff $ newJsZip jz2 < - loadAsync jz1 bufZip zo < - liftEff $ file jz2 "sample.txt" bufTxt < - async zo str < - liftEff $ toString UTF8 bufTxt liftEff $ log str |
Here we use runAff to run the rest in Aff monad. This allows for logging exceptions if there are any. The steps are:
- Load a sample zip file as a Buffer object
- Create a new JsZip object
- Load the buffer into JsZip
- Get the ZipObject corresponding to a file named 'sample.txt'
- Get the contents of the file as another Buffer object
- Convert the buffer to a String
- Log the string to the console
Sample setup and invocation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ mkdir var $ cd var $ cat > sample.txt When you come to a fork in a road, take it. -- Yogi Berra ^D $ zip sample.zip sample.txt adding: sample.txt (stored 0%) $ cd .. $ pulp run ... <lines ommited> * Build successful. When you come to a fork in a road, take it. -- Yogi Berra unit $ |