i_slint_compiler/passes/
check_public_api.rs1use std::rc::Rc;
7
8use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel};
9use crate::langtype::ElementType;
10use crate::object_tree::{Component, Document, ExportedName, PropertyVisibility};
11use crate::{CompilerConfiguration, ComponentSelection};
12use itertools::Either;
13
14pub fn check_public_api(
15 doc: &mut Document,
16 config: &CompilerConfiguration,
17 diag: &mut BuildDiagnostics,
18) {
19 let last = doc.last_exported_component();
20
21 if last.is_none() && !matches!(&config.components_to_generate, ComponentSelection::Named(_)) {
22 let last_imported = doc
23 .node
24 .as_ref()
25 .and_then(|n| {
26 let import_node = n.ImportSpecifier().last()?;
27 let import = crate::typeloader::ImportedName::extract_imported_names(&import_node.ImportIdentifierList()?).last()?;
28 let ElementType::Component(c) = doc.local_registry.lookup_element(&import.internal_name).ok()? else { return None };
29 diag.push_warning(format!("No component is exported. The last imported component '{}' will be used. This is deprecated", import.internal_name), &import_node);
30 let exported_name = ExportedName{ name: import.internal_name, name_ident: import_node.into() };
31 Some((exported_name, Either::Left(c)))
32 });
33 doc.exports.add_reexports(last_imported, diag);
34 }
35
36 match &config.components_to_generate {
37 ComponentSelection::ExportedWindows => doc.exports.retain(|export| {
38 if let Either::Left(c) = &export.1 {
40 if !c.is_global() && !super::ensure_window::inherits_window(c) {
41 let is_last = last.as_ref().is_some_and(|last| !Rc::ptr_eq(last, c));
42 if is_last {
43 diag.push_warning(format!("Exported component '{}' doesn't inherit Window. No code will be generated for it", export.0.name), &export.0.name_ident);
44 return false;
45 } else {
46 diag.push_warning(format!("Exported component '{}' doesn't inherit Window. This is deprecated", export.0.name), &export.0.name_ident);
47 }
48 }
49 }
50 true
51 }),
52 ComponentSelection::LastExported => doc.exports.retain(|export| {
54 if let Either::Left(c) = &export.1 {
55 c.is_global() || last.as_ref().map_or(true, |last| Rc::ptr_eq(last, c))
56 } else {
57 true
58 }
59 }),
60 ComponentSelection::Named(name) => {
62 doc.exports.retain(|export| {
63 if let Either::Left(c) = &export.1 {
64 c.is_global() || &c.id == name
65 } else {
66 true
67 }
68 });
69 if doc.last_exported_component().is_none() {
70 if let Ok(ElementType::Component(c)) = doc.local_registry.lookup_element(name) {
72 if let Some(name_ident) = c.node.as_ref().map(|n| n.DeclaredIdentifier().into()) {
73 doc.exports.add_reexports(
74 [(ExportedName{ name: name.into(), name_ident }, Either::Left(c))],
75 diag,
76 );
77 }
78 }
79 }
80 },
81 }
82
83 for c in doc.exported_roots() {
84 check_public_api_component(&c, diag);
85 }
86 for (export_name, e) in &*doc.exports {
87 if let Some(c) = e.as_ref().left() {
88 if c.is_global() {
89 c.exported_global_names.borrow_mut().push(export_name.clone());
91 check_public_api_component(c, diag)
92 }
93 }
94 }
95}
96
97fn check_public_api_component(root_component: &Rc<Component>, diag: &mut BuildDiagnostics) {
98 let mut root_elem = root_component.root_element.borrow_mut();
99 let root_elem = &mut *root_elem;
100 let mut pa = root_elem.property_analysis.borrow_mut();
101 root_elem.property_declarations.iter_mut().for_each(|(n, d)| {
102 if d.property_type.ok_for_public_api() {
103 if d.visibility == PropertyVisibility::Private {
104 root_component.private_properties.borrow_mut().push((n.clone(), d.property_type.clone()));
105 } else {
106 d.expose_in_public_api = true;
107 if d.visibility != PropertyVisibility::Output {
108 pa.entry(n.clone()).or_default().is_set = true;
109 }
110 }
111 } else {
112 diag.push_diagnostic(
113 format!("Properties of type {} are not supported yet for public API. The property will not be exposed", d.property_type),
114 &d.type_node(),
115 DiagnosticLevel::Warning
116 );
117 }
118 });
119}