elm / compiler

Compiler for Elm, a functional language for reliable webapps.
https://elm-lang.org/
BSD 3-Clause "New" or "Revised" License
7.51k stars 656 forks source link

Prevent publishing packages that don't expose types used in public signatures #2219

Open jfmengels opened 3 years ago

jfmengels commented 3 years ago

Quick Summary: Some packages are published with values and functions that reference a type, but that type is not exposed. This causes problems because it makes it impossible to annotate some values. I believe it would be beneficial if the compiler prevented the publication of packages with these issues.

SSCCE

1.

module Happiness exposing (happy, toString)

-- Type `Happiness` is private because it's not been exposed

type Happiness
    = Happy

-- Private type `Happiness` used by exposed function `toString`
toString : Happiness -> String
toString happiness =
    "Happy"

-- Private type `Happiness` used by exposed value `happy`
happy : Happiness
happy =
    Happy
  1. Elements referenced indirectly by types should also be exposed
module Happiness exposing (Happiness)

type alias Happiness
  { reason : Reason
  , howMuchHappy : Int
  }

-- Reason should be exposed because people could want to annotate `happiness.reason`
type Reason
  = SawCuteBunny
  | UsingElmAtWork 
  1. References to types that are part of modules not exposed by the package should not be allowed while trying to publish
module Exposed exposing (value)

import NotExposed

-- Users can't access NotExposed and therefore can't annotate this value.
value : NotExposed.Value
value =
    NotExposed.value

An exception to all of the above are type aliases, which should be able to reference non-exposed types. That is a common use that doesn't cause any problems that I'm aware of.

Additional benefit

Super tiny benefit: If all types are exposed, it becomes very easy to know whether a package returns Tasks or Cmds simply by looking at the docs.json, which could be valuable for people who wish to audit their dependencies. Currently, it is possible to hide these behind unexposed type aliases. (It's still possible to hide things when using type aliases that come from dependencies).

Additional Details

There is an elm-review rule to prevent this kind of problem: https://package.elm-lang.org/packages/jfmengels/elm-review-common/latest/NoMissingTypeExpose

Unfortunately, not everybody uses this rule before publishing and therefore some packages end up being published with some types not being exposed while they should have.

We plan on running the elm-review-bot to detect this problem and open pull requests in the packages that have this problem.

github-actions[bot] commented 3 years ago

Thanks for reporting this! To set expectations:

Finally, please be patient with the core team. They are trying their best with limited resources.