Repository: elm-tooling/elm-language-server
Pull request #396 (2020-12-11 07:21:21)

Fix module resolution
Changed a few things. The main thing is that we don't resolve modules just based on the file system (should also be faster without checking the disk). Instead we resolve accessible (exposed) modules on workspace init. There isn't a good way to a test for this right now, but I did a good amount of manual testing and the diagnostics tests will show a regression is resolving modules.

As a side note, we need a better way structure our tests. Right now, we have MockElmWorkspace, which has a lot of duplicated logic from ElmWorkspace. Ideally, we should be able to test logic in ElmWorkspace as well. 


Patch:
diff --git a/package-lock.json b/package-lock.json
index 4107ea8..9564023 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "@elm-tooling/elm-language-server",
-  "version": "2.0.0-RC2",
+  "version": "1.13.2",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -434,9 +434,9 @@
       }
     },
     "@eslint/eslintrc": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
-      "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz",
+      "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==",
       "dev": true,
       "requires": {
         "ajv": "^6.12.4",
@@ -737,18 +737,35 @@
       "dev": true
     },
     "@textlint/markdown-to-ast": {
-      "version": "6.1.7",
-      "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-6.1.7.tgz",
-      "integrity": "sha512-B0QtokeQR4a9+4q0NQr8T9l7A1fFihTN5Ze57tVgqW+3ymzXEouh8DvPHeNQ4T6jEkAThvdjk95mxAMpGRJ79w==",
+      "version": "6.0.9",
+      "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-6.0.9.tgz",
+      "integrity": "sha512-hfAWBvTeUGh5t5kTn2U3uP3qOSM1BSrxzl1jF3nn0ywfZXpRBZr5yRjXnl4DzIYawCtZOshmRi/tI3/x4TE1jQ==",
       "dev": true,
       "requires": {
-        "@textlint/ast-node-types": "^4.2.5",
-        "debug": "^4.1.1",
+        "@textlint/ast-node-types": "^4.0.3",
+        "debug": "^2.1.3",
         "remark-frontmatter": "^1.2.0",
         "remark-parse": "^5.0.0",
         "structured-source": "^3.0.2",
         "traverse": "^0.6.6",
         "unified": "^6.1.6"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
       }
     },
     "@types/babel__core": {
@@ -826,9 +843,9 @@
       }
     },
     "@types/jest": {
-      "version": "26.0.19",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.19.tgz",
-      "integrity": "sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ==",
+      "version": "26.0.16",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.16.tgz",
+      "integrity": "sha512-Gp12+7tmKCgv9JjtltxUXokohCAEZfpJaEW5tn871SGRp8I+bRWBonQO7vW5NHwnAHe5dd50+Q4zyKuN35i09g==",
       "dev": true,
       "requires": {
         "jest-diff": "^26.0.0",
@@ -842,9 +859,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "14.14.12",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.12.tgz",
-      "integrity": "sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g==",
+      "version": "14.14.10",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
+      "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==",
       "dev": true
     },
     "@types/normalize-package-data": {
@@ -881,13 +898,13 @@
       "dev": true
     },
     "@typescript-eslint/eslint-plugin": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.1.tgz",
-      "integrity": "sha512-QRLDSvIPeI1pz5tVuurD+cStNR4sle4avtHhxA+2uyixWGFjKzJ+EaFVRW6dA/jOgjV5DTAjOxboQkRDE8cRlQ==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.9.0.tgz",
+      "integrity": "sha512-WrVzGMzzCrgrpnQMQm4Tnf+dk+wdl/YbgIgd5hKGa2P+lnJ2MON+nQnbwgbxtN9QDLi8HO+JAq0/krMnjQK6Cw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "4.9.1",
-        "@typescript-eslint/scope-manager": "4.9.1",
+        "@typescript-eslint/experimental-utils": "4.9.0",
+        "@typescript-eslint/scope-manager": "4.9.0",
         "debug": "^4.1.1",
         "functional-red-black-tree": "^1.0.1",
         "regexpp": "^3.0.0",
@@ -896,55 +913,55 @@
       }
     },
     "@typescript-eslint/experimental-utils": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.1.tgz",
-      "integrity": "sha512-c3k/xJqk0exLFs+cWSJxIjqLYwdHCuLWhnpnikmPQD2+NGAx9KjLYlBDcSI81EArh9FDYSL6dslAUSwILeWOxg==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.9.0.tgz",
+      "integrity": "sha512-0p8GnDWB3R2oGhmRXlEnCvYOtaBCijtA5uBfH5GxQKsukdSQyI4opC4NGTUb88CagsoNQ4rb/hId2JuMbzWKFQ==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
-        "@typescript-eslint/scope-manager": "4.9.1",
-        "@typescript-eslint/types": "4.9.1",
-        "@typescript-eslint/typescript-estree": "4.9.1",
+        "@typescript-eslint/scope-manager": "4.9.0",
+        "@typescript-eslint/types": "4.9.0",
+        "@typescript-eslint/typescript-estree": "4.9.0",
         "eslint-scope": "^5.0.0",
         "eslint-utils": "^2.0.0"
       }
     },
     "@typescript-eslint/parser": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.1.tgz",
-      "integrity": "sha512-Gv2VpqiomvQ2v4UL+dXlQcZ8zCX4eTkoIW+1aGVWT6yTO+6jbxsw7yQl2z2pPl/4B9qa5JXeIbhJpONKjXIy3g==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.9.0.tgz",
+      "integrity": "sha512-QRSDAV8tGZoQye/ogp28ypb8qpsZPV6FOLD+tbN4ohKUWHD2n/u0Q2tIBnCsGwQCiD94RdtLkcqpdK4vKcLCCw==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/scope-manager": "4.9.1",
-        "@typescript-eslint/types": "4.9.1",
-        "@typescript-eslint/typescript-estree": "4.9.1",
+        "@typescript-eslint/scope-manager": "4.9.0",
+        "@typescript-eslint/types": "4.9.0",
+        "@typescript-eslint/typescript-estree": "4.9.0",
         "debug": "^4.1.1"
       }
     },
     "@typescript-eslint/scope-manager": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.1.tgz",
-      "integrity": "sha512-sa4L9yUfD/1sg9Kl8OxPxvpUcqxKXRjBeZxBuZSSV1v13hjfEJkn84n0An2hN8oLQ1PmEl2uA6FkI07idXeFgQ==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.9.0.tgz",
+      "integrity": "sha512-q/81jtmcDtMRE+nfFt5pWqO0R41k46gpVLnuefqVOXl4QV1GdQoBWfk5REcipoJNQH9+F5l+dwa9Li5fbALjzg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.9.1",
-        "@typescript-eslint/visitor-keys": "4.9.1"
+        "@typescript-eslint/types": "4.9.0",
+        "@typescript-eslint/visitor-keys": "4.9.0"
       }
     },
     "@typescript-eslint/types": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.1.tgz",
-      "integrity": "sha512-fjkT+tXR13ks6Le7JiEdagnwEFc49IkOyys7ueWQ4O8k4quKPwPJudrwlVOJCUQhXo45PrfIvIarcrEjFTNwUA==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.9.0.tgz",
+      "integrity": "sha512-luzLKmowfiM/IoJL/rus1K9iZpSJK6GlOS/1ezKplb7MkORt2dDcfi8g9B0bsF6JoRGhqn0D3Va55b+vredFHA==",
       "dev": true
     },
     "@typescript-eslint/typescript-estree": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.1.tgz",
-      "integrity": "sha512-bzP8vqwX6Vgmvs81bPtCkLtM/Skh36NE6unu6tsDeU/ZFoYthlTXbBmpIrvosgiDKlWTfb2ZpPELHH89aQjeQw==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.9.0.tgz",
+      "integrity": "sha512-rmDR++PGrIyQzAtt3pPcmKWLr7MA+u/Cmq9b/rON3//t5WofNR4m/Ybft2vOLj0WtUzjn018ekHjTsnIyBsQug==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.9.1",
-        "@typescript-eslint/visitor-keys": "4.9.1",
+        "@typescript-eslint/types": "4.9.0",
+        "@typescript-eslint/visitor-keys": "4.9.0",
         "debug": "^4.1.1",
         "globby": "^11.0.1",
         "is-glob": "^4.0.1",
@@ -954,12 +971,12 @@
       }
     },
     "@typescript-eslint/visitor-keys": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.1.tgz",
-      "integrity": "sha512-9gspzc6UqLQHd7lXQS7oWs+hrYggspv/rk6zzEMhCbYwPE/sF7oxo7GAjkS35Tdlt7wguIG+ViWCPtVZHz/ybQ==",
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.9.0.tgz",
+      "integrity": "sha512-sV45zfdRqQo1A97pOSx3fsjR+3blmwtdCt8LDrXgCX36v4Vmz4KHrhpV6Fo2cRdXmyumxx11AHw0pNJqCNpDyg==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/types": "4.9.1",
+        "@typescript-eslint/types": "4.9.0",
         "eslint-visitor-keys": "^2.0.0"
       }
     },
@@ -1766,12 +1783,6 @@
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
       "dev": true
     },
-    "create-require": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
-      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
-      "dev": true
-    },
     "cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1965,17 +1976,17 @@
       }
     },
     "doctoc": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/doctoc/-/doctoc-2.0.0.tgz",
-      "integrity": "sha512-thvCndtwVPe3GCDUG09NYPu9D72Ons3MFh/Fe5A3bePMHGa1XSMgJWyL04bkxa0DyyaylEB2UrqigbQM0fcj7w==",
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/doctoc/-/doctoc-1.4.0.tgz",
+      "integrity": "sha512-8IAq3KdMkxhXCUF+xdZxdJxwuz8N2j25sMgqiu4U4JWluN9tRKMlAalxGASszQjlZaBprdD2YfXpL3VPWUD4eg==",
       "dev": true,
       "requires": {
-        "@textlint/markdown-to-ast": "~6.1.7",
-        "anchor-markdown-header": "~0.5.7",
-        "htmlparser2": "~4.1.0",
-        "minimist": "~1.2.5",
-        "underscore": "~1.10.2",
-        "update-section": "~0.3.3"
+        "@textlint/markdown-to-ast": "~6.0.9",
+        "anchor-markdown-header": "^0.5.5",
+        "htmlparser2": "~3.9.2",
+        "minimist": "~1.2.0",
+        "underscore": "~1.8.3",
+        "update-section": "^0.3.0"
       }
     },
     "doctrine": {
@@ -1988,31 +1999,33 @@
       }
     },
     "dom-serializer": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz",
-      "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==",
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+      "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
       "dev": true,
       "requires": {
         "domelementtype": "^2.0.1",
-        "domhandler": "^4.0.0",
         "entities": "^2.0.0"
       },
       "dependencies": {
-        "domhandler": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz",
-          "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==",
-          "dev": true,
-          "requires": {
-            "domelementtype": "^2.1.0"
-          }
+        "domelementtype": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
+          "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==",
+          "dev": true
+        },
+        "entities": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+          "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+          "dev": true
         }
       }
     },
     "domelementtype": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz",
-      "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==",
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+      "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
       "dev": true
     },
     "domexception": {
@@ -2033,34 +2046,22 @@
       }
     },
     "domhandler": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
-      "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+      "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
       "dev": true,
       "requires": {
-        "domelementtype": "^2.0.1"
+        "domelementtype": "1"
       }
     },
     "domutils": {
-      "version": "2.4.4",
-      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz",
-      "integrity": "sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==",
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+      "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
       "dev": true,
       "requires": {
-        "dom-serializer": "^1.0.1",
-        "domelementtype": "^2.0.1",
-        "domhandler": "^4.0.0"
-      },
-      "dependencies": {
-        "domhandler": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz",
-          "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==",
-          "dev": true,
-          "requires": {
-            "domelementtype": "^2.1.0"
-          }
-        }
+        "dom-serializer": "0",
+        "domelementtype": "1"
       }
     },
     "ecc-jsbn": {
@@ -2089,7 +2090,6 @@
       "version": "1.4.4",
       "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
       "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
-      "dev": true,
       "requires": {
         "once": "^1.4.0"
       }
@@ -2104,9 +2104,9 @@
       }
     },
     "entities": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
-      "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+      "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
       "dev": true
     },
     "error-ex": {
@@ -2184,13 +2184,13 @@
       }
     },
     "eslint": {
-      "version": "7.15.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz",
-      "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==",
+      "version": "7.14.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz",
+      "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
-        "@eslint/eslintrc": "^0.2.2",
+        "@eslint/eslintrc": "^0.2.1",
         "ajv": "^6.10.0",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
@@ -2200,10 +2200,10 @@
         "eslint-scope": "^5.1.1",
         "eslint-utils": "^2.1.0",
         "eslint-visitor-keys": "^2.0.0",
-        "espree": "^7.3.1",
+        "espree": "^7.3.0",
         "esquery": "^1.2.0",
         "esutils": "^2.0.2",
-        "file-entry-cache": "^6.0.0",
+        "file-entry-cache": "^5.0.1",
         "functional-red-black-tree": "^1.0.1",
         "glob-parent": "^5.0.0",
         "globals": "^12.1.0",
@@ -2237,15 +2237,18 @@
       }
     },
     "eslint-config-prettier": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.0.0.tgz",
-      "integrity": "sha512-8Y8lGLVPPZdaNA7JXqnvETVC7IiVRgAP6afQu9gOQRn90YY3otMNh+x7Vr2vMePQntF+5erdSUBqSzCmU/AxaQ==",
-      "dev": true
+      "version": "6.15.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz",
+      "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^6.0.0"
+      }
     },
     "eslint-plugin-prettier": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.2.0.tgz",
-      "integrity": "sha512-kOUSJnFjAUFKwVxuzy6sA5yyMx6+o9ino4gCdShzBNx4eyFRudWRYKCFolKjoM40PEiuU6Cn7wBLfq3WsGg7qg==",
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz",
+      "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==",
       "dev": true,
       "requires": {
         "prettier-linter-helpers": "^1.0.0"
@@ -2285,13 +2288,13 @@
       "dev": true
     },
     "espree": {
-      "version": "7.3.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
-      "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz",
+      "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==",
       "dev": true,
       "requires": {
         "acorn": "^7.4.0",
-        "acorn-jsx": "^5.3.1",
+        "acorn-jsx": "^5.2.0",
         "eslint-visitor-keys": "^1.3.0"
       },
       "dependencies": {
@@ -2362,18 +2365,18 @@
       "dev": true
     },
     "execa": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz",
-      "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+      "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
       "requires": {
-        "cross-spawn": "^7.0.3",
-        "get-stream": "^6.0.0",
-        "human-signals": "^2.1.0",
+        "cross-spawn": "^7.0.0",
+        "get-stream": "^5.0.0",
+        "human-signals": "^1.1.1",
         "is-stream": "^2.0.0",
         "merge-stream": "^2.0.0",
-        "npm-run-path": "^4.0.1",
-        "onetime": "^5.1.2",
-        "signal-exit": "^3.0.3",
+        "npm-run-path": "^4.0.0",
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2",
         "strip-final-newline": "^2.0.0"
       }
     },
@@ -2614,12 +2617,12 @@
       }
     },
     "file-entry-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz",
-      "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==",
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+      "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
       "dev": true,
       "requires": {
-        "flat-cache": "^3.0.4"
+        "flat-cache": "^2.0.1"
       }
     },
     "fill-range": {
@@ -2641,19 +2644,20 @@
       }
     },
     "flat-cache": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
-      "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+      "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
       "dev": true,
       "requires": {
-        "flatted": "^3.1.0",
-        "rimraf": "^3.0.2"
+        "flatted": "^2.0.0",
+        "rimraf": "2.6.3",
+        "write": "1.0.3"
       }
     },
     "flatted": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz",
-      "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+      "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
       "dev": true
     },
     "for-in": {
@@ -2801,10 +2805,19 @@
       "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
       "dev": true
     },
-    "get-stream": {
+    "get-stdin": {
       "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz",
-      "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg=="
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+      "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+      "dev": true
+    },
+    "get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "requires": {
+        "pump": "^3.0.0"
+      }
     },
     "get-value": {
       "version": "2.0.6",
@@ -2995,15 +3008,49 @@
       "dev": true
     },
     "htmlparser2": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
-      "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
+      "version": "3.9.2",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz",
+      "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=",
       "dev": true,
       "requires": {
-        "domelementtype": "^2.0.1",
-        "domhandler": "^3.0.0",
-        "domutils": "^2.0.0",
-        "entities": "^2.0.0"
+        "domelementtype": "^1.3.0",
+        "domhandler": "^2.3.0",
+        "domutils": "^1.5.1",
+        "entities": "^1.1.1",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.2"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+          "dev": true
+        },
+        "readable-stream": {
+          "version": "2.3.7",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+          "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+          "dev": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "dev": true,
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
       }
     },
     "http-signature": {
@@ -3018,9 +3065,9 @@
       }
     },
     "human-signals": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
