From 9df6d42cafb0e8896deef9b5f45d184d1b702417 Mon Sep 17 00:00:00 2001
From: john gravois <jgravois@esri.com>
Date: Thu, 4 Apr 2019 00:12:01 -0700
Subject: [PATCH] add @terraformer/wkt module

---
 .gitignore                        |   5 +-
 .travis.yml                       |  19 +-
 README.md                         |   2 +-
 package-lock.json                 | 598 +++++++++--------------------
 package.json                      |   6 +-
 packages/arcgis/README.md         |   2 +-
 packages/wkt/README.md            |  63 ++++
 packages/wkt/build-esm.js         |  14 +
 packages/wkt/package-lock.json    | 126 +++++++
 packages/wkt/package.json         |  47 +++
 packages/wkt/test/geojson.test.js | 452 ++++++++++++++++++++++
 packages/wkt/test/wkt.test.js     | 606 ++++++++++++++++++++++++++++++
 packages/wkt/wkt.js               | 334 ++++++++++++++++
 packages/wkt/wkt.yy               | 178 +++++++++
 14 files changed, 2025 insertions(+), 427 deletions(-)
 create mode 100644 packages/wkt/README.md
 create mode 100644 packages/wkt/build-esm.js
 create mode 100644 packages/wkt/package-lock.json
 create mode 100644 packages/wkt/package.json
 create mode 100644 packages/wkt/test/geojson.test.js
 create mode 100644 packages/wkt/test/wkt.test.js
 create mode 100644 packages/wkt/wkt.js
 create mode 100644 packages/wkt/wkt.yy

diff --git a/.gitignore b/.gitignore
index e4b8ec3..c1fb7cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,4 +94,7 @@ demos/test/*
 packages/**/.source.**.html
 
 # jsdoc
