@wcstack/autoloader 1.3.11
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 +250 -0
- package/README.md +250 -0
- package/dist/auto.js +3 -0
- package/dist/auto.min.js +3 -0
- package/dist/bootstrapAutoloader.d.ts +3 -0
- package/dist/bootstrapAutoloader.d.ts.map +1 -0
- package/dist/bootstrapAutoloader.js +9 -0
- package/dist/bootstrapAutoloader.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/eagerload.d.ts +3 -0
- package/dist/eagerload.d.ts.map +1 -0
- package/dist/eagerload.js +122 -0
- package/dist/eagerload.js.map +1 -0
- package/dist/exports.d.ts +3 -0
- package/dist/exports.d.ts.map +1 -0
- package/dist/exports.js +2 -0
- package/dist/exports.js.map +1 -0
- package/dist/importmap.d.ts +7 -0
- package/dist/importmap.d.ts.map +1 -0
- package/dist/importmap.js +69 -0
- package/dist/importmap.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.esm.js +460 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.esm.min.js +2 -0
- package/dist/index.esm.min.js.map +1 -0
- package/dist/lazyLoad.d.ts +4 -0
- package/dist/lazyLoad.d.ts.map +1 -0
- package/dist/lazyLoad.js +166 -0
- package/dist/lazyLoad.js.map +1 -0
- package/dist/registerHandler.d.ts +2 -0
- package/dist/registerHandler.d.ts.map +1 -0
- package/dist/registerHandler.js +22 -0
- package/dist/registerHandler.js.map +1 -0
- package/dist/resolveLoader.d.ts +3 -0
- package/dist/resolveLoader.d.ts.map +1 -0
- package/dist/resolveLoader.js +45 -0
- package/dist/resolveLoader.js.map +1 -0
- package/dist/tags.d.ts +4 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +7 -0
- package/dist/tags.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vanilla.d.ts +2 -0
- package/dist/vanilla.d.ts.map +1 -0
- package/dist/vanilla.js +5 -0
- package/dist/vanilla.js.map +1 -0
- package/package.json +73 -0
package/README.ja.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# @wcstack/autoloader
|
|
2
|
+
|
|
3
|
+
カスタム要素(要 Web Components 対応)を、HTMLにタグを書くだけで自動的にロードします。
|
|
4
|
+
|
|
5
|
+
## 特徴
|
|
6
|
+
|
|
7
|
+
### 基本機能
|
|
8
|
+
* **自動検出とロード**: 未定義のカスタム要素タグを検知し、自動的に `import()` します。
|
|
9
|
+
* **動的変更への対応**: `innerHTML` や `appendChild` で後から追加された要素も即座に検知します。
|
|
10
|
+
* **ゼロコンフィグ / ビルドレス**: バンドラー設定不要で、ブラウザ標準機能のみで動作します。
|
|
11
|
+
* **依存関係ゼロ**: 外部ライブラリに依存せず軽量です。
|
|
12
|
+
|
|
13
|
+
### ユニークな機能
|
|
14
|
+
* **Import Map 拡張**: 標準の Import Map 内に `@components/` ルールを記述する標準準拠のアプローチ。
|
|
15
|
+
* **名前空間プレフィックスによる自動解決**: 1つ1つの登録は不要。`@components/ui/` のようなプレフィックス定義だけで、`<ui-button>` → `button.js` のように自動解決します。
|
|
16
|
+
* **インラインローダー指定**: Import Map のキーで `@components/ui|lit/` のようにローダーを指定可能。複数フレームワークの混在も容易です。
|
|
17
|
+
* **高度な `is` 属性サポート**: 拡張ビルトイン要素も自動ロード。クラス定義から `extends` を推論し、適切に `define` します。
|
|
18
|
+
* **抽象化されたローダー**: ファイルの読み込みロジック自体がプラガブルで、拡張子や処理系をカスタマイズ可能です。
|
|
19
|
+
|
|
20
|
+
## 使い方
|
|
21
|
+
|
|
22
|
+
### 1. Import Mapの設定
|
|
23
|
+
|
|
24
|
+
`@wcstack/autoloader`キーに、オートローダーのパスを定義します。
|
|
25
|
+
`@components/`プレフィックスを使用して、importmap内にコンポーネントのパスを定義します。
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<script type="importmap">
|
|
29
|
+
{
|
|
30
|
+
"imports": {
|
|
31
|
+
"@wcstack/autoloader": "/path/to/autoloader",
|
|
32
|
+
"@components/ui/": "./components/ui/",
|
|
33
|
+
"@components/app/": "./components/app/"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. ハンドラーの登録
|
|
40
|
+
|
|
41
|
+
メインスクリプトで`bootstrapAutoloader`をインポートして呼び出します。
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script type="module">
|
|
45
|
+
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
46
|
+
bootstrapAutoloader();
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. コンポーネントの使用
|
|
51
|
+
|
|
52
|
+
HTMLでカスタム要素を使用するだけです。`@wcstack/autoloader`が自動的に対応するファイルをインポートします。
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<!-- ./components/ui/button.js を読み込み -->
|
|
56
|
+
<ui-button></ui-button>
|
|
57
|
+
|
|
58
|
+
<!-- ./components/app/header.js を読み込み -->
|
|
59
|
+
<app-header></app-header>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Import Map構文
|
|
63
|
+
|
|
64
|
+
`@wcstack/autoloader`は`@components/`で始まるimportmapのキーを解析します。
|
|
65
|
+
|
|
66
|
+
### 遅延読み込み(名前空間)
|
|
67
|
+
|
|
68
|
+
コンポーネントグループの遅延読み込みを有効にするには、`/`で終わるキーを使用します。
|
|
69
|
+
|
|
70
|
+
形式: `"@components/<プレフィックス>[|<ローダー>]/": "<パス>"`
|
|
71
|
+
|
|
72
|
+
- **プレフィックス**: タグのプレフィックス。スラッシュはダッシュに変換されます。
|
|
73
|
+
- **ローダー**(オプション): 使用するローダー(例: `vanilla`、`lit`)。デフォルトは`vanilla`。
|
|
74
|
+
|
|
75
|
+
**例:**
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"imports": {
|
|
80
|
+
// <my-component> を ./components/component.js にマッピング
|
|
81
|
+
"@components/my/": "./components/",
|
|
82
|
+
|
|
83
|
+
// <ui-button> を ./ui/button.js にマッピング('lit'ローダーを使用)
|
|
84
|
+
"@components/ui|lit/": "./ui/"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 即時読み込み
|
|
90
|
+
|
|
91
|
+
特定のコンポーネントを即座に読み込むには、`/`で終わらないキーを使用します。
|
|
92
|
+
|
|
93
|
+
形式: `"@components/<タグ名>[|<ローダー>[,<extends>]]": "<パス>"`
|
|
94
|
+
|
|
95
|
+
- **ローダー**(オプション): 省略した場合、ファイル拡張子に基づいて自動解決されます(例: `.js` -> デフォルトローダー、`.lit.js` -> litローダー)。
|
|
96
|
+
- **extends**(オプション): 省略した場合、コンポーネントクラスがビルトインHTML要素を継承しているかどうかを自動検出します(例: `HTMLButtonElement` -> `extends: 'button'`)。
|
|
97
|
+
|
|
98
|
+
**例:**
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"imports": {
|
|
103
|
+
// <my-button> を ./my-button.js から即時読み込み
|
|
104
|
+
// ローダー: 自動検出(.js)
|
|
105
|
+
// extends: 自動検出(例: クラスがHTMLButtonElementを継承している場合)
|
|
106
|
+
"@components/my-button": "./my-button.js",
|
|
107
|
+
|
|
108
|
+
// ローダーとextendsを明示的に指定
|
|
109
|
+
"@components/fancy-input|vanilla,input": "./fancy-input.js",
|
|
110
|
+
|
|
111
|
+
// Lit要素のローダーを自動検出(litローダーが設定されている場合)
|
|
112
|
+
"@components/my-lit-button": "./my-button.lit.js"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## コンポーネントの要件
|
|
118
|
+
|
|
119
|
+
デフォルト(`vanilla`ローダー使用時)では、コンポーネントファイルは以下を満たす必要があります:
|
|
120
|
+
|
|
121
|
+
1. `.js`拡張子(設定で変更可能)
|
|
122
|
+
2. カスタム要素クラスを`default`としてエクスポート
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
// components/ui/button.js
|
|
126
|
+
export default class UiButton extends HTMLElement {
|
|
127
|
+
constructor() {
|
|
128
|
+
super();
|
|
129
|
+
this.attachShadow({ mode: 'open' }).innerHTML = '<button><slot></slot></button>';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## カスタマイズドビルトイン要素(`is`属性)
|
|
135
|
+
|
|
136
|
+
オートローダーは`is`属性を使用したカスタマイズドビルトイン要素を検出します:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<!-- オートローダーが自動的に "my-button" を検出してロード -->
|
|
140
|
+
<button is="my-button">Click me</button>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**遅延読み込み**: `extends`値はホスト要素のタグから自動推論されます(例: `<button>` → `extends: "button"`)。
|
|
144
|
+
|
|
145
|
+
**即時読み込み**: `extends`値はコンポーネントクラスのプロトタイプから推論されます(例: `HTMLButtonElement` → `extends: "button"`)。Import Mapで明示的に指定することもできます:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"imports": {
|
|
150
|
+
"@components/my-button|vanilla,button": "./my-button.js"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// my-button.js
|
|
157
|
+
export default class MyButton extends HTMLButtonElement {
|
|
158
|
+
connectedCallback() {
|
|
159
|
+
this.style.color = 'red';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// オートローダーが呼び出す: customElements.define('my-button', MyButton, { extends: 'button' })
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## 設定
|
|
166
|
+
|
|
167
|
+
`bootstrapAutoloader()`にオプションの設定オブジェクトを渡して初期化します:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
interface ILoader {
|
|
171
|
+
postfix: string;
|
|
172
|
+
loader: (path: string) => Promise<CustomElementConstructor | null>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface IWritableConfig {
|
|
176
|
+
loaders?: Record<string, ILoader | string>;
|
|
177
|
+
observable?: boolean;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
| オプション | 型 | デフォルト | 説明 |
|
|
182
|
+
|--------|------|---------|-------------|
|
|
183
|
+
| `loaders` | `Record<string, ILoader \| string>` | 下記参照 | ローダー定義。値は`ILoader`オブジェクトまたは他のローダーキーへの文字列エイリアス。 |
|
|
184
|
+
| `observable` | `boolean` | `true` | MutationObserverによる動的追加要素の検出を有効化。`false`で無効化。 |
|
|
185
|
+
|
|
186
|
+
### デフォルト設定
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
{
|
|
190
|
+
loaders: {
|
|
191
|
+
// 組み込みvanillaローダー: モジュールをインポートしdefaultエクスポートを返す
|
|
192
|
+
vanilla: { postfix: ".js", loader: vanillaLoader },
|
|
193
|
+
// デフォルトキー: どのローダーにも一致しない場合のフォールバック
|
|
194
|
+
"*": "vanilla"
|
|
195
|
+
},
|
|
196
|
+
observable: true
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- **`vanilla`**: 組み込みローダー。モジュールを動的インポートし、`default`エクスポートをカスタム要素コンストラクタとして返します。
|
|
201
|
+
- **`"*"`(デフォルトキー)**: フォールバックローダー。値は文字列エイリアス`"vanilla"`で、マッチしないコンポーネントはvanillaローダーを使用します。
|
|
202
|
+
|
|
203
|
+
### ローダー解決
|
|
204
|
+
|
|
205
|
+
コンポーネントに明示的なローダーキーがない場合(例: `|loader`なしの遅延読み込み名前空間)、以下の順序でローダーを解決します:
|
|
206
|
+
|
|
207
|
+
1. **postfix一致**: ファイルパスを登録済みローダーの`postfix`値と照合(最長一致優先)。
|
|
208
|
+
2. **デフォルトキーフォールバック**: postfixが一致しない場合、`"*"`キーで参照されるローダーを使用。
|
|
209
|
+
|
|
210
|
+
### 例
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
214
|
+
|
|
215
|
+
bootstrapAutoloader({
|
|
216
|
+
loaders: {
|
|
217
|
+
// vanillaローダーのファイル拡張子を変更
|
|
218
|
+
vanilla: { postfix: ".vanilla.js" },
|
|
219
|
+
// .lit.jsファイル用のカスタムローダーを追加
|
|
220
|
+
lit: {
|
|
221
|
+
postfix: ".lit.js",
|
|
222
|
+
loader: async (path) => {
|
|
223
|
+
const module = await import(path);
|
|
224
|
+
return module.default;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
// MutationObserverを無効化(動的コンテンツ検出なし)
|
|
229
|
+
observable: false
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## 動作の仕組み
|
|
234
|
+
|
|
235
|
+
### ロードライフサイクル
|
|
236
|
+
|
|
237
|
+
1. **Import Map解析**: `bootstrapAutoloader()`呼び出し時に、すべての`<script type="importmap">`要素から`@components/`エントリを解析。
|
|
238
|
+
2. **即時読み込み**: 名前空間でないキー(`/`で終わらない)のコンポーネントを即座に並列ロード。
|
|
239
|
+
3. **遅延読み込み**(`DOMContentLoaded`時): TreeWalkerを使用してDOMをスキャンし、登録済み名前空間に一致する未定義カスタム要素を検出。
|
|
240
|
+
4. **ネストされたロード**: カスタム要素が定義・アップグレードされた後、そのShadow DOM(存在する場合)もスキャンしてネストされたカスタム要素を検出。
|
|
241
|
+
5. **監視**(`observable: true`の場合): MutationObserverがDOMへの新規要素追加を監視し、遅延読み込みをトリガー。
|
|
242
|
+
|
|
243
|
+
### エラーハンドリング
|
|
244
|
+
|
|
245
|
+
- ロードに失敗したコンポーネントは内部的に追跡され、以降のスキャンで再試行されません。
|
|
246
|
+
- 重複ロードの防止: コンポーネントが既にロード中の場合、後続のリクエストは既存のロード完了を待機します。
|
|
247
|
+
|
|
248
|
+
## ライセンス
|
|
249
|
+
|
|
250
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# @wcstack/autoloader
|
|
2
|
+
|
|
3
|
+
Automatically loads custom elements (requires Web Components support) just by writing the tags in HTML.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
### Basic Features
|
|
8
|
+
- **Auto Detection & Loading**: Detects undefined custom element tags and automatically `import()`s them.
|
|
9
|
+
- **Dynamic Content Support**: Instantly detects elements added later via `innerHTML` or `appendChild`.
|
|
10
|
+
- **Zero Config / Buildless**: Works with browser standard features only; no bundler configuration required.
|
|
11
|
+
- **Zero Dependencies**: Lightweight with no external dependencies.
|
|
12
|
+
|
|
13
|
+
### Unique Features
|
|
14
|
+
- **Import Map Extension**: A standards-compliant approach that defines `@components/` rules within standard Import Maps.
|
|
15
|
+
- **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`.
|
|
16
|
+
- **Inline Loader Specification**: Specify loaders in Import Map keys like `@components/ui|lit/`. Easily mix multiple frameworks.
|
|
17
|
+
- **Advanced `is` Attribute Support**: Automatically loads extended built-in elements. Infers `extends` from class definitions and calls `define` appropriately.
|
|
18
|
+
- **Abstracted Loaders**: The file loading logic itself is pluggable, allowing customization of extensions and processing systems.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### 1. Setup Import Map
|
|
23
|
+
|
|
24
|
+
Define the autoloader path using the `@wcstack/autoloader` key.
|
|
25
|
+
Define your component paths in an import map using the `@components/` prefix.
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<script type="importmap">
|
|
29
|
+
{
|
|
30
|
+
"imports": {
|
|
31
|
+
"@wcstack/autoloader": "/path/to/autoloader",
|
|
32
|
+
"@components/ui/": "./components/ui/",
|
|
33
|
+
"@components/app/": "./components/app/"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Register the Handler
|
|
40
|
+
|
|
41
|
+
Import and call `bootstrapAutoloader` in your main script.
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script type="module">
|
|
45
|
+
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
46
|
+
bootstrapAutoloader();
|
|
47
|
+
</script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Use Components
|
|
51
|
+
|
|
52
|
+
Just use your custom elements in HTML. `@wcstack/autoloader` will automatically import the matching file.
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<!-- Loads ./components/ui/button.js -->
|
|
56
|
+
<ui-button></ui-button>
|
|
57
|
+
|
|
58
|
+
<!-- Loads ./components/app/header.js -->
|
|
59
|
+
<app-header></app-header>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Import Map Syntax
|
|
63
|
+
|
|
64
|
+
`@wcstack/autoloader` parses keys in the import map starting with `@components/`.
|
|
65
|
+
|
|
66
|
+
### Lazy Loading (Namespaces)
|
|
67
|
+
|
|
68
|
+
To enable lazy loading for a group of components, use a key ending with `/`.
|
|
69
|
+
|
|
70
|
+
Format: `"@components/<prefix>[|<loader>]/": "<path>"`
|
|
71
|
+
|
|
72
|
+
- **Prefix**: The tag prefix. Slashes are converted to dashes.
|
|
73
|
+
- **Loader** (Optional): The loader to use (e.g., `vanilla`, `lit`). Defaults to `vanilla`.
|
|
74
|
+
|
|
75
|
+
**Examples:**
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"imports": {
|
|
80
|
+
// Maps <my-component> to ./components/component.js
|
|
81
|
+
"@components/my/": "./components/",
|
|
82
|
+
|
|
83
|
+
// Maps <ui-button> to ./ui/button.js (using 'lit' loader if configured)
|
|
84
|
+
"@components/ui|lit/": "./ui/"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Eager Loading
|
|
90
|
+
|
|
91
|
+
To load a specific component immediately, use a key that does NOT end with `/`.
|
|
92
|
+
|
|
93
|
+
Format: `"@components/<tagName>[|<loader>[,<extends>]]": "<path>"`
|
|
94
|
+
|
|
95
|
+
- **Loader** (Optional): If omitted, it is automatically resolved based on the file extension (e.g., `.js` -> default loader, `.lit.js` -> lit-loader).
|
|
96
|
+
- **Extends** (Optional): If omitted, it is automatically detected if the component class extends a built-in HTML element (e.g., `HTMLButtonElement` -> `extends: 'button'`).
|
|
97
|
+
|
|
98
|
+
**Examples:**
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"imports": {
|
|
103
|
+
// Eager loads <my-button> from ./my-button.js
|
|
104
|
+
// Loader: Auto-detected (.js)
|
|
105
|
+
// Extends: Auto-detected (e.g. if class extends HTMLButtonElement)
|
|
106
|
+
"@components/my-button": "./my-button.js",
|
|
107
|
+
|
|
108
|
+
// Explicitly specifying loader and extends
|
|
109
|
+
"@components/fancy-input|vanilla,input": "./fancy-input.js",
|
|
110
|
+
|
|
111
|
+
// Auto-detect loader for Lit element (if lit-loader is configured)
|
|
112
|
+
"@components/my-lit-button": "./my-button.lit.js"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Component Requirements
|
|
118
|
+
|
|
119
|
+
By default (using the `vanilla` loader), your component files should:
|
|
120
|
+
|
|
121
|
+
1. Have a `.js` extension (configurable).
|
|
122
|
+
2. Export the custom element class as `default`.
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
// components/ui/button.js
|
|
126
|
+
export default class UiButton extends HTMLElement {
|
|
127
|
+
constructor() {
|
|
128
|
+
super();
|
|
129
|
+
this.attachShadow({ mode: 'open' }).innerHTML = '<button><slot></slot></button>';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Customized Built-in Elements (`is` attribute)
|
|
135
|
+
|
|
136
|
+
The autoloader detects customized built-in elements using the `is` attribute:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<!-- Autoloader detects and loads "my-button" -->
|
|
140
|
+
<button is="my-button">Click me</button>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Lazy loading**: The `extends` value is automatically inferred from the host element tag (e.g., `<button>` → `extends: "button"`).
|
|
144
|
+
|
|
145
|
+
**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:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"imports": {
|
|
150
|
+
"@components/my-button|vanilla,button": "./my-button.js"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// my-button.js
|
|
157
|
+
export default class MyButton extends HTMLButtonElement {
|
|
158
|
+
connectedCallback() {
|
|
159
|
+
this.style.color = 'red';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Autoloader calls: customElements.define('my-button', MyButton, { extends: 'button' })
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Configuration
|
|
166
|
+
|
|
167
|
+
Initialize the autoloader with optional configuration via `bootstrapAutoloader()`:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
interface ILoader {
|
|
171
|
+
postfix: string;
|
|
172
|
+
loader: (path: string) => Promise<CustomElementConstructor | null>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface IWritableConfig {
|
|
176
|
+
loaders?: Record<string, ILoader | string>;
|
|
177
|
+
observable?: boolean;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
| Option | Type | Default | Description |
|
|
182
|
+
|--------|------|---------|-------------|
|
|
183
|
+
| `loaders` | `Record<string, ILoader \| string>` | See below | Loader definitions. Values can be `ILoader` objects or string aliases pointing to other loader keys. |
|
|
184
|
+
| `observable` | `boolean` | `true` | Enables MutationObserver to detect dynamically added elements. Set to `false` to disable. |
|
|
185
|
+
|
|
186
|
+
### Default Configuration
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
{
|
|
190
|
+
loaders: {
|
|
191
|
+
// Built-in vanilla loader: imports module and returns default export
|
|
192
|
+
vanilla: { postfix: ".js", loader: vanillaLoader },
|
|
193
|
+
// Default key: used as fallback when no loader matches
|
|
194
|
+
"*": "vanilla"
|
|
195
|
+
},
|
|
196
|
+
observable: true
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
- **`vanilla`**: The built-in loader that dynamically imports a module and returns its `default` export as the custom element constructor.
|
|
201
|
+
- **`"*"` (default key)**: Fallback loader. Its value is a string alias `"vanilla"`, meaning unmatched components use the vanilla loader.
|
|
202
|
+
|
|
203
|
+
### Loader Resolution
|
|
204
|
+
|
|
205
|
+
When a component has no explicit loader key (e.g., lazy-loaded namespaces without `|loader`), the autoloader resolves the loader as follows:
|
|
206
|
+
|
|
207
|
+
1. **Postfix matching**: Checks the file path against all registered loaders' `postfix` values (longest match first).
|
|
208
|
+
2. **Default key fallback**: If no postfix matches, uses the loader referenced by the `"*"` key.
|
|
209
|
+
|
|
210
|
+
### Example
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
import { bootstrapAutoloader } from "@wcstack/autoloader";
|
|
214
|
+
|
|
215
|
+
bootstrapAutoloader({
|
|
216
|
+
loaders: {
|
|
217
|
+
// Override vanilla loader's file extension
|
|
218
|
+
vanilla: { postfix: ".vanilla.js" },
|
|
219
|
+
// Add a custom loader for .lit.js files
|
|
220
|
+
lit: {
|
|
221
|
+
postfix: ".lit.js",
|
|
222
|
+
loader: async (path) => {
|
|
223
|
+
const module = await import(path);
|
|
224
|
+
return module.default;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
// Disable MutationObserver (no dynamic content detection)
|
|
229
|
+
observable: false
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## How it Works
|
|
234
|
+
|
|
235
|
+
### Loading Lifecycle
|
|
236
|
+
|
|
237
|
+
1. **Import Map Parsing**: On `bootstrapAutoloader()` call, all `<script type="importmap">` elements are parsed for `@components/` entries.
|
|
238
|
+
2. **Eager Loading**: Components with non-namespaced keys (not ending with `/`) are loaded immediately, in parallel.
|
|
239
|
+
3. **Lazy Loading** (on `DOMContentLoaded`): The DOM is scanned using TreeWalker for undefined custom elements matching registered namespaces.
|
|
240
|
+
4. **Nested Loading**: After each custom element is defined and upgraded, its Shadow DOM (if present) is also scanned for nested custom elements.
|
|
241
|
+
5. **Observation** (if `observable: true`): A MutationObserver watches for new elements added to the DOM and triggers lazy loading.
|
|
242
|
+
|
|
243
|
+
### Error Handling
|
|
244
|
+
|
|
245
|
+
- Components that fail to load are tracked internally and will not be retried on subsequent scans.
|
|
246
|
+
- Duplicate loading is prevented: if a component is already being loaded, subsequent requests wait for the existing load to complete.
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
MIT
|
package/dist/auto.js
ADDED
package/dist/auto.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapAutoloader.d.ts","sourceRoot":"","sources":["../src/bootstrapAutoloader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAK1F"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { setConfig } from "./config.js";
|
|
2
|
+
import { registerHandler } from "./registerHandler.js";
|
|
3
|
+
export async function bootstrapAutoloader(config) {
|
|
4
|
+
if (config) {
|
|
5
|
+
setConfig(config);
|
|
6
|
+
}
|
|
7
|
+
await registerHandler();
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=bootstrapAutoloader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapAutoloader.js","sourceRoot":"","sources":["../src/bootstrapAutoloader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAiC;IACzE,IAAI,MAAM,EAAE,CAAC;QACX,SAAS,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;IACD,MAAM,eAAe,EAAE,CAAC;AAC1B,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IConfig, IWritableConfig } from "./types.js";
|
|
2
|
+
import { load } from "./vanilla.js";
|
|
3
|
+
export declare const DEFAULT_KEY = "*";
|
|
4
|
+
export declare const VANILLA_KEY = "vanilla";
|
|
5
|
+
export declare const VANILLA_LOADER: {
|
|
6
|
+
postfix: string;
|
|
7
|
+
loader: typeof load;
|
|
8
|
+
};
|
|
9
|
+
export declare const config: IConfig;
|
|
10
|
+
export declare function getConfig(): IConfig;
|
|
11
|
+
export declare function setConfig(partialConfig: IWritableConfig): void;
|
|
12
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAW,eAAe,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAQnC,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,eAAO,MAAM,cAAc;;;CAG1B,CAAA;AAgCD,eAAO,MAAM,MAAM,EAAE,OAA4B,CAAC;AAElD,wBAAgB,SAAS,IAAI,OAAO,CAKnC;AAED,wBAAgB,SAAS,CAAC,aAAa,EAAE,eAAe,GAAG,IAAI,CAW9D"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { load } from "./vanilla.js";
|
|
2
|
+
export const DEFAULT_KEY = "*";
|
|
3
|
+
export const VANILLA_KEY = "vanilla";
|
|
4
|
+
export const VANILLA_LOADER = {
|
|
5
|
+
postfix: ".js",
|
|
6
|
+
loader: load
|
|
7
|
+
};
|
|
8
|
+
const _config = {
|
|
9
|
+
scanImportmap: true,
|
|
10
|
+
loaders: {
|
|
11
|
+
[VANILLA_KEY]: VANILLA_LOADER,
|
|
12
|
+
[DEFAULT_KEY]: VANILLA_KEY
|
|
13
|
+
},
|
|
14
|
+
observable: true
|
|
15
|
+
};
|
|
16
|
+
function deepFreeze(obj) {
|
|
17
|
+
if (obj === null || typeof obj !== "object")
|
|
18
|
+
return obj;
|
|
19
|
+
Object.freeze(obj);
|
|
20
|
+
for (const key of Object.keys(obj)) {
|
|
21
|
+
deepFreeze(obj[key]);
|
|
22
|
+
}
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
function deepClone(obj) {
|
|
26
|
+
if (obj === null || typeof obj !== "object")
|
|
27
|
+
return obj;
|
|
28
|
+
const clone = {};
|
|
29
|
+
for (const key of Object.keys(obj)) {
|
|
30
|
+
clone[key] = deepClone(obj[key]);
|
|
31
|
+
}
|
|
32
|
+
return clone;
|
|
33
|
+
}
|
|
34
|
+
let frozenConfig = null;
|
|
35
|
+
// 後方互換のため config もエクスポート(読み取り専用として使用)
|
|
36
|
+
export const config = _config;
|
|
37
|
+
export function getConfig() {
|
|
38
|
+
if (!frozenConfig) {
|
|
39
|
+
frozenConfig = deepFreeze(deepClone(_config));
|
|
40
|
+
}
|
|
41
|
+
return frozenConfig;
|
|
42
|
+
}
|
|
43
|
+
export function setConfig(partialConfig) {
|
|
44
|
+
if (typeof partialConfig.scanImportmap === "boolean") {
|
|
45
|
+
_config.scanImportmap = partialConfig.scanImportmap;
|
|
46
|
+
}
|
|
47
|
+
if (partialConfig.loaders) {
|
|
48
|
+
Object.assign(_config.loaders, partialConfig.loaders);
|
|
49
|
+
}
|
|
50
|
+
if (typeof partialConfig.observable === "boolean") {
|
|
51
|
+
_config.observable = partialConfig.observable;
|
|
52
|
+
}
|
|
53
|
+
frozenConfig = null;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAQnC,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAE/B,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE,KAAK;IACd,MAAM,EAAE,IAAI;CACb,CAAA;AAED,MAAM,OAAO,GAAoB;IAC/B,aAAa,EAAE,IAAI;IACnB,OAAO,EAAE;QACP,CAAC,WAAW,CAAC,EAAE,cAAc;QAC7B,CAAC,WAAW,CAAC,EAAE,WAAW;KAC3B;IACD,UAAU,EAAE,IAAI;CACjB,CAAA;AAED,SAAS,UAAU,CAAI,GAAM;IAC3B,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxD,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAI,GAAM;IAC1B,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxD,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAED,IAAI,YAAY,GAAmB,IAAI,CAAC;AAExC,sCAAsC;AACtC,MAAM,CAAC,MAAM,MAAM,GAAY,OAAkB,CAAC;AAElD,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,aAA8B;IACtD,IAAI,OAAO,aAAa,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACrD,OAAO,CAAC,aAAa,GAAG,aAAa,CAAC,aAAa,CAAC;IACtD,CAAC;IACD,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,OAAO,aAAa,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAClD,OAAO,CAAC,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC;IAChD,CAAC;IACD,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eagerload.d.ts","sourceRoot":"","sources":["../src/eagerload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAgHrD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC,CAYf"}
|