@@ -59,15 +59,28 @@ def install(
5959 ]
6060
6161
62+ NAME_SOURCE = "NAME"
63+ """A named souce - usually a Javascript package name"""
64+
65+ URL_SOURCE = "URL"
66+ """A source loaded from a URL, usually from a CDN"""
67+
68+ SOURCE_TYPES = {NAME_SOURCE , URL_SOURCE }
69+ """The possible source types for a :class:`Module`"""
70+
71+
6272class Module :
6373 """A Javascript module
6474
6575 Parameters:
66- url_or_name :
76+ source :
6777 The URL to an ECMAScript module which exports React components
6878 (*with* a ``.js`` file extension) or name of a module installed in the
6979 built-in client application (*without* a ``.js`` file extension).
70- source_file:
80+ source_type:
81+ The type of the given ``source``. See :const:`SOURCE_TYPES` for the set of
82+ possible values.
83+ file:
7184 Only applicable if running on a client app which supports this feature.
7285 Dynamically install the code in the give file as a single-file module. The
7386 built-in client will inject this module adjacent to other installed modules
@@ -87,50 +100,48 @@ class Module:
87100 The URL this module will be imported from.
88101 """
89102
90- __slots__ = (
91- "url" ,
92- "fallback" ,
93- "exports" ,
94- "exports_mount" ,
95- "check_exports" ,
96- "_export_names" ,
97- )
103+ __slots__ = "source" , "source_type" , "fallback" , "exports" , "exports_mount"
98104
99105 def __init__ (
100106 self ,
101- url_or_name : str ,
107+ source : str ,
108+ source_type : Optional [str ] = None ,
102109 source_file : Optional [Union [str , Path ]] = None ,
103110 fallback : Optional [str ] = None ,
104111 exports_mount : bool = False ,
105- check_exports : bool = True ,
112+ check_exports : Optional [ bool ] = None ,
106113 ) -> None :
114+ self .source = source
107115 self .fallback = fallback
108116 self .exports_mount = exports_mount
109- self .check_exports = check_exports
117+ self .exports : Optional [ Set [ str ]] = None
110118
111- self .exports : Set [str ] = set ()
112- if source_file is not None :
113- self .url = (
114- manage .web_module_url (url_or_name )
115- if manage .web_module_exists (url_or_name )
116- else manage .add_web_module (url_or_name , source_file )
117- )
118- if check_exports :
119- self .exports = manage .web_module_exports (url_or_name )
120- elif _is_url (url_or_name ):
121- self .url = url_or_name
122- self .check_exports = check_exports = False
123- elif manage .web_module_exists (url_or_name ):
124- self .url = manage .web_module_url (url_or_name )
125- if check_exports :
126- self .exports = manage .web_module_exports (url_or_name )
119+ if source_type is None :
120+ self .source_type = URL_SOURCE if _is_url (source ) else NAME_SOURCE
121+ elif source_type in SOURCE_TYPES :
122+ self .source_type = source_type
127123 else :
128- raise ValueError (f"{ url_or_name !r} is not installed or is not a URL" )
124+ raise ValueError (f"Invalid source type { source_type !r} " )
125+
126+ if self .source_type == URL_SOURCE :
127+ if check_exports is True :
128+ raise ValueError ("Cannot check exports for source type {source_type!r}" )
129+ elif source_file is not None :
130+ raise ValueError (f"File given, but source type is { source_type !r} " )
131+ else :
132+ return None
133+ elif check_exports is None :
134+ check_exports = True
129135
130- if check_exports and exports_mount and "mount" not in self .exports :
131- raise ValueError (
132- f"Module { url_or_name !r} does not export 'mount' but exports_mount=True"
133- )
136+ if source_file is not None :
137+ manage .add_web_module (source , source_file )
138+ elif not manage .web_module_exists (source ):
139+ raise ValueError (f"Module { source !r} does not exist" )
140+
141+ if check_exports :
142+ self .exports = manage .web_module_exports (source )
143+ if exports_mount and "mount" not in self .exports :
144+ raise ValueError (f"Module { source !r} does not export 'mount'" )
134145
135146 def declare (
136147 self ,
@@ -149,24 +160,35 @@ def declare(
149160 Where ``name`` is the given name, and ``module`` is the :attr:`Module.url` of
150161 this :class:`Module` instance.
151162 """
152- if self .check_exports and name not in self .exports :
163+ if self .exports is not None and name not in self .exports :
153164 raise ValueError (
154165 f"{ self } does not export { name !r} , available options are { list (self .exports )} "
155166 )
156167
157168 return Import (
158- self .url ,
159169 name ,
160- has_children = has_children ,
161- exports_mount = self .exports_mount ,
162- fallback = fallback or self .fallback ,
170+ self .source ,
171+ self .source_type ,
172+ has_children ,
173+ self .exports_mount ,
174+ fallback or self .fallback ,
163175 )
164176
165177 def __getattr__ (self , name : str ) -> Import :
178+ if name [0 ].lower () == name [0 ]:
179+ # component names should be capitalized
180+ raise AttributeError (f"{ self } has no attribute { name !r} " )
166181 return self .declare (name )
167182
183+ def __eq__ (self , other : Any ) -> bool :
184+ return (
185+ isinstance (other , Module )
186+ and self .source == other .source
187+ and self .source_type == other .source_type
188+ )
189+
168190 def __repr__ (self ) -> str :
169- return f"{ type (self ).__name__ } ({ self .url } )"
191+ return f"{ type (self ).__name__ } ({ self .source } )"
170192
171193
172194class Import :
@@ -188,8 +210,9 @@ class Import:
188210
189211 def __init__ (
190212 self ,
191- module : str ,
192213 name : str ,
214+ source : str ,
215+ source_type : str ,
193216 has_children : bool = True ,
194217 exports_mount : bool = False ,
195218 fallback : Optional [str ] = None ,
@@ -199,12 +222,15 @@ def __init__(
199222 # set after Import instances have been constructed. A more comprehensive
200223 # check can be introduced if that is shown to be an issue in practice.
201224 raise RuntimeError (
202- f"{ IDOM_CLIENT_MODULES_MUST_HAVE_MOUNT } is set and { module } has no mount"
225+ f"{ IDOM_CLIENT_MODULES_MUST_HAVE_MOUNT } is set and { source } has no mount"
203226 )
204227 self ._name = name
205228 self ._constructor = make_vdom_constructor (name , has_children )
206229 self ._import_source = ImportSourceDict (
207- source = module , fallback = fallback , exportsMount = exports_mount
230+ source = source ,
231+ sourceType = source_type ,
232+ fallback = fallback ,
233+ exportsMount = exports_mount ,
208234 )
209235
210236 def __call__ (
0 commit comments