Reactコア(http://facebook.github.io/react/)のユニットテストにjsdomを使用しようとしています。
残念ながら、Webコンポーネントの仕様はjsdomでネイティブにサポートされておらず、webcomponents.jsポリフィルはjsdomでは実行されません。 この問題は、WebComponentサポート(カスタム要素、シャドウdom、htmlインポートなど)の追加を要求します。
jsdomがサポートしていないAPIをwebcomponents.jsがどのAPIで使用しているかを調べるのは興味深いことです。 推測しなければならない場合、それは完全なWebコンポーネント仕様よりもはるかに簡単に実装できます。
そうは言っても、Webコンポーネントを実装するのはかなりクールでしょう。 おそらく思ったほど難しくはないでしょう---スペックは比較的小さいです。
これを少し掘り下げる時間がありました:
まず、ウィンドウスコープにWindow
が定義されていません。 Window
コンストラクターでthis.Window = this.prototype
をパッチしました。
次に、webcomponentsjsは、 Window
に別のプロトタイプ、つまりEventTarget
プロトタイプがあることを想定していますが、これは個別のエンティティとして実装していません。
少し時間があったので、ちょっとした情報。
良い。 Windowをかなり簡単に公開できるはずです。 EventTargetプロトタイプは少しトリッキーですが、現在そのようなものを実装する方法を考えると実行可能のようです。 それは私のTODOでした。
さて、これまでのパッチはかなり簡単です:
this.Window = Window;
inherits(dom.EventTarget, Window, dom.EventTarget.prototype);
webcomponents.jsの次のクラッシュは、 SVGUseElement
を実装する必要があるとシミングした後、 HTMLUnknownElement
(#1068)を実装していないことが原因で発生します。 webcomponents.jsは、 HTMLDivElement
によってシムされたSVGUseElement
が気に入らず、アサーションをスローするため、現在ブロックされています。
さて、私はもう少しポリフィルにチェックインしました、私たちは実装する必要があります/あなたは以下をシムする必要があります:
HTMLUnknownElement
#1068SVGUseElement
window.CanvasRenderingContext2D
document.getRange()
、 window.getSelection()
、 window.Range
、 window.Selection
含む;#804が始まりかもしれません)npm i canvas
(今のところ網羅的ではないリスト)
開始は次のようなものです。
jsdom.env({
file: __dirname + '/index.htm', // refers to webcomponent.js
created: function (err, window) {
jsdom.getVirtualConsole(window).sendTo(console)
window.document.createRange = function () { }
window.getSelection = function () { }
window.Range = function () { }
window.Selection = function () { }
window.CanvasRenderingContext2D = function () { } // Object.getPrototypeOf(require("canvas")(0,0).getContext("2d")) might be better
window.SVGUseElement = window.HTMLUnknownElement
},
done: function (err, window) {
console.log(err[0].data.error);
console.log(window.CustomElements)
},
features: {
ProcessExternalResources: ['script']
}
});
これで、 HTMLDocument
コンストラクターにいくつかのバグがあり、最大の呼び出しスタックエラーが発生します。 現時点では、コンストラクターは内部使用のみを目的としていますが、サイト上の一部のスクリプトがコンストラクターを呼び出すことは有効であるため、そのコンストラクターを公開する必要があります。
+1 jsdomでWebComponentsを見てみたいと思います。特に、Polymerの人気が高まるにつれ、ヘッドレスシステムでカスタム要素をテストできるようになるのは素晴らしいことです。
現在、Webコンポーネントのクロスブラウザー定義はないため、実装するのは時期尚早です。 (Chromeをコピーするだけではありません。)それまでの間、jsdomでPolymerを使用してみてください。
@domenicフェアで十分です。 それは、Polymerが依存しているWebComponents.jsポリフィルのサポートです。つまり、現時点ではwebcomponents-lite
(Shadow DOMを除くすべてのポリフィル)です。 Polymerをjsdomで動作させるためにいくつかの試みを行いましたが、これまでのところ運がありません-上記のコメントの@Sebmasterのタスクは、少なくとも最初にパッチを適用する必要があると思います。
私の理解では、問題のポリフィルは3つあります。 OPにあるものはPolymerとは別のものです。 次に、old-Polymerで使用されていたwebcomponents.orgポリフィルがあります。 次に、Polymer 1.0では、独自のポリフィルがあります。これは実際にはポリフィルではなく、代わりにWebコンポーネントのようなことを行う代替ライブラリです。 たぶんそれはwebcomponents-liteです。
WebComponentsJSリポジトリでは、 webcomponentsjs-lite
はバリアントであり、すべての_but_ Shadow DOMにポリフィルを提供し、PolymerはShadyDOMシステムを使用して独自にシムを試みます。 そのため、Polymerは可能な限りWebComponentsに依存しており、WebComponentsJSpolyfillがうなり声を上げていると確信しています。 ライトバージョンは大幅に軽量化されているはずなので(おかしなことに..)、jsdomがライトバージョンに必要なものを正確に特定できるかどうかを確認します。 jsdomでポリフィル(ライトまたはフル)が機能する可能性は何だと思いますか?
調査なしで言うのは本当に難しいです...あなたが見つけたものを楽しみにしています。
ええ、私のToDoタスクのリストはまだ適用可能であり、シムを使用するために必要だと思います。 #1227をマージすると、標準に準拠したインターフェイスの実装が大幅に速くなり、不足しているインターフェイスをより迅速に修正できる可能性があります。
私は(おそらく素朴に)jsdomがどのように構造化されているかを理解する方法としてCustomElementsRegistryの追加に取り組み始めました。 「custom-elements/custom-elements-registry / define.html」をWebプラットフォームのテストリストに追加しましたが、実行すべきでないときに合格しました(まだ十分に実装されていません)。 テストの先頭にthrow
を追加しても、テストの合格が妨げられることはないため、テストは実際には実行されていないと確信しています。 だから私は明らかに何かを逃しました。 test/web-platform-tests/index.js
にテストを追加する以外に、他に何かする必要がありますか?
contentDocument
が何らかの理由で未定義であるため、最初のconst testWindow = iframe.contentDocument.defaultView;
行で失敗したことが原因のようです。 読み込み順序とスクリプトの実行に問題がある可能性がありますが、それについては掘り下げていません。 それがあなたがそれを回避するのに役立つことを願っています。 (今のところ)目的のためにテストを単純化する必要があるかもしれません。
それはとても役に立ちます、ありがとう! そこで何が起こっているのか理解できるかどうかを確認し、理解できない場合は、推奨されているように簡略化されたテストを作成します。
@Sebmaster興味のある方のために、私はそのテストで何が起こっているのかを少し調べましたが、その結果は私にとって驚くべきものです。
テストでは、htmlの名前付きアクセス機能を使用しています。 これは、次のようなことができることを意味します。
<div id="foo"></div>
<script>
console.log(window.foo === document.getElementById('foo'));
</script>
_ただし_、要素にネストされたブラウジングコンテキストがある場合、グローバルは代わりにそれを指す必要があります(リンクされた仕様を参照)。 iframeの場合、それはcontentWindow
です。 jsdomはこれを正しく理解し、テストもあります。 Safariもそれを正しく理解しています。
クレイジーなのは、ChromeとFirefoxがこれを間違えることです。 グローバルは、contentWindowではなくiframeを指します。 これを見て、私はそれがjsdomのバグであると思い、いくつかのハンティングを行い、最終的にそのテストを見つけ、それが私を仕様に導きました。
tldr; jsdomでの作業は非常に教育的であり、皆さんは素晴らしい仕事をしています。
それぞれのブラウザでバグを報告します。 また、PRをweb-platform-testsに送信しますが、テストで他のいくつかの間違いも見つけました。
これは、 https://github.com/tmpvar/jsdom/blob/master/test/living-html/named-properties-window.jsのようなWPTへのアップストリームテストへのさらに大きな動機です。 投稿ありがとうございます! jsdomについて本当に素晴らしい気分にさせてくれます^_^
やあ!
組み合わせることで、カスタム要素のポリフィルをjsdomで機能させることができました
注:リポジトリはjsdom8.5.0を使用します。 その理由は、Mutationイベントを内部で使用するMutationObserverポリフィルでしか成功しなかったためです。 パフォーマンスが悪いため、8.5.0以降にミューテーションイベントが削除されました。 ネイティブのMutationObserverが出てきたら、ポリフィルを削除して最新のjsdomに更新します。
私は最新のjsdomを持っており、 https://github.com/WebReflection/document-register-elementが機能しています。 私はより公式なポリフィルを試してきましたが、何らかの理由で問題が発生しています。 私の目標は、少なくともカスタム要素とhtmlインポートを機能させることです...Polymerも機能させることができれば素晴らしいと思います。
Polymerスクリプトをエラーなしで実行できます。 コンポーネントを作成して、Polymerコンストラクターに渡すこともできます。 その後、それは静かに失敗します。 ShadowDOMが問題だと思います。
私はwebcomponentsjsHTMLインポートポリフィルを機能させることを試みてきました。 スクリプトを実行できます。HTMLインポートでxmlhttprequestが実行されると思いますが、インポートのスクリプトが実行されていないようです。
@lastmjsの例を共有してみませんか? 私は現在、自分自身でWebコンポーネントにひざまずいています。 私が助けてくれるなら、喜んであなたと一緒に貢献したいと思います。
@snuggsありがとう! 私に1日か2日与えてください、私は現在いくつかの差し迫ったことの真っ只中にいます。
@snuggs webcomponents-lite
ポリフィルを機能させることができれば、Polymerを使用できるはずです。 Shadow DOMは、これまでのところ機能させるのが最も難しいポリフィルのようです。 webcomponents-lite
を使用する場合、 template
にアクセスできるため、当面はそれについて心配する必要はありません。 、 custom elements
、およびHTML imports
。
HTMLインポートをwebcomponents-lite
ポリフィルで機能させることができます。 私はいくつかの奇妙な振る舞いに遭遇しました、そして私はこれに出くわしました: https ://github.com/Polymer/polymer/issues/1535HTMLインポートはcors対応の非ファイルプロトコルを介してのみロードできるようです。 そこで、プロジェクトのルートディレクトリにあるクイックhttpサーバーを起動しました。
npm install -g http-server
http-server --cors
そして、これが私が扱ってきた基本的なコードです:
const jsdom = require('jsdom');
const doc = jsdom.jsdom(`
<!DOCTYPE html>
<html>
<head>
<script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="http://localhost:8080/bower_components/polymer/polymer.html">
</head>
<body>
<test-app></test-app>
<dom-module id="test-app">
<template>
</template>
<script>
setTimeout(() => {
class TestApp {
beforeRegister() {
this.is = 'test-app';
console.log('before register');
}
ready() {
console.log('ready');
}
created() {
console.log('created');
}
attached() {
console.log('attached');
}
}
Polymer(TestApp);
}, 1000);
</script>
</dom-module>
</body>
</html>
`, {
virtualConsole: jsdom.createVirtualConsole().sendTo(console)
});
何らかの理由で、 TestApp
のインスタンス化をsetTimeout
でラップする必要があります。 Polymer HTMLのインポートが残りのHTMLのレンダリングをブロックしていないように見えるため、 setTimeout
がないとPolymerコンストラクターは定義されません。 HTMLインポートの通常の動作ですか?
beforeRegister
が呼び出されるので、Polymerコンストラクターは何かを実行しています。 これで、HTMLのインポートが効果的に行われるようになりました。もちろん、テンプレートはwebcomponents-lite
ポリフィルで機能します。 カスタム要素のポリフィルがどのように機能しているかわかりません。
TestApp
クラスの中にready
またはcreated
メソッドを入れると、それらは呼び出されません。 ライフサイクルイベントが適切に処理されていないようです。 その問題の根本は、カスタム要素のポリフィルの実装にある可能性があります。 遊び続けます。
解決すべき潜在的な問題:
より多くのいじくり回しは、より多くの洞察につながります。 輸入と登録の順番がめちゃくちゃになっているのではないかと思います。 Polymerコンストラクターが呼び出された後にconst testApp = document.createElement('test-app');
を実行すると、 created
#$メソッドとready
メソッドが呼び出されますが、 attached
$メソッドは呼び出されません。 おそらく、jsdomはカスタム要素リテラルを正しく処理していませんか? また、 document.body.appendChild(testApp)
を呼び出す場合でも、 attached
ライフサイクルメソッドが呼び出されることはありません。
これは、読み込み順序を理解するのに役立つ場合があります: https ://github.com/webcomponents/webcomponentsjs#helper -utilities
@lastmjs私は現在、 CustomElementRegistry.define()
とdocument.registerElement()
の間でコインを投げています。 Domenicがいくつかの素晴らしい情報を提供し、whatwg(https://github.com/whatwg/html/issues/1329)に関連するいくつかの作業をマージしているのを見ました。APIの移行が行われているようです。 たとえば、仕様ではconnectedCallback
が呼び出され、これはattachedCallback
機能とペアになっていると思います。 また、ハンドラーはAPIの一部ではないため、 attached
と言ったときにattachedCallback
を意味していると仮定します。 define()
とregisterElement()
が、メソッドごとに異なるコールバックを起動するのを経験しました。 カスタム要素の戦略を理解しました。 XMLHTTPRequestパッチを使用する実装の前に言及されたHTMLImportsDomenic。 応答から直接DocumentFragment
に変換できると思います。 そのような匂いは、「輸入品」のスネークオイルである可能性があります。 「偽の」輸入品は正気が住んでいるところかもしれません。
ES6-> ES5からトランスパイルするときに、 super()
がHTMLElement
で呼び出されるという、いくつかの厄介な問題もあるようです。 私はRollup.js/Babelでこれを経験しており、webcomponentsパッケージの(軽量の)シムを使用することを余儀なくされました。
https://developers.google.com/web/fundamentals/getting-started/primers/customelements
最後に、プロトタイプタグを使用して作成すると、(より)成功するようです。
document.createElement('main', 'test-app')
@domenicが前に私に言ったように、GOOGLEが行うことだけでなく、最小公分母の仕様を実装することに注意したいと思います。 Webコンポーネントで線がぼやけているようです。 しかし、私はファンです。
どのような方法で取り組んできましたか?
これまでのところ、私は主にwebcomponents-lite
ポリフィルのみで遊んでおり、Polymer<2.0です。 したがって、 attached
メソッドについて言及したときは、 attachedCallback
の代わりに使用するPolymerライフサイクルメソッドを意味しました。 また、私が知る限り、ポリフィルはまだ新しいv1カスタム要素仕様に切り替えられていません。 したがって、私が遊んでいるのは、Polymerが現在のポリフィルで動作するようにすることだけを目的としています。
@snuggs現在ポリフィルを使用していますか、それともjsdomで実際の実装に取り組んでいますか?
@lastmjs 80%を取得する必要はないと感じているので、ポリフィルは使用しません。 プラットフォームは十分に成熟しているため、少し前もって調整するだけで、ネイティブ構造を使用できます。 フレームワークの代わりに軽量(通常は手巻き)ツールを使用するのが好きです。 それはほとんどの人ではないと言った。 Domenicの意図はカスタム要素👍htmlインポート👎のようですが、XMLHTTPRequestを拡張してドキュメントのfetching
を処理することに問題はありません。 それは約6ヶ月前のことです。 実装以来、多くの変更がありました。 おそらく考えています。 では、@ lastmjsはどこで終了しますか?
@snuggsおそらく最も健全で将来を見据えた方法は、jsdomでカスタム要素とShadowDOMのファーストクラスのサポートを実装することです。 どちらの標準もv1であり、私が聞いているところによると、主要なブラウザーの大部分がそれらを実装しているようです。 これについてはどうすればよいですか? 今は限られた時間ですが、少なくとも何をする必要があるかを説明できるかもしれません。 @domenicこれらの実装を進める方法、またはすべきでない理由について何か提案はありますか?
仕様を実装する以外に、私からの具体的な提案はありません:)
しばらく前にこれに取り組んだブランチがあります(それ以来、仕様が少し変更されています)。 CustomElementsRegistryの実装は非常に簡単で、カスタム要素のリアクションをコードベースに組み込む方法と、それらをいつどこから呼び出すかを理解するのに苦労しました。 私がこれを取り戻すとしたら(約束はありません)、おそらくそれが私が焦点を当てるでしょう。
@matthewpそれは役に立ちそうですが、そのブランチはどこにありますか?
@matthewpええそれはいいでしょう
https://github.com/matthewp/jsdom/commits/custom-elements先ほど言ったように、それ以降、仕様が変更されたため、古くなっています。 そして、これは最も簡単な部分ですが、誰かがそれを望むなら、それは出発点です。 @snuggs @lastmjs
(http://jonrimmer.github.io/are-we-componentized-yet/)
個人的に単にカスタム要素をサポートすることはすでに素晴らしいでしょう。
(私の理解では、phantomJS 2.5は、最新バージョンのWebkitに移行するため、少なくともテンプレートと、おそらくカスタム要素をサポートする必要があります。どちらかはわかりません)。
実際、libdocument -register-elementを使用してcustomElementsをモックします
const {before} = require('mocha')
before(mockDOM)
before(mockCustomElements)
function mockDOM() {
const {JSDOM: Dom} = require('jsdom')
const dom = new Dom('<!doctype html><html><body></body></html>')
global.document = dom.window.document
global.window = document.defaultView
window.Object = Object
window.Math = Math
}
function mockCustomElements() {
require('document-register-element/pony')(window)
}
素晴らしい、何か問題がありましたか?
今まで、いいえ:D
しかし、私はより多くのスペックを書き、気分を良くするためにより多くのものをカバーする必要があります
方法があるのを見るのは素晴らしい。 私がポリマーが好きなのと同じくらい、テストsetuは地獄であり、フォールバックとしてjsdomを持っていることは素晴らしいです;)作業を入れてくれてありがとう
これを前進させるPRがあるようです! https://github.com/tmpvar/jsdom/pull/1872
実際、lib document-register-element @darlanmendoncaを使用して、customElementsをモックします。
jsdomグローバルをノードグローバルにアタッチすることについてのこのリンクを読む必要があります。 これはアンチパターンです。
こんにちは皆さん、
JSDOM内でPolymerを実行している状況(Node.js6.7.0およびJSDOM11.1.0を使用)に関して少し混乱しています。 私はさまざまなことを試しましたが、結果はまちまちでした。 誰かが私をここに記入してくれたら本当にありがたいです...
私がこれまでにしたこと:
1)ルートディレクトリからhttpサーバーを起動しました
./node_modules/http-server/bin/http-server --cors
2)ポリマーコンポーネントの1つをJSDOMにロードしました。
jsdom.JSDOM.fromURL("http://localhost:8080/path/to/my-component.html",
{ runScripts: "dangerously",
resources: "usable"
})
.then(function (dom) {
setTimeout(() => {
window = dom.window;
component = window.document.querySelector("my-component");
}, 10000);
})
(ファイルシステムからコンポーネントファイルをロードしようとしましたが、同じ結果になりました。)
3)これは私のコンポーネントコードです:
<!DOCTYPE html>
<html>
<head>
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
</head>
<body>
<link rel="import" href="/bower_components/polymer/polymer.html">
<dom-module id="order-app">
<template>
<h1>Hello Polymer</h1>
</template>
<script>
console.log("javascript is being executed");
addEventListener('WebComponentsReady', function () {
console.log("web components are ready");
Polymer({
is: 'order-app'
});
});
</script>
</dom-module>
</body>
</html>
(WebコンポーネントのポリフィルをロードするためにHTMLヘッドを追加しました。)
何を観察できますか?
これを実行すると、
見えないもの
これにより、WebComponentsReadyイベントが発生していないという結論に至ります(おそらくHTMLインポートが機能しないためですか?)。 また、
window.WebComponents
には{ flags: { log: {} } }
$が含まれています- ready
インジケーターがありません。
また、モックとポリフィリングも試しました。
window.Object = Object;
window.Math = Math;
require('document-register-element/pony')(window);
しかし、それは何も変わらなかったようです。
今、私は疑問に思っています:-)これはまったく機能するはずですか? もしそうなら、なぜそれは私のために機能しないのですか? そうでない場合、それを機能させるために何が欠けている/必要ですか?
洞察をありがとう!
Moin
私はそれを試してみましたが、成功はさらに少なく、ここでのこの議論の結果がどうなるかを待つのをあきらめました。
https://github.com/sebs/noframework/blob/master/test/configurator.js
解決策ではなく、別の失敗した試みです。 ところで同じ混乱。 同じ結論も
jsdomでカスタム要素をポリフィルすることは、非常に困難であることが証明されています。 誰かがそれをjsdomに実装する際の課題をリストできますか? それを取り入れるための努力のレベルを評価しようとしています。
基本的な障害は、jsdomがコンストラクターとそのプロトタイプを共有することです。
HTMLElement
コンストラクターはすべてのウィンドウで共有されるため、これにより、ウィンドウごとのカスタム要素レジストリを実装することは基本的に不可能になります。 したがって、カスタム要素コンストラクターでsuper()
呼び出しを行うと、現在実行中のHTMLElement
コンストラクターは、どのウィンドウで検索するかを認識しません。これは最悪です。
良い中間ソリューションがあるかどうかはわかりません。 大きな銃は、jsdomを非共有コンストラクター/プロトタイプを許可するアーキテクチャーに移行することです。 これはいくつかの方法で行うことができますが、すべて異なるトレードオフがあります。 チームやコミュニティと話し合うための専用の問題を開きたいと思うかもしれませんが、今のところ、頭の中で頭に浮かぶものをリストアップしましょう。
[WebIDL2JSFactory]
を使用するか、少なくともHTMLElement
とそのすべての子孫を使用します。 [WebIDL2JSFactory]
が継承でもうまく機能するかどうかはまだわかりませんが、機能させることはできます。 この代替手段により、すべての人が追加のコンストラクター/プロトタイプのコストを支払うことになりますが、カスタム要素をオプトイン機能にするよりもおそらくそれが優れています。vm
サンドボックス内のすべてのクラス定義モジュールを実行するオプションがあります。 たとえば、jsdomですべてのWeb APIをバンドルするビルドステップがあり、新しいウィンドウを作成するときに、新しいサンドボックスグローバル内でそのバンドルを使用してvm.runScript()
を実行します。 これにより、おそらく[WebIDL2JSFactory]
を取り除くことができます。別の解決策は、カスタム要素レジストリがNode.jsプロセスごとにグローバルであるという巨大な警告とともにカスタム要素を実装することだと思いますか? しかし、それはひどいようです。
その最初のハードルの後、残りは仕様に従うという点で比較的簡単です。 最も難しい部分は、おそらく[CEReactions]
を実装し、すべてのIDLファイルを更新して適切な場所に配置することですが、それほど難しくはありません。
別のプロトタイプバージョンも考えていました。 これが私の考えの一部です。
[WebIDL2JSFactory]
が継承でもうまく機能するかどうかはまだわかりませんが、機能させることはできます。
いいえ、そうではありません。正確にどのように機能させるかはわかりません。 私の意見では、2番目の解決策ははるかに簡単です。
jsdomが
vm
サンドボックス内のすべてのクラス定義モジュールを実行するオプションがあります。
これが私が好むものです。 主な問題は、初期化中にimplクラスをvm
サンドボックスに渡すことですが、これは、外部コンテキストからすべてを1つのグローバルプロパティに入れ、完了後にそのグローバルプロパティをdelete
することで実行できます。 。 また、 [NamedConstructor]
と他のいくつかの拡張属性を適切に実装でき、誰かが大胆に対応している場合は、jsdom環境用のV8スタートアップスナップショットを生成することもできます。
そもそも[WebIDL2JSFactory]
のビジネス全体がハックだったので、できるだけ早くそれを取り除きたいと思っています。
+1コメントはこの機能の開発には役立たないため、最近のコメントを少なくとも1つ削除します。
こんにちは、 @TimothyGuがこれに取り組んでいることに気づいていませんでした。
私は実際にカスタム要素の登録と作成を行っています
https://github.com/mraerino/jsdom/tree/custom-elements-spec
私はできるだけ侵襲性を最小限に抑え、できるだけ仕様に近づけるように努めています。
カスタム要素レジストリのWebプラットフォームテストに合格しています。
この昨夜のハッキング中に、webIdl2JSを変更せずに機能するソリューションを見つけました。
ここを参照してください: https ://github.com/mraerino/jsdom/commit/592ad1236e9ca8f63f789d48e1887003305bc618
@TimothyGuあなたはこれに力を合わせても構わないと思いますか?
ここでいくつかの更新:
私は仕様の実装にかなり自信がありますが、 [HTMLConstructor]
拡張IDL属性のために現在行き詰まっています。 そのため、 https://github.com/jsdom/webidl2js/issues/87を開きました
それまでの間、後で簡単に切り替えることができるように、 [Constructor]
属性を使用して[HTMLConstructor]
アルゴリズムを実装します。 (最初は、模擬HTMLElementクラスをwindow
に挿入して実装しましたが、これは正しくないようでした。)
ええ、 https: //github.com/tmpvar/jsdom/issues/1030#issuecomment -333994158に記載されているように、HTMLConstructorを正しく実装するには、jsdomのアーキテクチャに根本的な変更を加える必要があります。
お使いのバージョンが合格しているWebプラットフォームテストの数に関する情報はありますか?
今のところcustomElementRegistryのものだけで、進捗状況については完全に間違っている可能性があります。
編集:わかりました、あなたのコメントを読み直した後、私はあなたが意味することを理解しました。 私の実装で試してみますが、 @TimothyGuも分離に取り組んでいるようです。
私はPolymerを使用しているので、このリクエスト機能で:+ 1:です
@ [email protected]開発者も同じです。 SlimはネイティブWebコンポーネントAPIを使用しており、jsdomをハックせずにHTMLElementを継承することはできません。
この号が発行されてから3年が経過しました。 ほぼjsdomがカスタム要素をサポートする時期を誰かが言うことができますか?
私は過去2週間にわたって、jsdomにカスタム要素のサポートを追加する可能性の評価に取り組みました。 調査結果はこちらです。
カスタム要素の仕様に準拠した実装は、 https ://github.com/jsdom/jsdom/compare/master...pmdartus:custom-elements?expand=1にあります。 あちこちにまだ荒削りな部分がありますが、少なくともほとんどのWPTテストに合格しています。 残りの失敗したテストは、既知のJSDOMの問題か、jsdomでの実際の実装に取り組むときに取り組むことができるマイナーな問題のいずれかです。
これが朗報です。ShadowDOMがネイティブでサポートされており、カスタム要素ブランチとミューテーションオブザーバーの両方で、最新バージョンのPolymer 3helloworldアプリケーションの例をjsdomでロードしてレンダリングすることができました🎉。 現在の状態では、ブランチはステンシルアプリケーションをロードできません(ステンシル開発モードにはmodule
などのサポートされていない機能が必要であり、prodモードは不明な理由でスローされます)。
これは、実際のカスタム要素仕様の実装に取り組み始める前に、最初に行う必要のある変更のリストです。 リストの各項目は独立しており、並行して取り組むことができます。
[CEReactions]
IDL拡張属性のサポートカスタム要素のサポートを追加するためにjsdomに機能的に欠けているコアの1つは、 [CEReactions]
属性です。 適切なプロトタイププロパティにパッチを適用することで、この問題を部分的に回避することができました。 このアプローチは、カスタム要素のリアクションスタックがグローバルであり、関連する類似オリジンのブラウジングコンテキストの単位ごとではない限り機能します。これは、すべてのインターフェイスのプロトタイプが共有されているためです。
一部のインターフェイスにはインデックス付きプロパティ( HTMLOptionsCollection
、 DOMStringMap
)に関連付けられた[CEReactions]
属性があるため、このアプローチにはいくつかの欠点があります。 内部的には、jsdomはプロキシを使用してこれらのプロパティへの変更を追跡します。 この場合、インターフェイスのプロトタイプパッチは機能しません。 この問題を回避するための別のアプローチは、インターフェースではなく実装にパッチを適用することです(実装されていません)。
私はwebidl2jsの内部に精通していませんが、 [CEReactions]
のグローバルフックを追加することを検討する必要がありますか?
変更点:
ce-reaction.js
:[HTMLConstructor]
IDL拡張属性のサポート@domenicが上で説明したように、 [HTMLConstructor]
のサポートを追加することは、ここでの主要なブロッカーの1つです。
ここでは、ブラウジングコンテキストごとにインターフェイスコンストラクターにパッチを適用することで、この問題を回避することができました。 インターフェイスコンストラクターは、共有プロトタイプを維持しながら、適切なウィンドウとドキュメントオブジェクトにアクセスできます。 このアプローチにより、新しいブラウジングコンテキストごとにインターフェイスプロトタイプを再評価することによるパフォーマンスのオーバーヘッドも回避されます。
ここでそれが最善のアプローチであるかどうかはわかりませんが、追加のパフォーマンスオーバーヘッドを導入することなく要件に適合します。
変更点:
ここで説明したように、 Element.innerHTML
およびElement.outerHTML
で使用されているHTMLフラグメント解析アルゴリズムの実装は正しくありません。 カスタム要素のリアクションコールバックが適切に呼び出されるようにするには、解析アルゴリズムをリファクタリングする必要があります。
変更点:
私がすぐに遭遇した問題の1つは、カスタム要素作成のサポートを追加するときに新しい循環依存関係が導入されたことです。 CustomElementRegistryとcreateelementアルゴリズムはどちらも、Elementインターフェースへのアクセスを必要とし、循環依存の悪夢を生み出します。
ブランチで採用されたアプローチは、 InterfaceCache
を作成することでした。これにより、要素の名前空間と名前だけでなく、インターフェイス名によるインターフェイスのルックアップが可能になります。 インターフェイスモジュールは遅延評価され、評価されるとキャッシュされます。 このアプローチでは、最上位レベルでインターフェースが不要になったため、循環依存関係がなくなります。
これは、jsdomのこの長年の問題を解決するための1つのアプローチです。このアプローチのこの問題の1つは、jsdomのWebパック/ブラウザー化バージョン(テストされていない)を壊す可能性があることです。
変更点:
Element.isConnected
を修正します〜これは、要素がシャドウツリーの一部である場合、isConnectedがfalse
返すシャドウDOMの導入に伴う問題です。 この動作をチェックするテストはないため、ここに新しいWPTテストを追加する必要があります。
変更点:
HTMLTemplateElement.templateContents
ノードドキュメントを修正しました(#2426)仕様で定義されているテンプレートコンテンツには、HTMLTemplateElement自体とは異なるノードドキュメントがあります。 jsdomは現在この動作を実装しておらず、HTMLTemplateElementとそのテンプレートコンテンツは同じものを共有しています
ドキュメントノード。
変更点:
HTMLTemplateElement-impl.js
htmltodom.js
。 この変更は、パーサーにもダウンストリームの影響を及ぼします。 コンテキスト要素がHTMLTemplateElementの場合、HTMLフラグメント解析アルゴリズムは、要素自体からではなく、テンプレートコンテンツからドキュメントノードを取得する必要があります。HTMLTemplateElement
に追加(#2426)HTMLTemplateElement
は、別のドキュメントに採用されるときに、いくつかの特定の手順を実行する必要があります。 私の知る限り、特別な養子縁組のステップがあるのは土壌の境界面です。 この採用ステップを呼び出すには、採用ノードアルゴリズムの実装も更新する必要があります。
変更点:
シリアル化されたHTMLフラグメントは、要素をシリアル化するときに、要素に関連付けられたis値を検索し、シリアル化されたコンテンツの属性として反映します。 parse5ツリーアダプタに別のフックを追加すると、要素getIsValue(element: Element): void | string
に関連付けられたis値が検索されます。
別のアプローチ(実装されていない)は、要素に関連付けられたis値がある場合、現在のgetAttrList
フックの動作を変更してis値を属性リストに返すことです。
パフォーマンスの最適化を行う前に、ブランチでの変更のパフォーマンスも確認したいと思いました。 カスタム要素を追加すると、ツリーミューテーションベンチマークのマスターでの現在の結果と比較して、10%のパフォーマンスオーバーヘッドが追加されます。 ただし、新しいJSDOM環境の作成は、マスターと比較して3倍から6倍遅くなりました。根本的な原因を特定するには、より詳細な調査が必要になります。
詳細:こちら
@pmdartusこれは非常に有望で、素晴らしい仕事です! より良いオプションがないため、ハックブランチjsdom-wcを使用しています。 私はいくつかの奇妙な振る舞いを見ていて、あなたのブランチと交換することを望んでいましたが、私は問題にぶつかっています。
次のようなカスタム要素を登録します。
class Component extends HTMLElement {
}
customElements.define('custom-component', Component);
しかし、私がそうする場合:
const el = assign(this.fixture, {
innerHTML: `
<custom-component></custom-component>
`,
});
すぐに取得します: Error: Uncaught [TypeError: Illegal constructor]
。
これについて何か考えはありますか?
次のコードスニペットは、フォークのcustom-elements
ブランチで正しく実行されます: https ://github.com/pmdartus/jsdom/tree/custom-elements
const { JSDOM } = require("jsdom");
const dom = new JSDOM(`
<body>
<div id="container"></div>
<script>
class Component extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = "<p>Hello world</p>";
}
}
customElements.define('custom-component', Component);
const container = document.querySelector("#container");
Object.assign(container, {
innerHTML: "<custom-component></custom-component>"
})
console.log(container.innerHTML); // <custom-component></custom-component>
console.log(container.firstChild.shadowRoot.innerHTML); // <p>Hello world</p>
</script>
</body>
`, {
runScripts: "dangerously"
});
不正なコンストラクターは、おそらく元のHTMLElementコンストラクターによってスローされます。ブランチで行われた変更により、新しいウィンドウオブジェクトごとにコンストラクターにパッチが適用されます。 @tbranyenローカルで試すことができるように、完全な複製の例がありますか?
こんにちは@pmdartus何が私の問題を引き起こしているのかまだよくわかりませんが、完全に機能するいくつかの分離されたコードをブランチに直接書きました:
const { JSDOM } = require('.');
const window = (new JSDOM()).window;
const { HTMLElement, customElements, document } = window;
class CustomElement extends HTMLElement {
constructor() {
super();
console.log('constructed');
}
connectedCallback() {
console.log('connected');
}
}
customElements.define('custom-element', CustomElement);
document.body.appendChild(new CustomElement());
//constructed
//connected
{
const window = (new JSDOM()).window;
const { HTMLElement, customElements, document } = window;
class CustomElement extends HTMLElement {
constructor() {
super();
console.log('Constructed');
}
connectedCallback() {
console.log('Connected');
}
}
customElements.define('custom-element', CustomElement);
document.body.appendChild(new CustomElement());
//constructed
//connected
}
これは事実上私のテストシステムが行うことですが、壊れます。 だからそれは私の側の何かかもしれません。
編集:
ああ、問題が発生する可能性が最も高い場所を絞り込んだと思います。 作成された最初のHTMLElement
コンストラクターを保持している必要があります。 上記のコードを調整してコンストラクターを再利用する場合:
// Inside the block, second component, reuse the HTMLElement.
const { customElements, document } = window;
これにより、次のようになります。
connected
/home/tbranyen/git/pmdartus/jsdom/lib/jsdom/living/helpers/create-element.js:643
throw new TypeError("Illegal constructor");
編集2:
それを見つけた:
// Don't reuse the previously defined Element...
global.HTMLElement = global.HTMLElement || jsdom.window.HTMLElement;
このスレッドが4年前のものであることに気づきましたが、Webコンポーネントはサポートされていますか、またはサポートされる予定ですか?
これにWebコンポーネントがあると便利ですが、誰かが知りたい場合の代替手段として....ヘッドレスクロームをノードで使用して、htmlstingファイルをレンダリング/ビルドできるようになりました。
このスレッドが4年前のものであることに気づきましたが、Webコンポーネントはサポートされていますか、またはサポートされる予定ですか?
仕様が1つずつ実装されているため、作業が進行中です。
https://github.com/WebReflection/document-register-elementのポリフィルは魅力のように機能します! 著者に心から感謝します!
同じ問題に苦しんでいる人は、次のようにしてください。
npm install -D document-register-element
jest構成で、すべてのテストの前に実行されるセットアップファイルを設定します。
{
"setupFilesAfterEnv": [
"./tests/setup.js"
]
}
そして最後に、そのファイル('tests / setup.js')内:
import 'document-register-element'
これを行った後、document.createElement('custom-component')を介してjsdomにカスタム要素を登録および作成することは完全に機能します! ただし、フラグメントは機能していないようです。 (ちなみに、私はshadow domを使用していません)。
@tebanepは、polyfillはほとんどのWebコンポーネントの作業には不適切であると述べたように、Shadow DOMをサポートしていない場合、これが達成していることと実際に比較することはできません。
@tebanepありがとう。 Shadow DOMは必要ないので、これは素晴らしい情報です
これが実装されることを期待していますか? 現在、私たちはjsdom-wcを使用しており、多くのバグがありますが、これ以上の解決策はありません。 このトピックについての私の希望と祈り。
@dknight jsdom-wcは、ちょっとした機能を実現するためのハックです。 私の個人的なnpmスコープの下で、互換性が大幅に向上したモジュールを公開しました。 次のコマンドでインストールできます。
npm install @tbranyen/[email protected] --save-dev
安定した状態になるまで、JSDOMWebコンポーネントのすべてのニーズにこれを使用します。
@tbranyenフォークを非公開にしましたか? npmで見つかりません。
@KingHenne dangit、「エンタープライズ」レジストリに登録されたようです。 公開npmに公開しました。 申し訳ありません!
@ meしないでください。ただし、実際のブラウザで、たとえばpuppeteerを使用してWebUIコードをテストするだけではいけません。 シャドウDOM/カスタム要素のサポートの問題はなくなります。
@Georgegriffになりたくない場合は、コメントを投稿しないでください。 これは有効な戦略ですが、パペッティアを使用している場合でも、IPCを効果的に実行しているため、他の点では遅く、バグがあります。 ブラウザが停止すると、多くの場合、その理由は明らかではありません。 冗談でパペッティアの問題をデバッグしてみて、それが常に最良のアイデアであるとは限らない理由を理解してください。
個人的には、同期して同じスレッドでテストを続けたいと思います。 仕様の分離された実装がコンポーネントをテストするための合理的なランタイムであってはならない理由はありません。 JSDOMは、現時点では事実上ブラウザであり、ビッグ3ほど安定していません。
https://github.com/WebReflection/document-register-elementのポリフィルは魅力のように機能します! 著者に心から感謝します!
同じ問題に苦しんでいる人は、次のようにしてください。
npm install -D document-register-element
jest構成で、すべてのテストの前に実行されるセットアップファイルを設定します。
{ "setupFilesAfterEnv": [ "./tests/setup.js" ] }
そして最後に、そのファイル('tests / setup.js')内:
import 'document-register-element'
これを行った後、document.createElement('custom-component')を介してjsdomにカスタム要素を登録および作成することは完全に機能します! ただし、フラグメントは機能していないようです。 (ちなみに、私はshadow domを使用していません)。
私にとっては問題なく動作しますが、 connectedCallback
が呼び出されることはありません、何か考えはありますか?
@FaBeyyy私も同じです:(
@ FaBeyyy @ majo44つまり、コンポーネントをドキュメントに追加する必要があります。 $# connectedCallback
が解雇されるためのdocument.body.appendChild(...)
。 仕様により、コンポーネントがDomに接続されているときに呼び出されます。
JSDOMは、現時点では事実上ブラウザであり、ビッグ3ほど安定していません。
現時点では、MicrosoftはWindowsと同じくらい長い間彼らと一緒にいた彼らを捨てているので、それはより大きな2つに似ています。
@ FaBeyyy @ majo44つまり、コンポーネントをドキュメントに追加する必要があります。 $#
connectedCallback
が解雇されるためのdocument.body.appendChild(...)
。 仕様により、コンポーネントがDomに接続されているときに呼び出されます。
キャプテンに感謝しますが、それはもちろんここでは問題ではありません。 コンポーネントのライフサイクルがどのように機能するかを知らなかった場合、おそらくテストを作成しようとはしなかったでしょう😄。 後で時間を見つけたら、リポジトリのショーケースを作成します。
@FaBeyyy
だから私は自分に合ったセットアップを見つけました。 MutationObserverにポリフィルを追加する必要がありました。 Jestを使用してネズミイルカをテストするためにJSDOMを使用していますが、動作するセットアップは次のとおりです。
// package.json
{ ...
"jest": {
"transform": {
"^.+\\.(mjs|jsx|js)$": "babel-jest"
},
"setupFiles": [
"<rootDir>/node_modules/babel-polyfill/dist/polyfill.js",
"<rootDir>/node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
"<rootDir>/node_modules/document-register-element/build/document-register-element.node.js"
]
}
...
}
//.bablerc
{
"presets": [
["@babel/preset-env", { "modules": "commonjs"}]
]
}
@FaBeyyy
だから私は自分に合ったセットアップを見つけました。 MutationObserverにポリフィルを追加する必要がありました。 Jestを使用してネズミイルカをテストするためにJSDOMを使用していますが、動作するセットアップは次のとおりです。// package.json { ... "jest": { "transform": { "^.+\\.(mjs|jsx|js)$": "babel-jest" }, "setupFiles": [ "<rootDir>/node_modules/babel-polyfill/dist/polyfill.js", "<rootDir>/node_modules/mutationobserver-shim/dist/mutationobserver.min.js", "<rootDir>/node_modules/document-register-element/build/document-register-element.node.js" ] } ... }
//.bablerc { "presets": [ ["@babel/preset-env", { "modules": "commonjs"}] ] }
よかった、ありがとう!
@ majo44これは、最新のjsdomでは必要ありません。 Jest(jsdom v11を使用している)を使用する場合は、更新された環境を使用できます: https ://www.npmjs.com/package/jest-environment-jsdom-fourteen
@mgibasありがとう、jest-environment-jsdom-fourteenを使用すると、正常に動作し、ミューテーションオブザーバーのポリフィルは必要ありません(ただし、バージョンは0.1.0、シングルコミットパッケージ:))
現在JSDOMでサポートされているWebコンポーネントAPIの内訳はありますか? Shadow DOMはサポートされているようですが、カスタム要素はサポートされていません(少なくともリリースブランチ/リポジトリでは)?
npm install @tbranyen/[email protected] --save-dev
@tbranyenフォークのソースコードはどこかにありますか? 差分を見てみたいと思います🙂
@ majo44が提案するようなjest-environment-jsdom-fifteenと、babel-polyfillおよびdocument-register-elementを使用しています( @mgibasの回答を参照)。 しかし、テストのためにWebコンポーネントのシャドウDOMを取得しようとすると、エラーが発生します。
el.shadowRoot
はnullです:
const el;
beforeEach(async() => {
const tag= 'my-component'
const myEl = document.createElement(tag);
document.body.appendChild(myEl);
await customElements.whenDefined(tag);
await new Promise(resolve => requestAnimationFrame(() => resolve()));
el = document.querySelector(tag);
}
it(() => {
const fooContent = el.shadowRoot.querySelectorAll('slot[name=foo] > *');
})
回避策のアイデアはありますか? 参考までに、それはすでにカルマ、モカ、チャイ、ジャスミンでテストされています。
私のコンポーネントには特別なことは何もありません:
customElements.define(
'my-component',
class extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
...
}
})
編集
問題をよりよく理解するために、jsdom15.1.1を使用してデバッグを行いました。
それでも、なぜここでnullになるのかわかりません...
したがって、Element.shadowRootは88e72ef以降に実装されています
https://github.com/jsdom/jsdom/blob/1951a19d8d40bc196cfda62a8dafa76ddda6a0d2/lib/jsdom/living/nodes/Element-impl.js#L388 -L415
document.createElementの後、this._shadowDomはhttps://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/living/nodes/Element-impl.js#L403で問題ありません。 そして、shadow dom内のすべての要素が作成されます(正しい値で呼び出される要素コンストラクター)。
しかし、document.body.appendChild(el)の直後にel.shadowDom
を呼び出すと、(https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/living/nodes/Element-impl .js#L408)、 this. _shadowRoot
はnullです!
後も同じ
await customElements.whenDefined(tag);
await new Promise(resolve => requestAnimationFrame(() => resolve()));
または、ドキュメントの代わりに以下を使用しても。
document.body.innerHTML = `
<my-component id="fixture"></my-component>
`:
複製については、以下を参照してください。
https://github.com/noelmace/devcards/tree/jest
@nminhnguyen@tbranyanによって作成されたフォークのソースコードはここにあると思いますhttps://github.com/tbranyen/jsdom/tree/initial-custom-elements-impl
lit-htmlとlit-elementで作成されたWebコンポーネントをテストしようとしていますが、要素を作成するときにこの違いに気づきました。
const myElem = new MyElem();
document.body.appendChild(myElem);
await myElem.updateComplete;
console.log(myElem.shadowRoot) // exists and has the rendered markup
document.createElementを使用するとき
const myElem = document.createElement('my-elem');
document.body.appendChild(myElem);
await myElem.updateComplete;
console.log(myElem.shadowRoot) // null
jestを構成するために、次の1つのポリフィルのみを使用します: setupFiles: ['document-register-element']
myElem
のrenderメソッドが呼び出されないようです。 もう少しデバッグすると、lit-elementにあるメソッドinitialize
が呼び出されないことがわかりました。
したがって、2番目の例は私が行う場合に機能します
const myElem = document.createElement('my-elem');
myElem.initialize();
document.body.appendChild(myElem);
await myElem.updateComplete;
console.log(myElem.shadowRoot) // exists and has the rendered markup
Webコンポーネントをサポートする代替DOMを作成しました。 私は最初にPRを試みましたが、JSDOMの仕組みにより、そこでのニーズを解決するのが困難でした。 あなたはそれを自由に使うか、コードを見ることができます。
DOM:
https://www.npmjs.com/package/happy-dom
ジェスト環境:
https://www.npmjs.com/package/jest-environment-happy-dom
すごいですね。 ありがとう。
2019年10月7日月曜日、午前1時8分[email protected]は次のように書いています。
Webコンポーネントをサポートする代替DOMを作成しました。 私が最初に
PRを作ろうとしましたが、JSDOMの仕組みが原因で、自分の問題を解決するのが困難でした。
そこに必要です。 あなたはそれを自由に使用したり、コードを見たりすることができます。DOM:
https://www.npmjs.com/package/happy-domジェスト環境:
https://www.npmjs.com/package/jest-environment-happy-dom—
このスレッドにサブスクライブしているため、これを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/jsdom/jsdom/issues/1030?email_source=notifications&email_token=ACQ5ZD5QUEITPND4SXWOHW3QNILSRA5CNFSM4A4G5SF2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/ACQ5ZDYU465DXI4KHBQH4KTQNILSRANCNFSM4A4G5SFQ
。
@ capricorn86
あなたの仕事は私のテスト環境をシンプルにしてくれます、ありがとう!
https://github.com/EasyWebApp/WebCell/tree/v2/MobX
@ capricorn86
あなたの仕事は私のテスト環境をシンプルにしてくれます、ありがとう!
https://github.com/EasyWebApp/WebCell/tree/v2/MobX
ありがとう@TechQuery!
すごいですね。 ありがとう。
…
2019年10月7日月曜日、午前1時8分capricorn86 @ 。 * >書き込み:Webコンポーネントをサポートする代替DOMを作成しました。 私は最初にPRを試みましたが、JSDOMの仕組みにより、そこでのニーズを解決するのが困難でした。 あなたはそれを自由に使用したり、コードを見たりすることができます。 DOM: https ://www.npmjs.com/package/happy-dom Jest環境: https ://www.npmjs.com/package/jest-environment-happy-dom —これを購読しているため、これを受け取っています糸。 このメールに直接返信、GitHubの上でそれを見る<#1030?email_source =通知&email_token = ACQ5ZD5QUEITPND4SXWOHW3QNILSRA5CNFSM4A4G5SF2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAOO5ZA#issuecomment-538767076>、またはスレッドミュートhttps://github.com/notifications/unsubscribe-auth/ACQ5ZDYU465DXI4KHBQH4KTQNILSRANCNFSM4A4G5SFQを。
ありがとう@motss!
現在JSDOMでサポートされているWebコンポーネントAPIの内訳はありますか? Shadow DOMはサポートされているようですが、カスタム要素はサポートされていません(少なくともリリースブランチ/リポジトリでは)?
私もこれに興味があります:)
最も参考になるコメント
TL; DR
私は過去2週間にわたって、jsdomにカスタム要素のサポートを追加する可能性の評価に取り組みました。 調査結果はこちらです。
カスタム要素の仕様に準拠した実装は、 https ://github.com/jsdom/jsdom/compare/master...pmdartus:custom-elements?expand=1にあります。 あちこちにまだ荒削りな部分がありますが、少なくともほとんどのWPTテストに合格しています。 残りの失敗したテストは、既知のJSDOMの問題か、jsdomでの実際の実装に取り組むときに取り組むことができるマイナーな問題のいずれかです。
これが朗報です。ShadowDOMがネイティブでサポートされており、カスタム要素ブランチとミューテーションオブザーバーの両方で、最新バージョンのPolymer 3helloworldアプリケーションの例をjsdomでロードしてレンダリングすることができました🎉。 現在の状態では、ブランチはステンシルアプリケーションをロードできません(ステンシル開発モードには
module
などのサポートされていない機能が必要であり、prodモードは不明な理由でスローされます)。行動計画
これは、実際のカスタム要素仕様の実装に取り組み始める前に、最初に行う必要のある変更のリストです。 リストの各項目は独立しており、並行して取り組むことができます。
[CEReactions]
IDL拡張属性のサポートカスタム要素のサポートを追加するためにjsdomに機能的に欠けているコアの1つは、
[CEReactions]
属性です。 適切なプロトタイププロパティにパッチを適用することで、この問題を部分的に回避することができました。 このアプローチは、カスタム要素のリアクションスタックがグローバルであり、関連する類似オリジンのブラウジングコンテキストの単位ごとではない限り機能します。これは、すべてのインターフェイスのプロトタイプが共有されているためです。一部のインターフェイスにはインデックス付きプロパティ(
HTMLOptionsCollection
、DOMStringMap
)に関連付けられた[CEReactions]
属性があるため、このアプローチにはいくつかの欠点があります。 内部的には、jsdomはプロキシを使用してこれらのプロパティへの変更を追跡します。 この場合、インターフェイスのプロトタイプパッチは機能しません。 この問題を回避するための別のアプローチは、インターフェースではなく実装にパッチを適用することです(実装されていません)。私はwebidl2jsの内部に精通していませんが、
[CEReactions]
のグローバルフックを追加することを検討する必要がありますか?変更点:
ce-reaction.js
:[HTMLConstructor]
IDL拡張属性のサポート@domenicが上で説明したように、
[HTMLConstructor]
のサポートを追加することは、ここでの主要なブロッカーの1つです。ここでは、ブラウジングコンテキストごとにインターフェイスコンストラクターにパッチを適用することで、この問題を回避することができました。 インターフェイスコンストラクターは、共有プロトタイプを維持しながら、適切なウィンドウとドキュメントオブジェクトにアクセスできます。 このアプローチにより、新しいブラウジングコンテキストごとにインターフェイスプロトタイプを再評価することによるパフォーマンスのオーバーヘッドも回避されます。
ここでそれが最善のアプローチであるかどうかはわかりませんが、追加のパフォーマンスオーバーヘッドを導入することなく要件に適合します。
変更点:
create-element.js
makeフラグメント解析アルゴリズムを仕様に準拠させる(#2522)
ここで説明したように、
Element.innerHTML
およびElement.outerHTML
で使用されているHTMLフラグメント解析アルゴリズムの実装は正しくありません。 カスタム要素のリアクションコールバックが適切に呼び出されるようにするには、解析アルゴリズムをリファクタリングする必要があります。変更点:
createelementalgoのインターフェースルックアップを改善
私がすぐに遭遇した問題の1つは、カスタム要素作成のサポートを追加するときに新しい循環依存関係が導入されたことです。 CustomElementRegistryとcreateelementアルゴリズムはどちらも、Elementインターフェースへのアクセスを必要とし、循環依存の悪夢を生み出します。
ブランチで採用されたアプローチは、
InterfaceCache
を作成することでした。これにより、要素の名前空間と名前だけでなく、インターフェイス名によるインターフェイスのルックアップが可能になります。 インターフェイスモジュールは遅延評価され、評価されるとキャッシュされます。 このアプローチでは、最上位レベルでインターフェースが不要になったため、循環依存関係がなくなります。これは、jsdomのこの長年の問題を解決するための1つのアプローチです。このアプローチのこの問題の1つは、jsdomのWebパック/ブラウザー化バージョン(テストされていない)を壊す可能性があることです。
変更点:
create-element.js
〜Shadow DOM(https://github.com/jsdom/jsdom/pull/2424)をサポートするように
Element.isConnected
を修正します〜これは、要素がシャドウツリーの一部である場合、isConnectedが
false
返すシャドウDOMの導入に伴う問題です。 この動作をチェックするテストはないため、ここに新しいWPTテストを追加する必要があります。変更点:
Node-impl.js
HTMLTemplateElement.templateContents
ノードドキュメントを修正しました(#2426)仕様で定義されているテンプレートコンテンツには、HTMLTemplateElement自体とは異なるノードドキュメントがあります。 jsdomは現在この動作を実装しておらず、HTMLTemplateElementとそのテンプレートコンテンツは同じものを共有しています
ドキュメントノード。
変更点:
HTMLTemplateElement-impl.js
htmltodom.js
。 この変更は、パーサーにもダウンストリームの影響を及ぼします。 コンテキスト要素がHTMLTemplateElementの場合、HTMLフラグメント解析アルゴリズムは、要素自体からではなく、テンプレートコンテンツからドキュメントノードを取得する必要があります。不足している採用手順を
HTMLTemplateElement
に追加(#2426)HTMLTemplateElement
は、別のドキュメントに採用されるときに、いくつかの特定の手順を実行する必要があります。 私の知る限り、特別な養子縁組のステップがあるのは土壌の境界面です。 この採用ステップを呼び出すには、採用ノードアルゴリズムの実装も更新する必要があります。変更点:
HTMLTemplateElement-impl.js
Document-impl.js
parse5シリアライザーでisValueルックアップのサポートを追加
シリアル化されたHTMLフラグメントは、要素をシリアル化するときに、要素に関連付けられたis値を検索し、シリアル化されたコンテンツの属性として反映します。 parse5ツリーアダプタに別のフックを追加すると、要素
getIsValue(element: Element): void | string
に関連付けられたis値が検索されます。別のアプローチ(実装されていない)は、要素に関連付けられたis値がある場合、現在の
getAttrList
フックの動作を変更してis値を属性リストに返すことです。パフォーマンス
パフォーマンスの最適化を行う前に、ブランチでの変更のパフォーマンスも確認したいと思いました。 カスタム要素を追加すると、ツリーミューテーションベンチマークのマスターでの現在の結果と比較して、10%のパフォーマンスオーバーヘッドが追加されます。 ただし、新しいJSDOM環境の作成は、マスターと比較して3倍から6倍遅くなりました。根本的な原因を特定するには、より詳細な調査が必要になります。
詳細:こちら