ページのロード時に全部の Custom Element の定義を読み込むのはムダが多い
めったに開かないページでしか使わないものまでロードする必要ないし

基本は Custom Element の定義ごとにファイルを分けてモジュール化してるので モジュールごとに内部で使う Custom Element のモジュールを import するのが一般的だと思う
この方法でも よく使うモジュールを毎回 import したり Shadow DOM 内の HTML を変えるたびに import も見直しが必要で面倒

未定義要素が Document や ShadowRoot に接続されたときにイベントが起きれば そのイベントのリスナでモジュールを読み込んで customElements.define を呼び出しできるのに
PHP の autoload 風で良さそう

ただ 今のところはそんなイベントは無いみたい
自分でやるなら MutationObserver 使って全部の Node の追加を監視して 「-」 を含むタグ名の要素があれば定義済みかチェックすることになりそう
DOM の更新が多いページは少し重そうなのと Shadow DOM 内を検知できないのが問題点

この機能用に HTMLElement を継承したクラスを作って 全部の Custom Element がこのクラスを継承するように作れば 共通部分で MutationObserver の監視設定を入れて Shadow DOM 内も要素の追加を監視できるけど ライブラリを使うとか既存のコンポーネントを使うとかもあってその制限は結構不便
attachShadow を上書きしてしまうのが簡単
とりあえずこんなので動きそう

const mo = new MutationObserver((mutations) => {
const added_nodes = mutations.flatMap((m) => [...m.addedNodes])
const collect = (node) => {
for (const child of node.childNodes) {
added_nodes.push(child)
collect(child)
}
}
added_nodes.slice().forEach(collect)

const undefined_tags = added_nodes
.map((n) => n.localName)
.filter((t) => t && t.includes("-") && !customElements.get(t))

for (const tag of new Set(undefined_tags)) {
// load module and define custom element
// (ex) import(`/path/to/components/${tag}.js`)
console.log(tag)
}
})
const mo_conf = { childList: true, subtree: true }
mo.observe(document.body, mo_conf)

const attachShadow = Element.prototype.attachShadow
Element.prototype.attachShadow = function (...args) {
const shadow = attachShadow.apply(this, args)
mo.observe(shadow, mo_conf)
return shadow
}

import するところは仮の console.log にしてる
実際のロード処理は

import(`/components/${tag}.js`)

みたいに 1 ファイルを動的 import して import するモジュール内で customElements.define まで行う
Custom Element を定義するモジュールがすべて同じフォルダにない場合は importmaps でそれぞれの場所を設定で良いかも