stil4m / elm-analyse

A tool that allows you to analyse your Elm code, identify deficiencies and apply best practices.
https://stil4m.github.io/elm-analyse/
MIT License
415 stars 56 forks source link

Elm-analyse fails with `Cannot find module` error. #218

Open akhilman opened 5 years ago

akhilman commented 5 years ago

I have a project with the code from Elm’s tutorial. Code itself compiles and works great, elm-format also works well. But elm-language-server and elm-analyse fails with same error:

`--> elm-analyse src/Main.elm
Fetching package information from package.elm-lang.org.
Fetched dependencies
INFO: Started...
INFO: No configuration provided. Using default configuration.
INFO: Load dependency elm/browser 1.0.1
internal/modules/cjs/loader.js:638
    throw err;
    ^

Error: Cannot find module '/home/ildar/Projects/learning-elm/test/elm-stuff/packages/elm/browser/1.0.1/elm.json'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:636:15)
    at Function.Module._load (internal/modules/cjs/loader.js:562:25)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.getDependencyFiles (/home/ildar/.config/yarn/global/node_modules/elm-analyse/dist/app/util/file-gatherer.js:63:26)
    at Array.<anonymous> (/home/ildar/.config/yarn/global/node_modules/elm-analyse/dist/app/ports/dependency-files.js:13:35)
    at Function.f (/home/ildar/.config/yarn/global/node_modules/elm-analyse/dist/app/backend-elm.js:2170:19)
    at A3 (/home/ildar/.config/yarn/global/node_modules/elm-analyse/dist/app/backend-elm.js:59:28)
    at Object.b (/home/ildar/.config/yarn/global/node_modules/elm-analyse/dist/app/backend-elm.js:1974:7)
    at _Scheduler_step (/home/ildar/.config/yarn/global/node_modules/elm-analyse/dist/app/backend-elm.js:1818:20)

As I figured out all modules located in local cache inside ~/elm directory. How I can make elm-analyse load modules form local cache?

Environment: installed globally with yarn

antew commented 5 years ago

It looks like it is failing in the test directory, does the elm.json in there have elm/browser listed as a dependency in it?

akhilman commented 5 years ago

Yes elm/browser is listed in elm.json Here is a content from my elm.json:

{
    "type": "application",
    "source-directories": [
        "src"
    ],
    "elm-version": "0.19.0",
    "dependencies": {
        "direct": {
            "elm/browser": "1.0.1",
            "elm/core": "1.0.2",
            "elm/html": "1.0.0"
        },
        "indirect": {
            "elm/json": "1.1.3",
            "elm/time": "1.0.0",
            "elm/url": "1.0.0",
            "elm/virtual-dom": "1.0.2"
        }
    },
    "test-dependencies": {
        "direct": {},
        "indirect": {}
    }
}

And here is list of all files I have in my project:

.:
elm.json
elm-stuff
index.html
src

./elm-stuff:
0.19.0

./elm-stuff/0.19.0:
Main.elmi
Main.elmo
summary.dat

./src:
Main.elm
ValeTheVioletMote commented 4 years ago

I'm having the same problem. Fresh install, basic project made from the tutorial.

I'm on Windows 10.

Where/why/how is it expecting to get these files?

elm-stuff/packages/elm/core/1.0.4/elm.json elm-stuff/packages/elm/json/1.1.3/elm.json elm-stuff/packages/elm/html/1.0.0/elm.json elm-stuff/packages/elm/http/2.0.0/elm.json

Is it trying to download? Does it need proxy setup?

ValeTheVioletMote commented 4 years ago

It looks like...

In elm-analyse\dist\app\util\file-gatherer.js

function getDependencyFiles(directory, dep) {
    var depPath = directory + "/elm-stuff/packages/" + dep.name + "/" + dep.version;
    var depPackageFile = require(depPath + '/elm.json');
    var unfilteredTargetFiles = targetFilesForPathAndPackage(directory, depPath, depPackageFile);
    var exposedModules = depPackageFile['exposed-modules'].map(function (x) {
        return _path.normalize('/' + x.replace(new RegExp('\\.', 'g'), '/') + '.elm');
    });
    return unfilteredTargetFiles.filter(function (x) {
        return exposedModules.filter(function (e) { return lodash_1.default.endsWith(x, e); })[0];
    });
}

Instead of grabbing from %appdata%/elm/0.19.1/packages it's assuming that the elm-stuff folder will have its own copy for some reason?

I'll try overriding this.

ValeTheVioletMote commented 4 years ago

So now with a shimmy:

function getDependencyFiles(directory, dep) {
    var elm_version = "0.19.1"; // <-- this is really bad
    var depPath = process.env.APPDATA + "/elm/"+elm_version+"/packages/" + dep.name + "/" + dep.version; // directory var useless
    var depPackageFile = require(depPath + '/elm.json');
    var unfilteredTargetFiles = targetFilesForPathAndPackage(directory, depPath, depPackageFile);
    var exposedModules = depPackageFile['exposed-modules'].map(function (x) {
        return _path.normalize('/' + x.replace(new RegExp('\\.', 'g'), '/') + '.elm');
    });
    return unfilteredTargetFiles.filter(function (x) {
        return exposedModules.filter(function (e) { return lodash_1.default.endsWith(x, e); })[0];
    });
}

I get: TypeError: depPackageFile.exposed-modules.map is not a function

Which makes sense, because you can't use .map on an object, which 'exposed-modules' most definitely is:

"exposed-modules": {
        "Primitives": [
            "Basics",
            "String",
            "Char",
            "Bitwise",
            "Tuple"
        ],
        "Collections": [
            "List",
            "Dict",
            "Set",
            "Array"
        ],
        "Error Handling": [
            "Maybe",
            "Result"
        ],
        "Debug": [
            "Debug"
        ],
        "Effects": [
            "Platform.Cmd",
            "Platform.Sub",
            "Platform",
            "Process",
            "Task"
        ]
    }

Is elm-analyse far too outdated for elm 0.19? It's expecting an array where an object lives now.

antew commented 4 years ago

Anyone have a repo that reproduces this issue?

Elm Analyse does work with 0.19, we use 0.19.1 in the project at work and it is working in there.

ValeTheVioletMote commented 4 years ago

On my home machine now. Works just fine there. Of note, getDependencyFiles doesn't even appear to ever be called on this setup.

So a hunt for what logic calls gDF might help us track it down.

ValeTheVioletMote commented 4 years ago

More accurately, seems to be something to do with loadDependencyFiles. I'm not proficient in Elm whatsoever but I'll try to see how it's called.

akhilman commented 4 years ago

https://github.com/akhilman/elm-counter - repository that causes error for me.

ValeTheVioletMote commented 4 years ago

I don't even need a Main.elm @akhilman Are you behind a proxy?

Looking at this... (src/Analyser/Files/DependencyLoader.elm) :

        OnOnlineDocs result ->
            case result of
                Nothing ->
                    ( model, Cmd.none )

                Just (Err _) ->
                    ( { model | state = RawDiskLoading }
                    , DependencyHandler.loadDependencyFiles model.dependency
                    )

                Just (Ok decodedDependency) ->
                    ( { model | state = Done decodedDependency }
                    , Cmd.batch
                        [ DependencyHandler.storeToDisk decodedDependency
                        , Logger.info ("Loaded " ++ model.dependency.name ++ " from package.elm-lang.org")
                        ]
                    )

Here's my guess... When OnOnlineDocs has a result of Err _, it fires off loadDependencyFiles -> getDependencyFiles -> our crash.

Looks like the idea is, "If we can't get the docs online, grab them offline from the ones we already got." Seems to miss the case where you can't grab docs online BEFORE you could ever get them in the first place, resulting in our exception.

What causes OnOnlineDocs to have a failure? I'm going to guess it might be trying to download something, as I first theorized, and for my machine behind an incredibly finnicky proxy at least, that may be causing it to fail.

I'll keep looking. Slowly. I am trying to learn Elm and got snagged here while doing the tutorial. Guess this is a crash course.. :)

akhilman commented 4 years ago

I don't even need a Main.elm @akhilman Are you behind a proxy?

No. I'm not. But here in Russia we have a lot of IP banned and elm may hit some of them.

ValeTheVioletMote commented 4 years ago

DependencyLoader init -> DependencyHandler.readFromDisk -> Failed -> DependencyHandler.loadOnlineDocumentation --> ?

but I can't get much further here. I don't understand what occurs in what order:

-- This.. (loadOnlineDocumentation)?
Failed ->
                    ( { model | state = LoadingOnlineDocs }
                    , DependencyHandler.loadOnlineDocumentation model.dependency
                    )

-- Or this (onOnlineDocumentation)?
LoadingOnlineDocs ->
            DependencyHandler.onOnlineDocumentation model.dependency
                |> Sub.map OnOnlineDocs

I presume once the state is changed, the latter piece is fired. But why does it seem to be doing the same work as the former? Or is the latter only called on init..??

The latter is what causes our error, presumably, so why is it favored over the former? Or what's the purpose of the former? Very confusing.

Or is the former what loads the docs, which then triggers onOnlineDocumentation, which then triggers Sub.map OnlineDocs...? I can't tell. Don't know the lang well enough.

@akhilman our best lead is that it has something to do with a network problem, then.

@antew could you help me understand this code when you have some time?

I'll try to see if I can replicate this in a docker container after routing online docs to localhost or something.

ValeTheVioletMote commented 4 years ago

CONFIRMED.

docker run -it debian /bin/bash

apt update -y && apt install curl npm -y
npm install -g npm
cd ~/ && curl -L -o elm.gz https://github.com/elm/compiler/releases/download/0.19.1/binary-for-linux-64-bit.gz
gzip -d elm.gz
chmod +x elm
mv elm /usr/local/bin/
mkdir elmproj && cd elmproj/
elm init
npm install -g elm-analyse
echo "172.0.0.1 package.elm-lang.org elm-lang.org" >> /etc/hosts
elm-analyse

Replicates the exception on a 'normal' system. See:

Fetching package information from package.elm-lang.org.
Fetched dependencies
INFO: Started...
INFO: No configuration provided. Using default configuration.
INFO: Load dependency elm/html 1.0.0
INFO: Load dependency elm/core 1.0.4
INFO: Load dependency elm/browser 1.0.2
internal/modules/cjs/loader.js:583
    throw err;
    ^

Error: Cannot find module '/root/elmproj/elm-stuff/packages/elm/core/1.0.4/elm.json'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.getDependencyFiles (/usr/local/lib/node_modules/elm-analyse/dist/app/util/file-gatherer.js:63:26)
    at Array.<anonymous> (/usr/local/lib/node_modules/elm-analyse/dist/app/ports/dependency-files.js:13:35)
    at Function.f (/usr/local/lib/node_modules/elm-analyse/dist/app/backend-elm.js:2174:19)
    at A3 (/usr/local/lib/node_modules/elm-analyse/dist/app/backend-elm.js:59:28)
    at Object.b (/usr/local/lib/node_modules/elm-analyse/dist/app/backend-elm.js:1978:7)
    at _Scheduler_step (/usr/local/lib/node_modules/elm-analyse/dist/app/backend-elm.js:1822:20)

I cause an error with a timeout here. I think on my work computer, it's an SSL or proxy error. You match Err _ so it takes 'em all.

Also worth noting is that you get no info about the package info failing to fetch.

akhilman commented 4 years ago

Also found aliases in my shell settings that was added when package.elm-lang.org was blacklisted: elm => env http_proxy=elm.dmy.fr:9999 elm elm-analyse => env http_proxy=elm.dmy.fr:9999 elm-analyse

Today package.elm-lang.org can be accessed without proxy. Without aliases all works fine.

ValeTheVioletMote commented 4 years ago

I can get it to perform correctly at work if I set HTTP_PROXY, HTTPS_PROXY, and NODE_TLS_REJECT_UNAUTHORIZED=0.

Now getting VSCode to do that for the tooling is a different matter altogether.....

Looks like my workaround for now will be to run elm-analyse manually, restart VSCode, and then do that again whenever I have new packages.

ValeTheVioletMote commented 4 years ago

At the very least, I would suggest that

        OnOnlineDocs result ->
            case result of
                Nothing ->
                    ( model, Cmd.none )

                Just (Err _) ->
                    ( { model | state = RawDiskLoading }
                    , DependencyHandler.loadDependencyFiles model.dependency
                    )

Be changed such that the network error underlying it all is logged to the console.