-      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+      "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
     },
     "iconv-lite": {
       "version": "0.4.24",
@@ -3085,9 +3132,9 @@
       "dev": true
     },
     "ini": {
-      "version": "1.3.8",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
-      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
       "dev": true
     },
     "ip-regex": {
@@ -3440,40 +3487,6 @@
         "@jest/types": "^26.6.2",
         "execa": "^4.0.0",
         "throat": "^5.0.0"
-      },
-      "dependencies": {
-        "execa": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
-          "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
-          "dev": true,
-          "requires": {
-            "cross-spawn": "^7.0.0",
-            "get-stream": "^5.0.0",
-            "human-signals": "^1.1.1",
-            "is-stream": "^2.0.0",
-            "merge-stream": "^2.0.0",
-            "npm-run-path": "^4.0.0",
-            "onetime": "^5.1.0",
-            "signal-exit": "^3.0.2",
-            "strip-final-newline": "^2.0.0"
-          }
-        },
-        "get-stream": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
-          "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
-          "dev": true,
-          "requires": {
-            "pump": "^3.0.0"
-          }
-        },
-        "human-signals": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
-          "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
-          "dev": true
-        }
       }
     },
     "jest-config": {
@@ -4427,7 +4440,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -4693,7 +4705,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
       "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
-      "dev": true,
       "requires": {
         "end-of-stream": "^1.1.0",
         "once": "^1.3.1"
@@ -4999,9 +5010,9 @@
       "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
     },
     "rimraf": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
-      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "version": "2.6.3",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+      "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
       "dev": true,
       "requires": {
         "glob": "^7.1.3"
@@ -6043,9 +6054,9 @@
       "dev": true
     },
     "tree-sitter-elm": {
-      "version": "4.4.1",
-      "resolved": "https://registry.npmjs.org/tree-sitter-elm/-/tree-sitter-elm-4.4.1.tgz",
-      "integrity": "sha512-fR1tVMASq+ZOb+0u0xIV/a95AAyb730hIlp9VeGnTEW19gipqDmaM0Nnt8c6x6jL3KEJPRIV18h6EtFOE8uklg==",
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/tree-sitter-elm/-/tree-sitter-elm-4.4.0.tgz",
+      "integrity": "sha512-VhY75Z6s1yXToJdP0E6VQdVJmAGhttHD6H9kRK5m537XMtgaA1ySpi7TeJpN7oSUCuOqfPdMRzfuVkBzRr7QEg==",
       "dev": true,
       "requires": {
         "nan": "^2.14.2",
@@ -6109,13 +6120,12 @@
       }
     },
     "ts-node": {
-      "version": "9.1.1",
-      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
-      "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
+      "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
       "dev": true,
       "requires": {
         "arg": "^4.1.0",
-        "create-require": "^1.1.0",
         "diff": "^4.0.1",
         "make-error": "^1.1.1",
         "source-map-support": "^0.5.17",
@@ -6190,15 +6200,15 @@
       }
     },
     "typescript": {
-      "version": "4.1.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
-      "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz",
+      "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==",
       "dev": true
     },
     "underscore": {
-      "version": "1.10.2",
-      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
-      "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==",
+      "version": "1.8.3",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
+      "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=",
       "dev": true
     },
     "unherit": {
@@ -6442,25 +6452,25 @@
       }
     },
     "vscode-jsonrpc": {
-      "version": "6.0.0-next.10",
-      "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.10.tgz",
-      "integrity": "sha512-qaChR+84spdF4JRf/HLh1/Eva1umL6Zf4uoEyk/JUdfiLbdsZqjL1EZMyWUpjEUbIJ2BeXbsdOR+Jw/CaLKCWg=="
+      "version": "6.0.0-next.7",
+      "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0-next.7.tgz",
+      "integrity": "sha512-1nG+6cuTtpzmXe7yYfO9GCkYlyV6Ai+jDnwidHiT2T7zhc+bJM+VTtc0T/CdTlDyTNTqIcCj0V1nD4TcVjJ7Ug=="
     },
     "vscode-languageserver": {
-      "version": "7.0.0-next.13",
-      "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0-next.13.tgz",
-      "integrity": "sha512-YBXKgWxmYMQcPxY0FL5VOPMlohP9hA6LD8Z68xoTfXS9N6Vl8A8QZ9Vre46fQwiEyOSHJZdfLMTGlJi/+LAQNw==",
+      "version": "7.0.0-next.11",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0-next.11.tgz",
+      "integrity": "sha512-FVJ9se5fJsMvoPKRLlVFaNv6+RJ+EWP4vYkF8J+eR2CPbbQ6CzN1wLq3lqZTXBVOBh1toJR5B6z4j+j+OffFNA==",
       "requires": {
-        "vscode-languageserver-protocol": "3.16.0-next.13"
+        "vscode-languageserver-protocol": "3.16.0-next.11"
       }
     },
     "vscode-languageserver-protocol": {
-      "version": "3.16.0-next.13",
-      "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.13.tgz",
-      "integrity": "sha512-ZzoC7GEnuSdqord7HGnvKmIZXPeusWkl5j+TRUlQ4O2xKIZKnuCjCnTTJzQopuw15qGgZ+SESpeTGnPVyFTVAA==",
+      "version": "3.16.0-next.11",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.11.tgz",
+      "integrity": "sha512-31FmupmSmfznuMuGp7qN6h3d/hKUbexbvcwTvrUE/igqRlzFU542s8MtGICx1ERbVuDOLGp96W2Z92qbUbmBPA==",
       "requires": {
-        "vscode-jsonrpc": "6.0.0-next.10",
-        "vscode-languageserver-types": "3.16.0-next.7"
+        "vscode-jsonrpc": "6.0.0-next.7",
+        "vscode-languageserver-types": "3.16.0-next.5"
       }
     },
     "vscode-languageserver-textdocument": {
@@ -6469,9 +6479,9 @@
       "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
     },
     "vscode-languageserver-types": {
-      "version": "3.16.0-next.7",
-      "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.7.tgz",
-      "integrity": "sha512-KDH9Xb2R898kY+nfFzAVNDkguGJm0/ItQnbturcCrfRtMiuqSQpcj9mtZVHtFeFd6nWVX4fOySMEM41C2kFpCg=="
+      "version": "3.16.0-next.5",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.5.tgz",
+      "integrity": "sha512-lf8Y1XXMtF1r2oDDAmJe+drizNXkybSRXAQQk5dPy2rYJsY9SPXYNO074L3THu9zNYepzV5fRJZUPo/V/TLBRQ=="
     },
     "vscode-uri": {
       "version": "2.1.2",
@@ -6624,8 +6634,27 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+    },
+    "write": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+      "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+      "dev": true,
+      "requires": {
+        "mkdirp": "^0.5.1"
+      },
+      "dependencies": {
+        "mkdirp": {
+          "version": "0.5.5",
+          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+          "dev": true,
+          "requires": {
+            "minimist": "^1.2.5"
+          }
+        }
+      }
     },
     "write-file-atomic": {
       "version": "3.0.3",
diff --git a/package.json b/package.json
index a5ec8af..90aa348 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@elm-tooling/elm-language-server",
   "description": "Implementation of an elm language server in node.",
-  "version": "2.0.0-RC2",
+  "version": "1.13.2",
   "author": "Kolja Lampe",
   "license": "MIT",
   "main": "./out/module.js",
@@ -17,7 +17,7 @@
   },
   "dependencies": {
     "escape-string-regexp": "^4.0.0",
-    "execa": "^5.0.0",
+    "execa": "^4.1.0",
     "fast-diff": "^1.2.0",
     "fromentries": "^1.3.2",
     "globby": "^11.0.1",
@@ -25,29 +25,29 @@
     "reflect-metadata": "^0.1.13",
     "ts-debounce": "^2.1.0",
     "tsyringe": "^4.4.0",
-    "vscode-languageserver": "^7.0.0-next.13",
+    "vscode-languageserver": "^7.0.0-next.11",
     "vscode-languageserver-textdocument": "1.0.1",
     "vscode-uri": "^2.1.2",
     "web-tree-sitter": "^0.17.1"
   },
   "devDependencies": {
-    "@types/jest": "^26.0.19",
-    "@types/node": "14.14.12",
-    "@typescript-eslint/eslint-plugin": "^4.9.1",
-    "@typescript-eslint/parser": "^4.9.1",
+    "@types/jest": "^26.0.16",
+    "@types/node": "14.14.10",
+    "@typescript-eslint/eslint-plugin": "^4.9.0",
+    "@typescript-eslint/parser": "^4.9.0",
     "copyfiles": "^2.4.1",
-    "doctoc": "^2.0.0",
-    "eslint": "^7.15.0",
-    "eslint-config-prettier": "^7.0.0",
-    "eslint-plugin-prettier": "^3.2.0",
+    "doctoc": "^1.4.0",
+    "eslint": "^7.14.0",
+    "eslint-config-prettier": "^6.15.0",
+    "eslint-plugin-prettier": "^3.1.4",
     "jest": "^26.6.3",
     "jest-mock-extended": "^1.0.10",
     "prettier": "^2.2.1",
     "tree-sitter-cli": "^0.17.3",
-    "tree-sitter-elm": "^4.4.1",
+    "tree-sitter-elm": "^4.4.0",
     "ts-jest": "^26.4.4",
-    "ts-node": "^9.1.1",
-    "typescript": "4.1.3"
+    "ts-node": "^9.0.0",
+    "typescript": "4.1.2"
   },
   "scripts": {
     "version-patch": "npm --no-git-tag-version version patch",
diff --git a/src/elmWorkspace.ts b/src/elmWorkspace.ts
index 7480ba4..5a48cfa 100644
--- a/src/elmWorkspace.ts
+++ b/src/elmWorkspace.ts
@@ -1,7 +1,6 @@
-import fs from "fs";
+import fs, { readdirSync } from "fs";
 import globby from "globby";
 import os from "os";
-import path from "path";
 import { container } from "tsyringe";
 import util from "util";
 import { Connection } from "vscode-languageserver";
@@ -10,6 +9,8 @@ import Parser, { Tree } from "web-tree-sitter";
 import { ICancellationToken } from "./cancellation";
 import { Forest, IForest, ITreeContainer } from "./forest";
 import * as utils from "./util/elmUtils";
+import * as path from "./util/path";
+import { normalizeUri } from "./util/path";
 import {
   IPossibleImportsCache,
   PossibleImportsCache,
@@ -24,13 +25,11 @@ import {
 } from "./util/types/typeChecker";
 
 const readFile = util.promisify(fs.readFile);
-const readdir = util.promisify(fs.readdir);
 
-interface IFolder {
+interface IElmFile {
   path: string;
   maintainerAndPackageName?: string;
-  writeable: boolean;
-  isExposed: boolean;
+  project: ElmProject;
 }
 
 type ElmJson = IElmApplicationJson | IElmPackageJson;
@@ -63,7 +62,7 @@ interface IElmPackageJson {
   summary: string;
   license: string;
   version: string;
-  "exposed-modules": string[];
+  "exposed-modules": string[] | { [name: string]: string[] };
   "elm-version": string;
   dependencies: {
     [module: string]: string;
@@ -99,16 +98,116 @@ export interface IElmWorkspace {
     sourceFile: ITreeContainer,
     cancellationToken?: ICancellationToken,
   ): Diagnostic[];
+  hasAccessibleModule(moduleName: string): boolean;
 }
 
-export interface IRootFolder {
-  writeable: boolean;
-  maintainerAndPackageName?: string;
+export type ElmProject = IElmApplication | IElmPackage;
+
+interface IElmProject {
+  type: string;
+  uri: string;
+  dependencies: Map<string, IElmPackage>;
+  testDependencies: Map<string, IElmPackage>;
+  sourceDirectories: string[];
+  testDirectories: string[];
+  moduleToUriMap: Map<string, string>;
+}
+
+interface IElmApplication extends IElmProject {
+  type: "application";
+}
+
+interface IElmPackage extends IElmProject {
+  type: "package";
+  maintainerAndPackageName: string;
+  exposedModules: Set<string>;
+}
+
+export interface IVersion {
+  major: number;
+  minor: number;
+  patch: number;
+  string: string;
+}
+
+export interface IConstraint {
+  upper: IVersion;
+  lower: IVersion;
+  upperOperator: "<" | "<=";
+  lowerOperator: "<" | "<=";
+}
+
+export interface IPackage {
+  dependencies: Map<string, IConstraint>;
+  version: IVersion;
+}
+
+export interface IElmPackageCache {
+  get(packageName: string): IPackage[];
+}
+
+export class ElmPackageCache implements IElmPackageCache {
+  private cache = new Map<string, IPackage[]>();
+
+  constructor(
+    private packagesRoot: string,
+    private loadElmJson: (elmJsonPath: string) => ElmJson,
+  ) {}
+
+  public get(packageName: string): IPackage[] {
+    const cached = this.cache.get(packageName);
+
+    if (cached) {
+      return cached;
+    }
+
+    const maintainer = packageName.substring(0, packageName.indexOf("/"));
+    const name = packageName.substring(
+      packageName.indexOf("/") + 1,
+      packageName.length,
+    );
+
+    const pathToPackage = `${this.packagesRoot}${maintainer}/${name}/`;
+    const readDir = readdirSync(pathToPackage, "utf8");
+
+    const allVersions: IPackage[] = [];
+
+    for (const folderName of readDir) {
+      const version = utils.parseVersion(folderName);
+
+      if (
+        Number.isInteger(version.major) &&
+        Number.isInteger(version.minor) &&
+        Number.isInteger(version.patch)
+      ) {
+        const elmJsonPath = path.join(pathToPackage, folderName, "elm.json");
+        const elmJson = this.loadElmJson(elmJsonPath);
+
+        allVersions.push({
+          version,
+          dependencies: new Map(
+            Object.entries(elmJson.dependencies).map(([name, constraint]) => [
+              name,
+              utils.parseContraint(constraint),
+            ]),
+          ),
+        });
+      }
+    }
+
+    this.cache.set(packageName, allVersions);
+
+    return allVersions;
+  }
+}
+
+interface IProgramHost {
+  readFile(uri: string): Promise<string>;
+  readFileSync(uri: string): string;
+  readDirectory(uri: string): Promise<string[]>;
 }
 
 export class ElmWorkspace implements IElmWorkspace {
-  private elmFolders = new Map<string, IRootFolder>();
-  private forest: IForest = new Forest(new Map());
   private parser: Parser;
   private connection: Connection;
   private settings: Settings;
@@ -118,8 +217,14 @@ export class ElmWorkspace implements IElmWorkspace {
   private possibleImportsCache: IPossibleImportsCache;
   private operatorsCache: Map<string, DefinitionResult>;
   private diagnosticsCache: Map<string, Diagnostic[]>;
-
-  constructor(private rootPath: URI) {
+  private rootProject!: ElmProject;
+  private packagesRoot!: string;
+  private forest!: IForest;
+  private elmPackageCache!: IElmPackageCache;
+  private resolvedPackageCache = new Map<string, IElmPackage>();
+  private host: IProgramHost;
+
+  constructor(private rootPath: URI, programHost?: IProgramHost) {
     this.settings = container.resolve("Settings");
     this.connection = container.resolve("Connection");
     this.parser = container.resolve("Parser");
@@ -131,6 +236,7 @@ export class ElmWorkspace implements IElmWorkspace {
     this.possibleImportsCache = new PossibleImportsCache();
     this.operatorsCache = new Map<string, DefinitionResult>();
     this.diagnosticsCache = new Map<string, Diagnostic[]>();
+    this.host = programHost ?? this.createProgramHost();
   }
 
   public async init(
@@ -148,9 +254,10 @@ export class ElmWorkspace implements IElmWorkspace {
   }
 
   public getPath(uri: URI): string | undefined {
-    return Array.from(this.elmFolders.keys()).find((elmFolder) =>
-      uri.fsPath.startsWith(elmFolder),
-    );
+    return [
+      ...this.rootProject.sourceDirectories,
+      ...this.rootProject.testDirectories,
+    ].find((elmFolder) => uri.fsPath.startsWith(elmFolder));
   }
 
   public getSourceFile(uri: string): ITreeContainer | undefined {
@@ -253,6 +360,10 @@ export class ElmWorkspace implements IElmWorkspace {
     );
   }
 
+  public hasAccessibleModule(moduleName: string): boolean {
+    return this.rootProject.moduleToUriMap.has(moduleName);
+  }
+
   private async initWorkspace(
     progressCallback: (percent: number) => void,
   ): Promise<void> {
@@ -273,74 +384,31 @@ export class ElmWorkspace implements IElmWorkspace {
 
     const pathToElmJson = path.join(this.rootPath.fsPath, "elm.json");
     this.connection.console.info(`Reading elm.json from ${pathToElmJson}`);
-    try {
-      // Find elm files and feed them to tree sitter
-      const elmJson = require(pathToElmJson) as ElmJson;
-      if (elmJson.type === "application") {
-        elmJson["source-directories"].forEach((folder: string) => {
-          this.elmFolders.set(path.resolve(this.rootPath.fsPath, folder), {
-            maintainerAndPackageName: undefined,
-            writeable: true,
-          });
-        });
-      } else {
-        this.elmFolders.set(path.join(this.rootPath.fsPath, "src"), {
-          maintainerAndPackageName: undefined,
-          writeable: true,
-        });
-      }
-      this.elmFolders.set(path.join(this.rootPath.fsPath, "tests"), {
-        maintainerAndPackageName: undefined,
-        writeable: true,
-      });
-      this.connection.console.info(
-        `${this.elmFolders.size} source-dirs and test folders found`,
-      );
 
+    try {
       const elmHome = this.findElmHome();
-      const packagesRoot = `${elmHome}/${elmVersion}/${this.packageOrPackagesFolder(
+      this.packagesRoot = `${elmHome}/${elmVersion}/${this.packageOrPackagesFolder(
         elmVersion,
       )}/`;
-      const dependencies: { [index: string]: string } =
-        elmJson.type === "application"
-          ? {
-              ...elmJson.dependencies.direct,
-              ...elmJson.dependencies.indirect,
-              ...elmJson["test-dependencies"].direct,
-              ...elmJson["test-dependencies"].indirect,
-            }
-          : { ...elmJson.dependencies, ...elmJson["test-dependencies"] };
-      if (elmJson.type === "application") {
-        for (const key in dependencies) {
-          if (Object.prototype.hasOwnProperty.call(dependencies, key)) {
-            const maintainer = key.substring(0, key.indexOf("/"));
-            const packageName = key.substring(key.indexOf("/") + 1, key.length);
-
-            const pathToPackageWithVersion = `${packagesRoot}${maintainer}/${packageName}/${dependencies[key]}`;
-            this.elmFolders.set(pathToPackageWithVersion, {
-              maintainerAndPackageName: `${maintainer}/${packageName}`,
-              writeable: false,
-            });
-          }
-        }
-      } else {
-        // Resolve dependency tree recursively
-        await this.resolveDependencies(dependencies, packagesRoot);
-      }
 
-      const elmFilePaths = await this.findElmFilesInFolders(this.elmFolders);
+      this.elmPackageCache = new ElmPackageCache(
+        this.packagesRoot,
+        this.loadElmJson.bind(this),
+      );
+      this.rootProject = await this.loadRootProject(pathToElmJson);
+      this.forest = new Forest(this.rootProject);
+
+      const elmFilePaths = await this.findElmFilesInProject(this.rootProject);
       this.connection.console.info(
         `Found ${elmFilePaths.length.toString()} files to add to the project`,
       );
 
-      if (elmFilePaths.every((a) => !a.writeable)) {
+      if (elmFilePaths.every((a) => a.project !== this.rootProject)) {
         this.connection.window.showErrorMessage(
           "The path or paths you entered in the 'source-directories' field of your 'elm.json' does not contain any elm files.",
         );
       }
 
-      this.forest = new Forest(this.elmFolders);
-
       const promiseList: Promise<void>[] = [];
       const PARSE_STAGES = 3;
       const progressDelta = 100 / (elmFilePaths.length * PARSE_STAGES);
@@ -354,6 +422,8 @@ export class ElmWorkspace implements IElmWorkspace {
       }
       await Promise.all(promiseList);
 
+      this.findExposedModulesOfDependencies(this.rootProject);
+
       this.connection.console.info(
         `Done parsing all files for ${pathToElmJson}`,
       );
@@ -364,143 +434,225 @@ export class ElmWorkspace implements IElmWorkspace {
     }
   }
 
-  private async resolveDependencies(
-    dependencies: { [index: string]: string },
-    packagesRoot: string,
-  ): Promise<void> {
-    for (const key in dependencies) {
-      const maintainer = key.substring(0, key.indexOf("/"));
-      const packageName = key.substring(key.indexOf("/") + 1, key.length);
-
-      const pathToPackage = `${packagesRoot}${maintainer}/${packageName}/`;
-      const readDir = await readdir(pathToPackage, "utf8");
-
-      const allVersionFolders = readDir.map((folderName) => {
-        return {
-          version: folderName,
-          versionPath: `${pathToPackage}${folderName}`,
-        };
-      });
+  private async loadRootProject(elmJsonPath: string): Promise<ElmProject> {
+    const elmJson = this.loadElmJson(elmJsonPath);
+
+    if (elmJson.type === "application") {
+      const allDependencies = new Map(
+        Object.entries(
+          Object.assign(
+            elmJson.dependencies.direct,
+            elmJson.dependencies.indirect,
+            elmJson["test-dependencies"].direct,
+            elmJson["test-dependencies"].indirect,
+          ),
+        ).map(([dep, version]) => [dep, utils.parseVersion(version)]),
+      );
 
-      const matchedFolder = utils.findDepVersion(
-        allVersionFolders,
-        dependencies[key],
+      return {
+        type: "application",
+        uri: this.rootPath.toString(),
+        sourceDirectories: elmJson["source-directories"].map((folder) =>
+          path.resolve(this.rootPath.fsPath, folder),
+        ),
+        testDirectories: [path.join(this.rootPath.fsPath, "tests")],
+        dependencies: await this.loadDependencyMap(
+          elmJson.dependencies.direct,
+          allDependencies,
+        ),
+        testDependencies: await this.loadDependencyMap(
+          elmJson["test-dependencies"].direct,
+          allDependencies,
+        ),
+        moduleToUriMap: new Map<string, string>(),
+      } as IElmApplication;
+    } else {
+      const deps = new Map(
+        Object.entries(
+          Object.assign(elmJson.dependencies, elmJson["test-dependencies"]),
+        ).map(([dep, version]) => [dep, utils.parseContraint(version)]),
       );
-      const pathToPackageWithVersion = matchedFolder
-        ? `${matchedFolder.versionPath}`
-        : `${allVersionFolders[allVersionFolders.length - 1].versionPath}`;
-
-      if (!this.elmFolders.has(pathToPackageWithVersion)) {
-        this.elmFolders.set(pathToPackageWithVersion, {
-          maintainerAndPackageName: `${maintainer}/${packageName}`,
-          writeable: false,
-        });
-      }
 
-      // Resolve all dependencies for this dependency
-      const elmJsonPath = path.join(pathToPackageWithVersion, "elm.json");
-      const elmJson = require(elmJsonPath) as ElmJson;
+      const solvedVersions = utils.solveDependencies(
+        this.elmPackageCache,
+        deps,
+      );
 
-      if (elmJson.type === "package") {
-        await this.resolveDependencies(elmJson.dependencies, packagesRoot);
+      if (!solvedVersions) {
+        throw new Error("Unsolvable package constraints");
       }
+
+      return {
+        type: "package",
+        uri: this.rootPath.toString(),
+        sourceDirectories: [path.join(this.rootPath.fsPath, "src")],
+        testDirectories: [path.join(this.rootPath.fsPath, "tests")],
+        dependencies: await this.loadDependencyMap(
+          elmJson.dependencies,
+          solvedVersions,
+        ),
+        testDependencies: await this.loadDependencyMap(
+          elmJson["test-dependencies"],
+          solvedVersions,
+        ),
+        exposedModules: new Set(
+          this.flatternExposedModules(elmJson["exposed-modules"]),
+        ),
+        moduleToUriMap: new Map<string, string>(),
+      } as IElmPackage;
     }
   }
 
-  private async findElmFilesInFolders(
-    elmFolders: Map<string, IRootFolder>,
-  ): Promise<IFolder[]> {
-    let elmFilePathPromises: Promise<IFolder[]>[] = [];
-    for (const [uri, element] of elmFolders) {
-      elmFilePathPromises = elmFilePathPromises.concat(
-        this.findElmFilesInFolder({ uri, ...element }),
+  private async loadPackage(
+    packageName: string,
+    packageVersions: ReadonlyMap<string, IVersion>,
+  ): Promise<IElmPackage> {
+    const version = packageVersions.get(packageName);
+
+    if (!version) {
+      throw new Error("Problem getting package version");
+    }
+
+    // Version shouldn't be necessary, but it won't hurt
+    const cacheKey = `${packageName}@${version.string}`;
+    const cached = this.resolvedPackageCache.get(cacheKey);
+    if (cached) {
+      return Promise.resolve(cached);
+    }
+
+    const maintainer = packageName.substring(0, packageName.indexOf("/"));
+    const name = packageName.substring(
+      packageName.indexOf("/") + 1,
+      packageName.length,
+    );
+
+    const pathToPackageWithVersion = `${this.packagesRoot}${maintainer}/${name}/${version.string}`;
+
+    const elmJsonPath = path.join(pathToPackageWithVersion, "elm.json");
+    const elmJson = this.loadElmJson(elmJsonPath);
+
+    if (elmJson.type === "package") {
+      const resolvedPackage = {
+        type: "package",
+        uri: URI.file(pathToPackageWithVersion).toString(),
+        sourceDirectories: [path.join(pathToPackageWithVersion, "src")],
+        testDirectories: [path.join(pathToPackageWithVersion, "tests")],
+        dependencies: await this.loadDependencyMap(
+          elmJson.dependencies,
+          packageVersions,
+        ),
+        testDependencies: new Map<string, IElmPackage>(),
+        exposedModules: new Set(
+          this.flatternExposedModules(elmJson["exposed-modules"]),
+        ),
+        moduleToUriMap: new Map<string, string>(),
+      } as IElmPackage;
+
+      this.resolvedPackageCache.set(cacheKey, resolvedPackage);
+      return resolvedPackage;
+    } else {
+      throw new Error("Should never happen");
+    }
+  }
+
+  private async loadDependencyMap(
+    deps: {
+      [module: string]: string;
+    },
+    packageVersions: ReadonlyMap<string, IVersion>,
+  ): Promise<Map<string, IElmPackage>> {
+    const dependencyMap = new Map();
+    for (const dep in deps) {
+      dependencyMap.set(dep, await this.loadPackage(dep, packageVersions));
+    }
+    return dependencyMap;
+  }
+
+  /**
+   * Get all unique source directories from project dependency tree
+   */
+  private getSourceDirectories(project: ElmProject): Map<string, ElmProject> {
+    const sourceDirs = new Map(
+      [
+        ...project.sourceDirectories,
+        ...(project === this.rootProject ? project.testDirectories : []),
+      ].map((sourceDir) => [normalizeUri(sourceDir), project]),
+    );
+
+    project.dependencies.forEach((dep) =>
+      this.getSourceDirectories(dep).forEach((project, sourceDir) =>
+        sourceDirs.set(sourceDir, project),
+      ),
+    );
+
+    if (project === this.rootProject) {
+      project.testDependencies.forEach((dep) =>
+        this.getSourceDirectories(dep).forEach((project, sourceDir) =>
+          sourceDirs.set(sourceDir, project),
+        ),
       );
     }
+
+    return sourceDirs;
+  }
+
+  private async findElmFilesInProject(
+    project: ElmProject,
+  ): Promise<IElmFile[]> {
+    const elmFilePathPromises: Promise<IElmFile[]>[] = [];
+
+    this.getSourceDirectories(project).forEach((project, sourceDir) => {
+      elmFilePathPromises.push(
+        this.findElmFilesInProjectWorker(sourceDir, project),
+      );
+    });
+
     return (await Promise.all(elmFilePathPromises)).reduce(
       (a, b) => a.concat(b),
       [],
     );
   }
 
-  private async findElmFilesInFolder(element: {
-    uri: string;
-    writeable: boolean;
-    maintainerAndPackageName?: string;
-  }): Promise<IFolder[]> {
-    // Cleanup the path on windows, as globby does not like backslashes
-    const globUri = element.uri.replace(/\\/g, "/");
+  private async findElmFilesInProjectWorker(
+    sourceDir: string,
+    project: ElmProject,
+  ): Promise<IElmFile[]> {
+    const elmFiles: IElmFile[] = [];
 
-    this.connection.console.info(`Glob ${globUri}/**/*.elm`);
+    const maintainerAndPackageName =
+      project.type === "package" ? project.maintainerAndPackageName : undefined;
 
-    // As packages are not writeable, we want to handle these differently
-    if (element.writeable) {
-      return (
-        await globby(`${globUri}/**/*.elm`, {
-          suppressErrors: true,
-        })
-      ).map((matchingPath) => ({
-        maintainerAndPackageName: element.maintainerAndPackageName,
-        path: matchingPath,
-        writeable: element.writeable,
-        isExposed: true,
-      }));
-    } else {
-      const [elmFiles, elmJsonString] = await Promise.all([
-        globby(`${globUri}/src/**/*.elm`, { suppressErrors: true }),
-        readFile(`${element.uri}/elm.json`, {
-          encoding: "utf-8",
-        }),
-      ]);
-      const exposedModules = this.modulesToFilenames(
-        JSON.parse(elmJsonString),
-        element.uri,
-      );
-      return elmFiles.map((matchingPath) => ({
-        maintainerAndPackageName: element.maintainerAndPackageName,
+    this.connection.console.info(`Glob ${sourceDir}/**/*.elm`);
+
+    (await this.host.readDirectory(sourceDir)).forEach((matchingPath) => {
+      matchingPath = normalizeUri(matchingPath);
+
+      const moduleName = path
+        .relative(sourceDir, matchingPath)
+        .replace(".elm", "")
+        .split("/")
+        .join(".");
+
+      project.moduleToUriMap.set(moduleName, URI.file(matchingPath).toString());
+
+      elmFiles.push({
+        maintainerAndPackageName,
         path: matchingPath,
-        writeable: element.writeable,
-        isExposed: exposedModules.some(
-          (a) => a.fsPath === URI.file(matchingPath).fsPath,
-        ),
-      }));
-    }
+        project,
+      });
+    });
+
+    return elmFiles;
   }
 
-  private modulesToFilenames(elmJson: unknown, pathToPackage: string): URI[] {
-    if (!elmJson || !Object.hasOwnProperty.call(elmJson, "exposed-modules")) {
-      return [];
+  private flatternExposedModules(
+    exposedModules: string[] | { [name: string]: string[] },
+  ): string[] {
+    if (Array.isArray(exposedModules)) {
+      return exposedModules;
     }
-    const x = (elmJson as {
-      "exposed-modules": Record<string, string | string[]>;
-    })["exposed-modules"];
-
-    const result: URI[] = [];
-
-    for (const key in x) {
-      if (Object.hasOwnProperty.call(x, key)) {
-        const element = x[key];
-        if (typeof element === "string") {
-          result.push(
-            URI.file(
-              pathToPackage
-                .concat("/src/")
-                .concat(element.split(".").join("/").concat(".elm")),
-            ),
-          );
-        } else {
-          result.push(
-            ...element.map((element) =>
-              URI.file(
-                pathToPackage
-                  .concat("/src/")
-                  .concat(element.split(".").join("/").concat(".elm")),
-              ),
-            ),
-          );
-        }
-      }
-    }
-    return result;
+
+    return Object.values(exposedModules).reduce((a, b) => a.concat(b), []);
   }
 
   private packageOrPackagesFolder(elmVersion: string | undefined): string {
@@ -520,22 +672,22 @@ export class ElmWorkspace implements IElmWorkspace {
   }
 
   private async readAndAddToForest(
-    filePath: IFolder,
+    filePath: IElmFile,
     callback: () => void,
   ): Promise<void> {
     try {
       this.connection.console.info(`Adding ${filePath.path.toString()}`);
-      const fileContent: string = await readFile(filePath.path.toString(), {
-        encoding: "utf-8",
-      });
+      const fileContent: string = await this.host.readFile(
+        filePath.path.toString(),
+      );
 
       const tree: Tree = this.parser.parse(fileContent);
       this.forest.setTree(
         URI.file(filePath.path).toString(),
-        filePath.writeable,
+        filePath.project === this.rootProject,
         true,
         tree,
-        filePath.isExposed,
+        filePath.project,
         filePath.maintainerAndPackageName,
       );
       callback();
@@ -543,4 +695,46 @@ export class ElmWorkspace implements IElmWorkspace {
       this.connection.console.error(error.stack);
     }
   }
+
+  private findExposedModulesOfDependencies(project: ElmProject): void {
+    const loadForDependencies = (deps: Map<string, IElmPackage>): void => {
+      // For each dependecy, find every exposed module
+      deps.forEach((dep) => {
+        dep.moduleToUriMap.forEach((uri, module) => {
+          if (dep.exposedModules.has(module)) {
+            project.moduleToUriMap.set(module, uri);
+          }
+        });
+        this.findExposedModulesOfDependencies(dep);
+      });
+    };
+
+    loadForDependencies(project.dependencies);
+
+    if (project === this.rootProject) {
+      loadForDependencies(project.testDependencies);
+    }
+  }
+
+  private loadElmJson(elmJsonPath: string): ElmJson {
+    return JSON.parse(this.host.readFileSync(elmJsonPath)) as ElmJson;
+  }
+
+  private createProgramHost(): IProgramHost {
+    return {
+      readFile: (uri): Promise<string> =>
+        readFile(uri, {
+          encoding: "utf-8",
+        }),
+      readFileSync: (uri): string =>
+        fs.readFileSync(uri, {
+          encoding: "utf-8",
+        }),
+      readDirectory: (uri: string): Promise<string[]> =>
+        // Cleanup the path on windows, as globby does not like backslashes
+        globby(`${uri.replace(/\\/g, "/")}/**/*.elm`, {
+          suppressErrors: true,
+        }),
+    };
+  }
 }
diff --git a/src/forest.ts b/src/forest.ts
index a7e07f7..4bd6bc6 100644
--- a/src/forest.ts
+++ b/src/forest.ts
@@ -1,13 +1,10 @@
 import { SyntaxNode, Tree } from "web-tree-sitter";
-import { IRootFolder } from "./elmWorkspace";
 import { Imports } from "./imports";
 import { IExposing, TreeUtils } from "./util/treeUtils";
-import * as path from "path";
-import { existsSync } from "fs";
-import { URI } from "vscode-uri";
 import { SyntaxNodeMap } from "./util/types/syntaxNodeMap";
 import { SymbolMap } from "./util/types/binder";
 import { Diagnostic } from "./util/types/diagnostics";
+import { ElmProject } from "./elmWorkspace";
 
 export interface ITreeContainer {
   uri: string;
@@ -15,7 +12,7 @@ export interface ITreeContainer {
   referenced: boolean;
   maintainerAndPackageName?: string;
   tree: Tree;
-  isExposed: boolean; // Is this file exposed by the elm.json
+  project: ElmProject; // The project this source file is associated with
 
   parseDiagnostics: Diagnostic[];
 
@@ -39,7 +36,7 @@ export interface IForest {
     writeable: boolean,
     referenced: boolean,
     tree: Tree,
-    isExposed: boolean,
+    project?: ElmProject,
     packageName?: string,
   ): ITreeContainer;
   removeTree(uri: string): void;
@@ -52,27 +49,15 @@ export class Forest implements IForest {
     string,
     ITreeContainer
   >();
-  public moduleToUrisMap: Map<string, string[]> = new Map<string, string[]>();
 
-  private elmFolders: { uri: string; writeable: boolean }[] = [];
-
-  constructor(elmFolders: ReadonlyMap<string, IRootFolder>) {
-    elmFolders.forEach(({ writeable }, uri) =>
-      this.elmFolders.push({ writeable, uri }),
-    );
-  }
+  constructor(private rootProject: ElmProject) {}
 
   public getTree(uri: string): Tree | undefined {
     return this.getByUri(uri)?.tree;
   }
 
   public getByModuleName(moduleName: string): ITreeContainer | undefined {
-    return this.moduleToUrisMap
-      .get(moduleName)
-      ?.map(this.getByUri.bind(this))
-      .sort((x, y) => {
-        return x?.isExposed === y?.isExposed ? 0 : x?.isExposed ? -1 : 1;
-      })[0];
+    return this.getByUri(this.rootProject.moduleToUriMap.get(moduleName) ?? "");
   }
 
   public getByUri(uri: string): ITreeContainer | undefined {
@@ -84,7 +69,7 @@ export class Forest implements IForest {
     writeable: boolean,
     referenced: boolean,
     tree: Tree,
-    isExposed: boolean,
+    project: ElmProject = this.rootProject,
     maintainerAndPackageName?: string,
   ): ITreeContainer {
     tree.uri = uri;
@@ -95,7 +80,7 @@ export class Forest implements IForest {
       tree,
       uri,
       writeable,
-      isExposed,
+      project,
       parseDiagnostics: [],
     };
 
@@ -105,16 +90,11 @@ export class Forest implements IForest {
   }
 
   public removeTree(uri: string): void {
-    const existing = this.getByUri(uri);
-    if (existing && existing.moduleName) {
-      this.deleteModuleFromUriMap(existing.moduleName, uri);
-    }
-
     this.treeMap.delete(uri);
   }
 
   public synchronize(): void {
-    this.treeMap.forEach((treeContainer, uri) => {
+    this.treeMap.forEach((treeContainer) => {
       // Resolve import modules
       if (!treeContainer.resolvedModules) {
         treeContainer.resolvedModules = this.resolveModules(treeContainer);
@@ -126,7 +106,13 @@ export class Forest implements IForest {
 
         if (moduleName) {
           treeContainer.moduleName = moduleName;
-          this.addModuleToUriMap(moduleName, uri);
+
+          if (
+            treeContainer.project === this.rootProject &&
+            !this.rootProject.moduleToUriMap.has(moduleName)
+          ) {
+            this.rootProject.moduleToUriMap.set(moduleName, treeContainer.uri);
+          }
         }
       }
     });
@@ -138,63 +124,26 @@ export class Forest implements IForest {
     });
   }
 
-  private resolveModules(treeContainer: ITreeContainer): Map<string, string> {
+  private resolveModules(sourceFile: ITreeContainer): Map<string, string> {
     const importClauses = [
       ...Imports.getVirtualImports(),
-      ...(TreeUtils.findAllImportClauseNodes(treeContainer.tree) ?? []),
+      ...(TreeUtils.findAllImportClauseNodes(sourceFile.tree) ?? []),
     ];
 
     const resolvedModules = new Map<string, string>();
 
-    // It should be faster to look directly at elmFolders instead of traversing the forest
     importClauses.forEach((importClause) => {
       const moduleName = importClause.childForFieldName("moduleName")?.text;
 
       if (moduleName) {
-        const modulePath = moduleName.split(".").join("/") + ".elm";
-        const found = this.elmFolders.find((folder) =>
-          existsSync(
-            path.join(
-              !folder.writeable ? folder.uri + "/src" : folder.uri,
-              modulePath,
-            ),
-          ),
-        );
+        const found = sourceFile.project.moduleToUriMap.get(moduleName);
 
         if (found) {
-          resolvedModules.set(
-            moduleName,
-            URI.file(
-              path.join(
-                !found.writeable ? found.uri + "/src" : found.uri,
-                modulePath,
-              ),
-            ).toString(),
-          );
+          resolvedModules.set(moduleName, found);
         }
       }
     });
 
     return resolvedModules;
   }
-
-  private addModuleToUriMap(module: string, uri: string): void {
-    const existing = this.moduleToUrisMap.get(module);
-
-    if (existing) {
-      if (!existing.includes(uri)) {
-        existing.push(uri);
-      }
-    } else {
-      this.moduleToUrisMap.set(module, [uri]);
-    }
-  }
-
-  private deleteModuleFromUriMap(module: string, uri: string): void {
-    const existing = this.moduleToUrisMap.get(module);
-
-    if (existing) {
-      existing.splice(existing.indexOf(uri), 1);
-    }
-  }
 }
diff --git a/src/providers/astProvider.ts b/src/providers/astProvider.ts
index 61827cb..36c34a4 100644
--- a/src/providers/astProvider.ts
+++ b/src/providers/astProvider.ts
@@ -66,7 +66,8 @@ export class ASTProvider {
     const forest = params.program.getForest(false); // Don't synchronize the forest, we are only looking at the tree
     const document: VersionedTextDocumentIdentifier = params.textDocument;
 
-    let tree: Tree = params.sourceFile.tree;
+    // Source file could be undefined here
+    let tree: Tree = params.sourceFile?.tree;
 
     if ("contentChanges" in params) {
       for (const change of params.contentChanges) {
@@ -117,13 +118,7 @@ export class ASTProvider {
     tree = newTree;
 
     if (tree) {
-      const treeContainer = forest.setTree(
-        document.uri,
-        true,
-        true,
-        tree,
-        true,
-      );
+      const treeContainer = forest.setTree(document.uri, true, true, tree);
 
       // The workspace now needs to be synchronized
       params.program.markAsDirty();
diff --git a/src/providers/completionProvider.ts b/src/providers/completionProvider.ts
index 18b4358..ee9dc86 100644
--- a/src/providers/completionProvider.ts
+++ b/src/providers/completionProvider.ts
@@ -318,7 +318,11 @@ export class CompletionProvider {
           }
         } else {
           if (contextNode.type === "import") {
-            return this.getImportableModules(tree, forest, replaceRange);
+            return this.getImportableModules(
+              params.program,
+              params.sourceFile,
+              replaceRange,
+            );
           }
         }
       }
@@ -407,29 +411,30 @@ export class CompletionProvider {
   }
 
   private getImportableModules(
-    tree: Tree,
-    forest: IForest,
+    program: IElmWorkspace,
+    sourceFile: ITreeContainer,
     range: Range,
     targetModule?: string,
   ): CompletionItem[] {
-    const currentModuleNameNode = TreeUtils.getModuleNameNode(tree);
-    return Array.from(forest.treeMap.values())
+    return Array.from(sourceFile.project.moduleToUriMap.entries())
       .filter(
-        (t) =>
-          t.moduleName &&
-          (!targetModule || t.moduleName?.startsWith(targetModule + ".")) &&
-          t.moduleName !== currentModuleNameNode?.text &&
-          t.moduleName !== targetModule,
+        ([moduleName]) =>
+          (!targetModule || moduleName?.startsWith(targetModule + ".")) &&
+          moduleName !== sourceFile.moduleName &&
+          moduleName !== targetModule,
       )
-      .map((t) => {
-        const moduleNode = TreeUtils.findModuleDeclaration(t.tree);
+      .map(([moduleName, uri]) => {
+        const sourceFileToImport = program.getSourceFile(uri)!;
+        const moduleNode = TreeUtils.findModuleDeclaration(
+          sourceFileToImport.tree,
+        );
         const markdownDocumentation = HintHelper.createHint(moduleNode);
 
         return this.createModuleCompletion({
           label:
             (targetModule
-              ? t.moduleName?.slice(targetModule.length + 1) ?? t.moduleName
-              : t.moduleName) ?? "",
+              ? moduleName?.slice(targetModule.length + 1) ?? moduleName
+              : moduleName) ?? "",
           sortPrefix: "b",
           range,
           markdownDocumentation,
@@ -1198,20 +1203,20 @@ export class CompletionProvider {
 
   private getSubmodulesOrValues(
     node: SyntaxNode,
-    treeContainer: ITreeContainer,
-    elmWorkspace: IElmWorkspace,
+    sourceFile: ITreeContainer,
+    program: IElmWorkspace,
     range: Range,
     targetModule: string,
   ): CompletionItem[] {
     const result: CompletionItem[] = [];
 
-    const forest = elmWorkspace.getForest();
-    const checker = elmWorkspace.getTypeChecker();
-    const tree = treeContainer.tree;
+    const forest = program.getForest();
+    const checker = program.getTypeChecker();
+    const tree = sourceFile.tree;
 
     // Handle possible submodules
     result.push(
-      ...this.getImportableModules(tree, forest, range, targetModule),
+      ...this.getImportableModules(program, sourceFile, range, targetModule),
     );
 
     // If we are in an import completion, don't return any values
@@ -1222,19 +1227,19 @@ export class CompletionProvider {
     let alreadyImported = true;
 
     // Try to find the module definition that is already imported
-    const definitionNode = checker.findDefinition(node, treeContainer);
+    const definitionNode = checker.findDefinition(node, sourceFile);
 
     let moduleTree: ITreeContainer | undefined;
 
     if (definitionNode && definitionNode.nodeType === "Module") {
-      moduleTree = forest.getByUri(definitionNode.uri);
+      moduleTree = program.getSourceFile(definitionNode.uri);
     } else {
       // Try to find this module in the forest to import
       moduleTree = forest.getByModuleName(targetModule);
       alreadyImported = false;
     }
 
-    if (moduleTree && moduleTree.isExposed) {
+    if (moduleTree) {
       // Get exposed values
       const imports = ImportUtils.getPossibleImportsOfTree(moduleTree);
       imports.forEach((value) => {
diff --git a/src/providers/diagnostics/elmLsDiagnostics.ts b/src/providers/diagnostics/elmLsDiagnostics.ts
index 067af4e..632bff2 100644
--- a/src/providers/diagnostics/elmLsDiagnostics.ts
+++ b/src/providers/diagnostics/elmLsDiagnostics.ts
@@ -5,7 +5,6 @@ import {
   CodeAction,
   CodeActionKind,
   CodeActionParams,
-  Connection,
   DiagnosticSeverity,
   DiagnosticTag,
   Range,
@@ -20,39 +19,10 @@ import { RefactorEditUtils } from "../../util/refactorEditUtils";
 import { TreeUtils } from "../../util/treeUtils";
 import { Utils } from "../../util/utils";
 import { IDiagnostic } from "./diagnosticsProvider";
-import * as path from "path";
-
-interface IElmAnalyseJson {
-  checks?: {
-    BooleanCase?: boolean;
-    DropConcatOfLists?: boolean;
-    DropConsOfItemAndList?: boolean;
-    ExposeAll?: boolean;
-    NoUncurriedPrefix?: boolean;
-    ImportAll?: boolean;
-    MapNothingToNothing?: boolean;
-    NoTopLevelSignature?: boolean;
-    SingleFieldRecord?: boolean;
-    UnnecessaryListConcat?: boolean;
-    UnnecessaryParens?: boolean;
-    UnnecessaryPortModule?: boolean;
-    UnusedImport?: boolean;
-    UnusedImportAlias?: boolean;
-    UnusedImportedVariable?: boolean;
-    UnusedPatternVariable?: boolean;
-    UnusedTopLevel?: boolean;
-    UnusedTypeAlias?: boolean;
-    UnusedValueConstructor?: boolean;
-    UnusedVariable?: boolean;
-    UseConsOverConcat?: boolean;
-  };
-  excludedPaths?: string[];
-}
+
 export class ElmLsDiagnostics {
   private language: Language;
   private elmWorkspaceMatcher: ElmWorkspaceMatcher<URI>;
-  private connection: Connection;
-  private elmAnalyseJson = new Map<string, IElmAnalyseJson>();
 
   private readonly exposedValuesAndTypesQuery: Query;
   private readonly moduleImportsQuery: Query;
@@ -76,7 +46,6 @@ export class ElmLsDiagnostics {
   constructor() {
     this.language = container.resolve<Parser>("Parser").getLanguage();
     this.elmWorkspaceMatcher = new ElmWorkspaceMatcher((uri) => uri);
-    this.connection = container.resolve<Connection>("Connection");
 
     this.exposedValuesAndTypesQuery = this.language.query(
       `
@@ -361,88 +330,32 @@ export class ElmLsDiagnostics {
     treeContainer: ITreeContainer,
     elmWorkspace: IElmWorkspace,
   ): IDiagnostic[] => {
-    const elmAnalyseJson = this.getElmAnalyseJson(
-      elmWorkspace.getRootPath().toString(),
-    );
     const tree = treeContainer.tree;
     const uri = treeContainer.uri;
     try {
       return [
-        ...(elmAnalyseJson.checks?.UnusedImport === false
-          ? []
-          : this.getUnusedImportDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UnusedImportedVariable === false
-          ? []
-          : this.getUnusedImportValueAndTypeDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UnusedImportAlias === false
-          ? []
-          : this.getUnusedImportAliasDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UnusedPatternVariable === false
-          ? []
-          : this.getUnusedPatternVariableDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.MapNothingToNothing === false
-          ? []
-          : this.getCaseBranchMapNothingToNothingDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.BooleanCase === false
-          ? []
-          : this.getBooleanCaseExpressionDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.DropConcatOfLists === false
-          ? []
-          : this.getDropConcatOfListsDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.DropConsOfItemAndList === false
-          ? []
-          : this.getDropConsOfItemAndListDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UseConsOverConcat === false
-          ? []
-          : this.getUseConsOverConcatDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.SingleFieldRecord === false
-          ? []
-          : this.getSingleFieldRecordDiagnostics(tree, uri, elmWorkspace)),
-        ...(elmAnalyseJson.checks?.UnnecessaryListConcat === false
-          ? []
-          : this.getUnnecessaryListConcatDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UnnecessaryPortModule === false
-          ? []
-          : this.getUnnecessaryPortModuleDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.NoUncurriedPrefix === false
-          ? []
-          : this.getFullyAppliedOperatorAsPrefixDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UnusedTypeAlias === false
-          ? []
-          : this.getUnusedTypeAliasDiagnostics(tree)),
-        ...(elmAnalyseJson.checks?.UnusedValueConstructor === false
-          ? []
-          : this.getUnusedValueConstructorDiagnostics(tree)),
+        ...this.getUnusedImportDiagnostics(tree),
+        ...this.getUnusedImportValueAndTypeDiagnostics(tree),
+        ...this.getUnusedImportAliasDiagnostics(tree),
+        ...this.getUnusedPatternVariableDiagnostics(tree),
+        ...this.getCaseBranchMapNothingToNothingDiagnostics(tree),
+        ...this.getBooleanCaseExpressionDiagnostics(tree),
+        ...this.getDropConcatOfListsDiagnostics(tree),
+        ...this.getDropConsOfItemAndListDiagnostics(tree),
+        ...this.getUseConsOverConcatDiagnostics(tree),
+        ...this.getSingleFieldRecordDiagnostics(tree, uri, elmWorkspace),
+        ...this.getUnnecessaryListConcatDiagnostics(tree),
+        ...this.getUnnecessaryPortModuleDiagnostics(tree),
+        ...this.getFullyAppliedOperatorAsPrefixDiagnostics(tree),
+        ...this.getUnusedTypeAliasDiagnostics(tree),
+        ...this.getUnusedValueConstructorDiagnostics(tree),
       ];
     } catch (e) {
-      this.connection.console.error(e);
+      console.log(e);
     }
     return [];
   };
 
-  private getElmAnalyseJson(workspacePath: string): IElmAnalyseJson {
-    const cached = this.elmAnalyseJson.get(workspacePath);
-
-    if (cached) {
-      return cached;
-    }
-
-    let elmAnalyseJson = {};
-    try {
-      elmAnalyseJson = require(path.join(
-        workspacePath,
-        "elm-analyse.json",
-      )) as IElmAnalyseJson;
-    } catch {
-      this.connection.console.info(
-        "No elm-analyse.json found, enabling all diagnostic checks.",
-      );
-    }
-
-    this.elmAnalyseJson.set(workspacePath, elmAnalyseJson);
-    return elmAnalyseJson;
-  }
-
   public onCodeAction(params: CodeActionParams): CodeAction[] {
     const { uri } = params.textDocument;
     const elmDiagnostics: IDiagnostic[] = params.context.diagnostics.filter(
diff --git a/src/providers/ranking.ts b/src/providers/ranking.ts
index 7e9cf95..f0f1d1a 100644
--- a/src/providers/ranking.ts
+++ b/src/providers/ranking.ts
@@ -16,974 +16,828 @@ const RANKING_LIST = {
   "avh4/elm-color": "0015",
   "elm/parser": "0016",
   "elm/regex": "0017",
-  "elm-community/maybe-extra": "0018",
+  "elm-explorations/markdown": "0018",
   "krisajenkins/remotedata": "0019",
-  "elm/file": "0020",
-  "elm-explorations/markdown": "0021",
+  "elm-community/maybe-extra": "0020",
+  "elm/file": "0021",
   "rtfeldman/elm-iso8601-date-strings": "0022",
   "elm-community/random-extra": "0023",
   "rundis/elm-bootstrap": "0024",
   "justinmimbs/date": "0025",
   "elm-community/json-extra": "0026",
   "elm-community/string-extra": "0027",
-  "mpizenberg/elm-pointer-events": "0028",
-  "elm-community/result-extra": "0029",
-  "elm-explorations/linear-algebra": "0030",
-  "elm/bytes": "0031",
-  "dillonkearns/elm-pages": "0032",
+  "elm-explorations/linear-algebra": "0028",
+  "mpizenberg/elm-pointer-events": "0029",
+  "dillonkearns/elm-pages": "0030",
+  "elm-community/result-extra": "0031",
+  "elm/bytes": "0032",
   "myrho/elm-round": "0033",
   "elm/virtual-dom": "0034",
-  "elm-community/typed-svg": "0035",
-  "dillonkearns/elm-sitemap": "0036",
-  "dillonkearns/elm-rss": "0037",
-  "rtfeldman/elm-hex": "0038",
-  "ryannhg/date-format": "0039",
-  "elm-explorations/webgl": "0040",
-  "dillonkearns/elm-markdown": "0041",
-  "elm-community/dict-extra": "0042",
-  "noahzgordon/elm-color-extra": "0043",
-  "dillonkearns/elm-graphql": "0044",
-  "lukewestby/elm-string-interpolate": "0045",
-  "joakin/elm-canvas": "0046",
-  "justinmimbs/time-extra": "0047",
-  "ianmackenzie/elm-geometry": "0048",
-  "elm-community/html-extra": "0049",
-  "ohanhi/keyboard": "0050",
-  "mdgriffith/elm-markup": "0051",
-  "ianmackenzie/elm-units": "0052",
-  "truqu/elm-base64": "0053",
-  "gampleman/elm-visualization": "0054",
-  "jfmengels/elm-review": "0055",
-  "hecrj/html-parser": "0056",
-  "mdgriffith/elm-animator": "0057",
-  "stil4m/elm-syntax": "0058",
-  "elm-community/array-extra": "0059",
-  "evancz/elm-playground": "0060",
-  "elm-explorations/benchmark": "0061",
+  "ryannhg/date-format": "0035",
+  "elm-explorations/webgl": "0036",
+  "rtfeldman/elm-hex": "0037",
+  "elm-community/typed-svg": "0038",
+  "dillonkearns/elm-graphql": "0039",
+  "elm-community/dict-extra": "0040",
+  "noahzgordon/elm-color-extra": "0041",
+  "lukewestby/elm-string-interpolate": "0042",
+  "justinmimbs/time-extra": "0043",
+  "mdgriffith/elm-markup": "0044",
+  "joakin/elm-canvas": "0045",
+  "elm-community/html-extra": "0046",
+  "ohanhi/keyboard": "0047",
+  "dillonkearns/elm-markdown": "0048",
+  "dillonkearns/elm-rss": "0049",
+  "dillonkearns/elm-sitemap": "0049",
+  "ianmackenzie/elm-geometry": "0050",
+  "truqu/elm-base64": "0051",
+  "evancz/elm-playground": "0052",
+  "rtfeldman/elm-validate": "0053",
+  "lukewestby/elm-http-builder": "0054",
+  "hecrj/html-parser": "0055",
+  "elm-explorations/benchmark": "0056",
+  "MacCASOutreach/graphicsvg": "0057",
+  "elm-community/array-extra": "0058",
+  "gampleman/elm-visualization": "0059",
+  "mdgriffith/elm-style-animation": "0060",
+  "ryannhg/elm-spa": "0061",
   "danyx23/elm-uuid": "0062",
-  "ianmackenzie/elm-3d-scene": "0063",
-  "lukewestby/elm-http-builder": "0064",
-  "rtfeldman/elm-validate": "0065",
-  "elm/project-metadata-utils": "0066",
-  "terezka/line-charts": "0067",
-  "cuducos/elm-format-number": "0068",
-  "mdgriffith/elm-style-animation": "0069",
-  "Janiczek/cmd-extra": "0070",
-  "pablohirafuji/elm-markdown": "0071",
-  "feathericons/elm-feather": "0072",
-  "lattyware/elm-fontawesome": "0073",
-  "elm-community/basics-extra": "0074",
-  "MacCASOutreach/graphicsvg": "0075",
-  "mgold/elm-nonempty-list": "0076",
-  "timjs/elm-collage": "0077",
-  "folkertdev/one-true-path-experiment": "0078",
-  "avh4/elm-program-test": "0079",
-  "surprisetalk/elm-bulma": "0080",
-  "elm-community/graph": "0081",
-  "ryannhg/elm-spa": "0082",
-  "pzp1997/assoc-list": "0083",
-  "elm-community/easing-functions": "0084",
-  "elm-community/intdict": "0085",
-  "bartavelle/json-helpers": "0086",
-  "danfishgold/base64-bytes": "0087",
-  "sparksp/elm-review-forbidden-words": "0088",
-  "debois/elm-dom": "0089",
-  "Fresheyeball/elm-return": "0090",
-  "fapian/elm-html-aria": "0091",
-  "Gizra/elm-keyboard-event": "0092",
-  "truqu/elm-md5": "0093",
-  "andrewMacmurray/elm-delay": "0094",
-  "aforemny/material-components-web-elm": "0095",
-  "justinmimbs/timezone-data": "0096",
-  "cmditch/elm-bigint": "0097",
-  "wernerdegroot/listzipper": "0098",
-  "jinjor/elm-debounce": "0099",
-  "PanagiotisGeorgiadis/elm-datetime": "0100",
-  "ianmackenzie/elm-geometry-svg": "0101",
-  "Chadtech/elm-bool-extra": "0102",
-  "lynn/elm-arithmetic": "0103",
-  "pablohirafuji/elm-syntax-highlight": "0104",
-  "arturopala/elm-monocle": "0105",
-  "frandibar/elm-font-awesome-5": "0106",
-  "justgook/elm-image": "0107",
-  "miniBill/elm-codec": "0108",
-  "zaboco/elm-draggable": "0109",
-  "billstclair/elm-port-funnel": "0110",
-  "norpan/elm-html5-drag-drop": "0111",
-  "TSFoster/elm-uuid": "0112",
-  "ianmackenzie/elm-3d-camera": "0113",
-  "jfmengels/elm-review-unused": "0114",
-  "ohanhi/remotedata-http": "0115",
-  "icidasset/elm-binary": "0116",
-  "CurrySoftware/elm-datepicker": "0117",
-  "abadi199/elm-input-extra": "0118",
-  "tesk9/palette": "0119",
-  "zwilias/elm-rosetree": "0120",
-  "annaghi/dnd-list": "0121",
-  "SwiftsNamesake/proper-keyboard": "0122",
-  "lovasoa/elm-csv": "0123",
-  "billstclair/elm-websocket-client": "0124",
-  "ccapndave/elm-update-extra": "0125",
-  "robinheghan/murmur3": "0126",
-  "icidasset/elm-material-icons": "0127",
-  "Orasund/elm-ui-widgets": "0128",
-  "truqu/elm-review-noleftpizza": "0129",
-  "turboMaCk/any-dict": "0130",
-  "turboMaCk/non-empty-list-alias": "0131",
-  "simonh1000/elm-jwt": "0132",
-  "pablohirafuji/elm-qrcode": "0133",
-  "j-panasiuk/elm-ionicons": "0134",
-  "lattyware/elm-json-diff": "0135",
-  "zwilias/elm-html-string": "0136",
-  "arowM/elm-form-decoder": "0137",
-  "mgold/elm-animation": "0138",
-  "ianmackenzie/elm-triangular-mesh": "0139",
-  "TSFoster/elm-tuple-extra": "0140",
-  "tripokey/elm-fuzzy": "0141",
-  "Punie/elm-parser-extras": "0142",
-  "pablen/toasty": "0143",
-  "elm-community/undo-redo": "0144",
-  "truqu/elm-oauth2": "0145",
-  "Gizra/elm-all-set": "0146",
-  "jweir/elm-iso8601": "0147",
-  "tortus/elm-array-2d": "0148",
-  "NoRedInk/elm-random-pcg-extended": "0149",
-  "webbhuset/elm-json-decode": "0150",
-  "ktonon/elm-crypto": "0151",
-  "elm-community/list-split": "0152",
-  "Herteby/simplex-noise": "0153",
-  "jzxhuang/http-extras": "0154",
-  "folkertdev/elm-deque": "0155",
-  "ianmackenzie/elm-1d-parameter": "0156",
+  "pablohirafuji/elm-markdown": "0063",
+  "elm-community/basics-extra": "0064",
+  "lattyware/elm-fontawesome": "0065",
+  "ianmackenzie/elm-units": "0066",
+  "cuducos/elm-format-number": "0067",
+  "Skinney/murmur3": "0068",
+  "Janiczek/cmd-extra": "0069",
+  "timjs/elm-collage": "0070",
+  "surprisetalk/elm-bulma": "0071",
+  "terezka/line-charts": "0072",
+  "mgold/elm-nonempty-list": "0073",
+  "feathericons/elm-feather": "0074",
+  "folkertdev/one-true-path-experiment": "0075",
+  "debois/elm-dom": "0076",
+  "stil4m/elm-syntax": "0077",
+  "elm-community/easing-functions": "0078",
+  "danfishgold/base64-bytes": "0079",
+  "elm-community/graph": "0080",
+  "elm-community/intdict": "0081",
+  "elm/project-metadata-utils": "0082",
+  "bartavelle/json-helpers": "0083",
+  "Fresheyeball/elm-return": "0084",
+  "fapian/elm-html-aria": "0085",
+  "jinjor/elm-debounce": "0086",
+  "miniBill/elm-codec": "0087",
+  "pzp1997/assoc-list": "0088",
+  "wernerdegroot/listzipper": "0089",
+  "ianmackenzie/elm-geometry-svg": "0090",
+  "truqu/elm-md5": "0091",
+  "aforemny/material-components-web-elm": "0092",
+  "andrewMacmurray/elm-delay": "0093",
+  "justinmimbs/timezone-data": "0094",
+  "PanagiotisGeorgiadis/elm-datetime": "0095",
+  "lynn/elm-arithmetic": "0096",
+  "Chadtech/elm-bool-extra": "0097",
+  "arturopala/elm-monocle": "0098",
+  "frandibar/elm-font-awesome-5": "0099",
+  "billstclair/elm-port-funnel": "0100",
+  "zaboco/elm-draggable": "0101",
+  "justgook/elm-image": "0102",
+  "pablohirafuji/elm-syntax-highlight": "0103",
+  "CurrySoftware/elm-datepicker": "0104",
+  "mgold/elm-animation": "0105",
+  "simonh1000/elm-jwt": "0106",
+  "Gizra/elm-keyboard-event": "0107",
+  "tesk9/palette": "0108",
+  "cmditch/elm-bigint": "0109",
+  "turboMaCk/any-dict": "0110",
+  "zwilias/elm-rosetree": "0111",
+  "billstclair/elm-websocket-client": "0112",
+  "etaque/elm-form": "0113",
+  "jweir/elm-iso8601": "0114",
+  "ccapndave/elm-update-extra": "0115",
+  "abadi199/elm-input-extra": "0116",
+  "jzxhuang/http-extras": "0117",
+  "GlobalWebIndex/cmd-extra": "0118",
+  "ohanhi/remotedata-http": "0119",
+  "capitalist/elm-octicons": "0120",
+  "pablen/toasty": "0121",
+  "arowM/elm-form-decoder": "0122",
+  "annaghi/dnd-list": "0123",
+  "klazuka/elm-json-tree-view": "0124",
+  "justgage/tachyons-elm": "0125",
+  "truqu/elm-oauth2": "0126",
+  "the-sett/elm-color": "0127",
+  "norpan/elm-html5-drag-drop": "0128",
+  "j-panasiuk/elm-ionicons": "0129",
+  "tripokey/elm-fuzzy": "0130",
+  "TSFoster/elm-uuid": "0131",
+  "avh4/elm-program-test": "0132",
+  "Punie/elm-parser-extras": "0133",
+  "elm-community/undo-redo": "0134",
+  "SwiftsNamesake/proper-keyboard": "0135",
+  "mdgriffith/style-elements": "0136",
+  "jfmengels/elm-review": "0137",
+  "turboMaCk/any-set": "0138",
+  "basti1302/elm-human-readable-filesize": "0139",
+  "toastal/either": "0140",
+  "jamesmacaulay/elm-graphql": "0141",
+  "danmarcab/material-icons": "0142",
+  "AdrianRibao/elm-derberos-date": "0143",
+  "elm-community/list-split": "0144",
+  "ktonon/elm-crypto": "0145",
+  "lovasoa/elm-csv": "0146",
+  "BrianHicks/elm-css-reset": "0147",
+  "cappyzawa/elm-ui-colors": "0148",
+  "folkertdev/elm-deque": "0149",
+  "kalutheo/elm-ui-explorer": "0150",
+  "NoRedInk/elm-random-pcg-extended": "0151",
+  "NoRedInk/elm-string-conversions": "0152",
+  "perzanko/elm-loading": "0153",
+  "gingko/time-distance": "0154",
+  "folkertdev/elm-state": "0155",
+  "pilatch/flip": "0156",
   "ccapndave/elm-flat-map": "0157",
-  "truqu/elm-review-nobooleancase": "0158",
-  "toastal/either": "0159",
-  "1602/elm-feather": "0160",
-  "mdgriffith/style-elements": "0161",
-  "NoRedInk/elm-uuid": "0162",
-  "perzanko/elm-loading": "0163",
-  "linuss/smooth-scroll": "0164",
-  "etaque/elm-form": "0165",
-  "gingko/time-distance": "0166",
-  "GlobalWebIndex/cmd-extra": "0167",
-  "NoRedInk/elm-string-conversions": "0168",
-  "AdrianRibao/elm-derberos-date": "0169",
-  "kalutheo/elm-ui-explorer": "0170",
-  "justgage/tachyons-elm": "0171",
-  "Gizra/elm-debouncer": "0172",
-  "turboMaCk/any-set": "0173",
-  "the-sett/elm-color": "0174",
-  "billstclair/elm-sortable-table": "0175",
-  "capitalist/elm-octicons": "0176",
-  "simonh1000/elm-colorpicker": "0177",
-  "pilatch/flip": "0178",
-  "folkertdev/elm-state": "0179",
-  "jfmengels/elm-review-debug": "0180",
-  "TSFoster/elm-sha1": "0181",
-  "klazuka/elm-json-tree-view": "0182",
-  "Janiczek/elm-bidict": "0183",
-  "danmarcab/material-icons": "0184",
-  "basti1302/elm-human-readable-filesize": "0185",
-  "panthershark/email-parser": "0186",
-  "fredcy/elm-parseint": "0187",
-  "jorgengranseth/elm-string-format": "0188",
-  "hecrj/composable-form": "0189",
-  "ianmackenzie/elm-geometry-test": "0190",
-  "billstclair/elm-sha256": "0191",
-  "ianmackenzie/elm-geometry-linear-algebra-interop": "0192",
-  "danhandrea/elm-date-format": "0193",
+  "tortus/elm-array-2d": "0158",
+  "ContaSystemer/elm-menu": "0159",
+  "hecrj/composable-form": "0160",
+  "matthewsj/elm-ordering": "0161",
+  "zwilias/json-decode-exploration": "0162",
+  "jorgengranseth/elm-string-format": "0163",
+  "mthadley/elm-hash-routing": "0164",
+  "jfmengels/review-unused": "0165",
+  "tesk9/accessible-html": "0166",
+  "lovasoa/elm-rolling-list": "0167",
+  "TSFoster/elm-tuple-extra": "0168",
+  "webbhuset/elm-json-decode": "0169",
+  "Skinney/keyboard-events": "0170",
+  "fredcy/elm-parseint": "0171",
+  "turboMaCk/lazy-tree-with-zipper": "0172",
+  "ianmackenzie/elm-1d-parameter": "0173",
+  "the-sett/elm-syntax-dsl": "0174",
+  "joneshf/elm-tagged": "0175",
+  "ianmackenzie/elm-3d-camera": "0176",
+  "marcosh/elm-html-to-unicode": "0177",
+  "jinjor/elm-diff": "0178",
+  "Janiczek/elm-bidict": "0179",
+  "pablohirafuji/elm-qrcode": "0180",
+  "mdgriffith/elm-animator": "0181",
+  "lattyware/elm-json-diff": "0181",
+  "danhandrea/elm-date-format": "0181",
+  "1602/elm-feather": "0182",
+  "Gizra/elm-all-set": "0183",
+  "zwilias/elm-html-string": "0184",
+  "billstclair/elm-sha256": "0185",
+  "billstclair/elm-localstorage": "0186",
+  "ivadzy/bbase64": "0187",
+  "damienklinnert/elm-spinner": "0188",
+  "linuss/smooth-scroll": "0189",
+  "simonh1000/elm-colorpicker": "0190",
+  "folkertdev/elm-flate": "0191",
+  "NoRedInk/elm-uuid": "0192",
+  "gicentre/elm-vegalite": "0193",
   "NoRedInk/elm-sortable-table": "0194",
-  "kuon/elm-string-normalize": "0195",
-  "dmy/elm-pratt-parser": "0196",
-  "Chadtech/unique-list": "0197",
-  "jamesmacaulay/elm-graphql": "0198",
-  "ymtszw/elm-xml-decode": "0199",
-  "billstclair/elm-localstorage": "0200",
-  "mthadley/elm-hash-routing": "0201",
-  "cappyzawa/elm-ui-colors": "0202",
-  "the-sett/elm-syntax-dsl": "0203",
-  "damienklinnert/elm-spinner": "0204",
-  "matthewsj/elm-ordering": "0205",
-  "folkertdev/elm-flate": "0206",
-  "carwow/elm-slider": "0207",
-  "jinjor/elm-diff": "0208",
-  "ursi/elm-css-colors": "0209",
-  "ContaSystemer/elm-menu": "0210",
-  "tricycle/elm-embed-youtube": "0211",
-  "PaackEng/elm-ui-dialog": "0212",
-  "CoderDennis/elm-time-format": "0213",
-  "gicentre/elm-vegalite": "0214",
-  "yotamDvir/elm-pivot": "0215",
-  "NoRedInk/elm-simple-fuzzy": "0216",
-  "ggb/numeral-elm": "0217",
-  "zwilias/json-decode-exploration": "0218",
-  "jfmengels/elm-review-common": "0219",
-  "BrianHicks/elm-css-reset": "0220",
-  "Janiczek/elm-graph": "0221",
-  "ericgj/elm-csv-decode": "0222",
-  "periodic/elm-csv": "0223",
-  "pd-andy/tuple-extra": "0224",
-  "tesk9/accessible-html": "0225",
-  "lovasoa/elm-rolling-list": "0226",
-  "krisajenkins/elm-astar": "0227",
-  "BrianHicks/elm-particle": "0228",
-  "folkertdev/elm-sha2": "0229",
-  "Garados007/elm-svg-parser": "0230",
-  "gampleman/elm-mapbox": "0231",
-  "NoRedInk/elm-compare": "0232",
-  "joneshf/elm-tagged": "0233",
+  "tricycle/elm-embed-youtube": "0195",
+  "yotamDvir/elm-pivot": "0196",
+  "ahstro/elm-bulma-classes": "0197",
+  "gampleman/elm-mapbox": "0198",
+  "NoRedInk/elm-simple-fuzzy": "0199",
+  "billstclair/elm-sortable-table": "0200",
+  "dillonkearns/elm-oembed": "0201",
+  "icidasset/elm-binary": "0202",
+  "TSFoster/elm-sha1": "0203",
+  "zwilias/elm-utf-tools": "0204",
+  "visotype/elm-dom": "0205",
+  "rluiten/elm-text-search": "0206",
+  "alex-tan/elm-dialog": "0207",
+  "Gizra/elm-debouncer": "0208",
+  "NoRedInk/elm-compare": "0209",
+  "NoRedInk/list-selection": "0210",
+  "ggb/numeral-elm": "0211",
+  "Orasund/elm-ui-framework": "0212",
+  "stoeffel/elm-verify": "0213",
+  "thaterikperson/elm-strftime": "0214",
+  "WhileTruu/elm-smooth-scroll": "0215",
+  "lucamug/style-framework": "0216",
+  "jinjor/elm-xml-parser": "0217",
+  "ericgj/elm-csv-decode": "0218",
+  "the-sett/elm-refine": "0219",
+  "periodic/elm-csv": "0220",
+  "miyamoen/select-list": "0221",
+  "justgook/webgl-playground": "0222",
+  "Herteby/simplex-noise": "0222",
+  "billstclair/elm-xml-eeue56": "0223",
+  "kuon/elm-string-normalize": "0224",
+  "mcordova47/elm-natural-ordering": "0225",
+  "joakin/elm-grid": "0226",
+  "carwow/elm-slider": "0227",
+  "jasonliang512/elm-heroicons": "0228",
+  "mdgriffith/stylish-elephants": "0229",
+  "jgrenat/elm-html-test-runner": "0230",
+  "Kraxorax/elm-matrix-a": "0231",
+  "bburdette/websocket": "0232",
+  "Janiczek/elm-graph": "0233",
   "the-sett/lazy-list": "0234",
-  "turboMaCk/lazy-tree-with-zipper": "0235",
-  "jfmengels/elm-review-documentation": "0236",
-  "y047aka/elm-reset-css": "0237",
-  "Orasund/elm-ui-framework": "0238",
-  "ianmackenzie/elm-units-prefixed": "0239",
-  "jxxcarlson/elm-typed-time": "0240",
-  "ianmackenzie/elm-float-extra": "0241",
-  "matken11235/html-styled-extra": "0242",
-  "supermacro/elm-antd": "0242",
-  "jasonliang-dev/elm-heroicons": "0242",
-  "robinheghan/keyboard-events": "0242",
-  "MaybeJustJames/yaml": "0242",
-  "coinop-logan/elm-format-number": "0243",
-  "truqu/elm-review-noredundantconcat": "0244",
-  "truqu/elm-review-noredundantcons": "0245",
-  "ahstro/elm-bulma-classes": "0246",
-  "rluiten/elm-text-search": "0247",
-  "norpan/elm-json-patch": "0248",
-  "avh4/elm-fifo": "0249",
-  "eriktim/elm-protocol-buffers": "0250",
-  "zwilias/elm-utf-tools": "0251",
-  "fabhof/elm-ui-datepicker": "0252",
-  "Kraxorax/elm-matrix-a": "0253",
-  "Zinggi/elm-2d-game": "0254",
-  "waratuman/time-extra": "0255",
-  "pd-andy/elm-web-audio": "0256",
+  "jxxcarlson/elm-markdown": "0235",
+  "CoderDennis/elm-time-format": "0236",
+  "folkertdev/elm-sha2": "0237",
+  "jxxcarlson/elm-typed-time": "0238",
+  "avh4/elm-fifo": "0239",
+  "ianmackenzie/elm-triangular-mesh": "0240",
+  "Zinggi/elm-game-resources": "0241",
+  "tesk9/accessible-html-with-css": "0242",
+  "Orasund/elm-game-essentials": "0243",
+  "Zinggi/elm-2d-game": "0244",
+  "rtfeldman/elm-sorter-experiment": "0245",
+  "krisajenkins/elm-astar": "0246",
+  "FabienHenon/elm-infinite-list-view": "0247",
+  "Voronchuk/hexagons": "0248",
+  "ThinkAlexandria/elm-pretty-print-json": "0249",
+  "Chadtech/unique-list": "0250",
+  "arowM/elm-reference": "0251",
+  "elm-athlete/athlete": "0252",
+  "joshforisha/elm-html-entities": "0253",
+  "ChristophP/elm-i18next": "0254",
+  "dmy/elm-pratt-parser": "0255",
+  "achutkiran/material-components-elm": "0256",
   "ianmackenzie/elm-units-interval": "0257",
-  "alex-tan/elm-dialog": "0258",
-  "joakin/elm-grid": "0259",
-  "marcosh/elm-html-to-unicode": "0260",
-  "K-Adam/elm-dom": "0261",
-  "stoeffel/elm-verify": "0262",
-  "thaterikperson/elm-strftime": "0263",
-  "joshforisha/elm-html-entities": "0264",
-  "ChristophP/elm-i18next": "0265",
-  "WhileTruu/elm-smooth-scroll": "0266",
-  "smucode/elm-flat-colors": "0267",
-  "PaackEng/elm-ui-dropdown": "0268",
-  "jinjor/elm-xml-parser": "0269",
-  "Spaxe/svg-pathd": "0270",
-  "Zinggi/elm-game-resources": "0271",
-  "justgook/webgl-shape": "0272",
-  "miyamoen/select-list": "0273",
-  "cmditch/elm-ethereum": "0274",
-  "visotype/elm-dom": "0275",
-  "folkertdev/svg-path-lowlevel": "0276",
-  "rtfeldman/elm-sorter-experiment": "0277",
-  "stil4m/structured-writer": "0278",
-  "lucamug/style-framework": "0279",
-  "stoeffel/set-extra": "0280",
-  "STTR13/ziplist": "0281",
-  "FabienHenon/elm-infinite-list-view": "0282",
-  "FabienHenon/elm-infinite-scroll": "0283",
-  "sporto/time-distance": "0284",
-  "dillonkearns/elm-cli-options-parser": "0285",
-  "mcordova47/elm-natural-ordering": "0286",
-  "hecrj/elm-slug": "0287",
-  "NoRedInk/list-selection": "0288",
-  "Voronchuk/hexagons": "0289",
-  "y0hy0h/ordered-containers": "0290",
-  "elm-athlete/athlete": "0291",
-  "laserpants/elm-update-pipeline": "0292",
-  "coinop-logan/phace": "0293",
-  "BrianHicks/elm-trend": "0294",
-  "MartinSStewart/elm-audio": "0295",
-  "rl-king/elm-scroll-to": "0296",
-  "erlandsona/assoc-set": "0297",
-  "justgook/webgl-playground": "0298",
-  "tesk9/accessible-html-with-css": "0299",
-  "w0rm/elm-physics": "0300",
-  "jgrenat/elm-html-test-runner": "0301",
-  "krisajenkins/elm-exts": "0302",
-  "ianmackenzie/elm-interval": "0303",
-  "mhoare/elm-stack": "0304",
-  "Punie/elm-matrix": "0305",
-  "bburdette/websocket": "0306",
-  "danyx23/elm-mimetype": "0307",
-  "arowM/elm-mixin": "0308",
-  "sporto/elm-select": "0309",
-  "tricycle/elm-actor-framework": "0310",
-  "ThinkAlexandria/elm-pretty-print-json": "0311",
-  "arowM/elm-reference": "0312",
-  "mdgriffith/stylish-elephants": "0313",
-  "yotamDvir/elm-katex": "0314",
-  "carmonw/elm-number-to-words": "0315",
-  "dillonkearns/elm-oembed": "0316",
-  "RomanErnst/erl": "0317",
-  "w0rm/elm-obj-file": "0318",
-  "lemol/ant-design-icons-elm-ui": "0318",
-  "avh4/elm-beautiful-example": "0319",
-  "arowM/elm-classname": "0320",
-  "the-sett/elm-state-machines": "0321",
-  "Orasund/pixelengine": "0322",
-  "the-sett/elm-auth-aws": "0323",
-  "rl-king/elm-gallery": "0324",
-  "the-sett/elm-update-helper": "0325",
-  "the-sett/elm-auth": "0326",
-  "drathier/elm-graph": "0327",
-  "arowM/elm-neat-layout": "0328",
-  "bgrosse-midokura/composable-form": "0329",
-  "jxxcarlson/elm-cell-grid": "0330",
+  "BrianHicks/elm-particle": "0258",
+  "coinop-logan/elm-format-number": "0259",
+  "Spaxe/svg-pathd": "0260",
+  "erlandsona/assoc-set": "0261",
+  "ymtszw/elm-xml-decode": "0262",
+  "andre-dietrich/parser-combinators": "0263",
+  "cultureamp/elm-css-modules-loader": "0264",
+  "xarvh/elm-slides": "0265",
+  "blissfully/elm-chartjs-webcomponent": "0266",
+  "newmana/chroma-elm": "0267",
+  "Orasund/pixelengine": "0268",
+  "krisajenkins/elm-exts": "0269",
+  "ianmackenzie/elm-geometry-linear-algebra-interop": "0270",
+  "savardd/elm-time-travel": "0271",
+  "waratuman/elm-coder": "0272",
+  "Chadtech/id": "0273",
+  "the-sett/decode-generic": "0274",
+  "jxxcarlson/elm-pseudorandom": "0275",
+  "stoeffel/set-extra": "0276",
+  "arowM/elm-neat-layout": "0277",
+  "rluiten/stringdistance": "0278",
+  "sporto/elm-select": "0279",
+  "jxxcarlson/elm-graph": "0280",
+  "Microsoft/elm-json-tree-view": "0281",
+  "brandly/elm-dot-lang": "0282",
+  "avh4/elm-beautiful-example": "0283",
+  "ianmackenzie/elm-float-extra": "0284",
+  "RomanErnst/erl": "0285",
+  "LesleyLai/elm-grid": "0286",
+  "pd-andy/tuple-extra": "0287",
+  "jxxcarlson/elm-cell-grid": "0288",
+  "cultureamp/babel-elm-assets-plugin": "0289",
+  "Chadtech/elm-vector": "0290",
+  "drathier/elm-graph": "0291",
+  "eriktim/elm-protocol-buffers": "0292",
+  "the-sett/elm-state-machines": "0293",
+  "allo-media/elm-daterange-picker": "0294",
+  "mhoare/elm-stack": "0295",
+  "stoeffel/editable": "0296",
+  "the-sett/elm-serverless": "0297",
+  "rogeriochaves/elm-test-bdd-style": "0298",
+  "kirchner/elm-selectize": "0299",
+  "proda-ai/elm-dropzone": "0300",
+  "EdutainmentLIVE/elm-dropdown": "0301",
+  "dillonkearns/elm-cli-options-parser": "0302",
+  "sporto/time-distance": "0303",
+  "mgold/elm-geojson": "0304",
+  "y0hy0h/ordered-containers": "0305",
+  "EdutainmentLIVE/elm-bootstrap": "0306",
+  "ursi/elm-css-colors": "0307",
+  "larribas/elm-multi-input": "0308",
+  "kuzminadya/mogeefont": "0309",
+  "arowM/elm-mixin": "0310",
+  "waratuman/time-extra": "0311",
+  "yotamDvir/elm-katex": "0312",
+  "folkertdev/svg-path-lowlevel": "0313",
+  "dmy/elm-imf-date-time": "0314",
+  "league/unique-id": "0315",
+  "prikhi/http-tasks": "0316",
+  "norpan/elm-json-patch": "0317",
+  "rielas/measurement": "0318",
+  "laserpants/elm-update-pipeline": "0318",
+  "owanturist/elm-bulletproof": "0318",
+  "ianmackenzie/elm-interval": "0319",
+  "erosson/number-suffix": "0320",
+  "Garados007/elm-svg-parser": "0321",
+  "lazamar/dict-parser": "0322",
+  "etaque/elm-transit-style": "0323",
+  "Chadtech/return": "0324",
+  "samhstn/time-format": "0325",
+  "stil4m/structured-writer": "0326",
+  "jfmengels/lint-unused": "0327",
+  "etaque/elm-transit": "0328",
+  "json-tools/json-value": "0329",
+  "stephenreddek/elm-emoji": "0330",
+  "folkertdev/elm-int64": "0331",
+  "pehota/elm-zondicons": "0331",
+  "joshforisha/elm-inflect": "0331",
+  "panthershark/email-parser": "0331",
+  "brian-watkins/elm-spec": "0331",
   "indicatrix/elm-chartjs-webcomponent": "0331",
-  "andre-dietrich/parser-combinators": "0332",
-  "achutkiran/material-components-elm": "0333",
-  "inkuzmin/elm-multiselect": "0334",
-  "stoeffel/editable": "0335",
-  "Chadtech/id": "0336",
-  "jonoabroad/commatosed": "0337",
-  "kirchner/elm-selectize": "0338",
-  "phollyer/elm-phoenix-websocket": "0339",
-  "hmsk/elm-css-modern-normalize": "0339",
-  "gampleman/elm-examples-helper": "0339",
-  "malaire/elm-uint64": "0339",
-  "JohnBugner/elm-matrix": "0339",
-  "TheSacredLipton/elm-ui-hexcolor": "0339",
-  "jfmengels/elm-review-the-elm-architecture": "0339",
-  "jschomay/elm-paginate": "0340",
-  "alex-tan/elm-tree-diagram": "0341",
-  "xarvh/elm-slides": "0342",
-  "rluiten/stringdistance": "0343",
-  "Orasund/elm-game-essentials": "0344",
-  "mgold/elm-geojson": "0345",
-  "marshallformula/elm-swiper": "0346",
-  "jxxcarlson/elm-markdown": "0347",
-  "Microsoft/elm-json-tree-view": "0348",
-  "newmana/chroma-elm": "0349",
-  "justgook/alt-linear-algebra": "0350",
-  "larribas/elm-multi-input": "0351",
-  "lukewestby/elm-template": "0352",
-  "EdutainmentLIVE/elm-bootstrap": "0353",
-  "vito/elm-ansi": "0354",
-  "z5h/component-result": "0355",
-  "BrianHicks/elm-string-graphemes": "0356",
-  "proda-ai/elm-dropzone": "0357",
-  "TSFoster/elm-bytes-extra": "0358",
-  "tremlab/bugsnag-elm": "0359",
-  "fredcy/elm-debouncer": "0359",
-  "dzuk-mutant/elm-html-styled-aria": "0359",
-  "proda-ai/murmur3": "0359",
-  "oaalto/time-values": "0359",
-  "chelovek0v/bbase64": "0359",
-  "andre-dietrich/elm-conditional": "0359",
-  "robinheghan/elm-deque": "0359",
-  "MartinSStewart/elm-serialize": "0359",
-  "jxxcarlson/hex": "0360",
-  "prikhi/decimal": "0361",
-  "league/unique-id": "0362",
-  "jschomay/elm-narrative-engine": "0363",
-  "blissfully/elm-chartjs-webcomponent": "0364",
-  "jxxcarlson/elm-graph": "0365",
-  "samhstn/time-format": "0366",
-  "jxxcarlson/elm-pseudorandom": "0367",
-  "the-sett/elm-pretty-printer": "0368",
-  "cultureamp/elm-css-modules-loader": "0369",
-  "Fresheyeball/deburr": "0370",
-  "f0i/statistics": "0371",
-  "ymtszw/elm-http-xml": "0372",
-  "PaackEng/elm-svg-string": "0373",
-  "jouderianjr/elm-dialog": "0374",
-  "newlandsvalley/elm-binary-base64": "0375",
-  "the-sett/elm-string-case": "0376",
-  "supermario/elm-countries": "0377",
-  "gribouille/elm-treeview": "0378",
-  "etaque/elm-response": "0379",
-  "harrysarson/elm-complex": "0380",
-  "FMFI-UK-1-AIN-412/elm-formula": "0381",
-  "owanturist/elm-union-find": "0382",
-  "z5h/jaro-winkler": "0383",
-  "brandly/elm-dot-lang": "0384",
-  "y047aka/elm-hsl-color": "0385",
-  "ryry0/elm-numeric": "0386",
-  "rogeriochaves/elm-test-bdd-style": "0387",
-  "waratuman/elm-coder": "0388",
-  "hrldcpr/elm-cons": "0389",
-  "prozacchiwawa/elm-keccak": "0390",
-  "jims/html-parser": "0391",
-  "PanagiotisGeorgiadis/elm-datepicker": "0392",
-  "LesleyLai/elm-grid": "0393",
-  "mweiss/elm-rte-toolkit": "0394",
-  "isaacseymour/deprecated-time": "0395",
-  "sporto/qs": "0396",
-  "Chadtech/elm-money": "0397",
-  "kuzminadya/mogeefont": "0398",
-  "fifth-postulate/priority-queue": "0399",
-  "sudo-rushil/elm-cards": "0400",
-  "bigardone/elm-css-placeholders": "0401",
-  "ronanyeah/helpers": "0402",
-  "YuyaAizawa/list-wrapper": "0403",
-  "truqu/line-charts": "0404",
-  "dmy/elm-imf-date-time": "0405",
-  "SiriusStarr/elm-password-strength": "0406",
-  "EdutainmentLIVE/elm-dropdown": "0407",
-  "peterszerzo/line-charts": "0408",
-  "ktonon/elm-word": "0409",
-  "the-sett/elm-aws-core": "0410",
-  "mercurymedia/elm-datetime-picker": "0411",
-  "MartinSStewart/elm-box-packing": "0412",
-  "GlobalWebIndex/elm-plural-rules": "0413",
-  "lazamar/dict-parser": "0414",
-  "savardd/elm-time-travel": "0415",
-  "cappyzawa/elm-ui-onedark": "0416",
-  "erosson/number-suffix": "0417",
-  "NoRedInk/elm-random-general": "0418",
-  "edkv/elm-generic-dict": "0419",
-  "Chadtech/return": "0420",
-  "skyqrose/assoc-list-extra": "0421",
-  "prikhi/http-tasks": "0422",
-  "cedric-h/elm-google-sign-in": "0423",
-  "pehota/elm-zondicons": "0424",
-  "JonRowe/elm-jwt": "0425",
-  "etaque/elm-transit-style": "0426",
-  "dosarf/elm-tree-view": "0427",
-  "json-tools/json-value": "0428",
-  "r-k-b/no-long-import-lines": "0429",
-  "billstclair/elm-xml-eeue56": "0430",
-  "etaque/elm-transit": "0431",
-  "ursi/elm-throttle": "0432",
-  "the-sett/elm-serverless": "0433",
-  "commonmind/elm-csv-encode": "0434",
-  "allo-media/elm-daterange-picker": "0435",
-  "stephenreddek/elm-emoji": "0436",
-  "driebit/elm-css-breakpoint": "0437",
-  "f0i/iso8601": "0438",
-  "jordymoos/pilf": "0439",
-  "folq/review-rgb-ranges": "0440",
-  "terezka/yaml": "0441",
-  "kyasu1/elm-ulid": "0442",
-  "Kinto/elm-kinto": "0443",
-  "ghivert/elm-graphql": "0444",
-  "ursi/elm-scroll": "0445",
-  "tiziano88/elm-protobuf": "0446",
-  "Punie/elm-reader": "0447",
-  "bburdette/cellme": "0448",
-  "samueldple/material-color": "0449",
-  "Bractlet/elm-plot": "0450",
-  "jschomay/elm-bounded-number": "0451",
-  "jinjor/elm-contextmenu": "0452",
-  "prozacchiwawa/elm-urlbase64": "0453",
-  "jluckyiv/elm-utc-date-strings": "0454",
-  "mikaxyz/elm-cropper": "0455",
-  "TSFoster/elm-heap": "0456",
-  "owanturist/elm-bulletproof": "0457",
-  "ensoft/entrance": "0458",
-  "rgrempel/elm-http-decorators": "0459",
-  "Chadtech/random-pipeline": "0460",
-  "allo-media/canopy": "0461",
-  "harmboschloo/graphql-to-elm": "0462",
-  "rielas/measurement": "0463",
-  "Chadtech/elm-relational-database": "0464",
-  "johnathanbostrom/elm-dice": "0465",
-  "jxxcarlson/elm-editor": "0466",
-  "0ui/elm-task-parallel": "0467",
-  "the-sett/elm-refine": "0468",
-  "brian-watkins/elm-spec": "0469",
-  "avh4/elm-debug-controls": "0470",
-  "phollyer/elm-ui-colors": "0471",
-  "Herteby/enum": "0472",
-  "kmbn/elm-hotkeys": "0473",
-  "cultureamp/babel-elm-assets-plugin": "0474",
-  "toastal/mailto": "0475",
-  "zwilias/elm-bytes-parser": "0476",
-  "tad-lispy/springs": "0477",
-  "Janiczek/transform": "0478",
-  "hermanverschooten/ip": "0479",
-  "jfmengels/lint-unused": "0480",
-  "PaackEng/elm-google-maps": "0481",
-  "ericgj/elm-uri-template": "0482",
-  "data-viz-lab/elm-chart-builder": "0483",
-  "folkertdev/elm-paragraph": "0484",
-  "icidasset/elm-sha": "0485",
-  "dasch/levenshtein": "0486",
-  "wittjosiah/elm-ordered-dict": "0487",
-  "ktonon/elm-test-extra": "0488",
-  "rl-king/elm-masonry": "0489",
-  "billstclair/elm-custom-element": "0490",
-  "prikhi/bootstrap-gallery": "0491",
-  "joshforisha/elm-inflect": "0492",
-  "bburdette/pdf-element": "0493",
-  "austinshenk/elm-w3": "0494",
-  "json-tools/json-schema": "0495",
-  "NoRedInk/datetimepicker-legacy": "0496",
-  "ccapndave/focus": "0497",
-  "gdamjan/elm-identicon": "0498",
-  "bemyak/elm-slider": "0499",
-  "FabienHenon/jsonapi": "0500",
-  "NoRedInk/elm-plot-19": "0501",
-  "mercurymedia/elm-message-toast": "0502",
-  "chain-partners/elm-bignum": "0503",
-  "jouderianjr/elm-loaders": "0504",
-  "Punie/elm-id": "0505",
-  "drathier/elm-test-tables": "0506",
-  "Orasund/elm-action": "0507",
-  "w0rm/elm-slice-show": "0508",
-  "peterszerzo/elm-arborist": "0509",
-  "ThinkAlexandria/elm-html-in-elm": "0510",
-  "turboMaCk/queue": "0511",
-  "emilianobovetti/edit-distance": "0512",
-  "kuzzmi/elm-gravatar": "0513",
-  "ThinkAlexandria/elm-drag-locations": "0514",
-  "dawehner/elm-colorbrewer": "0515",
-  "xarvh/elm-gamepad": "0516",
-  "benthepoet/elm-purecss": "0517",
-  "billstclair/elm-oauth-middleware": "0518",
-  "alexkorban/uicards": "0519",
-  "folkertdev/elm-int64": "0520",
-  "billstclair/elm-geolocation": "0521",
-  "justgook/elm-game-logic": "0522",
-  "zgohr/elm-csv": "0523",
-  "gicentre/elm-vega": "0524",
-  "zwilias/elm-reorderable": "0525",
-  "toastal/select-prism": "0526",
-  "ronanyeah/calendar-dates": "0527",
-  "kuon/elm-hsluv": "0528",
-  "ir4y/elm-dnd": "0529",
-  "xilnocas/step": "0530",
-  "stephenreddek/elm-range-slider": "0531",
-  "fabiommendes/elm-iter": "0532",
-  "akoppela/elm-logo": "0533",
-  "billstclair/elm-crypto-string": "0534",
-  "arowM/elm-form-validator": "0535",
-  "Elm-Canvas/raster-shapes": "0536",
-  "1602/json-schema": "0537",
-  "ohanhi/autoexpand": "0538",
-  "pdamoc/elm-hashids": "0539",
-  "ohanhi/lorem": "0540",
-  "stephenreddek/elm-time-picker": "0541",
-  "mercurymedia/elm-smart-select": "0542",
-  "elm-in-elm/compiler": "0543",
-  "the-sett/decode-generic": "0544",
-  "ljuglaret/fraction": "0545",
-  "folkertdev/elm-kmeans": "0546",
-  "nikita-volkov/typeclasses": "0547",
-  "miniBill/date-format-languages": "0548",
-  "jaredramirez/elm-s3": "0549",
-  "jonathanfishbein1/elm-field": "0550",
-  "bburdette/schelme": "0551",
-  "Chadtech/elm-css-grid": "0552",
-  "eike/json-decode-complete": "0553",
-  "webbhuset/elm-actor-model": "0554",
-  "bburdette/toop": "0555",
-  "lynn/elm-ordinal": "0556",
-  "tricycle/elm-email": "0557",
-  "tricycle/system-actor-model": "0558",
-  "nikita-volkov/hashing-containers": "0559",
-  "Orasund/elm-cellautomata": "0560",
-  "simplystuart/elm-scroll-to": "0561",
-  "MartinSStewart/elm-codec-bytes": "0562",
-  "Chadtech/elm-vector": "0563",
-  "arowM/html-extra": "0564",
-  "munksgaard/elm-charts": "0565",
-  "tesk9/modal": "0566",
-  "RalfNorthman/elm-zoom-plot": "0567",
-  "jxxcarlson/htree": "0568",
-  "jxxcarlson/elm-stat": "0569",
-  "billstclair/elm-svg-button": "0570",
-  "webbhuset/elm-actor-model-elm-ui": "0571",
-  "folkertdev/elm-tiny-inflate": "0572",
-  "fifth-postulate/elm-csv-decode": "0573",
-  "viir/simplegamedev": "0574",
-  "elm-toulouse/cbor": "0575",
-  "rl-king/elm-modular-scale": "0576",
-  "bowbahdoe/elm-history": "0577",
-  "jxxcarlson/elm-widget": "0578",
-  "jackhp95/elm-mapbox": "0579",
-  "alex-tan/task-extra": "0580",
-  "jjant/elm-printf": "0581",
-  "jigargosar/elm-material-color": "0582",
-  "avh4/elm-dropbox": "0583",
-  "jonathanfishbein1/linear-algebra": "0584",
-  "tomjkidd/elm-multiway-tree-zipper": "0585",
-  "munksgaard/char-extra": "0586",
-  "munksgaard/elm-data-uri": "0587",
-  "billstclair/elm-id-search": "0588",
-  "thought2/elm-interactive": "0589",
-  "brianvanburken/elm-list-date": "0590",
-  "andre-dietrich/elm-random-regex": "0591",
-  "flowlang-cc/elm-audio-graph": "0592",
-  "andre-dietrich/elm-svgbob": "0593",
-  "rluiten/trie": "0594",
-  "ringvold/elm-iso8601-date-strings": "0595",
-  "rl-king/elm-inview": "0596",
-  "afidegnum/elm-tailwind": "0597",
-  "fifth-postulate/combinatorics": "0598",
-  "indicatrix/elm-input-extra": "0599",
-  "abadi199/elm-creditcard": "0600",
-  "arowM/elm-css-modules-helper": "0601",
-  "alexanderkiel/elm-mdc-alpha": "0602",
-  "sparksp/elm-review-camelcase": "0603",
-  "dwyl/elm-criteria": "0604",
-  "ericgj/elm-validation": "0605",
-  "prikhi/paginate": "0606",
-  "romstad/elm-chess": "0607",
-  "basti1302/elm-non-empty-array": "0608",
-  "MichaelCombs28/elm-base85": "0609",
-  "arnau/elm-objecthash": "0610",
-  "NoRedInk/elm-debug-controls-without-datepicker": "0611",
-  "dasch/parser": "0612",
-  "NoRedInk/noredink-ui": "0613",
-  "ozmat/elm-forms": "0614",
-  "the-sett/ai-search": "0615",
-  "allenap/elm-json-decode-broken": "0616",
-  "nathanjohnson320/base58": "0617",
-  "noahzgordon/elm-jsonapi": "0618",
-  "labzero/elm-google-geocoding": "0619",
-  "pilatch/elm-chess": "0620",
-  "torreyatcitty/the-best-decimal": "0621",
-  "NoRedInk/elm-rails": "0622",
-  "FordLabs/elm-star-rating": "0623",
-  "Bernardoow/elm-alert-timer-message": "0624",
-  "JohnBugner/elm-bag": "0625",
-  "Gizra/elm-compat-019": "0626",
-  "goilluminate/elm-fancy-daterangepicker": "0627",
-  "janjelinek/creditcard-validation": "0628",
-  "r-k-b/complex": "0629",
-  "NoRedInk/elm-sweet-poll": "0630",
-  "TSFoster/elm-compare": "0631",
-  "fedragon/elm-typed-dropdown": "0632",
-  "emilianobovetti/elm-yajson": "0633",
-  "jjant/elm-comonad-zipper": "0634",
-  "ozmat/elm-validation": "0635",
-  "jackfranklin/elm-parse-link-header": "0636",
-  "Gizra/elm-attribute-builder": "0637",
-  "kkpoon/elm-auth0-urlparser": "0638",
-  "1602/json-value": "0639",
-  "ggb/elm-bloom": "0640",
-  "peterszerzo/elm-porter": "0641",
-  "jweir/sparkline": "0642",
-  "fbonetti/elm-geodesy": "0643",
-  "bChiquet/elm-accessors": "0644",
-  "tricycle/elm-actor-framework-template": "0645",
-  "robinheghan/elm-warrior": "0645",
-  "wsowens/term": "0645",
-  "finos/morphir-elm": "0645",
-  "sparksp/elm-review-always": "0646",
-  "YuyaAizawa/peg": "0647",
-  "jfmengels/lint-debug": "0648",
-  "billstclair/elm-dialog": "0649",
-  "jxxcarlson/meenylatex": "0650",
-  "the-sett/elm-error-handling": "0651",
-  "Arkham/elm-rttl": "0652",
-  "jfmengels/elm-lint": "0653",
-  "Chadtech/ct-colors": "0654",
-  "lemol/ant-design-icons-elm": "0655",
-  "Gizra/elm-fetch": "0656",
-  "GoldentTuft/elm-japanese-typing": "0657",
-  "tricycle/elm-actor-framework-template-html": "0657",
-  "groma84/elm-tachyons": "0657",
-  "shnewto/pgn": "0657",
-  "ContaSystemer/elm-review-no-regex": "0657",
-  "tasuki/elm-punycode": "0657",
-  "billstclair/elm-popup-picker": "0657",
-  "hallelujahdrive/elm-croppie": "0657",
-  "the-sett/elm-localstorage": "0657",
-  "mrpinsky/elm-keyed-list": "0657",
-  "choonkeat/elm-fullstack": "0657",
-  "carwow/elm-review-rules": "0657",
-  "rl-king/elm-index": "0657",
-  "shamansir/elm-aframe": "0657",
-  "opvasger/msg-replay": "0657",
-  "3kyro/xsrf-protection": "0657",
-  "sparksp/elm-review-imports": "0657",
-  "jfmengels/elm-review-license": "0657",
-  "JustinLove/elm-twitch-api": "0658",
-  "billstclair/elm-websocket-framework": "0659",
-  "calions-app/env": "0660",
-  "chazsconi/elm-phoenix-ports": "0661",
-  "chicode/lisa": "0662",
-  "leojpod/review-no-empty-html-text": "0663",
-  "showell/meta-elm": "0664",
-  "dasch/crockford": "0665",
-  "billstclair/elm-mastodon-websocket": "0666",
-  "sparksp/elm-review-ports": "0667",
-  "justgook/elm-tiled": "0668",
-  "matheus23/elm-markdown-transforms": "0669",
-  "ursi/support": "0670",
-  "billstclair/elm-mastodon": "0671",
-  "TSFoster/elm-md5": "0672",
-  "jxxcarlson/tree-extra": "0673",
-  "miyamoen/bibliopola": "0674",
-  "ThinkAlexandria/css-in-elm": "0675",
-  "r-k-b/no-float-ids": "0676",
-  "nishiurahiroki/elm-simple-pagenate": "0677",
-  "the-sett/elm-aws-cognito": "0678",
-  "arowM/html": "0679",
-  "owanturist/elm-avl-dict": "0680",
-  "z5h/timeline": "0681",
-  "JoshuaHall/elm-fraction": "0682",
-  "jxxcarlson/elm-text-editor": "0683",
-  "rjbma/elm-listview": "0684",
-  "visotype/elm-eval": "0685",
-  "waratuman/elm-standardapi": "0686",
-  "calions-app/test-attribute": "0687",
-  "jxxcarlson/elm-tar": "0688",
-  "waratuman/json-extra": "0689",
-  "vViktorPL/elm-jira-connector": "0690",
-  "nicmr/compgeo": "0691",
-  "showell/binary-tree-diagram": "0692",
-  "showell/dict-dot-dot": "0693",
-  "MartinSStewart/elm-nonempty-string": "0694",
-  "Gizra/elm-editable-webdata": "0695",
-  "Gizra/elm-storage-key": "0696",
-  "bburdette/typed-collections": "0697",
-  "wolfadex/elm-text-adventure": "0698",
-  "sashaafm/eetf": "0699",
-  "rluiten/stemmer": "0700",
-  "rluiten/sparsevector": "0701",
-  "ChristophP/elm-mark": "0702",
-  "calions-app/app-object": "0703",
-  "calions-app/remote-resource": "0704",
-  "calions-app/jsonapi-http": "0705",
-  "jfmengels/elm-lint-reporter": "0706",
-  "brian-watkins/elm-procedure": "0707",
-  "dosarf/elm-yet-another-polling": "0708",
-  "commonmind/elm-csexpr": "0709",
-  "folkertdev/elm-brotli": "0710",
-  "yumlonne/elm-japanese-calendar": "0711",
-  "jxxcarlson/math-markdown": "0712",
-  "billstclair/elm-websocket-framework-server": "0713",
-  "alex-tan/postgrest-client": "0714",
-  "primait/pyxis-components": "0715",
-  "maca/crdt-replicated-tree": "0716",
-  "avh4/elm-desktop-app": "0717",
-  "ceddlyburge/elm-bootstrap-starter-master-view": "0718",
-  "dvberkel/microkanren": "0719",
-  "getto-systems/elm-apply": "0720",
-  "getto-systems/elm-field": "0721",
-  "getto-systems/elm-html-table": "0722",
-  "getto-systems/elm-http-part": "0723",
-  "getto-systems/elm-http-header": "0724",
-  "getto-systems/elm-url": "0725",
-  "getto-systems/elm-json": "0726",
-  "getto-systems/elm-sort": "0727",
-  "getto-systems/elm-command": "0728",
-  "primait/forms": "0729",
-  "harmboschloo/elm-dict-intersect": "0730",
-  "anhmiuhv/pannablevideo": "0731",
-  "Spaxe/elm-lsystem": "0732",
-  "harmboschloo/elm-ecs": "0733",
-  "AuricSystemsInternational/creditcard-validator": "0734",
-  "alex-tan/postgrest-queries": "0735",
-  "emptyflash/typed-svg": "0736",
-  "billstclair/elm-crypto-aes": "0737",
-  "eelcoh/parser-indent": "0738",
-  "benansell/lobo-elm-test-extra": "0739",
-  "miyamoen/elm-command-pallet": "0740",
-  "melon-love/elm-gab-api": "0741",
-  "iodevs/elm-history": "0742",
-  "ymtszw/elm-broker": "0743",
-  "ThinkAlexandria/elm-primer-tooltips": "0744",
-  "ryan-senn/elm-compiler-error-sscce": "0745",
-  "genthaler/elm-enum": "0746",
-  "bigbinary/elm-reader": "0747",
-  "bigbinary/elm-form-field": "0748",
-  "the-sett/svg-text-fonts": "0749",
-  "billstclair/elm-chat": "0750",
-  "Libbum/elm-partition": "0751",
-  "turboMaCk/elm-continue": "0752",
-  "imjoehaines/afinn-165-elm": "0753",
-  "the-sett/json-optional": "0754",
-  "toastal/endo": "0755",
-  "jackhp95/palit": "0756",
-  "arowM/elm-html-extra-internal": "0757",
-  "arowM/elm-html-internal": "0758",
-  "elm-toulouse/float16": "0759",
-  "munksgaard/elm-media-type": "0760",
-  "jonathanfishbein1/complex-numbers": "0761",
-  "NoRedInk/style-elements": "0762",
-  "truqu/elm-dictset": "0763",
-  "Janiczek/architecture-test": "0764",
-  "NoRedInk/http-upgrade-shim": "0765",
-  "miyamoen/tree-with-zipper": "0766",
-  "rtfeldman/count": "0767",
-  "NoRedInk/elm-saved": "0768",
-  "NoRedInk/elm-rfc5988-parser": "0769",
-  "NoRedInk/elm-formatted-text-test-helpers": "0770",
-  "NoRedInk/elm-formatted-text-19": "0771",
-  "NoRedInk/elm-plot-rouge": "0772",
-  "NoRedInk/elm-rollbar": "0773",
-  "avh4/burndown-charts": "0774",
-  "mpizenberg/elm-placeholder-pkg": "0775",
-  "avh4/elm-github-v3": "0776",
-  "justinmimbs/tzif": "0777",
-  "folkertdev/elm-cff": "0778",
-  "the-sett/elm-one-many": "0779",
-  "ianmackenzie/elm-iso-10303": "0780",
-  "ianmackenzie/elm-step-file": "0781",
-  "ianmackenzie/elm-geometry-prerelease": "0782",
-  "terezka/elm-charts-alpha": "0783",
-  "terezka/charts": "0783",
-  "the-sett/salix-aws-spec": "0784",
-  "the-sett/parser-recoverable": "0784",
-  "lukewestby/accessible-html-with-css-temp-19": "0785",
-  "folkertdev/elm-iris": "0786",
-  "lukewestby/http-extra": "0787",
-  "jfmengels/elm-review-reporter": "0788",
-  "the-sett/elm-enum": "0789",
-  "Janiczek/elm-runescape": "0790",
-  "Janiczek/browser-extra": "0791",
-  "jxxcarlson/toc-editor": "0792",
-  "billstclair/elm-dev-random": "0793",
-  "miniBill/elm-bare": "0794",
-  "miniBill/elm-avataaars": "0795",
-  "zwilias/elm-holey-zipper": "0796",
-  "TSFoster/elm-envfile": "0797",
-  "PaackEng/paack-ui": "0798",
-  "PaackEng/paack-remotedata": "0798",
-  "the-sett/auth-elm": "0799",
-  "Chadtech/dependent-text": "0800",
-  "the-sett/tea-tree": "0801",
-  "Chadtech/elm-imperative-porting": "0802",
-  "Chadtech/elm-provider": "0803",
-  "Chadtech/elm-us-state-abbreviations": "0804",
-  "jinjor/elm-map-debug": "0805",
-  "jinjor/elm-req": "0806",
-  "jinjor/elm-insertable-key": "0807",
-  "ContaSystemer/elm-review-no-missing-documentation": "0808",
-  "ContaSystemer/elm-angularjs-custom-element": "0808",
-  "ContaSystemer/elm-effects": "0808",
-  "ContaSystemer/elm-effects-time": "0808",
-  "ContaSystemer/elm-js-data": "0808",
-  "ContaSystemer/elm-error-message": "0808",
-  "ContaSystemer/elm-effects-msg-from-js": "0808",
-  "turboMaCk/glue": "0809",
-  "tricycle/elm-actor-framework-template-markdown": "0810",
-  "tricycle/elm-actor-framework-sandbox": "0810",
-  "ccapndave/elm-eexl": "0811",
-  "ccapndave/elm-typed-tree": "0812",
-  "ccapndave/elm-translator": "0813",
-  "ccapndave/elm-statecharts": "0814",
-  "simonh1000/elm-sliding-menus": "0815",
-  "rl-king/elm-iso3166-country-codes": "0816",
-  "pd-andy/elm-limiter": "0817",
-  "joneshf/elm-these": "0818",
-  "arowM/elm-data-url": "0819",
-  "robinheghan/elm-phone-numbers": "0820",
-  "arowM/elm-html-with-context": "0821",
-  "tricycle/elm-storage": "0822",
-  "arowM/elm-parser-test": "0823",
-  "miyamoen/elm-origami": "0824",
-  "jweir/charter": "0825",
-  "tricycle/elm-parse-dont-validate": "0826",
-  "frandibar/elm-bootstrap": "0827",
-  "carwow/elm-slider-old": "0828",
-  "Zinggi/elm-glsl-generator": "0829",
-  "danhandrea/elm-router": "0830",
-  "lucamug/elm-box-drawing": "0830",
-  "danhandrea/elm-time-extra": "0830",
-  "justgook/elm-webdriver": "0831",
-  "ktonon/elm-jsonwebtoken": "0832",
-  "MartinSStewart/elm-geometry-serialize": "0833",
-  "abadi199/intl-phone-input": "0834",
-  "stoeffel/resetable": "0835",
-  "ahstro/elm-luhn": "0836",
-  "FabienHenon/elm-ckeditor5": "0837",
-  "GlobalWebIndex/quantify": "0838",
-  "FabienHenon/elm-iso8601-date-strings": "0839",
-  "proda-ai/elm-css": "0840",
-  "jgrenat/regression-testing": "0841",
-  "GlobalWebIndex/class-namespaces": "0842",
-  "PaackEng/elm-alert-beta": "0843",
-  "tricycle/elm-infinite-gallery": "0844",
-  "tricycle/elm-imgix": "0845",
-  "sporto/elm-countries": "0846",
-  "sporto/polylinear-scale": "0847",
-  "gicentre/tidy": "0848",
-  "rluiten/mailcheck": "0849",
-  "tricycle/elm-eventstream": "0850",
-  "panthershark/snackbar": "0851",
-  "alex-tan/loadable": "0852",
-  "WhileTruu/elm-blurhash": "0853",
-  "MartinSStewart/send-grid": "0854",
-  "MartinSStewart/elm-bayer-matrix": "0855",
-  "Herteby/url-builder-plus": "0856",
-  "andre-dietrich/elm-mapbox": "0857",
-  "z5h/zipper": "0858",
-  "tricycle/morty-api": "0859",
-  "mthadley/elm-typewriter": "0860",
-  "ggb/porterstemmer": "0861",
-  "ggb/elm-trend": "0862",
-  "ggb/elm-sentiment": "0863",
-  "pd-andy/elm-audio-graph": "0864",
-  "ThinkAlexandria/window-manager": "0865",
-  "owanturist/elm-queue": "0866",
-  "drathier/elm-test-graph": "0867",
-  "bburdette/stl": "0868",
-  "jonathanfishbein1/numeric-typeclasses": "0869",
-  "gribouille/elm-spinner": "0870",
-  "gribouille/elm-prelude": "0870",
-  "gribouille/elm-graphql": "0870",
-  "gribouille/elm-bulma": "0870",
-  "peterszerzo/elm-json-tree-view": "0871",
-  "allo-media/elm-es-simple-query-string": "0872",
-  "prozacchiwawa/elm-json-codec": "0873",
-  "danhandrea/elm-foo": "0874",
-  "proda-ai/formatting": "0875",
-  "proda-ai/elm-svg-loader": "0876",
-  "laserpants/elm-burrito-update": "0877",
-  "harrysarson/elm-decode-elmi": "0878",
-  "allo-media/fable": "0879",
-  "achutkiran/elm-material-color": "0880",
-  "kirchner/form-validation": "0881",
-  "marshallformula/arrangeable-list": "0882",
-  "RalfNorthman/elm-lttb": "0883",
-  "malaire/elm-safe-int": "0884",
-  "driebit/elm-ginger": "0885",
-  "MichaelCombs28/elm-css-bulma": "0886",
-  "league/difference-list": "0887",
-  "harrysarson/elm-hacky-unique": "0888",
-  "owanturist/elm-graphql": "0889",
-  "dosarf/elm-activemq": "0890",
-  "SiriusStarr/elm-spaced-repetition": "0891",
-  "r-k-b/elm-interval": "0892",
-  "SiriusStarr/elm-splat": "0893",
-  "calions-app/elm-placeholder-loading": "0894",
-  "r-k-b/map-accumulate": "0895",
-  "JohnBugner/elm-loop": "0896",
-  "jjant/elm-dict": "0897",
-  "calions-app/jsonapi-http-retry": "0898",
-  "bowbahdoe/lime-reset": "0899",
-  "ljuglaret/combinatoire": "0900",
-  "harmboschloo/graphql-to-elm-package": "0901",
-  "thought2/elm-wikimedia-commons": "0902",
-  "afidegnum/elm-bulmanizer": "0903",
-  "alexanderkiel/list-selection": "0904",
-  "Bernardoow/elm-rating-component": "0905",
-  "kkpoon/elm-echarts": "0906",
-  "kkpoon/elm-auth0": "0907",
-  "jaredramirez/elm-field": "0908",
-  "showell/elm-data-util": "0909",
-  "hallelujahdrive/elm-accordion": "0910",
-  "leojpod/elm-apex-charts-link": "0910",
-  "tasuki/elm-bloom": "0910",
-  "shamansir/tron-gui": "0910",
-  "leojpod/elm-keyboard-shortcut": "0910",
-  "choonkeat/elm-aws": "0910",
-  "shamansir/bin-pack": "0910",
-  "ryan-senn/elm-google-domains": "0911",
-  "ryan-senn/elm-tlds": "0912",
-  "ryan-senn/elm-readability": "0913",
-  "wolfadex/locale-negotiation": "0914",
-  "matheus23/elm-figma-api": "0915",
-  "vViktorPL/elm-incremental-list": "0916",
-  "Arkham/elm-chords": "0917",
-  "primait/elm-form": "0918",
-  "opvasger/amr": "0919",
-  "choonkeat/elm-retry": "0920",
-  "wolfadex/tiler": "0921",
-  "iodevs/elm-validate": "0922",
-  "Libbum/elm-redblacktrees": "0923",
-  "avh4-experimental/elm-transducers": "0924",
-  "ryan-senn/stellar-elm-sdk": "0925",
+  "ericgj/elm-uri-template": "0331",
+  "jouderianjr/elm-loaders": "0331",
+  "billstclair/elm-crypto-string": "0331",
+  "prikhi/bootstrap-gallery": "0331",
+  "toastal/select-prism": "0331",
+  "the-sett/elm-pretty-printer": "0332",
+  "driebit/elm-css-breakpoint": "0333",
+  "owanturist/elm-union-find": "0334",
+  "newlandsvalley/elm-binary-base64": "0335",
+  "jordymoos/pilf": "0336",
+  "w0rm/elm-physics": "0337",
+  "the-sett/elm-string-case": "0338",
+  "harrysarson/elm-complex": "0339",
+  "Herteby/enum": "0340",
+  "elm-in-elm/compiler": "0341",
+  "inkuzmin/elm-multiselect": "0342",
+  "abradley2/elm-datepicker": "0343",
+  "Kinto/elm-kinto": "0344",
+  "kmbn/elm-hotkeys": "0345",
+  "hrldcpr/elm-cons": "0346",
+  "jonoabroad/commatosed": "0347",
+  "tiziano88/elm-protobuf": "0348",
+  "FabienHenon/elm-infinite-scroll": "0349",
+  "gicentre/elm-vega": "0350",
+  "zwilias/elm-bytes-parser": "0351",
+  "prozacchiwawa/elm-keccak": "0352",
+  "alex-tan/elm-tree-diagram": "0353",
+  "Bractlet/elm-plot": "0354",
+  "isaacseymour/deprecated-time": "0355",
+  "YuyaAizawa/list-wrapper": "0356",
+  "danyx23/elm-mimetype": "0357",
+  "truqu/line-charts": "0358",
+  "prozacchiwawa/elm-urlbase64": "0359",
+  "marshallformula/elm-swiper": "0360",
+  "hecrj/elm-slug": "0361",
+  "ljuglaret/fraction": "0362",
+  "rgrempel/elm-http-decorators": "0363",
+  "Chadtech/random-pipeline": "0364",
+  "allo-media/canopy": "0365",
+  "tomjkidd/elm-multiway-tree-zipper": "0366",
+  "folkertdev/elm-kmeans": "0367",
+  "SiriusStarr/elm-password-strength": "0368",
+  "Janiczek/transform": "0369",
+  "doanythingfordethklok/snackbar": "0370",
+  "hermanverschooten/ip": "0371",
+  "jaredramirez/elm-s3": "0372",
+  "arowM/elm-classname": "0373",
+  "Punie/elm-matrix": "0374",
+  "Orasund/elm-action": "0375",
+  "vito/elm-ansi": "0376",
+  "cmditch/elm-ethereum": "0377",
+  "BrianHicks/elm-trend": "0378",
+  "ktonon/elm-word": "0379",
+  "folkertdev/elm-paragraph": "0380",
+  "z5h/component-result": "0381",
+  "wittjosiah/elm-ordered-dict": "0382",
+  "the-sett/elm-aws-core": "0383",
+  "ktonon/elm-test-extra": "0384",
+  "pd-andy/elm-web-audio": "0385",
+  "edkv/elm-generic-dict": "0386",
+  "jxxcarlson/hex": "0387",
+  "Gizra/elm-fetch": "0388",
+  "NoRedInk/elm-random-general": "0389",
+  "ccapndave/focus": "0390",
+  "prikhi/decimal": "0391",
+  "gdamjan/elm-identicon": "0392",
+  "JonRowe/elm-jwt": "0393",
+  "NoRedInk/datetimepicker-legacy": "0394",
+  "abinayasudhir/html-parser": "0395",
+  "jschomay/elm-narrative-engine": "0396",
+  "json-tools/json-schema": "0397",
+  "the-sett/elm-update-helper": "0398",
+  "billstclair/elm-custom-element": "0399",
+  "dosarf/elm-tree-view": "0400",
+  "nikita-volkov/hashing-containers": "0401",
+  "nikita-volkov/typeclasses": "0402",
+  "PaackEng/elm-svg-string": "0403",
+  "jouderianjr/elm-dialog": "0404",
+  "rl-king/elm-gallery": "0405",
+  "MartinSStewart/elm-codec-bytes": "0406",
+  "etaque/elm-response": "0407",
+  "commonmind/elm-csv-encode": "0408",
+  "arsduo/elm-dom-drag-drop": "0409",
+  "kuzzmi/elm-gravatar": "0410",
+  "w0rm/elm-slice-show": "0411",
+  "billstclair/elm-oauth-middleware": "0412",
+  "alexkorban/uicards": "0413",
+  "terezka/yaml": "0414",
+  "emilianobovetti/edit-distance": "0415",
+  "jfmengels/review-debug": "0416",
+  "ThinkAlexandria/elm-drag-locations": "0417",
+  "BrianHicks/elm-string-graphemes": "0418",
+  "webbhuset/elm-actor-model": "0419",
+  "xarvh/elm-gamepad": "0420",
+  "benthepoet/elm-purecss": "0421",
+  "Punie/elm-reader": "0422",
+  "zgohr/elm-csv": "0423",
+  "xilnocas/step": "0424",
+  "jinjor/elm-contextmenu": "0425",
+  "mikaxyz/elm-cropper": "0426",
+  "turboMaCk/queue": "0427",
+  "fedragon/elm-typed-dropdown": "0428",
+  "sporto/qs": "0429",
+  "jschomay/elm-bounded-number": "0430",
+  "kuon/elm-hsluv": "0431",
+  "arowM/elm-form-validator": "0432",
+  "TSFoster/elm-heap": "0433",
+  "Elm-Canvas/raster-shapes": "0434",
+  "1602/json-value": "0435",
+  "Chadtech/elm-money": "0436",
+  "jschomay/elm-paginate": "0437",
+  "lukewestby/elm-template": "0438",
+  "ohanhi/lorem": "0439",
+  "stephenreddek/elm-time-picker": "0440",
+  "tesk9/modal": "0441",
+  "icidasset/elm-material-icons": "0442",
+  "arowM/html-extra": "0443",
+  "Chadtech/elm-relational-database": "0444",
+  "kyasu1/elm-ulid": "0445",
+  "webbhuset/elm-actor-model-elm-ui": "0446",
+  "fifth-postulate/elm-csv-decode": "0447",
+  "Orasund/elm-cellautomata": "0448",
+  "Chadtech/elm-css-grid": "0449",
+  "viir/simplegamedev": "0450",
+  "PaackEng/elm-ui-dialog": "0451",
+  "RalfNorthman/elm-zoom-plot": "0452",
+  "jfmengels/review-common": "0453",
+  "y047aka/elm-reset-css": "0453",
+  "PanagiotisGeorgiadis/elm-datepicker": "0454",
+  "avh4/elm-debug-controls": "0455",
+  "jjant/elm-printf": "0456",
+  "toastal/mailto": "0457",
+  "avh4/elm-dropbox": "0458",
+  "rl-king/elm-modular-scale": "0459",
+  "tad-lispy/springs": "0460",
+  "billstclair/elm-id-search": "0461",
+  "thought2/elm-interactive": "0462",
+  "jigargosar/elm-material-color": "0463",
+  "peterszerzo/line-charts": "0464",
+  "andre-dietrich/elm-random-regex": "0465",
+  "flowlang-cc/elm-audio-graph": "0466",
+  "andre-dietrich/elm-svgbob": "0467",
+  "icidasset/elm-sha": "0468",
+  "jxxcarlson/htree": "0469",
+  "y047aka/elm-hsl-color": "0470",
+  "billstclair/elm-svg-button": "0471",
+  "abadi199/elm-creditcard": "0472",
+  "indicatrix/elm-input-extra": "0473",
+  "ringvold/elm-iso8601-date-strings": "0474",
+  "rl-king/elm-inview": "0475",
+  "dwyl/elm-criteria": "0476",
+  "rluiten/trie": "0477",
+  "nathanjohnson320/elm-ui-components": "0478",
+  "prikhi/paginate": "0479",
+  "romstad/elm-chess": "0480",
+  "alexanderkiel/elm-mdc-alpha": "0481",
+  "FabienHenon/jsonapi": "0482",
+  "Gizra/elm-editable-webdata": "0483",
+  "dasch/parser": "0484",
+  "Gizra/elm-storage-key": "0485",
+  "NoRedInk/elm-plot-19": "0486",
+  "NoRedInk/noredink-ui": "0487",
+  "allenap/elm-json-decode-broken": "0488",
+  "nathanjohnson320/base58": "0489",
+  "peterszerzo/elm-arborist": "0490",
+  "Punie/elm-id": "0491",
+  "arnau/elm-objecthash": "0492",
+  "noahzgordon/elm-jsonapi": "0493",
+  "labzero/elm-google-geocoding": "0494",
+  "ozmat/elm-forms": "0495",
+  "ymtszw/elm-http-xml": "0496",
+  "drathier/elm-test-tables": "0497",
+  "dawehner/elm-colorbrewer": "0498",
+  "supermario/elm-countries": "0499",
+  "torreyatcitty/the-best-decimal": "0500",
+  "JeremyBellows/elm-bootstrapify": "0501",
+  "billstclair/elm-geolocation": "0502",
+  "FordLabs/elm-star-rating": "0503",
+  "ghivert/elm-graphql": "0504",
+  "JohnBugner/elm-bag": "0505",
+  "Gizra/elm-compat-019": "0506",
+  "zwilias/elm-reorderable": "0507",
+  "goilluminate/elm-fancy-daterangepicker": "0508",
+  "janjelinek/creditcard-validation": "0509",
+  "r-k-b/complex": "0510",
+  "ir4y/elm-dnd": "0511",
+  "stephenreddek/elm-range-slider": "0512",
+  "NoRedInk/elm-sweet-poll": "0513",
+  "fabiommendes/elm-iter": "0514",
+  "akoppela/elm-logo": "0515",
+  "emilianobovetti/elm-yajson": "0516",
+  "ozmat/elm-validation": "0517",
+  "the-sett/ai-search": "0518",
+  "jackfranklin/elm-parse-link-header": "0519",
+  "Gizra/elm-attribute-builder": "0520",
+  "peterszerzo/elm-porter": "0521",
+  "jweir/sparkline": "0522",
+  "ohanhi/autoexpand": "0523",
+  "bChiquet/elm-accessors": "0524",
+  "jfmengels/lint-debug": "0525",
+  "ronanyeah/helpers": "0526",
+  "justgook/webgl-shape": "0527",
+  "dasch/crockford": "0528",
+  "justgook/alt-linear-algebra": "0528",
+  "burnable-tech/elm-ethereum": "0528",
+  "Arkham/elm-rttl": "0528",
+  "calions-app/env": "0528",
+  "YuyaAizawa/peg": "0528",
+  "Orasund/elm-jsonstore": "0528",
+  "cedric-h/elm-google-sign-in": "0528",
+  "showell/meta-elm": "0528",
+  "the-sett/elm-error-handling": "0528",
+  "billstclair/elm-dialog": "0529",
+  "the-sett/the-sett-laf": "0530",
+  "billstclair/elm-websocket-framework": "0531",
+  "justgook/elm-tiled": "0532",
+  "chicode/lisa": "0533",
+  "Chadtech/ct-colors": "0534",
+  "billstclair/elm-mastodon": "0535",
+  "jfmengels/elm-lint": "0536",
+  "justgook/elm-game-logic": "0537",
+  "owanturist/elm-avl-dict": "0538",
+  "bburdette/toop": "0539",
+  "rjbma/elm-listview": "0540",
+  "coinop-logan/phace": "0540",
+  "nishiurahiroki/elm-simple-pagenate": "0540",
+  "austinshenk/elm-w3": "0540",
+  "jxxcarlson/elm-text-editor": "0540",
+  "jxxcarlson/elm-editor": "0540",
+  "jxxcarlson/tree-extra": "0540",
+  "calions-app/test-attribute": "0540",
+  "phollyer/elm-ui-colors": "0540",
+  "ursi/elm-throttle": "0540",
+  "z5h/timeline": "0540",
+  "waratuman/elm-standardapi": "0540",
+  "waratuman/json-extra": "0540",
+  "mweiss/elm-rte-toolkit": "0540",
+  "MartinSStewart/elm-audio": "0540",
+  "showell/binary-tree-diagram": "0540",
+  "showell/dict-dot-dot": "0540",
+  "JoshuaHall/elm-fraction": "0540",
+  "rl-king/elm-masonry": "0540",
+  "bburdette/pdf-element": "0540",
+  "nicmr/compgeo": "0540",
+  "arowM/html": "0541",
+  "bburdette/schelme": "0542",
+  "wolfadex/elm-text-adventure": "0543",
+  "skyqrose/assoc-list-extra": "0544",
+  "miyamoen/bibliopola": "0545",
+  "sashaafm/eetf": "0546",
+  "ChristophP/elm-mark": "0547",
+  "billstclair/elm-mastodon-websocket": "0548",
+  "ThinkAlexandria/css-in-elm": "0549",
+  "jxxcarlson/meenylatex": "0550",
+  "calions-app/app-object": "0551",
+  "calions-app/remote-resource": "0552",
+  "calions-app/jsonapi-http": "0553",
+  "visotype/elm-eval": "0554",
+  "jfmengels/elm-lint-reporter": "0555",
+  "brian-watkins/elm-procedure": "0556",
+  "cappyzawa/elm-ui-onedark": "0557",
+  "dosarf/elm-yet-another-polling": "0558",
+  "jxxcarlson/elm-tar": "0559",
+  "TSFoster/elm-bytes-extra": "0560",
+  "vViktorPL/elm-jira-connector": "0561",
+  "commonmind/elm-csexpr": "0562",
+  "yumlonne/elm-japanese-calendar": "0563",
+  "jxxcarlson/math-markdown": "0564",
+  "billstclair/elm-websocket-framework-server": "0565",
+  "simplystuart/elm-scroll-to": "0566",
+  "alex-tan/postgrest-client": "0567",
+  "rluiten/stemmer": "0568",
+  "TSFoster/elm-md5": "0569",
+  "rluiten/sparsevector": "0570",
+  "maca/crdt-replicated-tree": "0571",
+  "avh4/elm-desktop-app": "0572",
+  "ensoft/entrance": "0573",
+  "ceddlyburge/elm-bootstrap-starter-master-view": "0574",
+  "dvberkel/microkanren": "0575",
+  "getto-systems/elm-apply": "0576",
+  "getto-systems/elm-field": "0577",
+  "getto-systems/elm-html-table": "0578",
+  "getto-systems/elm-http-part": "0579",
+  "getto-systems/elm-http-header": "0580",
+  "getto-systems/elm-url": "0581",
+  "getto-systems/elm-json": "0582",
+  "getto-systems/elm-sort": "0583",
+  "getto-systems/elm-command": "0584",
+  "anhmiuhv/pannablevideo": "0585",
+  "harmboschloo/elm-dict-intersect": "0586",
+  "bowbahdoe/elm-history": "0587",
+  "jxxcarlson/elm-stat": "0588",
+  "Spaxe/elm-lsystem": "0589",
+  "JustinLove/elm-twitch-api": "0590",
+  "AuricSystemsInternational/creditcard-validator": "0591",
+  "alex-tan/postgrest-queries": "0592",
+  "eelcoh/parser-indent": "0593",
+  "benansell/lobo-elm-test-extra": "0594",
+  "fifth-postulate/combinatorics": "0595",
+  "miyamoen/elm-command-pallet": "0596",
+  "melon-love/elm-gab-api": "0597",
+  "billstclair/elm-crypto-aes": "0598",
+  "abradley2/form-elements": "0599",
+  "arowM/elm-css-modules-helper": "0600",
+  "iodevs/elm-history": "0601",
+  "ymtszw/elm-broker": "0602",
+  "ThinkAlexandria/elm-primer-tooltips": "0603",
+  "ryan-senn/elm-compiler-error-sscce": "0604",
+  "genthaler/elm-enum": "0605",
+  "bigbinary/elm-reader": "0606",
+  "bigbinary/elm-form-field": "0607",
+  "ryry0/elm-numeric": "0608",
+  "bburdette/typed-collections": "0609",
+  "r-k-b/map-accumulate": "0610",
+  "emptyflash/typed-svg": "0611",
+  "the-sett/svg-text-fonts": "0612",
+  "billstclair/elm-chat": "0613",
+  "Libbum/elm-partition": "0614",
+  "turboMaCk/elm-continue": "0615",
+  "imjoehaines/afinn-165-elm": "0616",
+  "arowM/elm-html-extra-internal": "0617",
+  "arowM/elm-html-internal": "0618",
+  "folkertdev/elm-tiny-inflate": "0619",
+  "toastal/endo": "0620",
+  "truqu/elm-dictset": "0621",
+  "Janiczek/architecture-test": "0622",
+  "NoRedInk/http-upgrade-shim": "0623",
+  "miyamoen/tree-with-zipper": "0624",
+  "rtfeldman/count": "0625",
+  "NoRedInk/elm-saved": "0626",
+  "NoRedInk/elm-rfc5988-parser": "0627",
+  "NoRedInk/elm-debug-controls-without-datepicker": "0628",
+  "NoRedInk/elm-formatted-text-test-helpers": "0629",
+  "NoRedInk/elm-formatted-text-19": "0630",
+  "NoRedInk/elm-plot-rouge": "0631",
+  "NoRedInk/elm-rails": "0632",
+  "justinmimbs/tzif": "0633",
+  "NoRedInk/elm-rollbar": "0634",
+  "avh4/burndown-charts": "0635",
+  "avh4/elm-github-v3": "0636",
+  "ianmackenzie/elm-geometry-test": "0637",
+  "the-sett/elm-one-many": "0638",
+  "folkertdev/elm-iris": "0639",
+  "ianmackenzie/elm-iso-10303": "0640",
+  "ianmackenzie/elm-step-file": "0641",
+  "ianmackenzie/elm-geometry-prerelease": "0642",
+  "lukewestby/accessible-html-with-css-temp-19": "0643",
+  "lukewestby/http-extra": "0644",
+  "folkertdev/elm-brotli": "0645",
+  "the-sett/elm-enum": "0646",
+  "the-sett/elm-aws-cognito": "0646",
+  "the-sett/elm-auth": "0646",
+  "the-sett/elm-auth-aws": "0646",
+  "the-sett/salix": "0646",
+  "the-sett/json-optional": "0646",
+  "Janiczek/elm-runescape": "0647",
+  "Janiczek/browser-extra": "0648",
+  "Fresheyeball/deburr": "0649",
+  "Skinney/elm-deque": "0650",
+  "Skinney/elm-phone-numbers": "0651",
+  "billstclair/elm-dev-random": "0652",
+  "zwilias/elm-holey-zipper": "0653",
+  "jinjor/elm-map-debug": "0654",
+  "jinjor/elm-req": "0655",
+  "the-sett/auth-elm": "0656",
+  "the-sett/tea-tree": "0657",
+  "jinjor/elm-insertable-key": "0658",
+  "Chadtech/dependent-text": "0659",
+  "Chadtech/elm-imperative-porting": "0660",
+  "Chadtech/elm-provider": "0661",
+  "Chadtech/elm-us-state-abbreviations": "0662",
+  "miniBill/date-format-languages": "0663",
+  "jweir/charter": "0664",
+  "Orasund/elm-ui-widgets": "0665",
+  "turboMaCk/glue": "0666",
+  "TSFoster/elm-envfile": "0667",
+  "Zinggi/elm-glsl-generator": "0668",
+  "simonh1000/elm-sliding-menus": "0669",
+  "ccapndave/elm-eexl": "0670",
+  "ccapndave/elm-typed-tree": "0671",
+  "jfmengels/elm-review-reporter": "0672",
+  "ccapndave/elm-translator": "0673",
+  "ccapndave/elm-statecharts": "0674",
+  "TSFoster/elm-compare": "0675",
+  "arowM/elm-data-url": "0676",
+  "waratuman/elm-json-extra": "0677",
+  "FabienHenon/remote-resource": "0678",
+  "arowM/elm-html-with-context": "0679",
+  "arowM/elm-parser-test": "0680",
+  "frandibar/elm-bootstrap": "0681",
+  "carwow/elm-slider-old": "0682",
+  "ahstro/elm-luhn": "0683",
+  "stoeffel/resetable": "0684",
+  "GlobalWebIndex/quantify": "0685",
+  "ktonon/elm-jsonwebtoken": "0686",
+  "lynn/elm-ordinal": "0687",
+  "alex-tan/task-extra": "0688",
+  "andre-dietrich/elm-mapbox": "0689",
+  "GlobalWebIndex/class-namespaces": "0690",
+  "basti1302/elm-non-empty-array": "0691",
+  "gicentre/tidy": "0692",
+  "rl-king/elm-scroll-to": "0693",
+  "rluiten/mailcheck": "0694",
+  "FabienHenon/elm-ckeditor5": "0695",
+  "abadi199/intl-phone-input": "0696",
+  "FabienHenon/elm-iso8601-date-strings": "0697",
+  "WhileTruu/elm-blurhash": "0698",
+  "tricycle/elm-parse-dont-validate": "0698",
+  "pilatch/elm-chess": "0699",
+  "alex-tan/loadable": "0700",
+  "1602/json-schema": "0701",
+  "justgook/elm-webdriver": "0702",
+  "sporto/elm-countries": "0703",
+  "sporto/polylinear-scale": "0704",
+  "allo-media/elm-es-simple-query-string": "0705",
+  "owanturist/elm-queue": "0706",
+  "mthadley/elm-typewriter": "0707",
+  "PaackEng/elm-google-maps": "0708",
+  "Herteby/url-builder-plus": "0709",
+  "ericgj/elm-validation": "0710",
+  "tricycle/elm-email": "0711",
+  "tricycle/elm-infinite-gallery": "0712",
+  "tricycle/system-actor-model": "0713",
+  "bburdette/cellme": "0714",
+  "tricycle/elm-imgix": "0715",
+  "ThinkAlexandria/elm-html-in-elm": "0716",
+  "PaackEng/elm-ui-dropdown": "0717",
+  "drathier/elm-test-graph": "0718",
+  "tricycle/elm-infnite-gallery": "0719",
+  "ThinkAlexandria/window-manager": "0720",
+  "ggb/porterstemmer": "0721",
+  "ggb/elm-bloom": "0722",
+  "ggb/elm-trend": "0723",
+  "ggb/elm-sentiment": "0724",
+  "tricycle/elm-eventstream": "0725",
+  "z5h/jaro-winkler": "0726",
+  "z5h/zipper": "0726",
+  "proda-ai/elm-logger": "0727",
+  "driebit/elm-ginger": "0728",
+  "prozacchiwawa/elm-json-codec": "0729",
+  "achutkiran/elm-material-color": "0730",
+  "allo-media/fable": "0731",
+  "peterszerzo/elm-json-tree-view": "0732",
+  "harrysarson/elm-decode-elmi": "0733",
+  "tricycle/morty-api": "0734",
+  "ursi/elm-scroll": "0735",
+  "kirchner/form-validation": "0736",
+  "proda-ai/formatting": "0737",
+  "proda-ai/elm-svg-loader": "0738",
+  "league/difference-list": "0739",
+  "MartinSStewart/elm-nonempty-string": "0740",
+  "MartinSStewart/elm-bayer-matrix": "0740",
+  "MartinSStewart/send-grid": "0740",
+  "r-k-b/no-float-ids": "0740",
+  "r-k-b/no-long-import-lines": "0740",
+  "laserpants/elm-burrito-update": "0741",
+  "pd-andy/elm-audio-graph": "0742",
+  "bburdette/stl": "0743",
+  "danhandrea/elm-foo": "0744",
+  "abradley2/form-fields": "0745",
+  "harrysarson/elm-hacky-unique": "0746",
+  "panthershark/snackbar": "0747",
+  "abradley2/form-controls": "0748",
+  "PaackEng/elm-alert-beta": "0749",
+  "owanturist/elm-graphql": "0750",
+  "peterszerzo/elm-natural-ui": "0751",
+  "marshallformula/arrangeable-list": "0752",
+  "ljuglaret/combinatoire": "0753",
+  "abinayasudhir/elm-select": "0754",
+  "dosarf/elm-activemq": "0755",
+  "SiriusStarr/elm-spaced-repetition": "0756",
+  "abinayasudhir/elm-treeview": "0757",
+  "arsduo/elm-ui-drag-drop": "0758",
+  "r-k-b/elm-interval": "0759",
+  "SiriusStarr/elm-splat": "0760",
+  "dasch/levenshtein": "0761",
+  "thought2/elm-wikimedia-commons": "0762",
+  "getto-systems/getto-elm-command": "0763",
+  "showell/elm-data-util": "0764",
+  "alexanderkiel/list-selection": "0765",
+  "jjant/elm-dict": "0766",
+  "JohnBugner/elm-loop": "0767",
+  "abinayasudhir/outmessage": "0768",
+  "jjant/elm-comonad-zipper": "0769",
+  "ronanyeah/calendar-dates": "0770",
+  "jaredramirez/elm-field": "0771",
+  "vViktorPL/elm-incremental-list": "0772",
+  "harmboschloo/graphql-to-elm": "0773",
+  "Arkham/elm-chords": "0774",
+  "bowbahdoe/lime-reset": "0775",
+  "maca/crdt-replicated-graph": "0776",
+  "harmboschloo/graphql-to-elm-package": "0777",
+  "harmboschloo/elm-ecs": "0778",
+  "wolfadex/tiler": "0779",
+  "iodevs/elm-validate": "0780",
+  "Libbum/elm-redblacktrees": "0781",
+  "ryan-senn/stellar-elm-sdk": "0782",
 };
 
 export default RANKING_LIST;
diff --git a/src/util/elmUtils.ts b/src/util/elmUtils.ts
index 8b6a1b9..88b1b4b 100644
--- a/src/util/elmUtils.ts
+++ b/src/util/elmUtils.ts
@@ -2,6 +2,7 @@ import execa, { ExecaReturnValue } from "execa";
 import * as path from "path";
 import { Connection, CompletionItemKind } from "vscode-languageserver";
 import { URI } from "vscode-uri";
+import { IElmPackageCache, IConstraint, IVersion } from "../elmWorkspace";
 import { IClientSettings } from "./settings";
 
 export const isWindows = process.platform === "win32";
@@ -109,54 +110,187 @@ export async function getElmVersion(
   return Promise.resolve(version);
 }
 
-export function findDepVersion(
-  allVersionFolders: { version: string; versionPath: string }[],
-  versionRange: string,
-): { version: string; versionPath: string } | undefined {
+type SolverResult =
+  | {
+      pending: ReadonlyMap<string, IConstraint>;
+      solutions: ReadonlyMap<string, IVersion>;
+    }
+  | undefined;
+
+export function solveDependencies(
+  packageCache: IElmPackageCache,
+  deps: ReadonlyMap<string, IConstraint>,
+): ReadonlyMap<string, IVersion> | undefined {
+  const result = solveDependenciesWorker(
+    packageCache,
+    deps,
+    new Map<string, IVersion>(),
+  );
+
+  if (result) {
+    return result.solutions;
+  }
+}
+
+function solveDependenciesWorker(
+  packageCache: IElmPackageCache,
+  deps: ReadonlyMap<string, IConstraint>,
+  solutions: ReadonlyMap<string, IVersion>,
+): SolverResult {
+  function pickDep(): [
+    { name: string; constraint: IConstraint } | undefined,
+    Map<string, IConstraint>,
+  ] {
+    const restDeps = new Map(deps);
+    const firstDep = Array.from(deps.keys()).sort()[0];
+
+    restDeps.delete(firstDep);
+
+    return [
+      firstDep
+        ? { name: firstDep, constraint: deps.get(firstDep)! }
+        : undefined,
+      restDeps,
+    ];
+  }
+
+  function combineDeps(
+    a: ReadonlyMap<string, IConstraint>,
+    b: ReadonlyMap<string, IConstraint>,
+  ): Map<string, IConstraint> | undefined {
+    const deps = new Map<string, IConstraint>();
+    for (const key of new Set([...a.keys(), ...b.keys()])) {
+      const v1 = a.get(key);
+      const v2 = b.get(key);
+
+      if (v1 && v2) {
+        const intersect = constraintIntersect(v1, v2);
+
+        if (!intersect) {
+          return;
+        }
+
+        deps.set(key, intersect);
+      } else if (v1) {
+        deps.set(key, v1);
+      } else if (v2) {
+        deps.set(key, v2);
+      } else {
+        throw new Error("impossible");
+      }
+    }
+
+    return deps;
+  }
+
+  const [dep, restDeps] = pickDep();
+
+  if (!dep) {
+    return { pending: deps, solutions };
+  }
+
+  // Find versions that satisfy the constraint
+  let candidates = packageCache
+    .get(dep.name)
+    .filter(({ version }) => versionSatifiesConstraint(version, dep.constraint))
+    .sort((a, b) =>
+      a.version < b.version ? -1 : a.version > b.version ? 1 : 0,
+    )
+    .reverse();
+
+  const solvedVersion = solutions.get(dep.name);
+
+  if (solvedVersion) {
+    candidates = candidates.filter(
+      (a) => a.version.string === solvedVersion.string,
+    );
+  }
+
+  for (const candidate of candidates) {
+    const tentativeDeps = combineDeps(restDeps, candidate.dependencies);
+
+    if (!tentativeDeps) {
+      continue;
+    }
+
+    const tentativeSolutions = new Map(solutions);
+    tentativeSolutions.set(dep.name, candidate.version);
+
+    const result = solveDependenciesWorker(
+      packageCache,
+      tentativeDeps,
+      tentativeSolutions,
+    );
+
+    if (result) {
+      return solveDependenciesWorker(
+        packageCache,
+        result.pending,
+        result.solutions,
+      );
+    }
+  }
+}
+
+export function parseVersion(version: string): IVersion {
+  const [major, minor, patch] = version.split(".");
+
+  return {
+    major: parseInt(major),
+    minor: parseInt(minor),
+    patch: parseInt(patch),
+    string: version,
+  };
+}
+
+export function parseContraint(contraint: string): IConstraint {
   const regex = /^(\d+\.\d+\.\d+) (<|<=) v (<|<=) (\d+\.\d+\.\d+)$/gm;
 
-  const m = regex.exec(versionRange);
+  const m = regex.exec(contraint);
   if (m) {
     const lowerRange = m[1];
     const lowerOperator = m[2];
     const upperOperator = m[3];
     const upperRange = m[4];
 
-    const filteredVersionList = allVersionFolders
-      .filter((a) => filterSemver(a.version, lowerRange, lowerOperator))
-      .filter((a) => filterSemver(upperRange, a.version, upperOperator));
-
-    const latestVersionInRange = filteredVersionList
-      .map((a) => a.version)
-      .sort(cmp)
-      .reverse()[0];
-    return allVersionFolders.find((a) => a.version === latestVersionInRange);
-  } else {
-    // Regex did not work, probably not a version range
-    return allVersionFolders.find(
-      (it: { version: string; versionPath: string }) =>
-        versionRange.includes(it.version),
-    );
+    return {
+      lower: parseVersion(lowerRange),
+      upper: parseVersion(upperRange),
+      lowerOperator: lowerOperator as "<" | "<=",
+      upperOperator: upperOperator as "<" | "<=",
+    };
   }
+
+  throw new Error("Could not parse version constraint");
+}
+
+export function versionSatifiesConstraint(
+  version: IVersion,
+  constraint: IConstraint,
+): boolean {
+  return (
+    filterSemver(constraint.lower, version, constraint.lowerOperator) &&
+    filterSemver(version, constraint.upper, constraint.upperOperator)
+  );
 }
 
 function filterSemver(
-  lower: string,
-  upper: string,
-  operator: string,
-): boolean | undefined {
-  const currentCompare = cmp(lower, upper);
+  lower: IVersion,
+  upper: IVersion,
+  operator: "<" | "<=",
+): boolean {
+  const currentCompare = versionCompare(lower, upper);
   switch (operator) {
     case "<=":
-      return currentCompare !== -1;
+      return currentCompare === -1 || currentCompare === 0;
     case "<":
-      return !(currentCompare === -1 || currentCompare === 0);
+      return currentCompare === -1;
   }
 }
 
-function cmp(a: string, b: string): number {
-  const pa = a.split(".");
-  const pb = b.split(".");
+function versionCompare(a: IVersion, b: IVersion): number {
+  const pa = a.string.split(".");
+  const pb = b.string.split(".");
   for (let i = 0; i < 3; i++) {
     const na = Number(pa[i]);
     const nb = Number(pb[i]);
@@ -166,12 +300,71 @@ function cmp(a: string, b: string): number {
     if (nb > na) {
       return -1;
     }
-    if (!isNaN(na) && isNaN(nb)) {
-      return 1;
-    }
-    if (isNaN(na) && !isNaN(nb)) {
-      return -1;
-    }
   }
   return 0;
 }
+
+export function constraintIntersect(
+  a: IConstraint,
+  b: IConstraint,
+): IConstraint | undefined {
+  function merge(op1: "<=" | "<", op2: "<=" | "<"): "<=" | "<" {
+    return op1 === "<" || op2 === "<" ? "<" : "<=";
+  }
+
+  let newLower;
+  let newLowerOp;
+  let newUpper;
+  let newUpperOp;
+
+  const lowerCompare = versionCompare(a.lower, b.lower);
+
+  switch (lowerCompare) {
+    case -1:
+      newLower = b.lower;
+      newLowerOp = b.lowerOperator;
+      break;
+    case 0:
+      newLower = a.lower;
+      newLowerOp = merge(a.lowerOperator, b.lowerOperator);
+      break;
+    case 1:
+      newLower = a.lower;
+      newLowerOp = a.lowerOperator;
+      break;
+  }
+
+  const upperCompare = versionCompare(a.upper, b.upper);
+
+  switch (upperCompare) {
+    case -1:
+      newUpper = a.upper;
+      newUpperOp = a.upperOperator;
+      break;
+    case 0:
+      newUpper = a.upper;
+      newUpperOp = merge(a.upperOperator, b.upperOperator);
+      break;
+    case 1:
+      newUpper = b.upper;
+      newUpperOp = b.upperOperator;
+      break;
+  }
+
+  if (
+    !newLower ||
+    !newUpper ||
+    !newLowerOp ||
+    !newUpperOp ||
+    versionCompare(newLower, newUpper) !== -1
+  ) {
+    return;
+  }
+
+  return {
+    lower: newLower,
+    upper: newUpper,
+    lowerOperator: newLowerOp,
+    upperOperator: newUpperOp,
+  };
+}
diff --git a/src/util/elmWorkspaceMatcher.ts b/src/util/elmWorkspaceMatcher.ts
index 7f94889..b62b630 100644
--- a/src/util/elmWorkspaceMatcher.ts
+++ b/src/util/elmWorkspaceMatcher.ts
@@ -97,12 +97,6 @@ export class ElmWorkspaceMatcher<ParamType> {
   ): ITreeContainer {
     const uri = this.getUriFor(param).toString();
 
-    const sourceFile = program.getForest().getByUri(uri);
-
-    if (!sourceFile) {
-      throw new NoWorkspaceContainsError(this.getUriFor(param));
-    }
-
-    return sourceFile;
+    return program.getForest().getByUri(uri)!;
   }
 }
diff --git a/src/util/path.ts b/src/util/path.ts
new file mode 100644
index 0000000..b7c7e93
--- /dev/null
+++ b/src/util/path.ts
@@ -0,0 +1,26 @@
+import { URI } from "vscode-uri";
+
+import * as path from "path";
+
+export const directorySeparator = "/";
+const backslashRegExp = /\\/g;
+
+export function normalizeSlashes(path: string): string {
+  return path.replace(backslashRegExp, directorySeparator);
+}
+
+export function normalizeUri(uri: string): string {
+  return normalizeSlashes(URI.file(uri).fsPath);
+}
+
+export function join(...paths: string[]): string {
+  return normalizeSlashes(path.join(...paths));
+}
+
+export function relative(from: string, to: string): string {
+  return normalizeSlashes(path.relative(from, to));
+}
+
+export function resolve(...pathSegments: string[]): string {
+  return normalizeSlashes(path.resolve(...pathSegments));
+}
diff --git a/src/util/types/typeChecker.ts b/src/util/types/typeChecker.ts
index 2861c45..03c8b63 100644
--- a/src/util/types/typeChecker.ts
+++ b/src/util/types/typeChecker.ts
@@ -913,7 +913,7 @@ export function createTypeChecker(workspace: IElmWorkspace): TypeChecker {
 
     if (moduleNameNode) {
       const moduleName = moduleNameNode.text;
-      if (!workspace.getForest().getByModuleName(moduleName)) {
+      if (!workspace.hasAccessibleModule(moduleName)) {
         diagnostics.add(
           error(moduleNameNode, Diagnostics.ImportMissing, moduleName),
         );


Test patch:
diff --git a/src/util/elmUtils.test.ts b/src/util/elmUtils.test.ts
deleted file mode 100644
index e2cf1d7..0000000
--- a/src/util/elmUtils.test.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { findDepVersion } from "./elmUtils";
-
-const versionFolders = [
-  { version: "0.18.0", versionPath: "path/to/0.18.0" },
-  { version: "0.19.0", versionPath: "path/to/0.19.0" },
-  { version: "0.19.1", versionPath: "path/to/0.19.1" },
-  { version: "0.19.16", versionPath: "path/to/0.19.16" },
-  { version: "0.20.0", versionPath: "path/to/0.20.0" },
-  { version: "0.20.1", versionPath: "path/to/0.20.1" },
-];
-
-test("fallback to normal matching for invalid version ranges", () => {
-  const dependencies = "0.19.0";
-  expect(findDepVersion(versionFolders, dependencies)).toStrictEqual({
-    version: "0.19.0",
-    versionPath: "path/to/0.19.0",
-  });
-});
-
-test("finds the correct version in a version range", () => {
-  const dependencies = "0.19.0 <= v < 0.20.0";
-  expect(findDepVersion(versionFolders, dependencies)).toStrictEqual({
-    version: "0.19.16",
-    versionPath: "path/to/0.19.16",
-  });
-});
-
-test("finds the correct version in a version range when upper range equals", () => {
-  const dependencies = "0.19.0 <= v <= 0.20.0";
-  expect(findDepVersion(versionFolders, dependencies)).toStrictEqual({
-    version: "0.20.0",
-    versionPath: "path/to/0.20.0",
-  });
-});
-
-test("finds the correct version in a version range when lower range equals", () => {
-  const versionFolders = [
-    { version: "0.18.0", versionPath: "path/to/0.18.0" },
-    { version: "0.19.0", versionPath: "path/to/0.19.0" },
-    { version: "0.20.1", versionPath: "path/to/0.20.1" },
-  ];
-  const dependencies = "0.19.0 <= v < 0.20.0";
-  expect(findDepVersion(versionFolders, dependencies)).toStrictEqual({
-    version: "0.19.0",
-    versionPath: "path/to/0.19.0",
-  });
-});
diff --git a/test/codeActionProvider.test.ts b/test/codeActionProvider.test.ts
index d6753dd..f9e25bc 100644
--- a/test/codeActionProvider.test.ts
+++ b/test/codeActionProvider.test.ts
@@ -8,9 +8,8 @@ import {
 } from "../src/providers";
 import { ICodeActionParams } from "../src/providers/paramsExtensions";
 import { Utils } from "../src/util/utils";
-import { baseUri } from "./utils/mockElmWorkspace";
 import { getTargetPositionFromSource } from "./utils/sourceParser";
-import { SourceTreeParser } from "./utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "./utils/sourceTreeParser";
 
 function codeActionEquals(a: CodeAction, b: CodeAction): boolean {
   return a.title === b.title;
@@ -75,7 +74,7 @@ describe("test codeActionProvider", () => {
 
     const testUri = URI.file(baseUri + "Test.elm").toString();
 
-    const program = treeParser.getWorkspace(result.sources);
+    const program = await treeParser.getProgram(result.sources);
     const sourceFile = program.getForest().getByUri(testUri);
 
     if (!sourceFile) throw new Error("Getting tree failed");
diff --git a/test/completionProvider.test.ts b/test/completionProvider.test.ts
index 84764e4..4b66d47 100644
--- a/test/completionProvider.test.ts
+++ b/test/completionProvider.test.ts
@@ -8,9 +8,8 @@ import {
 import { URI } from "vscode-uri";
 import { CompletionProvider, CompletionResult } from "../src/providers";
 import { ICompletionParams } from "../src/providers/paramsExtensions";
-import { baseUri } from "./utils/mockElmWorkspace";
 import { getCaretPositionFromSource } from "./utils/sourceParser";
-import { SourceTreeParser } from "./utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "./utils/sourceTreeParser";
 
 class MockCompletionProvider extends CompletionProvider {
   public handleCompletion(params: ICompletionParams): CompletionResult {
@@ -52,11 +51,11 @@ describe("CompletionProvider", () => {
     }
 
     const testUri = URI.file(baseUri + fileWithCaret).toString();
-    const program = treeParser.getWorkspace(newSources);
-    const sourceFile = program.getForest().getByUri(testUri);
+    const program = await treeParser.getProgram(newSources);
+    const sourceFile = program.getSourceFile(testUri);
 
     function testCompletionsWithContext(context: CompletionContext): void {
-      if (!sourceFile) throw new Error("Getting tree failed");
+      if (!sourceFile) throw new Error("Getting source file failed");
 
       const completions =
         completionProvider.handleCompletion({
diff --git a/test/constraint.test.ts b/test/constraint.test.ts
new file mode 100644
index 0000000..fb71831
--- /dev/null
+++ b/test/constraint.test.ts
@@ -0,0 +1,120 @@
+import { assert } from "console";
+import { IConstraint, IVersion } from "../src/elmWorkspace";
+import {
+  constraintIntersect,
+  parseContraint,
+  versionSatifiesConstraint,
+} from "../src/util/elmUtils";
+import { Utils } from "../src/util/utils";
+
+describe("constraint test", () => {
+  function v(
+    major: number,
+    minor: number,
+    patch: number,
+    preReleaseFields?: string[],
+  ): IVersion {
+    return {
+      major,
+      minor,
+      patch,
+      string: preReleaseFields
+        ? `${major}.${minor}.${patch}-${preReleaseFields.join(".")}`
+        : `${major}.${minor}.${patch}`,
+    };
+  }
+
+  const c = parseContraint;
+
+  it("can determine whether a version satifies a half-open constraint", () => {
+    const c: IConstraint = {
+      lower: v(1, 0, 0),
+      upper: v(2, 0, 0),
+      lowerOperator: "<=",
+      upperOperator: "<",
+    };
+
+    expect(versionSatifiesConstraint(v(0, 9, 0), c)).toBe(false);
+    expect(versionSatifiesConstraint(v(1, 0, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(1, 1, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(2, 0, 0), c)).toBe(false);
+  });
+
+  it("can determine whether a version satisfies an inclusive constraint", () => {
+    const c: IConstraint = {
+      lower: v(1, 0, 0),
+      upper: v(2, 0, 0),
+      lowerOperator: "<=",
+      upperOperator: "<=",
+    };
+
+    expect(versionSatifiesConstraint(v(0, 9, 0), c)).toBe(false);
+    expect(versionSatifiesConstraint(v(1, 0, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(1, 1, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(2, 0, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(2, 1, 0), c)).toBe(false);
+  });
+
+  it("can determine whether a version satisfies a SemVer constraint", () => {
+    const c: IConstraint = {
+      lower: v(1, 0, 0),
+      upper: v(2, 0, 0),
+      lowerOperator: "<=",
+      upperOperator: "<",
+    };
+    expect(versionSatifiesConstraint(v(0, 9, 0), c)).toBe(false);
+    expect(versionSatifiesConstraint(v(0, 9, 0, ["alpha"]), c)).toBe(false);
+    expect(versionSatifiesConstraint(v(1, 0, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(1, 0, 0, ["alpha"]), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(1, 1, 0), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(1, 1, 0, ["alpha"]), c)).toBe(true);
+    expect(versionSatifiesConstraint(v(2, 0, 0), c)).toBe(false);
+    expect(versionSatifiesConstraint(v(2, 0, 0, ["alpha"]), c)).toBe(false);
+  });
+
+  it("parse works on good input", () => {
+    expect({
+      lower: v(1, 0, 0),
+      upper: v(2, 0, 0),
+      lowerOperator: "<=",
+      upperOperator: "<",
+    }).toEqual(c("1.0.0 <= v < 2.0.0"));
+  });
+
+  it("parse throws on bad input", () => {
+    expect(() => c("1.0.0 <= v <= bogus")).toThrow();
+  });
+
+  it("empty intersection", () => {
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 1.0.1"), c("1.0.1 <= v < 2.0.0")),
+    ).toBeUndefined();
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 1.1.0"), c("1.1.0 <= v < 2.0.0")),
+    ).toBeUndefined();
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 2.0.0"), c("2.0.0 <= v < 3.0.0")),
+    ).toBeUndefined();
+  });
+
+  it("non-empty intersection", () => {
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 2.0.0"), c("1.0.0 <= v < 3.0.0")),
+    ).toEqual(c("1.0.0 <= v < 2.0.0"));
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 3.0.0"), c("2.0.0 <= v < 3.0.0")),
+    ).toEqual(c("2.0.0 <= v < 3.0.0"));
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 3.0.0"), c("2.0.0 <= v < 4.0.0")),
+    ).toEqual(c("2.0.0 <= v < 3.0.0"));
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 2.0.0"), c("1.0.0 <= v < 1.1.0")),
+    ).toEqual(c("1.0.0 <= v < 1.1.0"));
+    expect(
+      constraintIntersect(c("1.0.0 <= v < 1.3.0"), c("1.2.0 <= v < 1.4.0")),
+    ).toEqual(c("1.2.0 <= v < 1.3.0"));
+    expect(
+      constraintIntersect(c("1.2.3 <= v < 2.0.0"), c("1.1.1 <= v < 1.3.0")),
+    ).toEqual(c("1.2.3 <= v < 1.3.0"));
+  });
+});
diff --git a/test/definitionProviderTests/definitionProviderTestBase.ts b/test/definitionProviderTests/definitionProviderTestBase.ts
index da61321..1b71531 100644
--- a/test/definitionProviderTests/definitionProviderTestBase.ts
+++ b/test/definitionProviderTests/definitionProviderTestBase.ts
@@ -3,9 +3,8 @@ import { URI } from "vscode-uri";
 import { DefinitionProvider, DefinitionResult } from "../../src/providers";
 import { ITextDocumentPositionParams } from "../../src/providers/paramsExtensions";
 import { TreeUtils } from "../../src/util/treeUtils";
-import { baseUri } from "../utils/mockElmWorkspace";
 import { getInvokeAndTargetPositionFromSource } from "../utils/sourceParser";
-import { SourceTreeParser } from "../utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "../utils/sourceTreeParser";
 
 class MockDefinitionProvider extends DefinitionProvider {
   public handleDefinition(
@@ -31,7 +30,9 @@ export class DefinitionProviderTestBase {
       baseUri + determinedTestType.invokeFile,
     ).toString();
 
-    const program = this.treeParser.getWorkspace(determinedTestType.sources);
+    const program = await this.treeParser.getProgram(
+      determinedTestType.sources,
+    );
     const sourceFile = program.getForest().getByUri(invokeUri);
 
     if (!sourceFile) throw new Error("Getting tree failed");
diff --git a/test/diagnosticTests/elmDiagnostics.test.ts b/test/diagnosticTests/elmDiagnostics.test.ts
index 7b1c2a6..b6873c3 100644
--- a/test/diagnosticTests/elmDiagnostics.test.ts
+++ b/test/diagnosticTests/elmDiagnostics.test.ts
@@ -10,12 +10,11 @@ import {
   IDiagnosticMessage,
 } from "../../src/util/types/diagnostics";
 import { Utils } from "../../src/util/utils";
-import { baseUri } from "../utils/mockElmWorkspace";
 import {
   getSourceFiles,
   getTargetPositionFromSource,
 } from "../utils/sourceParser";
-import { SourceTreeParser } from "../utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "../utils/sourceTreeParser";
 
 const basicsSources = `
 --@ Basics.elm
@@ -69,7 +68,7 @@ describe("test elm diagnostics", () => {
 
     const testUri = URI.file(baseUri + "Test.elm").toString();
 
-    const program = treeParser.getWorkspace(result.sources);
+    const program = await treeParser.getProgram(result.sources);
     const treeContainer = program.getForest().getByUri(testUri);
 
     if (!treeContainer) throw new Error("Getting tree failed");
diff --git a/test/diagnosticTests/elmLsDiagnostics.test.ts b/test/diagnosticTests/elmLsDiagnostics.test.ts
index 498e2d6..955c7ae 100644
--- a/test/diagnosticTests/elmLsDiagnostics.test.ts
+++ b/test/diagnosticTests/elmLsDiagnostics.test.ts
@@ -8,9 +8,8 @@ import { IDiagnostic } from "../../src/providers/diagnostics/diagnosticsProvider
 import { ElmLsDiagnostics } from "../../src/providers/diagnostics/elmLsDiagnostics";
 import { diagnosticsEquals } from "../../src/providers/diagnostics/fileDiagnostics";
 import { Utils } from "../../src/util/utils";
-import { baseUri } from "../utils/mockElmWorkspace";
 import { getSourceFiles } from "../utils/sourceParser";
-import { SourceTreeParser } from "../utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "../utils/sourceTreeParser";
 
 describe("ElmLsDiagnostics", () => {
   let elmDiagnostics: ElmLsDiagnostics;
@@ -27,16 +26,16 @@ describe("ElmLsDiagnostics", () => {
     await treeParser.init();
     elmDiagnostics = new ElmLsDiagnostics();
 
-    const workspace = treeParser.getWorkspace(getSourceFiles(source));
+    const program = await treeParser.getProgram(getSourceFiles(source));
 
-    const treeContainer = workspace.getForest().getByUri(uri);
+    const sourceFile = program.getSourceFile(uri);
 
-    if (!treeContainer) {
+    if (!sourceFile) {
       fail();
     }
 
     const diagnostics = elmDiagnostics
-      .createDiagnostics(treeContainer, workspace)
+      .createDiagnostics(sourceFile, program)
       .filter((diagnostic) => diagnostic.data.code === code);
 
     const diagnosticsEqual = Utils.arrayEquals(
diff --git a/test/diagnostics.ts b/test/diagnostics.ts
index f5598d9..ada34ba 100644
--- a/test/diagnostics.ts
+++ b/test/diagnostics.ts
@@ -140,7 +140,6 @@ const compilerFailures = ["mdgriffith/elm-ui", "frandibar/elm-bootstrap"];
 
 const otherFailures = [
   "Chadtech/elm-vector", // Too big
-  "arowM/html-extra", // Advanced module resolution bug
   "MattCheely/tryframe-coordinator", // Still on version 0.18
   "danmarcab/material-icons", // Still on version 0.18
   "krisajenkins/elm-astar", // Still on version 0.18
diff --git a/test/referencesProviderTests/referencesProviderTestBase.ts b/test/referencesProviderTests/referencesProviderTestBase.ts
index c3ba26b..8732d2a 100644
--- a/test/referencesProviderTests/referencesProviderTestBase.ts
+++ b/test/referencesProviderTests/referencesProviderTestBase.ts
@@ -2,9 +2,8 @@ import { URI } from "vscode-uri";
 import { ReferenceResult, ReferencesProvider } from "../../src/providers";
 import { IReferenceParams } from "../../src/providers/paramsExtensions";
 import { TreeUtils } from "../../src/util/treeUtils";
-import { baseUri } from "../utils/mockElmWorkspace";
 import { getReferencesTestFromSource } from "../utils/sourceParser";
-import { SourceTreeParser } from "../utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "../utils/sourceTreeParser";
 
 class MockReferencesProvider extends ReferencesProvider {
   public handleReference(params: IReferenceParams): ReferenceResult {
@@ -31,7 +30,7 @@ export class ReferencesProviderTestBase {
 
     const testUri = URI.file(baseUri + referenceTest.invokeFile).toString();
 
-    const program = this.treeParser.getWorkspace(referenceTest.sources);
+    const program = await this.treeParser.getProgram(referenceTest.sources);
     const sourceFile = program.getForest().getByUri(testUri);
 
     if (!sourceFile) throw new Error("Getting tree failed");
diff --git a/test/solver.test.ts b/test/solver.test.ts
new file mode 100644
index 0000000..b5e6a86
--- /dev/null
+++ b/test/solver.test.ts
@@ -0,0 +1,214 @@
+import {
+  IConstraint,
+  IElmPackageCache,
+  IPackage,
+  IVersion,
+} from "../src/elmWorkspace";
+import { parseContraint, solveDependencies } from "../src/util/elmUtils";
+
+describe("module resolution solver", () => {
+  function v(
+    major: number,
+    minor: number,
+    patch: number,
+    preReleaseFields?: string[],
+  ): IVersion {
+    return {
+      major,
+      minor,
+      patch,
+      string: preReleaseFields
+        ? `${major}.${minor}.${patch}-${preReleaseFields.join(".")}`
+        : `${major}.${minor}.${patch}`,
+    };
+  }
+
+  const c = parseContraint;
+
+  const packageCache: IElmPackageCache = {
+    get: (packageName: string): IPackage[] => {
+      switch (packageName) {
+        case "B":
+          return [
+            {
+              version: v(1, 0, 0),
+              dependencies: new Map(),
+            },
+            {
+              version: v(1, 0, 1),
+              dependencies: new Map(),
+            },
+            {
+              version: v(1, 0, 2),
+              dependencies: new Map(),
+            },
+            {
+              version: v(1, 0, 3),
+              dependencies: new Map(),
+            },
+          ];
+
+        case "C":
+          return [
+            {
+              version: v(1, 0, 0),
+              dependencies: new Map([["B", c("1.0.1 <= v < 1.0.3")]]),
+            },
+          ];
+
+        case "D":
+          return [
+            {
+              version: v(1, 0, 0),
+              dependencies: new Map([
+                ["B", c("1.0.1 <= v < 1.0.2")],
+                ["C", c("1.0.0 <= v < 2.0.0")],
+              ]),
+            },
+          ];
+      }
+
+      return [];
+    },
+  };
+
+  function test(
+    deps: [string, IConstraint][],
+    expected: [string, IVersion][] | undefined,
+    cache = packageCache,
+  ) {
+    expect(solveDependencies(cache, new Map(deps))).toEqual(
+      expected ? new Map(expected) : undefined,
+    );
+  }
+
+  it("handles empty input", () => {
+    test([], []);
+  });
+
+  it("trivial resolve", () => {
+    test(
+      [["C", c("1.0.0 <= v < 2.0.0")]],
+      [
+        ["B", v(1, 0, 2)],
+        ["C", v(1, 0, 0)],
+      ],
+    );
+  });
+
+  it("picks highest available version", () => {
+    test([["B", c("1.0.0 <= v < 2.0.0")]], [["B", v(1, 0, 3)]]);
+  });
+
+  it("resolve mutual constraints", () => {
+    test(
+      [
+        ["B", c("1.0.0 <= v < 1.0.4")],
+        ["C", c("1.0.0 <= v < 2.0.0")],
+      ],
+      [
+        ["B", v(1, 0, 2)],
+        ["C", v(1, 0, 0)],
+      ],
+    );
+  });
+
+  it("resolve mutual constraints - multiple levels", () => {
+    test(
+      [
+        ["B", c("1.0.0 <= v < 1.0.4")],
+        ["C", c("1.0.0 <= v < 2.0.0")],
+        ["D", c("1.0.0 <= v < 2.0.0")],
+      ],
+      [
+        ["B", v(1, 0, 1)],
+        ["C", v(1, 0, 0)],
+        ["D", v(1, 0, 0)],
+      ],
+    );
+  });
+
+  it("resolve extended constraints can be merged", () => {
+    test(
+      [
+        ["A", c("1.0.0 <= v < 2.0.0")],
+        ["B", c("1.0.0 <= v < 2.1.0")],
+      ],
+      [
+        ["A", v(1, 0, 0)],
+        ["B", v(2, 0, 0)],
+      ],
+      {
+        get: (packageName: string): IPackage[] => {
+          switch (packageName) {
+            case "A":
+              return [
+                {
+                  version: v(1, 0, 0),
+                  dependencies: new Map([["B", c("1.0.0 <= v < 3.0.0")]]),
+                },
+              ];
+
+            case "B":
+              return [
+                { version: v(1, 0, 0), dependencies: new Map() },
+                { version: v(2, 0, 0), dependencies: new Map() },
+                { version: v(2, 1, 0), dependencies: new Map() },
+              ];
+          }
+
+          return [];
+        },
+      },
+    );
+  });
+
+  it("resolve extended constraints cannot be merged - conflict on B", () => {
+    test(
+      [
+        ["A", c("1.0.0 <= v < 2.0.0")],
+        ["B", c("1.0.0 <= v < 2.0.0")],
+      ],
+      undefined,
+      {
+        get: (packageName: string): IPackage[] => {
+          switch (packageName) {
+            case "A":
+              return [
+                {
+                  version: v(1, 0, 0),
+                  dependencies: new Map([["B", c("2.0.0 <= v < 3.0.0")]]),
+                },
+              ];
+
+            case "B":
+              return [
+                { version: v(1, 0, 0), dependencies: new Map() },
+                { version: v(2, 0, 0), dependencies: new Map() },
+              ];
+          }
+
+          return [];
+        },
+      },
+    );
+  });
+
+  it("no solution because version constraint is lower than lowest version in package cache", () => {
+    test([["C", c("0.0.0 <= v < 1.0.0")]], undefined);
+  });
+
+  it("no solution because version constraint is higher than max version in package cache", () => {
+    test([["C", c("9.0.0 <= v < 10.0.0")]], undefined);
+  });
+
+  it("no solution because mutual constraints conflict with each other", () => {
+    test(
+      [
+        ["B", c("1.0.3 <= v < 1.0.4")],
+        ["C", c("1.0.0 <= v < 2.0.0")],
+      ],
+      undefined,
+    );
+  });
+});
diff --git a/test/typeInference.test.ts b/test/typeInference.test.ts
index 789abae..f5a9ad4 100644
--- a/test/typeInference.test.ts
+++ b/test/typeInference.test.ts
@@ -1,8 +1,7 @@
 import { URI } from "vscode-uri";
 import { TreeUtils } from "../src/util/treeUtils";
-import { baseUri } from "./utils/mockElmWorkspace";
 import { getTargetPositionFromSource } from "./utils/sourceParser";
-import { SourceTreeParser } from "./utils/sourceTreeParser";
+import { baseUri, SourceTreeParser } from "./utils/sourceTreeParser";
 
 const basicsSources = `
 --@ Basics.elm
@@ -44,13 +43,13 @@ describe("test type inference", () => {
 
     const testUri = URI.file(baseUri + "Test.elm").toString();
 
-    const workspace = treeParser.getWorkspace(result.sources);
-    const treeContainer = workspace.getForest().getByUri(testUri);
+    const program = await treeParser.getProgram(result.sources);
+    const sourceFile = program.getSourceFile(testUri);
 
-    if (!treeContainer) throw new Error("Getting tree failed");
+    if (!sourceFile) throw new Error("Getting tree failed");
 
     const nodeAtPosition = TreeUtils.getNamedDescendantForPosition(
-      treeContainer.tree.rootNode,
+      sourceFile.tree.rootNode,
       result.position,
     );
 
@@ -63,10 +62,10 @@ describe("test type inference", () => {
       throw new Error("Could not get value declaration");
     }
 
-    const checker = workspace.getTypeChecker();
+    const checker = program.getTypeChecker();
     const nodeType = checker.findType(declaration);
 
-    expect(checker.typeToString(nodeType, treeContainer)).toEqual(expectedType);
+    expect(checker.typeToString(nodeType, sourceFile)).toEqual(expectedType);
   }
 
   test("simple function", async () => {
diff --git a/test/utils/mockElmWorkspace.ts b/test/utils/mockElmWorkspace.ts
deleted file mode 100644
index 7e90576..0000000
--- a/test/utils/mockElmWorkspace.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import path, * as Path from "path";
-import { URI } from "vscode-uri";
-import Parser, { Tree } from "web-tree-sitter";
-import { IElmWorkspace } from "../../src/elmWorkspace";
-import { Forest, IForest, ITreeContainer } from "../../src/forest";
-import { Imports } from "../../src/imports";
-import { container } from "tsyringe";
-import { TypeCache } from "../../src/util/types/typeCache";
-import {
-  createTypeChecker,
-  DefinitionResult,
-  TypeChecker,
-} from "../../src/util/types/typeChecker";
-import { TreeUtils } from "../../src/util/treeUtils";
-import {
-  IPossibleImportsCache,
-  PossibleImportsCache,
-} from "../../src/util/possibleImportsCache";
-import { ICancellationToken } from "../../src/cancellation";
-import { Diagnostic } from "../../src/util/types/diagnostics";
-
-export const baseUri = Path.join(__dirname, "../sources/src/");
-
-export class MockElmWorkspace implements IElmWorkspace {
-  private forest: IForest = new Forest(new Map());
-  private parser: Parser;
-  private typeCache = new TypeCache();
-  private possibleImportsCache = new PossibleImportsCache();
-  private operatorsCache = new Map<string, DefinitionResult>();
-  private diagnosticsCache = new Map<string, Diagnostic[]>();
-
-  constructor(sources: { [K: string]: string }) {
-    this.parser = container.resolve("Parser");
-
-    for (const key in sources) {
-      if (Object.prototype.hasOwnProperty.call(sources, key)) {
-        this.parseAndAddToForest(key, sources[key]);
-      }
-    }
-
-    this.forest.treeMap.forEach((treeContainer) => {
-      treeContainer.resolvedModules = this.resolveModules(treeContainer.tree);
-    });
-
-    this.forest.synchronize();
-  }
-
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
-  init(progressCallback: (percent: number) => void): void {
-    return;
-  }
-
-  public hasDocument(uri: URI): boolean {
-    return !!this.forest.getTree(uri.toString());
-  }
-
-  public hasPath(uri: URI): boolean {
-    return !!this.getPath(uri);
-  }
-
-  public getPath(uri: URI): string | undefined {
-    return baseUri;
-  }
-
-  public getSourceFile(uri: string): ITreeContainer | undefined {
-    return this.getForest().getByUri(uri);
-  }
-
-  getForest(): IForest {
-    return this.forest;
-  }
-
-  getRootPath(): URI {
-    return URI.file(Path.join(__dirname, "sources"));
-  }
-
-  getTypeCache(): TypeCache {
-    return this.typeCache;
-  }
-
-  getTypeChecker(): TypeChecker {
-    return createTypeChecker(this);
-  }
-
-  markAsDirty(): void {
-    return;
-  }
-
-  getPossibleImportsCache(): IPossibleImportsCache {
-    return this.possibleImportsCache;
-  }
-
-  getOperatorsCache(): Map<string, DefinitionResult> {
-    return this.operatorsCache;
-  }
-
-  public getSyntacticDiagnostics(sourceFile: ITreeContainer): Diagnostic[] {
-    this.getTypeChecker();
-    return sourceFile.parseDiagnostics;
-  }
-
-  public getSemanticDiagnostics(
-    sourceFile: ITreeContainer,
-    cancellationToken?: ICancellationToken,
-  ): Diagnostic[] {
-    const cached = this.diagnosticsCache.get(sourceFile.uri);
-
-    if (cached) {
-      return cached;
-    }
-
-    const diagnostics = this.getTypeChecker().getDiagnostics(
-      sourceFile,
-      cancellationToken,
-    );
-
-    this.diagnosticsCache.set(sourceFile.uri, diagnostics);
-    return diagnostics;
-  }
-
-  public async getSemanticDiagnosticsAsync(
-    sourceFile: ITreeContainer,
-    cancellationToken?: ICancellationToken,
-  ): Promise<Diagnostic[]> {
-    const cached = this.diagnosticsCache.get(sourceFile.uri);
-
-    if (cached) {
-      return Promise.resolve(cached);
-    }
-
-    const diagnostics = await this.getTypeChecker().getDiagnosticsAsync(
-      sourceFile,
-      cancellationToken,
-    );
-
-    this.diagnosticsCache.set(sourceFile.uri, diagnostics);
-    return diagnostics;
-  }
-
-  public getSuggestionDiagnostics(
-    sourceFile: ITreeContainer,
-    cancellationToken?: ICancellationToken,
-  ): Diagnostic[] {
-    return this.getTypeChecker().getSuggestionDiagnostics(
-      sourceFile,
-      cancellationToken,
-    );
-  }
-
-  private parseAndAddToForest(fileName: string, source: string): void {
-    const tree: Tree = this.parser.parse(source);
-    this.forest.setTree(
-      URI.file(baseUri + fileName).toString(),
-      true,
-      true,
-      tree,
-      true,
-    );
-  }
-
-  private resolveModules(tree: Tree): Map<string, string> {
-    const importClauses = [
-      ...Imports.getVirtualImports(),
-      ...(TreeUtils.findAllImportClauseNodes(tree) ?? []),
-    ];
-
-    const resolvedModules = new Map<string, string>();
-
-    // It should be faster to look directly at elmFolders instead of traversing the forest
-    importClauses.forEach((importClause) => {
-      const moduleName = TreeUtils.findFirstNamedChildOfType(
-        "upper_case_qid",
-        importClause,
-      )?.text;
-
-      if (moduleName) {
-        const modulePath = moduleName.split(".").join("/") + ".elm";
-        const uri = URI.file(path.join(baseUri, modulePath)).toString();
-        const found = this.forest.getByUri(uri);
-
-        if (!found) {
-          // TODO: Diagnostics for unresolved imports
-        } else {
-          resolvedModules.set(moduleName, uri);
-        }
-      }
-    });
-
-    return resolvedModules;
-  }
-}
diff --git a/test/utils/sourceTreeParser.ts b/test/utils/sourceTreeParser.ts
index 222dde9..7bc169f 100644
--- a/test/utils/sourceTreeParser.ts
+++ b/test/utils/sourceTreeParser.ts
@@ -1,8 +1,11 @@
-import * as Path from "path";
-import Parser from "web-tree-sitter";
-import { IElmWorkspace } from "../../src/elmWorkspace";
-import { MockElmWorkspace } from "./mockElmWorkspace";
+import { parse } from "path";
 import { container } from "tsyringe";
+import { URI, uriToFsPath } from "vscode-uri";
+import Parser from "web-tree-sitter";
+import { ElmWorkspace, IElmWorkspace } from "../../src/elmWorkspace";
+import * as path from "../../src/util/path";
+
+export const baseUri = path.join(__dirname, "../sources/src/");
 
 export class SourceTreeParser {
   private parser?: Parser;
@@ -13,15 +16,61 @@ export class SourceTreeParser {
     }
 
     await Parser.init();
-    const absolute = Path.join(__dirname, "../../tree-sitter-elm.wasm");
-    const pathToWasm = Path.relative(process.cwd(), absolute);
+    const absolute = path.join(__dirname, "../../tree-sitter-elm.wasm");
+    const pathToWasm = path.relative(process.cwd(), absolute);
 
     const language = await Parser.Language.load(pathToWasm);
     container.registerSingleton("Parser", Parser);
     container.resolve<Parser>("Parser").setLanguage(language);
   }
 
-  public getWorkspace(sources: { [K: string]: string }): IElmWorkspace {
-    return new MockElmWorkspace(sources);
+  public async getProgram(sources: {
+    [K: string]: string;
+  }): Promise<IElmWorkspace> {
+    const readFile = (uri: string): string => {
+      if (uri.endsWith("elm.json")) {
+        return `
+        {
+          "type": "application",
+          "source-directories": [
+              "."
+          ],
+          "elm-version": "0.19.1",
+          "dependencies": {
+              "direct": {},
+              "indirect": {}
+          },
+          "test-dependencies": {
+              "direct": {},
+              "indirect": {}
+          }
+        }
+        `;
+      }
+
+      return sources[path.relative(baseUri, uri)];
+    };
+
+    const program = new ElmWorkspace(URI.file(baseUri), {
+      readFile: (uri: string): Promise<string> =>
+        Promise.resolve(readFile(uri)),
+      readFileSync: readFile,
+      readDirectory: (uri: string): Promise<string[]> => {
+        console.log(uri);
+        console.log(path.normalizeUri(baseUri.substr(0, baseUri.length - 1)));
+        return Promise.resolve(
+          path.normalizeUri(uri) ===
+            path.normalizeUri(baseUri.substr(0, baseUri.length - 1)) // Remove trailing / from baseUri
+            ? Object.keys(sources).map((sourceUri) => path.join(uri, sourceUri))
+            : [],
+        );
+      },
+    });
+
+    await program.init(() => {
+      //
+    });
+
+    return program;
   }
 }