-out/*
\ No newline at end of file
+out/*
+
+# created by jison
+packages/wkt/index.js
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 2c67e4c..c04d576 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,17 +5,14 @@ node_js:
   - 'node'
   - 'lts/*'
 sudo: false
-cache:
-  directories:
-    - node_modules
-install:
-  - export DISPLAY=':99.0'
-  - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
+cache: npm
 before_script:
-- npm install tape-run
-- npm run bootstrap
+  - npm install tape-run
+  - "export DISPLAY=:99.0"
+  - "sh -e /etc/init.d/xvfb start"
+  - sleep 3 # give xvfb some time to start
 script:
-- npm run test:ci
+  - npm run test:ci
 before_deploy:
 - npm run jsdoc
 deploy:
@@ -31,8 +28,8 @@ branches:
   only:
     - master
 addons:
+  chrome: stable
+  firefox: latest
   apt:
     packages:
       - xvfb
-  chrome: stable
-  firefox: latest
diff --git a/README.md b/README.md
index aed82b4..ad81bb4 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ See the [@terraformer](https://terraformer-js.github.io/terraformer/module-@terr
 
 * **[`@terraformer/spatial`](./packages/spatial/)** - Spatial predicates for [GeoJSON](https://tools.ietf.org/html/rfc7946).
 * **[`@terraformer/arcgis`](./packages/arcgis/)**  -  Convert ArcGIS JSON geometries to GeoJSON geometries and vice versa.
-* **`@terraformer/wkt`** - coming soon
+* **[`@terraformer/wkt`](./packages/wkt/)** - Convert WKT geometries to GeoJSON geometries and vice versa.
 
 ## Contributing
 
diff --git a/package-lock.json b/package-lock.json
index ec28d50..e4dea15 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3332,9 +3332,9 @@
       "dev": true
     },
     "estree-walker": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.5.2.tgz",
-      "integrity": "sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig==",
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.0.tgz",
+      "integrity": "sha512-peq1RfVAVzr3PU/jL31RaOjUKLoZJpObQWJJ+LgfcxDUifyLZ1RjPQZTl0pzj2uJ45b7A7XpyppXvxdEqzo4rw==",
       "dev": true
     },
     "esutils": {
@@ -3409,57 +3409,6 @@
         }
       }
     },
-    "expand-range": {
-      "version": "1.8.2",
-      "resolved": "http://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
-      "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
-      "dev": true,
-      "requires": {
-        "fill-range": "^2.1.0"
-      },
-      "dependencies": {
-        "fill-range": {
-          "version": "2.2.4",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
-          "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
-          "dev": true,
-          "requires": {
-            "is-number": "^2.1.0",
-            "isobject": "^2.0.0",
-            "randomatic": "^3.0.0",
-            "repeat-element": "^1.1.2",
-            "repeat-string": "^1.5.2"
-          }
-        },
-        "is-number": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
-          "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
-        },
-        "isobject": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-          "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-          "dev": true,
-          "requires": {
-            "isarray": "1.0.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -3692,12 +3641,6 @@
         "object-assign": "^4.0.1"
       }
     },
-    "filename-regex": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
-      "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
-      "dev": true
-    },
     "fill-range": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -3774,15 +3717,6 @@
       "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
       "dev": true
     },
-    "for-own": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
-      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.1"
-      }
-    },
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -3845,24 +3779,29 @@
       "dependencies": {
         "abbrev": {
           "version": "1.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
           "dev": true,
           "optional": true
         },
         "ansi-regex": {
           "version": "2.1.1",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+          "dev": true,
+          "optional": true
         },
         "aproba": {
           "version": "1.2.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
           "dev": true,
           "optional": true
         },
         "are-we-there-yet": {
           "version": "1.1.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
           "dev": true,
           "optional": true,
           "requires": {
@@ -3872,13 +3811,17 @@
         },
         "balanced-match": {
           "version": "1.0.0",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+          "dev": true,
+          "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
           "dev": true,
+          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -3886,34 +3829,43 @@
         },
         "chownr": {
           "version": "1.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
           "dev": true,
           "optional": true
         },
         "code-point-at": {
           "version": "1.1.0",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+          "dev": true,
+          "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
           "dev": true,
           "optional": true
         },
         "debug": {
           "version": "2.6.9",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -3922,25 +3874,29 @@
         },
         "deep-extend": {
           "version": "0.5.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
           "dev": true,
           "optional": true
         },
         "delegates": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
           "dev": true,
           "optional": true
         },
         "detect-libc": {
           "version": "1.0.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
           "dev": true,
           "optional": true
         },
         "fs-minipass": {
           "version": "1.2.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -3949,13 +3905,15 @@
         },
         "fs.realpath": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
           "dev": true,
           "optional": true
         },
         "gauge": {
           "version": "2.7.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
           "dev": true,
           "optional": true,
           "requires": {
@@ -3971,7 +3929,8 @@
         },
         "glob": {
           "version": "7.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -3985,13 +3944,15 @@
         },
         "has-unicode": {
           "version": "2.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
           "dev": true,
           "optional": true
         },
         "iconv-lite": {
           "version": "0.4.21",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4000,7 +3961,8 @@
         },
         "ignore-walk": {
           "version": "3.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4009,7 +3971,8 @@
         },
         "inflight": {
           "version": "1.0.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4019,46 +3982,58 @@
         },
         "inherits": {
           "version": "2.0.3",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
           "dev": true,
           "optional": true
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
         },
         "isarray": {
           "version": "1.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
           "dev": true,
           "optional": true
         },
         "minimatch": {
           "version": "3.0.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
           "dev": true,
+          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
         },
         "minimist": {
           "version": "0.0.8",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+          "dev": true,
+          "optional": true
         },
         "minipass": {
           "version": "2.2.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
           "dev": true,
+          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.1",
             "yallist": "^3.0.0"
@@ -4066,7 +4041,8 @@
         },
         "minizlib": {
           "version": "1.1.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4075,21 +4051,25 @@
         },
         "mkdirp": {
           "version": "0.5.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
           "dev": true,
+          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
         },
         "ms": {
           "version": "2.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true,
           "optional": true
         },
         "needle": {
           "version": "2.2.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4100,7 +4080,8 @@
         },
         "node-pre-gyp": {
           "version": "0.10.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4118,7 +4099,8 @@
         },
         "nopt": {
           "version": "4.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4128,13 +4110,15 @@
         },
         "npm-bundled": {
           "version": "1.0.3",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
           "dev": true,
           "optional": true
         },
         "npm-packlist": {
           "version": "1.1.10",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4144,7 +4128,8 @@
         },
         "npmlog": {
           "version": "4.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4156,38 +4141,46 @@
         },
         "number-is-nan": {
           "version": "1.0.1",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
           "dev": true,
           "optional": true
         },
         "once": {
           "version": "1.4.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
         },
         "os-homedir": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
           "dev": true,
           "optional": true
         },
         "os-tmpdir": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
           "dev": true,
           "optional": true
         },
         "osenv": {
           "version": "0.1.5",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4197,19 +4190,22 @@
         },
         "path-is-absolute": {
           "version": "1.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
           "dev": true,
           "optional": true
         },
         "process-nextick-args": {
           "version": "2.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
           "dev": true,
           "optional": true
         },
         "rc": {
           "version": "1.2.7",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4221,7 +4217,8 @@
           "dependencies": {
             "minimist": {
               "version": "1.2.0",
-              "bundled": true,
+              "resolved": false,
+              "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
               "dev": true,
               "optional": true
             }
@@ -4229,7 +4226,8 @@
         },
         "readable-stream": {
           "version": "2.3.6",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4244,7 +4242,8 @@
         },
         "rimraf": {
           "version": "2.6.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4253,43 +4252,52 @@
         },
         "safe-buffer": {
           "version": "5.1.1",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+          "dev": true,
+          "optional": true
         },
         "safer-buffer": {
           "version": "2.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
           "dev": true,
           "optional": true
         },
         "sax": {
           "version": "1.2.4",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
           "dev": true,
           "optional": true
         },
         "semver": {
           "version": "5.5.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
           "dev": true,
           "optional": true
         },
         "set-blocking": {
           "version": "2.0.0",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
           "dev": true,
           "optional": true
         },
         "signal-exit": {
           "version": "3.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
           "dev": true,
           "optional": true
         },
         "string-width": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -4298,7 +4306,8 @@
         },
         "string_decoder": {
           "version": "1.1.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4307,21 +4316,25 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
+          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
         },
         "strip-json-comments": {
           "version": "2.0.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
           "dev": true,
           "optional": true
         },
         "tar": {
           "version": "4.4.1",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4336,13 +4349,15 @@
         },
         "util-deprecate": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
           "dev": true,
           "optional": true
         },
         "wide-align": {
           "version": "1.1.2",
-          "bundled": true,
+          "resolved": false,
+          "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
           "dev": true,
           "optional": true,
           "requires": {
@@ -4351,13 +4366,17 @@
         },
         "wrappy": {
           "version": "1.0.2",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+          "dev": true,
+          "optional": true
         },
         "yallist": {
           "version": "3.0.2",
-          "bundled": true,
-          "dev": true
+          "resolved": false,
+          "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -4676,42 +4695,6 @@
         "path-is-absolute": "^1.0.0"
       }
     },
-    "glob-base": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
-      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
-      "dev": true,
-      "requires": {
-        "glob-parent": "^2.0.0",
-        "is-glob": "^2.0.0"
-      },
-      "dependencies": {
-        "glob-parent": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
-          "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
-          "dev": true,
-          "requires": {
-            "is-glob": "^2.0.0"
-          }
-        },
-        "is-extglob": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^1.0.0"
-          }
-        }
-      }
-    },
     "glob-parent": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -4795,26 +4778,17 @@
       "dev": true
     },
     "handlebars": {
-      "version": "4.0.12",
-      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz",
-      "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz",
+      "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==",
       "dev": true,
       "requires": {
-        "async": "^2.5.0",
+        "neo-async": "^2.6.0",
         "optimist": "^0.6.1",
         "source-map": "^0.6.1",
         "uglify-js": "^3.1.4"
       },
       "dependencies": {
-        "async": {
-          "version": "2.6.1",
-          "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz",
-          "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==",
-          "dev": true,
-          "requires": {
-            "lodash": "^4.17.10"
-          }
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -5337,21 +5311,6 @@
         }
       }
     },
-    "is-dotfile": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
-      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
-      "dev": true
-    },
-    "is-equal-shallow": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
-      "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
-      "dev": true,
-      "requires": {
-        "is-primitive": "^2.0.0"
-      }
-    },
     "is-extendable": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -5463,18 +5422,6 @@
         "isobject": "^3.0.1"
       }
     },
-    "is-posix-bracket": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
-      "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
-      "dev": true
-    },
-    "is-primitive": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
-      "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
-      "dev": true
-    },
     "is-promise": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
@@ -5599,9 +5546,9 @@
       "dev": true
     },
     "js-yaml": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-      "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+      "version": "3.13.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz",
+      "integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==",
       "dev": true,
       "requires": {
         "argparse": "^1.0.7",
@@ -6179,12 +6126,6 @@
       "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
       "dev": true
     },
-    "math-random": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
-      "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=",
-      "dev": true
-    },
     "md5.js": {
       "version": "1.3.5",
       "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -6444,6 +6385,12 @@
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
       "dev": true
     },
+    "neo-async": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
+      "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
+      "dev": true
+    },
     "nice-try": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -6577,16 +6524,6 @@
         "isobject": "^3.0.0"
       }
     },
-    "object.omit": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
-      "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
-      "dev": true,
-      "requires": {
-        "for-own": "^0.1.4",
-        "is-extendable": "^0.1.1"
-      }
-    },
     "object.pick": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
@@ -6792,35 +6729,6 @@
       "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=",
       "dev": true
     },
-    "parse-glob": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
-      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
-      "dev": true,
-      "requires": {
-        "glob-base": "^0.3.0",
-        "is-dotfile": "^1.0.0",
-        "is-extglob": "^1.0.0",
-        "is-glob": "^2.0.0"
-      },
-      "dependencies": {
-        "is-extglob": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^1.0.0"
-          }
-        }
-      }
-    },
     "parse-json": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
@@ -7003,12 +6911,6 @@
       "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
       "dev": true
     },
-    "preserve": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
-      "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
-      "dev": true
-    },
     "private": {
       "version": "0.1.8",
       "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
@@ -7115,25 +7017,6 @@
       "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=",
       "dev": true
     },
-    "randomatic": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
-      "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
-      "dev": true,
-      "requires": {
-        "is-number": "^4.0.0",
-        "kind-of": "^6.0.0",
-        "math-random": "^1.0.1"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
-          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
-          "dev": true
-        }
-      }
-    },
     "randombytes": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz",
@@ -7367,15 +7250,6 @@
         "private": "^0.1.6"
       }
     },
-    "regex-cache": {
-      "version": "0.4.4",
-      "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
-      "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
-      "dev": true,
-      "requires": {
-        "is-equal-shallow": "^0.1.3"
-      }
-    },
     "regex-not": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@@ -7653,104 +7527,13 @@
       }
     },
     "rollup-pluginutils": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz",
-      "integrity": "sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA==",
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.5.0.tgz",
+      "integrity": "sha512-9Muh1H+XB5f5ONmKMayUoTYR1EZwHbwJJ9oZLrKT5yuTf/RLIQ5mYIGsrERquVucJmjmaAW0Y7+6Qo1Ep+5w3Q==",
       "dev": true,
       "requires": {
-        "estree-walker": "^0.5.2",
-        "micromatch": "^2.3.11"
-      },
-      "dependencies": {
-        "arr-diff": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
-          "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.0.1"
-          }
-        },
-        "array-unique": {
-          "version": "0.2.1",
-          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
-          "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
-          "dev": true
-        },
-        "braces": {
-          "version": "1.8.5",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
-          "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
-          "dev": true,
-          "requires": {
-            "expand-range": "^1.8.1",
-            "preserve": "^0.2.0",
-            "repeat-element": "^1.1.2"
-          }
-        },
-        "expand-brackets": {
-          "version": "0.1.5",
-          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
-          "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
-          "dev": true,
-          "requires": {
-            "is-posix-bracket": "^0.1.0"
-          }
-        },
-        "extglob": {
-          "version": "0.3.2",
-          "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
-          "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^1.0.0"
-          }
-        },
-        "is-extglob": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
-          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
-          "dev": true
-        },
-        "is-glob": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
-          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^1.0.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        },
-        "micromatch": {
-          "version": "2.3.11",
-          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
-          "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
-          "dev": true,
-          "requires": {
-            "arr-diff": "^2.0.0",
-            "array-unique": "^0.2.1",
-            "braces": "^1.8.2",
-            "expand-brackets": "^0.1.4",
-            "extglob": "^0.3.1",
-            "filename-regex": "^2.0.0",
-            "is-extglob": "^1.0.0",
-            "is-glob": "^2.0.1",
-            "kind-of": "^3.0.2",
-            "normalize-path": "^2.0.1",
-            "object.omit": "^2.0.0",
-            "parse-glob": "^3.0.4",
-            "regex-cache": "^0.4.2"
-          }
-        }
+        "estree-walker": "^0.6.0",
+        "micromatch": "^3.1.10"
       }
     },
     "run-async": {
@@ -8853,23 +8636,16 @@
       "dev": true
     },
     "uglify-js": {
-      "version": "3.4.9",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
-      "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.3.tgz",
+      "integrity": "sha512-rIQPT2UMDnk4jRX+w4WO84/pebU2jiLsjgIyrCktYgSvx28enOE3iYQMr+BD1rHiitWnDmpu0cY/LfIEpKcjcw==",
       "dev": true,
       "optional": true,
       "requires": {
-        "commander": "~2.17.1",
+        "commander": "~2.19.0",
         "source-map": "~0.6.1"
       },
       "dependencies": {
-        "commander": {
-          "version": "2.17.1",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
-          "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
-          "dev": true,
-          "optional": true
-        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
diff --git a/package.json b/package.json
index fd3f441..679b6f7 100644
--- a/package.json
+++ b/package.json
@@ -13,9 +13,11 @@
     "test:electron": "browserify packages/**/test/*.js -t [ babelify --presets [ @babel/preset-env ] ] --debug | tape-run | faucet",
     "test:chrome": "browserify packages/**/test/*.js -t [ babelify --presets [ @babel/preset-env ] ] --debug | tape-run --browser chrome | faucet",
     "test:firefox": "browserify packages/**/test/*.js -t [ babelify --presets [ @babel/preset-env ] ] --debug | tape-run --browser firefox | faucet",
+    "pretest:ci": "npm run build",
     "test": "npm run lint && npm run test:node",
-    "test:ci": "npm run lint && npm run test:node && npm run test:chrome && npm run test:firefox",
-    "release": "./release.sh"
+    "test:ci": "npm run lint && npm run test:node && npm run test:electron",
+    "release": "./release.sh",
+    "postinstall": "npm run bootstrap"
   },
   "repository": {
     "type": "git",
diff --git a/packages/arcgis/README.md b/packages/arcgis/README.md
index aedcec5..4123d47 100644
--- a/packages/arcgis/README.md
+++ b/packages/arcgis/README.md
@@ -15,7 +15,7 @@
 
 ## Install
 
-```
+```shell
 npm install @terraformer/arcgis
 ```
 
diff --git a/packages/wkt/README.md b/packages/wkt/README.md
new file mode 100644
index 0000000..2a0b409
--- /dev/null
+++ b/packages/wkt/README.md
@@ -0,0 +1,63 @@
+# @terraformer/wkt
+
+[![npm][npm-image]][npm-url]
+[![travis][travis-image]][travis-url]
+[![standard][standard-image]][standard-url]
+
+[npm-image]: https://img.shields.io/npm/v/@terraformer/wkt.svg?style=flat-square
+[npm-url]: https://www.npmjs.com/package/@terraformer/wkt
+[travis-image]: https://img.shields.io/travis/terraformer-js/terraformer/master.svg?style=flat-square
+[travis-url]: https://travis-ci.org/terraformer-js/terraformer
+[standard-image]: https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg?style=flat-square
+[standard-url]: http://npm.im/semistandard
+
+> Tools to convert WKT geometries to GeoJSON geometries and vice versa.
+
+## Install
+
+```shell
+npm install @terraformer/wkt
+```
+
+## Usage
+
+### ES Module
+
+```js
+import { wktToGeoJSON, geojsonToWKT } from '@terraformer/wkt';
+
+// parse WKT and convert to GeoJSON
+const geojson = wktToGeoJSON("POINT (-122.6764, 45.5165)");
+
+>> { "type": "Point", "coordinates": [ -122.6764, 45.5165 ] }
+
+// parse GeoJSON and convert it to ArcGIS JSON
+const wkt = geojsonToWKT({
+  "type": "Point",
+  "coordinates": [ -122.6764, 45.5165 ]
+});
+
+>> "POINT (-122.6764, 45.5165)"
+```
+
+### Browser (from CDN)
+
+This package is distributed as a [UMD](https://github.com/umdjs/umd) module and can also be used in AMD based systems or as a global under the `Terraformer` namespace.
+
+```html
+<script src="https://unpkg.com/@terraformer/wkt"></script>
+```
+```js
+Terraformer.wktToGeoJSON("POINT (-122.6764, 45.5165)");
+```
+
+### Node.js
+
+```js
+const Terraformer = require('@terraformer/wkt');
+
+Terraformer.geojsonToWKT(/* ... */);
+Terraformer.wktToGeoJSON(/* ... */);
+```
+
+## [Contributing](./CONTRIBUTING.md)
diff --git a/packages/wkt/build-esm.js b/packages/wkt/build-esm.js
new file mode 100644
index 0000000..1344d3c
--- /dev/null
+++ b/packages/wkt/build-esm.js
@@ -0,0 +1,14 @@
+var fs = require('fs');
+var jison = require('jison');
+
+var grammar = fs.readFileSync('./wkt.yy', 'utf8');
+var wrapper = fs.readFileSync('./wkt.js', 'utf8');
+
+var Parser = jison.Parser;
+var parser = new Parser(grammar);
+
+// generate source, ready to be written to disk using a jison fork to get a es module output: https://github.com/zaach/jison/pull/326
+var parserSource = parser.generate({ moduleType: 'es' });
+
+wrapper = wrapper.replace('\'SOURCE\';', parserSource);
+fs.writeFileSync('./index.js', wrapper, 'utf8');
diff --git a/packages/wkt/package-lock.json b/packages/wkt/package-lock.json
new file mode 100644
index 0000000..ac4f834
--- /dev/null
+++ b/packages/wkt/package-lock.json
@@ -0,0 +1,126 @@
+{
+	"requires": true,
+	"lockfileVersion": 1,
+	"dependencies": {
+		"JSONSelect": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz",
+			"integrity": "sha1-oI7cxn6z/L6Z7WMIVTRKDPKCu40="
+		},
+		"JSV": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+			"integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
+		},
+		"amdefine": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+			"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+			"optional": true
+		},
+		"cjson": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz",
+			"integrity": "sha1-5kObkHA9MS/24iJAl76pLOPQKhQ=",
+			"requires": {
+				"jsonlint": "1.6.0"
+			}
+		},
+		"colors": {
+			"version": "0.5.1",
+			"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
+			"integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q="
+		},
+		"ebnf-parser": {
+			"version": "0.1.10",
+			"resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz",
+			"integrity": "sha1-zR9rpHfFY4xAyX7ZtXLbW6tdgzE="
+		},
+		"escodegen": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz",
+			"integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=",
+			"requires": {
+				"esprima": "~1.1.1",
+				"estraverse": "~1.5.0",
+				"esutils": "~1.0.0",
+				"source-map": "~0.1.33"
+			}
+		},
+		"esprima": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz",
+			"integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk="
+		},
+		"estraverse": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz",
+			"integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E="
+		},
+		"esutils": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz",
+			"integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA="
+		},
+		"jison": {
+			"version": "github:GabrielRatener/jison#2668d7efbb628c763dc7cce41ab24a4632528942",
+			"from": "github:GabrielRatener/jison",
+			"requires": {
+				"JSONSelect": "0.4.0",
+				"cjson": "0.3.0",
+				"ebnf-parser": "0.1.10",
+				"escodegen": "1.3.x",
+				"esprima": "1.1.x",
+				"jison-lex": "0.3.x",
+				"lex-parser": "~0.1.3",
+				"nomnom": "1.5.2"
+			}
+		},
+		"jison-lex": {
+			"version": "0.3.4",
+			"resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz",
+			"integrity": "sha1-gcoo2E+ESZ36jFlNzePYo/Jux6U=",
+			"requires": {
+				"lex-parser": "0.1.x",
+				"nomnom": "1.5.2"
+			}
+		},
+		"jsonlint": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz",
+			"integrity": "sha1-iKpGvCiaesk7tGyuLVihh6m7SUo=",
+			"requires": {
+				"JSV": ">= 4.0.x",
+				"nomnom": ">= 1.5.x"
+			}
+		},
+		"lex-parser": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz",
+			"integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA="
+		},
+		"nomnom": {
+			"version": "1.5.2",
+			"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
+			"integrity": "sha1-9DRUSKhTz71cDSYyDyR3qwUm/i8=",
+			"requires": {
+				"colors": "0.5.x",
+				"underscore": "1.1.x"
+			}
+		},
+		"source-map": {
+			"version": "0.1.43",
+			"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+			"integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+			"optional": true,
+			"requires": {
+				"amdefine": ">=0.0.4"
+			}
+		},
+		"underscore": {
+			"version": "1.1.7",
+			"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz",
+			"integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA="
+		}
+	}
+}
diff --git a/packages/wkt/package.json b/packages/wkt/package.json
new file mode 100644
index 0000000..f44aa8f
--- /dev/null
+++ b/packages/wkt/package.json
@@ -0,0 +1,47 @@
+{
+  "name": "@terraformer/wkt",
+  "description": "Tools to convert WKT geometries to GeoJSON geometries and vica-versa.",
+  "version": "2.0.0",
+  "author": "Jerry Sievert <code@legitimatesounding.com> (http://legitimatesounding.com)",
+  "bugs": {
+    "url": "https://github.com/terraformer-js/terraformer/issues"
+  },
+  "contributors": [
+    "John Gravois <john@esri.com>"
+  ],
+  "dependencies": {},
+  "devDependencies": {
+    "jison": "GabrielRatener/jison"
+  },
+  "files": [
+    "index.js",
+    "dist/wkt.umd.js.map"
+  ],
+  "homepage": "https://github.com/terraformer-js/terraformer",
+  "keywords": [
+    "wkt",
+    "convert",
+    "geo",
+    "geojson",
+    "geometry"
+  ],
+  "license": "MIT",
+  "main": "dist/wkt.umd.js",
+  "unpkg": "dist/wkt.umd.js",
+  "module": "index.js",
+  "jspm": {
+    "main": "index.js",
+    "registry": "npm",
+    "format": "es6"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/terraformer-js/terraformer"
+  },
+  "scripts": {
+    "build:module": "node build-esm.js",
+    "prebuild": "npm run build:module",
+    "build": "npm run build:module && rollup -c ../../rollup.config.js",
+    "prepare": "npm run build"
+  }
+}
diff --git a/packages/wkt/test/geojson.test.js b/packages/wkt/test/geojson.test.js
new file mode 100644
index 0000000..4948e51
--- /dev/null
+++ b/packages/wkt/test/geojson.test.js
@@ -0,0 +1,452 @@
+
+import test from 'tape';
+import { geojsonToWKT } from '../index.js';
+
+test('should exist', function (t) {
+  t.plan(1);
+  t.ok(geojsonToWKT);
+});
+
+test('should turn a GeoJSON Point into WKT', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Point',
+    coordinates: [ 30, 10 ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POINT (30 10)');
+});
+
+test('should convert a POINT with Z', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Point',
+    coordinates: [ 30, 10, 10 ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POINT Z (30 10 10)');
+});
+
+test('should convert a POINT with M (nonstandard)', function (t) {
+  t.plan(1);
+
+  const input = {
+    properties: { m: true },
+    type: 'Point',
+    coordinates: [ 30, 10, 10 ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POINT M (30 10 10)');
+});
+
+test('should convert a POINT with Z and M', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Point',
+    coordinates: [ 30, 10, 10, 12 ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POINT ZM (30 10 10 12)');
+});
+
+test('should convert an empty POINT', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Point',
+    coordinates: [ ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POINT EMPTY');
+});
+
+test('should convert a POLYGON', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Polygon',
+    coordinates: [ [ [ 30, 10 ], [ 20, 20 ], [ 30, 20 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POLYGON ((30 10, 20 20, 30 20))');
+});
+
+test('should convert a POLYGON with Z', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Polygon',
+    coordinates: [ [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POLYGON Z ((30 10 1, 20 20 2, 30 20 3))');
+});
+
+test('should convert a POLYGON with ZM', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Polygon',
+    coordinates: [ [ [ 30, 10, 1, 3 ], [ 20, 20, 2, 2 ], [ 30, 20, 3, 1 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POLYGON ZM ((30 10 1 3, 20 20 2 2, 30 20 3 1))');
+});
+
+test('should convert a POLYGON with M (nonstandard)', function (t) {
+  t.plan(1);
+
+  const input = {
+    properties: { m: true },
+    type: 'Polygon',
+    coordinates: [ [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POLYGON M ((30 10 1, 20 20 2, 30 20 3))');
+});
+
+test('should convert an EMPTY POLYGON', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'Polygon',
+    coordinates: [ ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'POLYGON EMPTY');
+});
+
+test('should convert a MULTIPOINT', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiPoint',
+    coordinates: [ [ 30, 10 ], [ 20, 20 ], [ 30, 20 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOINT (30 10, 20 20, 30 20)');
+});
+
+test('should convert a MULTIPOINT with Z', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiPoint',
+    coordinates: [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOINT Z (30 10 1, 20 20 2, 30 20 3)');
+});
+
+test('should convert a MULTIPOINT with ZM', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiPoint',
+    coordinates: [ [ 30, 10, 1, 2 ], [ 20, 20, 3, 4 ], [ 30, 20, 5, 6 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOINT ZM (30 10 1 2, 20 20 3 4, 30 20 5 6)');
+});
+
+test('should convert a MULTIPOINT with M (nonstandard)', function (t) {
+  t.plan(1);
+
+  const input = {
+    properties: { m: true },
+    type: 'MultiPoint',
+    coordinates: [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOINT M (30 10 1, 20 20 2, 30 20 3)');
+});
+
+test('should convert an EMPTY MULTIPOINT', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiPoint',
+    coordinates: [ ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOINT EMPTY');
+});
+
+test('should convert a LINESTRING with Z', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'LineString',
+    coordinates: [ [ 30, 10, 2 ], [ 20, 20, 1 ], [ 30, 20, 0 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'LINESTRING Z (30 10 2, 20 20 1, 30 20 0)');
+});
+
+test('should convert a LINESTRING with ZM', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'LineString',
+    coordinates: [ [ 30, 10, 1, 2 ], [ 20, 20, 3, 4 ], [ 30, 20, 5, 6 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'LINESTRING ZM (30 10 1 2, 20 20 3 4, 30 20 5 6)');
+});
+
+test('should convert a LINESTRING with M (nonstandard)', function (t) {
+  t.plan(1);
+
+  const input = {
+    properties: { m: true },
+    type: 'LineString',
+    coordinates: [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'LINESTRING M (30 10 1, 20 20 2, 30 20 3)');
+});
+
+test('should convert an empty LINESTRING', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'LineString',
+    coordinates: [ ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'LINESTRING EMPTY');
+});
+
+test('should convert a LINESTRING', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'LineString',
+    coordinates: [ [ 30, 10 ], [ 20, 20 ], [ 30, 20 ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'LINESTRING (30 10, 20 20, 30 20)');
+});
+
+test('should convert a MULTILINESTRING', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiLineString',
+    coordinates: [ [ [ 30, 10 ], [ 20, 20 ], [ 30, 20 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTILINESTRING ((30 10, 20 20, 30 20))');
+});
+
+test('should convert a MULTILINESTRING with Z', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiLineString',
+    coordinates: [ [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTILINESTRING Z ((30 10 1, 20 20 2, 30 20 3))');
+});
+
+test('should convert a MULTILINESTRING with Z and M', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiLineString',
+    coordinates: [ [ [ 30, 10, 1, 2 ], [ 20, 20, 3, 4 ], [ 30, 20, 5, 6 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTILINESTRING ZM ((30 10 1 2, 20 20 3 4, 30 20 5 6))');
+});
+
+test('should convert a MULTILINESTRING with M (nonstandard)', function (t) {
+  t.plan(1);
+
+  const input = {
+    properties: { m: true },
+    type: 'MultiLineString',
+    coordinates: [ [ [ 30, 10, 1 ], [ 20, 20, 2 ], [ 30, 20, 3 ] ] ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTILINESTRING M ((30 10 1, 20 20 2, 30 20 3))');
+});
+
+test('should convert an empty MULTILINESTRING', function (t) {
+  t.plan(1);
+
+  const input = {
+    type: 'MultiLineString',
+    coordinates: [ ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTILINESTRING EMPTY');
+});
+
+test('should convert a MULTIPOLYGON', function (t) {
+  t.plan(1);
+
+  const input = { 'type': 'MultiPolygon',
+    'coordinates': [
+      [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
+      [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+        [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+    ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOLYGON (((102 2, 103 2, 103 3, 102 3, 102 2)), ((100 0, 101 0, 101 1, 100 1, 100 0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2)))');
+});
+
+test('should convert a MULTIPOLYGON with Z', function (t) {
+  t.plan(1);
+
+  const input = { 'type': 'MultiPolygon',
+    'coordinates': [
+      [[[102.0, 2.0, 1], [103.0, 2.0, 2], [103.0, 3.0, 3], [102.0, 3.0, 4], [102.0, 2.0, 5]]],
+      [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+        [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+    ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOLYGON Z (((102 2 1, 103 2 2, 103 3 3, 102 3 4, 102 2 5)), ((100 0, 101 0, 101 1, 100 1, 100 0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2)))');
+});
+
+test('should convert a MULTIPOLYGON with Z and M', function (t) {
+  t.plan(1);
+
+  const input = { 'type': 'MultiPolygon',
+    'coordinates': [
+      [[[102.0, 2.0, 1, 2], [103.0, 2.0, 3, 4], [103.0, 3.0, 5, 6], [102.0, 3.0, 7, 8], [102.0, 2.0, 9, 10]]],
+      [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+        [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+    ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOLYGON ZM (((102 2 1 2, 103 2 3 4, 103 3 5 6, 102 3 7 8, 102 2 9 10)), ((100 0, 101 0, 101 1, 100 1, 100 0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2)))');
+});
+
+test('should convert a MULTIPOLYGON with M (non standard)', function (t) {
+  t.plan(1);
+
+  const input = { 'type': 'MultiPolygon',
+    properties: { m: true },
+    'coordinates': [
+      [[[102.0, 2.0, 1], [103.0, 2.0, 2], [103.0, 3.0, 3], [102.0, 3.0, 4], [102.0, 2.0, 5]]],
+      [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+        [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+    ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOLYGON M (((102 2 1, 103 2 2, 103 3 3, 102 3 4, 102 2 5)), ((100 0, 101 0, 101 1, 100 1, 100 0), (100.2 0.2, 100.8 0.2, 100.8 0.8, 100.2 0.8, 100.2 0.2)))');
+});
+
+test('should convert an EMPTY MULTIPOLYGON', function (t) {
+  t.plan(1);
+
+  const input = {
+    'type': 'MultiPolygon',
+    'coordinates': [ ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'MULTIPOLYGON EMPTY');
+});
+
+test('should convert a Geometry Collection', function (t) {
+  t.plan(1);
+
+  const input = {
+    'type': 'GeometryCollection',
+    'geometries': [
+      { 'type': 'Point',
+        'coordinates': [100.0, 0.0]
+      },
+      { 'type': 'LineString',
+        'coordinates': [ [101.0, 0.0], [102.0, 1.0] ]
+      }
+    ]
+  };
+
+  const output = geojsonToWKT(input);
+
+  t.deepEqual(output, 'GEOMETRYCOLLECTION(POINT (100 0), LINESTRING (101 0, 102 1))');
+});
+
+test('should fail a conversion on an unknown type', function (t) {
+  t.plan(1);
+
+  const input = {
+    'type': 'MultiPolygonLikeThingy',
+    'coordinates': [ ]
+  };
+
+  try {
+    geojsonToWKT(input);
+  } catch (err) {
+    const error = err.toString();
+    t.deepEqual(error, 'Error: Unknown Type: MultiPolygonLikeThingy');
+  }
+});
diff --git a/packages/wkt/test/wkt.test.js b/packages/wkt/test/wkt.test.js
new file mode 100644
index 0000000..e06ba03
--- /dev/null
+++ b/packages/wkt/test/wkt.test.js
@@ -0,0 +1,606 @@
+
+import test from 'tape';
+import { wktToGeoJSON } from '../index.js';
+
+test('should exist', function (t) {
+  t.plan(1);
+  t.ok(wktToGeoJSON);
+});
+
+test('should parse a WKT POINT', function (t) {
+  t.plan(1);
+
+  const input = 'POINT (30 10)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Point',
+    coordinates: [ 30, 10 ]
+  });
+});
+
+test('should parse an empty WKT POINT', function (t) {
+  t.plan(1);
+
+  const input = 'POINT EMPTY';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Point',
+    coordinates: [ ]
+  });
+});
+
+test('should parse a POINT with a Z coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'POINT Z (30 10 20)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    properties: { z: true },
+    type: 'Point',
+    coordinates: [ 30, 10, 20 ]
+  });
+});
+
+test('should parse a POINT with a M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'POINT M (30 10 20)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    properties: { m: true },
+    type: 'Point',
+    coordinates: [ 30, 10, 20 ]
+  });
+});
+
+test('should parse a POINT with a Z and M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'POINT ZM (30 10 20 15)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    properties: {
+      m: true,
+      z: true
+    },
+    type: 'Point',
+    coordinates: [ 30, 10, 20, 15 ]
+  });
+});
+
+test('should parse a POINT with scientific notation coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'POINT (30e0 10 2.0E+001 15)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Point',
+    coordinates: [ 30, 10, 20, 15 ]
+  });
+});
+
+test('should parse a LINESTRING', function (t) {
+  t.plan(1);
+
+  const input = 'LINESTRING (30 10, 10 30, 40 40)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'LineString',
+    coordinates: [ [30, 10], [10, 30], [40, 40] ]
+  });
+});
+
+test('should parse an EMPTY LINESTRING', function (t) {
+  t.plan(1);
+
+  const input = 'LINESTRING EMPTY';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'LineString',
+    coordinates: [ ]
+  });
+});
+
+test('should parse a LINESTRING with a Z coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'LINESTRING Z (30 10 5, 10 30 15, 40 40 25)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'LineString',
+    properties: {
+      z: true
+    },
+    coordinates: [ [30, 10, 5], [10, 30, 15], [40, 40, 25] ]
+  });
+});
+
+test('should parse a LINESTRING with an M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'LINESTRING M (30 10 5, 10 30 15, 40 40 25)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'LineString',
+    properties: {
+      m: true
+    },
+    coordinates: [ [30, 10, 5], [10, 30, 15], [40, 40, 25] ]
+  });
+});
+
+test('should parse a LINESTRING with Z and M coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'LINESTRING ZM (30 10 5 2, 10 30 15 8, 40 40 25 16)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'LineString',
+    properties: {
+      z: true,
+      m: true
+    },
+    coordinates: [ [30, 10, 5, 2], [10, 30, 15, 8], [40, 40, 25, 16] ]
+  });
+});
+
+test('should parse a POLYGON', function (t) {
+  t.plan(1);
+
+  const input = 'POLYGON ((30 10, 10 20, 20 40, 40 40, 30 10))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Polygon',
+    coordinates: [ [ [30, 10], [10, 20], [20, 40], [40, 40], [30, 10] ] ]
+  });
+});
+
+test('should parse an empty POLYGON', function (t) {
+  t.plan(1);
+
+  const input = 'POLYGON EMPTY';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Polygon',
+    coordinates: [ ]
+  });
+});
+
+test('should parse a POLYGON with a Z coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'POLYGON Z ((30 10 4, 10 20 6, 20 40 8, 40 40 1, 30 10 3))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Polygon',
+    properties: {
+      z: true
+    },
+    coordinates: [ [ [30, 10, 4], [10, 20, 6], [20, 40, 8], [40, 40, 1], [30, 10, 3] ] ]
+  });
+});
+
+test('should parse a POLYGON with an M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'POLYGON M ((30 10 4, 10 20 6, 20 40 8, 40 40 1, 30 10 3))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Polygon',
+    properties: {
+      m: true
+    },
+    coordinates: [ [ [30, 10, 4], [10, 20, 6], [20, 40, 8], [40, 40, 1], [30, 10, 3] ] ]
+  });
+});
+
+test('should parse a POLYGON with a Z and M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'POLYGON ZM ((30 10 4 1, 10 20 6 3, 20 40 8 5, 40 40 1 7, 30 10 3 9))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Polygon',
+    properties: {
+      m: true,
+      z: true
+    },
+    coordinates: [ [ [30, 10, 4, 1], [10, 20, 6, 3], [20, 40, 8, 5], [40, 40, 1, 7], [30, 10, 3, 9] ] ]
+  });
+});
+
+test('should parse a POLYGON with a hole', function (t) {
+  t.plan(1);
+
+  const input = 'POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10),(20 30, 35 35, 30 20, 20 30))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'Polygon',
+    coordinates: [
+      [ [35, 10], [10, 20], [15, 40], [45, 45], [35, 10] ],
+      [ [20, 30], [35, 35], [30, 20], [20, 30] ]
+    ]
+  });
+});
+
+test('should parse a MULTIPOINT', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT ((10 40), (40 30), (20 20), (30 10))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    coordinates: [
+      [10, 40], [40, 30], [20, 20], [30, 10]
+    ]
+  });
+});
+
+test('should parse an EMPTY MULTIPOINT', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT EMPTY';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    coordinates: [ ]
+  });
+});
+
+test('should parse a MULTIPOINT with a Z coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT Z ((10 40 1), (40 30 2), (20 20 3), (30 10 4))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    properties: {
+      z: true
+    },
+    coordinates: [ [10, 40, 1], [40, 30, 2], [20, 20, 3], [30, 10, 4] ]
+  });
+});
+
+test('should parse a MULTIPOINT with an M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT M ((10 40 1), (40 30 2), (20 20 3), (30 10 4))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    properties: {
+      m: true
+    },
+    coordinates: [ [10, 40, 1], [40, 30, 2], [20, 20, 3], [30, 10, 4] ]
+  });
+});
+
+test('should parse a MULTIPOINT with a Z and M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT ZM ((10 40 1 8), (40 30 2 9), (20 20 3 8), (30 10 4 9))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    properties: {
+      m: true,
+      z: true
+    },
+    coordinates: [ [10, 40, 1, 8], [40, 30, 2, 9], [20, 20, 3, 8], [30, 10, 4, 9] ]
+  });
+});
+
+test('should parse a MULTIPOINT with alternate syntax', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT (10 40, 40 30, 20 20, 30 10)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    coordinates: [ [10, 40], [40, 30], [20, 20], [30, 10] ]
+  });
+});
+
+test('should parse a MULTIPOINT with alternate syntax and Z coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT Z (10 40 1, 40 30 2, 20 20 3, 30 10 4)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    properties: {
+      z: true
+    },
+    coordinates: [ [10, 40, 1], [40, 30, 2], [20, 20, 3], [30, 10, 4] ]
+  });
+});
+
+test('should parse a MULTIPOINT with alternate syntax and M coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT M (10 40 1, 40 30 2, 20 20 3, 30 10 4)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    properties: {
+      m: true
+    },
+    coordinates: [ [10, 40, 1], [40, 30, 2], [20, 20, 3], [30, 10, 4] ]
+  });
+});
+
+test('should parse a MULTIPOINT with alternate syntax and Z and M coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOINT ZM (10 40 1 2, 40 30 2 3, 20 20 3 4, 30 10 4 5)';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPoint',
+    properties: {
+      m: true,
+      z: true
+    },
+    coordinates: [ [10, 40, 1, 2], [40, 30, 2, 3], [20, 20, 3, 4], [30, 10, 4, 5] ]
+  });
+});
+
+test('should parse a MULTILINESTRING with alternate syntax', function (t) {
+  t.plan(1);
+
+  const input = 'MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiLineString',
+    coordinates: [ [ [10, 10], [20, 20], [10, 40] ],
+      [ [40, 40], [30, 30], [40, 20], [30, 10] ] ]
+  });
+});
+
+test('should parse a MULTILINESTRING with alternate syntax', function (t) {
+  t.plan(1);
+
+  const input = 'MULTILINESTRING EMPTY';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiLineString',
+    coordinates: [ ]
+  });
+});
+
+test('should parse a MULTILINESTRING with alternate syntax and Z coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'MULTILINESTRING Z ((10 10 10, 20 20 20, 10 40 30),(40 40 30, 30 30 20, 40 20 10, 30 10 10))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiLineString',
+    properties: {
+      z: true
+    },
+    coordinates: [
+      [ [10, 10, 10], [20, 20, 20], [10, 40, 30] ],
+      [ [40, 40, 30], [30, 30, 20], [40, 20, 10], [30, 10, 10] ]
+    ]
+  });
+});
+
+test('should parse a MULTILINESTRING with alternate syntax and M coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'MULTILINESTRING M ((10 10 10, 20 20 20, 10 40 30),(40 40 30, 30 30 20, 40 20 10, 30 10 10))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiLineString',
+    properties: {
+      m: true
+    },
+    coordinates: [
+      [ [10, 10, 10], [20, 20, 20], [10, 40, 30] ],
+      [ [40, 40, 30], [30, 30, 20], [40, 20, 10], [30, 10, 10] ]
+    ]
+  });
+});
+
+test('should parse a MULTILINESTRING with alternate syntax and Z and M coordinates', function (t) {
+  t.plan(1);
+
+  const input = 'MULTILINESTRING ZM ((10 10 10 5, 20 20 20 4, 10 40 30 3),(40 40 30 2, 30 30 20 1, 40 20 10 2, 30 10 10 3))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiLineString',
+    properties: {
+      z: true,
+      m: true
+    },
+    coordinates: [
+      [ [10, 10, 10, 5], [20, 20, 20, 4], [10, 40, 30, 3] ],
+      [ [40, 40, 30, 2], [30, 30, 20, 1], [40, 20, 10, 2], [30, 10, 10, 3] ]
+    ]
+  });
+});
+
+test('should parse a MULTIPOLYGON', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOLYGON (((30 20, 10 40, 45 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPolygon',
+    coordinates: [
+      [
+        [ [30, 20], [10, 40], [45, 40], [30, 20] ]
+      ],
+      [
+        [ [15, 5], [40, 10], [10, 20], [5, 10], [15, 5] ]
+      ]
+    ]
+  });
+});
+
+test('should parse an empty MULTIPOLYGON', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOLYGON EMPTY';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPolygon',
+    coordinates: [ ]
+  });
+});
+
+test('should parse a MULTIPOLYGON with a Z coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOLYGON Z (((30 20 1, 10 40 2, 45 40 3, 30 20 4)),((15 5, 40 10, 10 20, 5 10, 15 5)))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPolygon',
+    properties: {
+      z: true
+    },
+    coordinates: [
+      [
+        [ [30, 20, 1], [10, 40, 2], [45, 40, 3], [30, 20, 4] ]
+      ],
+      [
+        [ [15, 5], [40, 10], [10, 20], [5, 10], [15, 5] ]
+      ]
+    ]
+  });
+});
+
+test('should parse a MULTIPOLYGON with a M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOLYGON M (((30 20 1, 10 40 2, 45 40 3, 30 20 4)),((15 5, 40 10, 10 20, 5 10, 15 5)))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPolygon',
+    properties: {
+      m: true
+    },
+    coordinates: [
+      [
+        [ [30, 20, 1], [10, 40, 2], [45, 40, 3], [30, 20, 4] ]
+      ],
+      [
+        [ [15, 5], [40, 10], [10, 20], [5, 10], [15, 5] ]
+      ]
+    ]
+  });
+});
+
+test('should parse a MULTIPOLYGON with a Z and M coordinate', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOLYGON ZM (((30 20 1 0, 10 40 2 1, 45 40 3 2, 30 20 4 3)),((15 5, 40 10, 10 20, 5 10, 15 5)))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPolygon',
+    properties: {
+      m: true,
+      z: true
+    },
+    coordinates: [
+      [
+        [ [30, 20, 1, 0], [10, 40, 2, 1], [45, 40, 3, 2], [30, 20, 4, 3] ]
+      ],
+      [
+        [ [15, 5], [40, 10], [10, 20], [5, 10], [15, 5] ]
+      ]
+    ]
+  });
+});
+
+test('should parse a MULTIPOLYGON with a hole', function (t) {
+  t.plan(1);
+
+  const input = 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20)))';
+
+  const output = wktToGeoJSON(input);
+
+  t.deepEqual(output, {
+    type: 'MultiPolygon',
+    coordinates: [
+      [
+        [ [40, 40], [20, 45], [45, 30], [40, 40] ]
+      ],
+      [
+        [ [20, 35], [45, 20], [30, 5], [10, 10], [10, 30], [20, 35] ],
+        [ [30, 20], [20, 25], [20, 15], [30, 20] ]
+      ]
+    ]
+  });
+});
diff --git a/packages/wkt/wkt.js b/packages/wkt/wkt.js
new file mode 100644
index 0000000..b7cee6b
--- /dev/null
+++ b/packages/wkt/wkt.js
@@ -0,0 +1,334 @@
+/* global parser */ // via jison
+
+/* Copyright (c) 2012-2019 Environmental Systems Research Institute, Inc.
+ * MIT */
+
+/** @module @terraformer/wkt */
+
+'SOURCE';
+
+function PointArray (point) {
+  this.data = [ point ];
+  this.type = 'PointArray';
+}
+
+PointArray.prototype.addPoint = function (point) {
+  if (point.type === 'PointArray') {
+    this.data = this.data.concat(point.data);
+  } else {
+    this.data.push(point);
+  }
+
+  return this;
+};
+
+PointArray.prototype.toJSON = function () {
+  return this.data;
+};
+
+function Ring (point) {
+  this.data = point;
+  this.type = 'Ring';
+}
+
+Ring.prototype.toJSON = function () {
+  var data = [ ];
+
+  for (var i = 0; i < this.data.data.length; i++) {
+    data.push(this.data.data[i]);
+  }
+
+  return data;
+};
+
+function RingList (ring) {
+  this.data = [ ring ];
+  this.type = 'RingList';
+}
+
+RingList.prototype.addRing = function (ring) {
+  this.data.push(ring);
+
+  return this;
+};
+
+RingList.prototype.toJSON = function () {
+  var data = [ ];
+
+  for (var i = 0; i < this.data.length; i++) {
+    data.push(this.data[i].toJSON());
+  }
+
+  if (data.length === 1) {
+    return data;
+  } else {
+    return data;
+  }
+};
+
+function PolygonList (polygon) {
+  this.data = [ polygon ];
+  this.type = 'PolygonList';
+}
+
+PolygonList.prototype.addPolygon = function (polygon) {
+  this.data.push(polygon);
+
+  return this;
+};
+
+PolygonList.prototype.toJSON = function () {
+  var data = [ ];
+
+  for (var i = 0; i < this.data.length; i++) {
+    data = data.concat([ this.data[i].toJSON() ]);
+  }
+
+  return data;
+};
+
+/**
+ * Converts a [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) geometry into a GeoJSON geometry.
+ * @function
+ * @param {string} WKT - The input WKT geometry.
+ * @return {object} GeoJSON.
+ *
+ * ```js
+ * import { wktToGeoJSON } from "@terraformer/wkt"
+ *
+ * wktToGeoJSON("POINT (-122.6764, 45.5165)");
+ *
+ * >> { "type": "Point", "coordinates": [ -122.6764, 45.5165 ] }
+ * ```
+ */
+export const wktToGeoJSON = (element) => {
+  let res;
+
+  try {
+    res = parser.parse(element);
+  } catch (err) {
+    throw Error('Unable to parse: ' + err);
+  }
+
+  return res;
+};
+
+const arrayToRing = (arr) => {
+  let parts = [ ];
+  let ret = '';
+
+  for (var i = 0; i < arr.length; i++) {
+    parts.push(arr[i].join(' '));
+  }
+
+  ret += '(' + parts.join(', ') + ')';
+
+  return ret;
+};
+
+const pointToWKTPoint = (geojson) => {
+  let ret = 'POINT ';
+
+  if (geojson.coordinates === undefined || geojson.coordinates.length === 0) {
+    ret += 'EMPTY';
+
+    return ret;
+  } else if (geojson.coordinates.length === 3) {
+    // 3d or time? default to 3d
+    if (geojson.properties && geojson.properties.m === true) {
+      ret += 'M ';
+    } else {
+      ret += 'Z ';
+    }
+  } else if (geojson.coordinates.length === 4) {
+    // 3d and time
+    ret += 'ZM ';
+  }
+
+  // include coordinates
+  ret += '(' + geojson.coordinates.join(' ') + ')';
+
+  return ret;
+};
+
+const lineStringToWKTLineString = (geojson) => {
+  let ret = 'LINESTRING ';
+
+  if (geojson.coordinates === undefined || geojson.coordinates.length === 0 || geojson.coordinates[0].length === 0) {
+    ret += 'EMPTY';
+
+    return ret;
+  } else if (geojson.coordinates[0].length === 3) {
+    if (geojson.properties && geojson.properties.m === true) {
+      ret += 'M ';
+    } else {
+      ret += 'Z ';
+    }
+  } else if (geojson.coordinates[0].length === 4) {
+    ret += 'ZM ';
+  }
+
+  ret += arrayToRing(geojson.coordinates);
+
+  return ret;
+};
+
+const polygonToWKTPolygon = (geojson) => {
+  let ret = 'POLYGON ';
+
+  if (geojson.coordinates === undefined || geojson.coordinates.length === 0 || geojson.coordinates[0].length === 0) {
+    ret += 'EMPTY';
+
+    return ret;
+  } else if (geojson.coordinates[0][0].length === 3) {
+    if (geojson.properties && geojson.properties.m === true) {
+      ret += 'M ';
+    } else {
+      ret += 'Z ';
+    }
+  } else if (geojson.coordinates[0][0].length === 4) {
+    ret += 'ZM ';
+  }
+
+  ret += '(';
+  var parts = [ ];
+  for (var i = 0; i < geojson.coordinates.length; i++) {
+    parts.push(arrayToRing(geojson.coordinates[i]));
+  }
+
+  ret += parts.join(', ');
+  ret += ')';
+
+  return ret;
+};
+
+const multiPointToWKTMultiPoint = (geojson) => {
+  var ret = 'MULTIPOINT ';
+
+  if (geojson.coordinates === undefined || geojson.coordinates.length === 0 || geojson.coordinates[0].length === 0) {
+    ret += 'EMPTY';
+
+    return ret;
+  } else if (geojson.coordinates[0].length === 3) {
+    if (geojson.properties && geojson.properties.m === true) {
+      ret += 'M ';
+    } else {
+      ret += 'Z ';
+    }
+  } else if (geojson.coordinates[0].length === 4) {
+    ret += 'ZM ';
+  }
+
+  ret += arrayToRing(geojson.coordinates);
+
+  return ret;
+};
+
+const multiLineStringToWKTMultiLineString = (geojson) => {
+  let ret = 'MULTILINESTRING ';
+
+  if (geojson.coordinates === undefined || geojson.coordinates.length === 0 || geojson.coordinates[0].length === 0) {
+    ret += 'EMPTY';
+
+    return ret;
+  } else if (geojson.coordinates[0][0].length === 3) {
+    if (geojson.properties && geojson.properties.m === true) {
+      ret += 'M ';
+    } else {
+      ret += 'Z ';
+    }
+  } else if (geojson.coordinates[0][0].length === 4) {
+    ret += 'ZM ';
+  }
+
+  ret += '(';
+  let parts = [ ];
+  for (var i = 0; i < geojson.coordinates.length; i++) {
+    parts.push(arrayToRing(geojson.coordinates[i]));
+  }
+
+  ret += parts.join(', ');
+  ret += ')';
+
+  return ret;
+};
+
+const multiPolygonToWKTMultiPolygon = (geojson) => {
+  var ret = 'MULTIPOLYGON ';
+
+  if (geojson.coordinates === undefined || geojson.coordinates.length === 0 || geojson.coordinates[0].length === 0) {
+    ret += 'EMPTY';
+
+    return ret;
+  } else if (geojson.coordinates[0][0][0].length === 3) {
+    if (geojson.properties && geojson.properties.m === true) {
+      ret += 'M ';
+    } else {
+      ret += 'Z ';
+    }
+  } else if (geojson.coordinates[0][0][0].length === 4) {
+    ret += 'ZM ';
+  }
+
+  ret += '(';
+  var inner = [ ];
+  for (var c = 0; c < geojson.coordinates.length; c++) {
+    var it = '(';
+    var parts = [ ];
+    for (var i = 0; i < geojson.coordinates[c].length; i++) {
+      parts.push(arrayToRing(geojson.coordinates[c][i]));
+    }
+
+    it += parts.join(', ');
+    it += ')';
+
+    inner.push(it);
+  }
+
+  ret += inner.join(', ');
+  ret += ')';
+
+  return ret;
+};
+
+/**
+ * Converts a GeoJSON geometry or GeometryCollection into a [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) string.
+ * @function
+ * @param {object} GeoJSON - The input GeoJSON geometry or GeometryCollection.
+ * @return {string} WKT.
+ * ```js
+ * import { geojsonToWKT } from "@terraformer/wkt"
+ *
+ * geojsonToWKT({
+ *   "type": "Point",
+ *   "coordinates": [-122.6764, 45.5165]
+ * })
+ *
+ * >> "POINT (-122.6764, 45.5165)"
+ * ```
+ */
+export const geojsonToWKT = (geojson) => {
+  switch (geojson.type) {
+    case 'Point':
+      return pointToWKTPoint(geojson);
+    case 'LineString':
+      return lineStringToWKTLineString(geojson);
+    case 'Polygon':
+      return polygonToWKTPolygon(geojson);
+    case 'MultiPoint':
+      return multiPointToWKTMultiPoint(geojson);
+    case 'MultiLineString':
+      return multiLineStringToWKTMultiLineString(geojson);
+    case 'MultiPolygon':
+      return multiPolygonToWKTMultiPolygon(geojson);
+    case 'GeometryCollection':
+      var ret = 'GEOMETRYCOLLECTION';
+      var parts = [ ];
+      for (let i = 0; i < geojson.geometries.length; i++) {
+        parts.push(geojsonToWKT(geojson.geometries[i]));
+      }
+      return ret + '(' + parts.join(', ') + ')';
+    default:
+      throw Error('Unknown Type: ' + geojson.type);
+  }
+};
diff --git a/packages/wkt/wkt.yy b/packages/wkt/wkt.yy
new file mode 100644
index 0000000..cc58897
--- /dev/null
+++ b/packages/wkt/wkt.yy
@@ -0,0 +1,178 @@
+
+
+%lex
+
+%%
+
+\s+                                          // ignore
+"("                                          return '('
+")"                                          return ')'
+"-"?[0-9]+("."[0-9]+)?([eE][\-\+]?[0-9]+)?   return 'DOUBLE_TOK'
+"POINT"                                      return 'POINT'
+"LINESTRING"                                 return 'LINESTRING'
+"POLYGON"                                    return 'POLYGON'
+"MULTIPOINT"                                 return 'MULTIPOINT'
+"MULTILINESTRING"                            return 'MULTILINESTRING'
+"MULTIPOLYGON"                               return 'MULTIPOLYGON'
+","                                          return 'COMMA'
+"EMPTY"                                      return 'EMPTY'
+"M"                                          return 'M'
+"Z"                                          return 'Z'
+"ZM"                                         return 'ZM'
+<<EOF>>                                      return 'EOF'
+.                                            return "INVALID"
+
+/lex
+
+
+%start expressions
+
+%% /* language grammar */
+
+expressions
+    : point EOF
+        { return $1; }
+    | linestring EOF
+        { return $1; }
+    | polygon EOF
+        { return $1; }
+    | multipoint EOF
+        { return $1; }
+    | multilinestring EOF
+        { return $1; }
+    | multipolygon EOF
+        { return $1; }
+    ;
+
+coordinate
+    : DOUBLE_TOK DOUBLE_TOK
+        { $$ = new PointArray([ Number($1), Number($2) ]); }
+    | DOUBLE_TOK DOUBLE_TOK DOUBLE_TOK
+        { $$ = new PointArray([ Number($1), Number($2), Number($3) ]); }
+    | DOUBLE_TOK DOUBLE_TOK DOUBLE_TOK DOUBLE_TOK
+        { $$ = new PointArray([ Number($1), Number($2), Number($3), Number($4) ]); }
+    ;
+
+ptarray
+    : ptarray COMMA coordinate
+        { $$ = $1.addPoint($3); }
+    | coordinate
+        { $$ = $1; }
+    ;
+
+ring_list
+    : ring_list COMMA ring
+        { $$ = $1.addRing($3); }
+    | ring
+        { $$ = new RingList($1); }
+    ;
+
+ring
+    : '(' ptarray ')'
+        { $$ = new Ring($2); }
+    ;
+
+point
+    : POINT '(' ptarray ')'
+        { $$ = { "type": "Point", "coordinates": $3.data[0] }; }
+    | POINT Z '(' ptarray ')'
+        { $$ = { "type": "Point", "coordinates": $4.data[0], "properties": { z: true } }; }
+    | POINT ZM '(' ptarray ')'
+        { $$ = { "type": "Point", "coordinates": $4.data[0], "properties": { z: true, m: true } }; }
+    | POINT M '(' ptarray ')'
+        { $$ = { "type": "Point", "coordinates": $4.data[0], "properties": { m: true } }; }
+    | POINT EMPTY
+        { $$ = { "type": "Point", "coordinates": [ ] }; }
+    ;
+
+point_untagged
+    : coordinate
+        { $$ = $1; }
+    | '(' coordinate ')'
+        { $$ = $2; }
+    ;
+
+polygon_list
+    : polygon_list COMMA polygon_untagged
+        { $$ = $1.addPolygon($3); }
+    | polygon_untagged
+        { $$ = new PolygonList($1); }
+    ;
+
+polygon_untagged
+    : '(' ring_list ')'
+        { $$ = $2; }
+    ;
+
+
+point_list
+    : point_list COMMA point_untagged
+        { $$ = $1.addPoint($3); }
+    | point_untagged
+        { $$ = $1; }
+    ;
+
+linestring
+    : LINESTRING '(' point_list ')'
+        { $$ = { "type": "LineString", "coordinates": $3.data }; }
+    | LINESTRING Z '(' point_list ')'
+        { $$ = { "type": "LineString", "coordinates": $4.data, "properties": { z: true } }; }
+    | LINESTRING M '(' point_list ')'
+        { $$ = { "type": "LineString", "coordinates": $4.data, "properties": { m: true } }; }
+    | LINESTRING ZM '(' point_list ')'
+        { $$ = { "type": "LineString", "coordinates": $4.data, "properties": { z: true, m: true } }; }
+    | LINESTRING EMPTY
+        { $$ = { "type": "LineString", "coordinates": [ ] }; }
+    ;
+
+polygon
+    : POLYGON '(' ring_list ')'
+        { $$ = { "type": "Polygon", "coordinates": $3.toJSON() }; }
+    | POLYGON Z '(' ring_list ')'
+        { $$ = { "type": "Polygon", "coordinates": $4.toJSON(), "properties": { z: true } }; }
+    | POLYGON M '(' ring_list ')'
+        { $$ = { "type": "Polygon", "coordinates": $4.toJSON(), "properties": { m: true } }; }
+    | POLYGON ZM '(' ring_list ')'
+        { $$ = { "type": "Polygon", "coordinates": $4.toJSON(), "properties": { z: true, m: true } }; }
+    | POLYGON EMPTY
+        { $$ = { "type": "Polygon", "coordinates": [ ] }; }
+    ;
+
+multipoint
+    : MULTIPOINT '(' point_list ')'
+        { $$ = { "type": "MultiPoint", "coordinates": $3.data }; }
+    | MULTIPOINT Z '(' point_list ')'
+        { $$ = { "type": "MultiPoint", "coordinates": $4.data, "properties": { z: true } }; }
+    | MULTIPOINT M '(' point_list ')'
+        { $$ = { "type": "MultiPoint", "coordinates": $4.data, "properties": { m: true } }; }
+    | MULTIPOINT ZM '(' point_list ')'
+        { $$ = { "type": "MultiPoint", "coordinates": $4.data, "properties": { z: true, m: true } }; }
+    | MULTIPOINT EMPTY
+        { $$ = { "type": "MultiPoint", "coordinates": [ ] } }
+    ;
+
+multilinestring
+    : MULTILINESTRING '(' ring_list ')'
+        { $$ = { "type": "MultiLineString", "coordinates": $3.toJSON() }; }
+    | MULTILINESTRING Z '(' ring_list ')'
+        { $$ = { "type": "MultiLineString", "coordinates": $4.toJSON(), "properties": { z: true } }; }
+    | MULTILINESTRING M '(' ring_list ')'
+        { $$ = { "type": "MultiLineString", "coordinates": $4.toJSON(), "properties": { m: true } }; }
+    | MULTILINESTRING ZM '(' ring_list ')'
+        { $$ = { "type": "MultiLineString", "coordinates": $4.toJSON(), "properties": { z: true, m: true } }; }
+    | MULTILINESTRING EMPTY
+        { $$ = { "type": "MultiLineString", "coordinates": [ ] }; }
+    ;
+
+multipolygon
+    : MULTIPOLYGON '(' polygon_list ')'
+        { $$ = { "type": "MultiPolygon", "coordinates": $3.toJSON() }; }
+    | MULTIPOLYGON Z '(' polygon_list ')'
+        { $$ = { "type": "MultiPolygon", "coordinates": $4.toJSON(), "properties": { z: true } }; }
+    | MULTIPOLYGON M '(' polygon_list ')'
+        { $$ = { "type": "MultiPolygon", "coordinates": $4.toJSON(), "properties": { m: true } }; }
+    | MULTIPOLYGON ZM '(' polygon_list ')'
+        { $$ = { "type": "MultiPolygon", "coordinates": $4.toJSON(), "properties": { z: true, m: true } }; }
+    | MULTIPOLYGON EMPTY
+        { $$ = { "type": "MultiPolygon", "coordinates": [ ] }; }
+    ;
\ No newline at end of file