···113113 .get(PRELUDE_MODULE_NAME)
114114 .expect("Unable to find prelude in importable modules");
115115116116- let mut names = Names::new();
117117- for name in prelude.values.keys() {
118118- names.named_constructor_in_scope(
119119- PRELUDE_MODULE_NAME.into(),
120120- name.clone(),
121121- name.clone(),
122122- );
123123- }
124124-125125- for name in prelude.types.keys() {
126126- names.named_type_in_scope(PRELUDE_MODULE_NAME.into(), name.clone(), name.clone());
127127- }
116116+ let names = Self::build_names(¤t_package, prelude, importable_modules);
128117129118 Self {
130130- current_package: current_package.clone(),
119119+ current_package,
131120 gleam_version,
132121 previous_id: ids.next(),
133122 ids,
···152141 references: ReferenceTracker::new(),
153142 dev_dependencies,
154143 }
144144+ }
145145+146146+ fn build_names(
147147+ current_package: &str,
148148+ prelude: &ModuleInterface,
149149+ importable_modules: &im::HashMap<EcoString, ModuleInterface>,
150150+ ) -> Names {
151151+ let mut names = Names::new();
152152+153153+ // Insert prelude types and values into scope
154154+ for name in prelude.values.keys() {
155155+ names.named_constructor_in_scope(
156156+ PRELUDE_MODULE_NAME.into(),
157157+ name.clone(),
158158+ name.clone(),
159159+ );
160160+ }
161161+ for name in prelude.types.keys() {
162162+ names.named_type_in_scope(PRELUDE_MODULE_NAME.into(), name.clone(), name.clone());
163163+ }
164164+165165+ // Find potential type aliases which reexport internal types
166166+ for module in importable_modules.values() {
167167+ // Internal types are accessibly within the package they are defined,
168168+ // so it doesn't make sense to look for reexports within the same
169169+ // package.
170170+ if module.package == current_package {
171171+ continue;
172172+ }
173173+ // Internal modules are not part of the public API so they are also
174174+ // not considered.
175175+ if module.is_internal {
176176+ continue;
177177+ }
178178+ for (alias_name, alias) in module.type_aliases.iter() {
179179+ // An alias can only be a public reexport if it is public.
180180+ if alias.publicity.is_public() {
181181+ names.maybe_register_reexport_alias(&module.package, alias_name, alias);
182182+ }
183183+ }
184184+ }
185185+186186+ names
155187 }
156188}
157189
···5566use crate::{
77 ast::SrcSpan,
88- type_::{Type, TypeVar},
88+ type_::{Type, TypeAliasConstructor, TypeVar},
99};
10101111/// This class keeps track of what names are used for modules in the current
···117117 /// - value: `"Woo"`
118118 ///
119119 local_value_constructors: BiMap<(EcoString, EcoString), EcoString>,
120120+121121+ /// A map containing information about public alias of internal types in
122122+ /// other packages. This is a common pattern in Gleam, in order to reexport
123123+ /// an internal type, without exposing its implementation details. Because
124124+ /// of this, we want to be able to properly handle this case, and use the
125125+ /// public alias rather than the internal underlying type. Since Gleam type
126126+ /// aliases are not part of the type system, we have to track them manually
127127+ /// here.
128128+ ///
129129+ /// This is a mapping of internal types to their public aliases that we want
130130+ /// to favour over the internal types.
131131+ ///
132132+ /// For example, if we had the following code:
133133+ ///
134134+ /// ```gleam
135135+ /// // lustre/element.gleam
136136+ /// import lustre/internal
137137+ ///
138138+ /// pub type Element(a) = internal.Element(a)
139139+ /// ```
140140+ ///
141141+ /// This map would contain a key of `("lustre/internal", "Element")` with a
142142+ /// value of `("lustre/element", "Element")`. This can then be used to look
143143+ /// up the alias we want to print based on the type we are printing.
144144+ ///
145145+ reexport_aliases: HashMap<(EcoString, EcoString), (EcoString, EcoString)>,
120146}
121147122148/// The `PartialEq` implementation for `Type` doesn't account for `TypeVar::Link`,
···139165 imported_modules: Default::default(),
140166 type_variables: Default::default(),
141167 local_value_constructors: Default::default(),
168168+ reexport_aliases: Default::default(),
142169 }
143170 }
144171···195222 .map(|(_, location)| location)
196223 }
197224225225+ pub fn maybe_register_reexport_alias(
226226+ &mut self,
227227+ package: &EcoString,
228228+ alias_name: &EcoString,
229229+ alias: &TypeAliasConstructor,
230230+ ) {
231231+ match alias.type_.as_ref() {
232232+ Type::Named {
233233+ publicity,
234234+ package: type_package,
235235+ module,
236236+ name,
237237+ arguments,
238238+ ..
239239+ } => {
240240+ // We only count this alias as a reexport if it is:
241241+ // - aliasing a type in the same package
242242+ // - the type is internal
243243+ // - the alias exposes the same type parameters as the internal type
244244+ if type_package == package
245245+ && publicity.is_internal()
246246+ && compare_arguments(arguments, &alias.parameters)
247247+ {
248248+ _ = self.reexport_aliases.insert(
249249+ (module.clone(), name.clone()),
250250+ (alias.module.clone(), alias_name.clone()),
251251+ );
252252+ }
253253+ }
254254+ _ => {}
255255+ }
256256+ }
257257+198258 /// Get the name and optional module qualifier for a named type.
199259 pub fn named_type<'a>(
200260 &'a self,
···207267 return NameContextInformation::Qualified(module, name.as_str());
208268 };
209269210210- return NameContextInformation::Unimported(name.as_str());
270270+ return NameContextInformation::Unimported(module, name);
211271 }
212272213273 let key = (module.clone(), name.clone());
···218278 return NameContextInformation::Unqualified(name.as_str());
219279 }
220280281281+ if let Some((module, alias)) = self.reexport_aliases.get(&key) {
282282+ if let Some((module, _)) = self.imported_modules.get(module) {
283283+ return NameContextInformation::Qualified(module, alias);
284284+ } else {
285285+ return NameContextInformation::Unimported(module, alias);
286286+ }
287287+ }
288288+221289 // This type is from a module that has been imported
222290 if let Some((module, _)) = self.imported_modules.get(module) {
223291 return NameContextInformation::Qualified(module, name.as_str());
224292 };
225293226226- NameContextInformation::Unimported(name.as_str())
294294+ NameContextInformation::Unimported(module, name)
227295 }
228296229297 /// Record a named value in this module.
···257325 return NameContextInformation::Qualified(module, name.as_str());
258326 };
259327260260- NameContextInformation::Unimported(name.as_str())
328328+ NameContextInformation::Unimported(module, name)
261329 }
262330263331 pub fn is_imported(&self, module: &str) -> bool {
···272340#[derive(Debug)]
273341pub enum NameContextInformation<'a> {
274342 /// This type is from a module that has not been imported in this module.
275275- Unimported(&'a str),
343343+ Unimported(&'a str, &'a str),
276344 /// This type has been imported in an unqualifid fashion in this module.
277345 Unqualified(&'a str),
278346 /// This type is from a module that has been imported.
···404472 ..
405473 } => {
406474 let (module, name) = match self.names.named_type(module, name, print_mode) {
407407- NameContextInformation::Qualified(m, n) => (Some(m), n),
408408- NameContextInformation::Unqualified(n) => (None, n),
475475+ NameContextInformation::Qualified(module, name) => (Some(module), name),
476476+ NameContextInformation::Unqualified(name) => (None, name),
409477 // TODO: indicate that the module is not import and as such
410478 // needs to be, as well as how.
411411- NameContextInformation::Unimported(n) => {
412412- (Some(module.split('/').next_back().unwrap_or(module)), n)
479479+ NameContextInformation::Unimported(module, name) => {
480480+ (module.split('/').next_back(), name)
413481 }
414482 };
415483···452520 let (module, name) = match self.names.named_constructor(module, name) {
453521 NameContextInformation::Qualified(module, name) => (Some(module), name),
454522 NameContextInformation::Unqualified(name) => (None, name),
455455- NameContextInformation::Unimported(name) => {
456456- (Some(module.split('/').next_back().unwrap_or(module)), name)
523523+ NameContextInformation::Unimported(module, name) => {
524524+ (module.split('/').next_back(), name)
457525 }
458526 };
459527