@wcstack/autoloader 1.9.1 → 1.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +275 -275
- package/README.md +276 -276
- package/dist/auto.js +3 -3
- package/dist/auto.min.js +3 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/package.json +74 -74
package/README.md
CHANGED
|
@@ -1,276 +1,276 @@
|
|
|
1
|
-
# @wcstack/autoloader
|
|
2
|
-
|
|
3
|
-
**What if custom elements loaded themselves?**
|
|
4
|
-
|
|
5
|
-
Imagine a future where you just write a custom element tag and the browser figures out where to find it. No `import`, no `customElements.define()`, no registration boilerplate. You write the tag, it loads.
|
|
6
|
-
|
|
7
|
-
That's what `<wcs-autoloader>` explores. One CDN import, zero dependencies, powered by Import Maps.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
### Basic Features
|
|
12
|
-
- **Auto Detection & Loading**: Detects undefined custom element tags and automatically `import()`s them.
|
|
13
|
-
- **Dynamic Content Support**: Instantly detects elements added later via `innerHTML` or `appendChild`.
|
|
14
|
-
- **Zero Config / Buildless**: Works with browser standard features only; no bundler configuration required.
|
|
15
|
-
- **Zero Dependencies**: Lightweight with no external dependencies.
|
|
16
|
-
|
|
17
|
-
### Unique Features
|
|
18
|
-
- **Import Map Extension**: A standards-compliant approach that defines `@components/` rules within standard Import Maps.
|
|
19
|
-
- **Namespace Prefix Auto-Resolution**: No need to register components one by one. Just define a prefix like `@components/ui/`, and it auto-resolves `<ui-button>` to `button.js`.
|
|
20
|
-
- **Inline Loader Specification**: Specify loaders in Import Map keys like `@components/ui|lit/`. Easily mix multiple frameworks.
|
|
21
|
-
- **Advanced `is` Attribute Support**: Automatically loads extended built-in elements. Infers `extends` from class definitions and calls `define` appropriately.
|
|
22
|
-
- **Abstracted Loaders**: The file loading logic itself is pluggable, allowing customization of extensions and processing systems.
|
|
23
|
-
|
|
24
|
-
## Usage
|
|
25
|
-
|
|
26
|
-
### 1. Setup Import Map
|
|
27
|
-
|
|
28
|
-
Define the autoloader path using the `@wcstack/autoloader` key.
|
|
29
|
-
Define your component paths in an import map using the `@components/` prefix.
|
|
30
|
-
|
|
31
|
-
```html
|
|
32
|
-
<script type="importmap">
|
|
33
|
-
{
|
|
34
|
-
"imports": {
|
|
35
|
-
"@wcstack/autoloader": "/path/to/autoloader",
|
|
36
|
-
"@components/ui/": "./components/ui/",
|
|
37
|
-
"@components/app/": "./components/app/"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
</script>
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### 2. Load the Autoloader
|
|
44
|
-
|
|
45
|
-
Load the autoloader script via `<script>` tag, or import and call `bootstrapAutoloader` manually.
|
|
46
|
-
|
|
47
|
-
```html
|
|
48
|
-
<!-- Option A: Zero-config script (recommended) -->
|
|
49
|
-
<script type="module" src="https://esm.run/@wcstack/autoloader/auto"></script>
|
|
50
|
-
|
|
51
|
-
<!-- Option B: Manual initialization -->
|
|
52
|
-
<script type="module">
|
|
53
|
-
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
54
|
-
bootstrapAutoloader();
|
|
55
|
-
</script>
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### 3. Place the `<wcs-autoloader>` Element
|
|
59
|
-
|
|
60
|
-
Add `<wcs-autoloader>` to your HTML. This element triggers the loading lifecycle — eager loading starts on element creation, and lazy loading starts when the element is connected to the DOM.
|
|
61
|
-
|
|
62
|
-
```html
|
|
63
|
-
<body>
|
|
64
|
-
<wcs-autoloader></wcs-autoloader>
|
|
65
|
-
<!-- your app components -->
|
|
66
|
-
</body>
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### 4. Use Components
|
|
70
|
-
|
|
71
|
-
Just use your custom elements in HTML. `@wcstack/autoloader` will automatically import the matching file.
|
|
72
|
-
|
|
73
|
-
```html
|
|
74
|
-
<!-- Loads ./components/ui/button.js -->
|
|
75
|
-
<ui-button></ui-button>
|
|
76
|
-
|
|
77
|
-
<!-- Loads ./components/app/header.js -->
|
|
78
|
-
<app-header></app-header>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Import Map Syntax
|
|
82
|
-
|
|
83
|
-
`@wcstack/autoloader` parses keys in the import map starting with `@components/`.
|
|
84
|
-
|
|
85
|
-
### Lazy Loading (Namespaces)
|
|
86
|
-
|
|
87
|
-
To enable lazy loading for a group of components, use a key ending with `/`.
|
|
88
|
-
|
|
89
|
-
Format: `"@components/<prefix>[|<loader>]/": "<path>"`
|
|
90
|
-
|
|
91
|
-
- **Prefix**: The tag prefix. Slashes are converted to dashes.
|
|
92
|
-
- **Loader** (Optional): The loader to use (e.g., `vanilla`, `lit`). Defaults to `vanilla`.
|
|
93
|
-
|
|
94
|
-
**Examples:**
|
|
95
|
-
|
|
96
|
-
```json
|
|
97
|
-
{
|
|
98
|
-
"imports": {
|
|
99
|
-
// Maps <my-component> to ./components/component.js
|
|
100
|
-
"@components/my/": "./components/",
|
|
101
|
-
|
|
102
|
-
// Maps <ui-button> to ./ui/button.js (using 'lit' loader if configured)
|
|
103
|
-
"@components/ui|lit/": "./ui/"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Eager Loading
|
|
109
|
-
|
|
110
|
-
To load a specific component immediately, use a key that does NOT end with `/`.
|
|
111
|
-
|
|
112
|
-
Format: `"@components/<tagName>[|<loader>[,<extends>]]": "<path>"`
|
|
113
|
-
|
|
114
|
-
- **Loader** (Optional): If omitted, it is automatically resolved based on the file extension (e.g., `.js` -> default loader, `.lit.js` -> lit-loader).
|
|
115
|
-
- **Extends** (Optional): If omitted, it is automatically detected if the component class extends a built-in HTML element (e.g., `HTMLButtonElement` -> `extends: 'button'`).
|
|
116
|
-
|
|
117
|
-
**Examples:**
|
|
118
|
-
|
|
119
|
-
```json
|
|
120
|
-
{
|
|
121
|
-
"imports": {
|
|
122
|
-
// Eager loads <my-button> from ./my-button.js
|
|
123
|
-
// Loader: Auto-detected (.js)
|
|
124
|
-
// Extends: Auto-detected (e.g. if class extends HTMLButtonElement)
|
|
125
|
-
"@components/my-button": "./my-button.js",
|
|
126
|
-
|
|
127
|
-
// Explicitly specifying loader and extends
|
|
128
|
-
"@components/fancy-input|vanilla,input": "./fancy-input.js",
|
|
129
|
-
|
|
130
|
-
// Auto-detect loader for Lit element (if lit-loader is configured)
|
|
131
|
-
"@components/my-lit-button": "./my-button.lit.js"
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Component Requirements
|
|
137
|
-
|
|
138
|
-
By default (using the `vanilla` loader), your component files should:
|
|
139
|
-
|
|
140
|
-
1. Have a `.js` extension (configurable).
|
|
141
|
-
2. Export the custom element class as `default`.
|
|
142
|
-
|
|
143
|
-
```javascript
|
|
144
|
-
// components/ui/button.js
|
|
145
|
-
export default class UiButton extends HTMLElement {
|
|
146
|
-
constructor() {
|
|
147
|
-
super();
|
|
148
|
-
this.attachShadow({ mode: 'open' }).innerHTML = '<button><slot></slot></button>';
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
## Customized Built-in Elements (`is` attribute)
|
|
154
|
-
|
|
155
|
-
The autoloader detects customized built-in elements using the `is` attribute:
|
|
156
|
-
|
|
157
|
-
```html
|
|
158
|
-
<!-- Autoloader detects and loads "my-button" -->
|
|
159
|
-
<button is="my-button">Click me</button>
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
**Lazy loading**: The `extends` value is automatically inferred from the host element tag (e.g., `<button>` → `extends: "button"`).
|
|
163
|
-
|
|
164
|
-
**Eager loading**: The `extends` value is inferred from the component class prototype (e.g., `HTMLButtonElement` → `extends: "button"`), or can be specified explicitly in the import map:
|
|
165
|
-
|
|
166
|
-
```json
|
|
167
|
-
{
|
|
168
|
-
"imports": {
|
|
169
|
-
"@components/my-button|vanilla,button": "./my-button.js"
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
```javascript
|
|
175
|
-
// my-button.js
|
|
176
|
-
export default class MyButton extends HTMLButtonElement {
|
|
177
|
-
connectedCallback() {
|
|
178
|
-
this.style.color = 'red';
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// Autoloader calls: customElements.define('my-button', MyButton, { extends: 'button' })
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## Configuration
|
|
185
|
-
|
|
186
|
-
Initialize the autoloader with optional configuration via `bootstrapAutoloader()`:
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
interface ILoader {
|
|
190
|
-
postfix: string;
|
|
191
|
-
loader: (path: string) => Promise<CustomElementConstructor | null>;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
interface IWritableTagNames {
|
|
195
|
-
autoloader?: string;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
interface IWritableConfig {
|
|
199
|
-
loaders?: Record<string, ILoader | string>;
|
|
200
|
-
observable?: boolean;
|
|
201
|
-
tagNames?: IWritableTagNames;
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
| Option | Type | Default | Description |
|
|
206
|
-
|--------|------|---------|-------------|
|
|
207
|
-
| `loaders` | `Record<string, ILoader \| string>` | See below | Loader definitions. Values can be `ILoader` objects or string aliases pointing to other loader keys. |
|
|
208
|
-
| `observable` | `boolean` | `true` | Enables MutationObserver to detect dynamically added elements. Set to `false` to disable. |
|
|
209
|
-
| `tagNames` | `IWritableTagNames` | `{ autoloader: "wcs-autoloader" }` | Custom element tag name. Can be changed to avoid naming conflicts. |
|
|
210
|
-
|
|
211
|
-
### Default Configuration
|
|
212
|
-
|
|
213
|
-
```javascript
|
|
214
|
-
{
|
|
215
|
-
loaders: {
|
|
216
|
-
// Built-in vanilla loader: imports module and returns default export
|
|
217
|
-
vanilla: { postfix: ".js", loader: vanillaLoader },
|
|
218
|
-
// Default key: used as fallback when no loader matches
|
|
219
|
-
"*": "vanilla"
|
|
220
|
-
},
|
|
221
|
-
observable: true
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
- **`vanilla`**: The built-in loader that dynamically imports a module and returns its `default` export as the custom element constructor.
|
|
226
|
-
- **`"*"` (default key)**: Fallback loader. Its value is a string alias `"vanilla"`, meaning unmatched components use the vanilla loader.
|
|
227
|
-
|
|
228
|
-
### Loader Resolution
|
|
229
|
-
|
|
230
|
-
When a component has no explicit loader key (e.g., lazy-loaded namespaces without `|loader`), the autoloader resolves the loader as follows:
|
|
231
|
-
|
|
232
|
-
1. **Postfix matching**: Checks the file path against all registered loaders' `postfix` values (longest match first).
|
|
233
|
-
2. **Default key fallback**: If no postfix matches, uses the loader referenced by the `"*"` key.
|
|
234
|
-
|
|
235
|
-
### Example
|
|
236
|
-
|
|
237
|
-
```javascript
|
|
238
|
-
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
239
|
-
|
|
240
|
-
bootstrapAutoloader({
|
|
241
|
-
loaders: {
|
|
242
|
-
// Override vanilla loader's file extension
|
|
243
|
-
vanilla: { postfix: ".vanilla.js" },
|
|
244
|
-
// Add a custom loader for .lit.js files
|
|
245
|
-
lit: {
|
|
246
|
-
postfix: ".lit.js",
|
|
247
|
-
loader: async (path) => {
|
|
248
|
-
const module = await import(path);
|
|
249
|
-
return module.default;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
// Disable MutationObserver (no dynamic content detection)
|
|
254
|
-
observable: false
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## How it Works
|
|
259
|
-
|
|
260
|
-
### Loading Lifecycle
|
|
261
|
-
|
|
262
|
-
1. **Registration**: `bootstrapAutoloader()` registers the `<wcs-autoloader>` custom element via `customElements.define()`.
|
|
263
|
-
2. **Constructor** (on element creation): All `<script type="importmap">` elements are parsed for `@components/` entries. Eager loading starts immediately for non-namespaced keys (not ending with `/`).
|
|
264
|
-
3. **connectedCallback** (on DOM attachment): Waits for `DOMContentLoaded` if the document is still loading, then scans the DOM using TreeWalker for undefined custom elements matching registered namespaces.
|
|
265
|
-
4. **Nested Loading**: After each custom element is defined and upgraded, its Shadow DOM (if present) is also scanned for nested custom elements.
|
|
266
|
-
5. **Observation** (if `observable: true`): A MutationObserver watches for new elements added to the DOM and triggers lazy loading.
|
|
267
|
-
6. **disconnectedCallback** (on removal): Disconnects the MutationObserver and releases the singleton instance.
|
|
268
|
-
|
|
269
|
-
### Error Handling
|
|
270
|
-
|
|
271
|
-
- Components that fail to load are tracked internally and will not be retried on subsequent scans.
|
|
272
|
-
- Duplicate loading is prevented: if a component is already being loaded, subsequent requests wait for the existing load to complete.
|
|
273
|
-
|
|
274
|
-
## License
|
|
275
|
-
|
|
276
|
-
MIT
|
|
1
|
+
# @wcstack/autoloader
|
|
2
|
+
|
|
3
|
+
**What if custom elements loaded themselves?**
|
|
4
|
+
|
|
5
|
+
Imagine a future where you just write a custom element tag and the browser figures out where to find it. No `import`, no `customElements.define()`, no registration boilerplate. You write the tag, it loads.
|
|
6
|
+
|
|
7
|
+
That's what `<wcs-autoloader>` explores. One CDN import, zero dependencies, powered by Import Maps.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### Basic Features
|
|
12
|
+
- **Auto Detection & Loading**: Detects undefined custom element tags and automatically `import()`s them.
|
|
13
|
+
- **Dynamic Content Support**: Instantly detects elements added later via `innerHTML` or `appendChild`.
|
|
14
|
+
- **Zero Config / Buildless**: Works with browser standard features only; no bundler configuration required.
|
|
15
|
+
- **Zero Dependencies**: Lightweight with no external dependencies.
|
|
16
|
+
|
|
17
|
+
### Unique Features
|
|
18
|
+
- **Import Map Extension**: A standards-compliant approach that defines `@components/` rules within standard Import Maps.
|
|
19
|
+
- **Namespace Prefix Auto-Resolution**: No need to register components one by one. Just define a prefix like `@components/ui/`, and it auto-resolves `<ui-button>` to `button.js`.
|
|
20
|
+
- **Inline Loader Specification**: Specify loaders in Import Map keys like `@components/ui|lit/`. Easily mix multiple frameworks.
|
|
21
|
+
- **Advanced `is` Attribute Support**: Automatically loads extended built-in elements. Infers `extends` from class definitions and calls `define` appropriately.
|
|
22
|
+
- **Abstracted Loaders**: The file loading logic itself is pluggable, allowing customization of extensions and processing systems.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### 1. Setup Import Map
|
|
27
|
+
|
|
28
|
+
Define the autoloader path using the `@wcstack/autoloader` key.
|
|
29
|
+
Define your component paths in an import map using the `@components/` prefix.
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<script type="importmap">
|
|
33
|
+
{
|
|
34
|
+
"imports": {
|
|
35
|
+
"@wcstack/autoloader": "/path/to/autoloader",
|
|
36
|
+
"@components/ui/": "./components/ui/",
|
|
37
|
+
"@components/app/": "./components/app/"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Load the Autoloader
|
|
44
|
+
|
|
45
|
+
Load the autoloader script via `<script>` tag, or import and call `bootstrapAutoloader` manually.
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<!-- Option A: Zero-config script (recommended) -->
|
|
49
|
+
<script type="module" src="https://esm.run/@wcstack/autoloader/auto"></script>
|
|
50
|
+
|
|
51
|
+
<!-- Option B: Manual initialization -->
|
|
52
|
+
<script type="module">
|
|
53
|
+
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
54
|
+
bootstrapAutoloader();
|
|
55
|
+
</script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Place the `<wcs-autoloader>` Element
|
|
59
|
+
|
|
60
|
+
Add `<wcs-autoloader>` to your HTML. This element triggers the loading lifecycle — eager loading starts on element creation, and lazy loading starts when the element is connected to the DOM.
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<body>
|
|
64
|
+
<wcs-autoloader></wcs-autoloader>
|
|
65
|
+
<!-- your app components -->
|
|
66
|
+
</body>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. Use Components
|
|
70
|
+
|
|
71
|
+
Just use your custom elements in HTML. `@wcstack/autoloader` will automatically import the matching file.
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!-- Loads ./components/ui/button.js -->
|
|
75
|
+
<ui-button></ui-button>
|
|
76
|
+
|
|
77
|
+
<!-- Loads ./components/app/header.js -->
|
|
78
|
+
<app-header></app-header>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Import Map Syntax
|
|
82
|
+
|
|
83
|
+
`@wcstack/autoloader` parses keys in the import map starting with `@components/`.
|
|
84
|
+
|
|
85
|
+
### Lazy Loading (Namespaces)
|
|
86
|
+
|
|
87
|
+
To enable lazy loading for a group of components, use a key ending with `/`.
|
|
88
|
+
|
|
89
|
+
Format: `"@components/<prefix>[|<loader>]/": "<path>"`
|
|
90
|
+
|
|
91
|
+
- **Prefix**: The tag prefix. Slashes are converted to dashes.
|
|
92
|
+
- **Loader** (Optional): The loader to use (e.g., `vanilla`, `lit`). Defaults to `vanilla`.
|
|
93
|
+
|
|
94
|
+
**Examples:**
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"imports": {
|
|
99
|
+
// Maps <my-component> to ./components/component.js
|
|
100
|
+
"@components/my/": "./components/",
|
|
101
|
+
|
|
102
|
+
// Maps <ui-button> to ./ui/button.js (using 'lit' loader if configured)
|
|
103
|
+
"@components/ui|lit/": "./ui/"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Eager Loading
|
|
109
|
+
|
|
110
|
+
To load a specific component immediately, use a key that does NOT end with `/`.
|
|
111
|
+
|
|
112
|
+
Format: `"@components/<tagName>[|<loader>[,<extends>]]": "<path>"`
|
|
113
|
+
|
|
114
|
+
- **Loader** (Optional): If omitted, it is automatically resolved based on the file extension (e.g., `.js` -> default loader, `.lit.js` -> lit-loader).
|
|
115
|
+
- **Extends** (Optional): If omitted, it is automatically detected if the component class extends a built-in HTML element (e.g., `HTMLButtonElement` -> `extends: 'button'`).
|
|
116
|
+
|
|
117
|
+
**Examples:**
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"imports": {
|
|
122
|
+
// Eager loads <my-button> from ./my-button.js
|
|
123
|
+
// Loader: Auto-detected (.js)
|
|
124
|
+
// Extends: Auto-detected (e.g. if class extends HTMLButtonElement)
|
|
125
|
+
"@components/my-button": "./my-button.js",
|
|
126
|
+
|
|
127
|
+
// Explicitly specifying loader and extends
|
|
128
|
+
"@components/fancy-input|vanilla,input": "./fancy-input.js",
|
|
129
|
+
|
|
130
|
+
// Auto-detect loader for Lit element (if lit-loader is configured)
|
|
131
|
+
"@components/my-lit-button": "./my-button.lit.js"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Component Requirements
|
|
137
|
+
|
|
138
|
+
By default (using the `vanilla` loader), your component files should:
|
|
139
|
+
|
|
140
|
+
1. Have a `.js` extension (configurable).
|
|
141
|
+
2. Export the custom element class as `default`.
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// components/ui/button.js
|
|
145
|
+
export default class UiButton extends HTMLElement {
|
|
146
|
+
constructor() {
|
|
147
|
+
super();
|
|
148
|
+
this.attachShadow({ mode: 'open' }).innerHTML = '<button><slot></slot></button>';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Customized Built-in Elements (`is` attribute)
|
|
154
|
+
|
|
155
|
+
The autoloader detects customized built-in elements using the `is` attribute:
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<!-- Autoloader detects and loads "my-button" -->
|
|
159
|
+
<button is="my-button">Click me</button>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Lazy loading**: The `extends` value is automatically inferred from the host element tag (e.g., `<button>` → `extends: "button"`).
|
|
163
|
+
|
|
164
|
+
**Eager loading**: The `extends` value is inferred from the component class prototype (e.g., `HTMLButtonElement` → `extends: "button"`), or can be specified explicitly in the import map:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"imports": {
|
|
169
|
+
"@components/my-button|vanilla,button": "./my-button.js"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
// my-button.js
|
|
176
|
+
export default class MyButton extends HTMLButtonElement {
|
|
177
|
+
connectedCallback() {
|
|
178
|
+
this.style.color = 'red';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Autoloader calls: customElements.define('my-button', MyButton, { extends: 'button' })
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Configuration
|
|
185
|
+
|
|
186
|
+
Initialize the autoloader with optional configuration via `bootstrapAutoloader()`:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
interface ILoader {
|
|
190
|
+
postfix: string;
|
|
191
|
+
loader: (path: string) => Promise<CustomElementConstructor | null>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
interface IWritableTagNames {
|
|
195
|
+
autoloader?: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface IWritableConfig {
|
|
199
|
+
loaders?: Record<string, ILoader | string>;
|
|
200
|
+
observable?: boolean;
|
|
201
|
+
tagNames?: IWritableTagNames;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Option | Type | Default | Description |
|
|
206
|
+
|--------|------|---------|-------------|
|
|
207
|
+
| `loaders` | `Record<string, ILoader \| string>` | See below | Loader definitions. Values can be `ILoader` objects or string aliases pointing to other loader keys. |
|
|
208
|
+
| `observable` | `boolean` | `true` | Enables MutationObserver to detect dynamically added elements. Set to `false` to disable. |
|
|
209
|
+
| `tagNames` | `IWritableTagNames` | `{ autoloader: "wcs-autoloader" }` | Custom element tag name. Can be changed to avoid naming conflicts. |
|
|
210
|
+
|
|
211
|
+
### Default Configuration
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
{
|
|
215
|
+
loaders: {
|
|
216
|
+
// Built-in vanilla loader: imports module and returns default export
|
|
217
|
+
vanilla: { postfix: ".js", loader: vanillaLoader },
|
|
218
|
+
// Default key: used as fallback when no loader matches
|
|
219
|
+
"*": "vanilla"
|
|
220
|
+
},
|
|
221
|
+
observable: true
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
- **`vanilla`**: The built-in loader that dynamically imports a module and returns its `default` export as the custom element constructor.
|
|
226
|
+
- **`"*"` (default key)**: Fallback loader. Its value is a string alias `"vanilla"`, meaning unmatched components use the vanilla loader.
|
|
227
|
+
|
|
228
|
+
### Loader Resolution
|
|
229
|
+
|
|
230
|
+
When a component has no explicit loader key (e.g., lazy-loaded namespaces without `|loader`), the autoloader resolves the loader as follows:
|
|
231
|
+
|
|
232
|
+
1. **Postfix matching**: Checks the file path against all registered loaders' `postfix` values (longest match first).
|
|
233
|
+
2. **Default key fallback**: If no postfix matches, uses the loader referenced by the `"*"` key.
|
|
234
|
+
|
|
235
|
+
### Example
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
239
|
+
|
|
240
|
+
bootstrapAutoloader({
|
|
241
|
+
loaders: {
|
|
242
|
+
// Override vanilla loader's file extension
|
|
243
|
+
vanilla: { postfix: ".vanilla.js" },
|
|
244
|
+
// Add a custom loader for .lit.js files
|
|
245
|
+
lit: {
|
|
246
|
+
postfix: ".lit.js",
|
|
247
|
+
loader: async (path) => {
|
|
248
|
+
const module = await import(path);
|
|
249
|
+
return module.default;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
// Disable MutationObserver (no dynamic content detection)
|
|
254
|
+
observable: false
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## How it Works
|
|
259
|
+
|
|
260
|
+
### Loading Lifecycle
|
|
261
|
+
|
|
262
|
+
1. **Registration**: `bootstrapAutoloader()` registers the `<wcs-autoloader>` custom element via `customElements.define()`.
|
|
263
|
+
2. **Constructor** (on element creation): All `<script type="importmap">` elements are parsed for `@components/` entries. Eager loading starts immediately for non-namespaced keys (not ending with `/`).
|
|
264
|
+
3. **connectedCallback** (on DOM attachment): Waits for `DOMContentLoaded` if the document is still loading, then scans the DOM using TreeWalker for undefined custom elements matching registered namespaces.
|
|
265
|
+
4. **Nested Loading**: After each custom element is defined and upgraded, its Shadow DOM (if present) is also scanned for nested custom elements.
|
|
266
|
+
5. **Observation** (if `observable: true`): A MutationObserver watches for new elements added to the DOM and triggers lazy loading.
|
|
267
|
+
6. **disconnectedCallback** (on removal): Disconnects the MutationObserver and releases the singleton instance.
|
|
268
|
+
|
|
269
|
+
### Error Handling
|
|
270
|
+
|
|
271
|
+
- Components that fail to load are tracked internally and will not be retried on subsequent scans.
|
|
272
|
+
- Duplicate loading is prevented: if a component is already being loaded, subsequent requests wait for the existing load to complete.
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
MIT
|
package/dist/auto.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { bootstrapAutoloader } from "./index.esm.js";
|
|
2
|
-
|
|
3
|
-
bootstrapAutoloader();
|
|
1
|
+
import { bootstrapAutoloader } from "./index.esm.js";
|
|
2
|
+
|
|
3
|
+
bootstrapAutoloader();
|
package/dist/auto.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { bootstrapAutoloader } from "./index.esm.min.js";
|
|
2
|
-
|
|
3
|
-
bootstrapAutoloader();
|
|
1
|
+
import { bootstrapAutoloader } from "./index.esm.min.js";
|
|
2
|
+
|
|
3
|
+
bootstrapAutoloader();
|