@@ -59,15 +59,28 @@ def install(
59
59
]
60
60
61
61
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
+
62
72
class Module :
63
73
"""A Javascript module
64
74
65
75
Parameters:
66
- url_or_name :
76
+ source :
67
77
The URL to an ECMAScript module which exports React components
68
78
(*with* a ``.js`` file extension) or name of a module installed in the
69
79
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:
71
84
Only applicable if running on a client app which supports this feature.
72
85
Dynamically install the code in the give file as a single-file module. The
73
86
built-in client will inject this module adjacent to other installed modules
@@ -87,50 +100,48 @@ class Module:
87
100
The URL this module will be imported from.
88
101
"""
89
102
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"
98
104
99
105
def __init__ (
100
106
self ,
101
- url_or_name : str ,
107
+ source : str ,
108
+ source_type : Optional [str ] = None ,
102
109
source_file : Optional [Union [str , Path ]] = None ,
103
110
fallback : Optional [str ] = None ,
104
111
exports_mount : bool = False ,
105
- check_exports : bool = True ,
112
+ check_exports : Optional [ bool ] = None ,
106
113
) -> None :
114
+ self .source = source
107
115
self .fallback = fallback
108
116
self .exports_mount = exports_mount
109
- self .check_exports = check_exports
117
+ self .exports : Optional [ Set [ str ]] = None
110
118
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
127
123
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
129
135
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'" )
134
145
135
146
def declare (
136
147
self ,
@@ -149,24 +160,35 @@ def declare(
149
160
Where ``name`` is the given name, and ``module`` is the :attr:`Module.url` of
150
161
this :class:`Module` instance.
151
162
"""
152
- if self .check_exports and name not in self .exports :
163
+ if self .exports is not None and name not in self .exports :
153
164
raise ValueError (
154
165
f"{ self } does not export { name !r} , available options are { list (self .exports )} "
155
166
)
156
167
157
168
return Import (
158
- self .url ,
159
169
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 ,
163
175
)
164
176
165
177
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} " )
166
181
return self .declare (name )
167
182
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
+
168
190
def __repr__ (self ) -> str :
169
- return f"{ type (self ).__name__ } ({ self .url } )"
191
+ return f"{ type (self ).__name__ } ({ self .source } )"
170
192
171
193
172
194
class Import :
@@ -188,8 +210,9 @@ class Import:
188
210
189
211
def __init__ (
190
212
self ,
191
- module : str ,
192
213
name : str ,
214
+ source : str ,
215
+ source_type : str ,
193
216
has_children : bool = True ,
194
217
exports_mount : bool = False ,
195
218
fallback : Optional [str ] = None ,
@@ -199,12 +222,15 @@ def __init__(
199
222
# set after Import instances have been constructed. A more comprehensive
200
223
# check can be introduced if that is shown to be an issue in practice.
201
224
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"
203
226
)
204
227
self ._name = name
205
228
self ._constructor = make_vdom_constructor (name , has_children )
206
229
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 ,
208
234
)
209
235
210
236
def __call__ (
0 commit comments