Jsdom: WebComponents API 지원

에 만든 2015년 02월 17일  ·  89코멘트  ·  출처: jsdom/jsdom

React core(http://facebook.github.io/react/)의 단위 테스트에 jsdom을 사용하려고 합니다.

불행히도 웹 구성 요소 사양은 기본적으로 jsdom에서 지원되지 않으며 webcomponents.js 폴리필은 jsdom에서 실행되지 않습니다. 이 문제는 WebComponent 지원(사용자 정의 요소, 섀도우 돔, html 가져오기 등) 추가를 요청합니다.

feature

가장 유용한 댓글

TL;DR

저는 지난 2주 동안 jsdom에서 사용자 정의 요소에 대한 지원을 추가하는 가능성을 평가하는 작업을 했습니다. 다음은 조사 결과입니다.

사용자 지정 요소의 사양 준수 구현은 https://github.com/jsdom/jsdom/compare/master...pmdartus :custom-elements?expand=1에서 찾을 수 있습니다. 여전히 여기 저기에 거친 가장자리가 있지만 적어도 대부분의 WPT 테스트는 통과 합니다. 나머지 실패한 테스트는 알려진 JSDOM 문제이거나 jsdom에서 실제 구현을 다룰 때 다룰 수 있는 사소한 문제입니다.

이제 좋은 소식이 있습니다. 이제 Shadow DOM이 기본적으로 사용자 정의 요소 분기와 돌연변이 관찰자와 함께 지원되므로 jsdom 🎉에서 최신 버전의 Polymer 3 hello world 애플리케이션 예제를 로드하고 렌더링할 수 있었습니다. 현재 상태에서 분기는 스텐실 애플리케이션을 로드할 수 없습니다(스텐실 개발 모드에는 module 와 같은 일부 지원되지 않는 기능이 필요하고 prod 모드는 알 수 없는 이유로 발생합니다).

행동 계획

다음은 실제 사용자 지정 요소 사양 구현을 시작하기 전에 먼저 발생해야 하는 변경 사항의 목록입니다. 목록의 각 항목은 독립적이며 병렬로 처리할 수 있습니다.

[CEReactions] IDL 확장 속성 지원

사용자 정의 요소에 대한 지원을 추가하기 위해 jsdom에서 기능적으로 누락된 핵심 중 하나는 [CEReactions] 속성입니다. 올바른 프로토타입 속성을 패치하여 이 문제를 부분적으로 해결할 수 있었습니다. 이 접근 방식은 모든 인터페이스 프로토타입이 공유되기 때문에 사용자 정의 요소 반응 스택이 전역적이며 관련된 유사 출처 브라우징 컨텍스트 단위가 아닌 한 작동합니다.

일부 인터페이스에는 인덱싱된 속성( HTMLOptionsCollection , DOMStringMap )과 연결된 [CEReactions] 속성이 있기 때문에 이 접근 방식에는 몇 가지 단점이 있습니다. 내부적으로 jsdom은 프록시를 사용하여 해당 속성에 대한 변형을 추적합니다. 이 경우 인터페이스의 프로토타입 패치가 작동하지 않습니다. 이 문제를 해결하기 위한 또 다른 접근 방식은 인터페이스(구현되지 않음) 대신 구현을 패치하는 것입니다.

나는 webidl2js 내부에 대해 충분히 익숙하지 않지만 [CEReactions] 에 대한 전역 후크를 추가하는 방법을 탐색해야 합니까?

변경 사항:

[HTMLConstructor] IDL 확장 속성 지원

위에서 @domenic이 설명 했듯이 [HTMLConstructor] 에 대한 지원을 추가하는 것은 여기에서 주요 방해 요소 중 하나입니다.

여기에서 각 탐색 컨텍스트에 대한 인터페이스 생성자를 패치하여 이 문제를 해결할 수 있었습니다. 인터페이스 생성자는 공유 프로토타입을 유지하면서 올바른 창과 문서 개체에 액세스할 수 있습니다. 이 접근 방식은 또한 각각의 새로운 브라우징 컨텍스트에 대해 인터페이스 프로토타입을 재평가하는 성능 오버헤드를 방지합니다.

이것이 최선의 접근 방식인지 확실하지 않지만 추가 성능 오버헤드를 도입하지 않고 요구 사항에 맞습니다.

변경 사항:

make 프래그먼트 구문 분석 알고리즘 사양을 준수하도록 합니다(#2522).

여기 에서 논의된 바와 같이 Element.innerHTMLElement.outerHTML 에 사용된 HTML 조각 구문 분석 알고리즘 구현이 올바르지 않습니다. 사용자 정의 요소 반응 콜백이 제대로 호출되려면 구문 분석 알고리즘을 리팩토링해야 합니다.

변경 사항:

요소 생성 알고리즘에 대한 인터페이스 조회 개선

내가 빠르게 발견한 문제 중 하나는 사용자 지정 요소 생성에 대한 지원을 추가할 때 새로운 순환 종속성의 도입이었습니다. CustomElementRegistry 및 요소 생성 알고리즘 모두 요소 인터페이스에 대한 액세스가 필요하므로 순환 종속성 악몽을 만듭니다.

분기에서 취한 접근 방식은 InterfaceCache 를 생성하여 요소 네임스페이스와 이름뿐 아니라 인터페이스 이름으로도 인터페이스 조회를 허용하는 것이었습니다. 인터페이스 모듈은 느리게 평가되고 평가되면 캐시됩니다. 이 접근 방식은 인터페이스가 더 이상 최상위 수준에서 필요하지 않기 때문에 순환 종속성을 제거합니다.

이것은 jsdom에서 이 오랜 문제를 해결하기 위한 하나의 접근 방식이며, 이 접근 방식의 이 문제 중 하나는 jsdom의 webpacked/browserified 버전(테스트되지 않음)을 손상시킬 수 있다는 것입니다.

변경 사항:

~Shadow DOM을 지원하도록 Element.isConnected 수정(https://github.com/jsdom/jsdom/pull/2424)~

이것은 요소가 섀도우 트리의 일부인 경우 isConnected가 false 를 반환하는 섀도우 DOM 의 도입과 함께 발생한 문제입니다. 이 동작을 확인하는 테스트가 없기 때문에 여기에 새로운 WPT 테스트를 추가해야 합니다.

변경 사항:

HTMLTemplateElement.templateContents 노드 문서 수정(#2426)

사양에 정의된 템플릿 콘텐츠 에는 HTMLTemplateElement 자체와 다른 노드 문서가 있습니다. jsdom은 오늘날 이 동작을 구현하지 않으며 HTMLTemplateElement와 템플릿 내용이 동일하게 공유됩니다.
문서 노드.

변경 사항:

  • HTMLTemplateElement-impl.js
  • htmltodom.js . 이 변경은 파서에 일부 다운스트림 효과도 있습니다. 컨텍스트 요소가 HTMLTemplateElement인 경우 HTML 조각 구문 분석 알고리즘은 요소 자체가 아니라 템플릿 콘텐츠에서 문서 노드를 선택해야 합니다.

HTMLTemplateElement 에 누락된 채택 단계 추가(#2426)

HTMLTemplateElement 는 다른 문서에 채택될 때 몇 가지 특정 단계를 실행해야 합니다. 내가 아는 한, 특별한 채택 단계가 있는 토양 인터페이스입니다. 이 채택 단계를 호출하려면 채택 노드 알고리즘 구현도 업데이트해야 합니다.

변경 사항:

parse5 직렬 변환기에서 isValue 조회 지원 추가

HTML 조각 을 직렬화하면 요소를 직렬화할 때 요소와 연결된 is 값 을 찾아 직렬화된 콘텐츠의 속성으로 반영합니다. getIsValue(element: Element): void | string 요소와 관련된 is 값을 조회하는 parse5 트리 어댑터에 다른 후크를 추가하는 것은 흥미로울 것입니다.

다른 접근 방식(구현되지 않음)은 요소에 연결된 is 값이 있는 경우 속성 목록에 is 값을 반환하도록 현재 getAttrList 후크의 동작을 변경하는 것입니다.

성능

성능 최적화를 수행하기 전에 분기에서 변경 사항의 성능도 확인하고 싶었습니다. 사용자 정의 요소를 추가하면 트리 돌연변이 벤치마크에 대한 마스터의 현재 결과와 비교하여 10% 성능 오버헤드가 추가됩니다. 그러나 새로운 JSDOM 환경의 생성은 이제 마스터에 비해 3배에서 6배 느리므로 근본 원인을 식별하려면 더 깊은 조사가 필요합니다.

자세한 내용: 여기

모든 89 댓글

webcomponents.js가 jsdom이 지원하지 않는 API를 사용하는지 살펴보는 것은 흥미로울 것입니다. 추측해야 한다면 전체 웹 구성 요소 사양보다 구현하기가 훨씬 쉬울 것입니다.

즉, 웹 구성 요소를 구현하는 것이 매우 멋질 것입니다. 아마도 생각만큼 어렵지 않을 것입니다. 사양은 상대적으로 작습니다.

이것에 대해 조금 파헤치는 시간을 가졌습니다.

먼저 창 범위에 Window 가 정의되어 있지 않습니다. 방금 Window 생성자에서 this.Window = this.prototype 로 이것을 패치했습니다.
둘째, webcomponentsjs는 Window 에 다른 프로토타입, 즉 EventTarget 프로토타입이 있을 것으로 예상합니다. 이 프로토타입은 별도의 엔티티로 구현하지 않습니다.

약간의 시간이 있었기 때문에 약간의 정보입니다.

멋진. Window를 아주 쉽게 노출할 수 있어야 합니다. EventTarget 프로토타입은 조금 더 까다롭지만 현재 우리가 해당 항목을 구현하는 방법을 고려할 때 실행 가능한 것처럼 보입니다. 그것은 나의 TODO였습니다.

좋아, 지금까지의 패치는 다소 쉽습니다.

  • [x] 창 생성자의 this.Window = Window;
  • [x] inherits(dom.EventTarget, Window, dom.EventTarget.prototype); 창 정의 후

webcomponents.js의 다음 충돌은 SVGUseElement 를 구현해야 한다고 시밍한 후 HTMLUnknownElement (#1068)를 구현하지 않았기 때문에 발생합니다. 그것이 내가 현재 차단된 것입니다. 왜냐하면 webcomponents.js는 분명히 HTMLDivElement 에 의해 shimm된 SVGUseElement 를 좋아하지 않고 주장을 던집니다.

자, Polyfill을 좀 더 확인했습니다. 구현해야 하거나 다음을 shim해야 합니다.

  • [x] HTMLUnknownElement #1068
  • [ ] SVGUseElement
  • [ ] window.CanvasRenderingContext2D
  • [ ] 범위 API( 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 특히 Polymer가 인기를 얻으면서 jsdom에서 WebComponents를 보고 싶습니다. 헤드리스 시스템에서 사용자 정의 요소를 테스트할 수 있게 되어 매우 좋습니다.

현재로서는 웹 구성 요소에 대한 브라우저 간 정의가 없으므로 구현하기에는 시기상조입니다. (단순히 Chrome을 복사하는 것이 아닙니다.) 그동안 jsdom과 함께 Polymer를 사용해 볼 수 있습니다.

@domenic 충분히 공정합니다. 제가 추구하는 것은 WebComponents.js 폴리필에 대한 지원입니다. 그것이 Polymer가 의존하는 것이기 때문입니다. 또는 webcomponents-lite (Shadow DOM을 제외한 모든 폴리필)입니다. Polymer가 jsdom에서 작동하도록 몇 번 시도했지만 지금까지는 운이 없었습니다. 위의 주석에서 @Sebmaster 의 작업이 최소한 먼저 패치되어야 한다고 가정합니다.

내 이해는 문제의 세 가지 개별 폴리필이 있다는 것입니다. OP에 있는 것은 폴리머와 별개입니다. 그런 다음 old-Polymer에서 사용되었던 webcomponents.org 폴리필이 있습니다. 그런 다음 Polymer 1.0에서는 자체 폴리필이 있다고 생각합니다. 실제로 폴리필이 아니라 웹 구성요소 같은 작업을 수행하는 대체 라이브러리입니다. 아마도 그것은 webcomponents-lite 일 것입니다.

WebComponentsJS 리포지토리에서 webcomponentsjs-lite 는 모든 Shadow DOM에 대한 폴리필을 제공하는 변형 이며 Polymer는 Shady DOM 시스템을 사용하여 독립적으로 shim을 시도합니다. 따라서 나는 Polymer가 WebComponentsJS 폴리필이 그런 작업을 수행하면서 가능한 한 WebComponents에 의존한다고 확신합니다. 라이트 버전은 무게가 훨씬 더 가벼워야 하므로(재미있게도..) jsdom이 라이트 버전에 필요한 것이 무엇인지 정확히 찾아낼 수 있는지 확인하겠습니다. jsdom에서 작동하는 폴리필(라이트 또는 전체)의 가능성은 무엇이라고 생각하십니까?

조사 없이는 말하기가 정말 어렵습니다... 당신이 무엇을 알아낼지 기대됩니다.

네, 제 할 일 목록이 여전히 적용 가능하고 shim을 사용하는 데 필요하다고 생각합니다. #1227을 병합하면 표준 준수 인터페이스를 훨씬 더 빠르게 구현하여 누락된 인터페이스를 더 빨리 수정할 수 있습니다.

나는 (아마 순진하게) jsdom이 어떻게 구성되어 있는지 이해하는 방법으로 CustomElementsRegistry를 추가하는 작업을 시작했습니다. 웹 플랫폼 테스트 목록에 "custom-elements/custom-elements-registry/define.html"을 추가했는데 통과하지 않아야 할 때 통과합니다(아직 거의 구현하지 않았습니다). 테스트 상단에 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을 보낼 것입니다. 테스트에서 다른 실수도 발견했습니다.

이는 https://github.com/tmpvar/jsdom/blob/master/test/living-html/named-properties-window.js 와 같은 테스트를 WPT로 업스트림하려는 동기입니다. 올려주셔서 감사합니다! jsdom에 대해 정말 기분이 좋습니다 ^_^

안녕!

나는 사용자 정의 요소 폴리필을 jsdom과 결합하여 작동하도록 관리했습니다.

참고: 저장소는 jsdom 8.5.0을 사용합니다. 그 이유는 내부적으로 Mutation Events를 사용하는 MutationObserver 폴리필로만 성공했기 때문입니다. 8.5.0 이후 성능 저하로 인해 돌연변이 이벤트가 제거되었습니다. 기본 Mutation Observer가 나오면 폴리필을 제거하고 최신 jsdom으로 업데이트하겠습니다.

나는 최신 jsdom을 가지고 있고 https://github.com/WebReflection/document-register-element 가 나를 위해 일하고 있습니다! 더 공식적인 폴리필을 실험해 보았는데 어떤 이유에서인지 문제가 있습니다. 제 목표는 최소한 사용자 정의 요소와 html 가져오기가 작동하도록 하는 것입니다. Polymer도 작동할 수 있다면 정말 좋을 것입니다.

Polymer 스크립트를 오류 없이 실행할 수 있습니다. 구성 요소를 만들어 Polymer 생성자에 전달할 수도 있습니다. 그 후에는 조용히 실패합니다. Shadow DOM이 문제라고 생각합니다.

webcomponentsjs HTML 가져오기 폴리필이 작동하도록 하려고 했습니다. 스크립트를 실행할 수 있고 HTML 가져오기가 xmlhttprequest를 실행한다고 생각하지만 가져오기의 스크립트가 실행되지 않는 것 같습니다.

@lastmjs 예제를 공유하시겠습니까? 나는 현재 웹 구성 요소에 대해 무릎을 꿇고 있습니다. 내가 도움이 될 수 있다면 기꺼이 당신과 함께 기여하겠습니다.

@snuggs 감사합니다! 하루나 이틀만 시간을 주세요. 지금 급한 일을 하고 있습니다.

@snuggs webcomponents-lite 폴리필이 작동하도록 할 수 있다면 폴리머를 사용할 수 있어야 합니다. Shadow DOM은 지금까지 작업하기 가장 어려운 폴리필인 것 같습니다. webcomponents-lite 를 사용하면 template 에 액세스할 수 있기 때문에 당분간 그것에 대해 걱정할 필요가 없습니다. , custom elementsHTML imports .

webcomponents-lite 폴리필과 함께 작동하도록 HTML 가져오기를 가져올 수 있습니다. 이상한 행동을 하다가 다음을 발견했습니다. https://github.com/Polymer/polymer/issues/1535 HTML 가져오기는 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 폴리필과 함께 작동하는 템플릿도 있습니다. 사용자 정의 요소 polyfill이 어떻게 작동하는지 잘 모르겠습니다.

TestApp 클래스 안에 ready 또는 created 메소드를 넣으면 호출되지 않습니다. 수명 주기 이벤트가 제대로 처리되지 않는 것 같습니다. 그 문제의 근원은 맞춤 요소 polyfill의 구현에 있을 수 있습니다. 계속 놀아줄게.

해결해야 할 잠재적인 문제:

  • [ ] HTML 가져오기가 제대로 차단되지 않음
  • [ ] 사용자 정의 요소 폴리필이 작동합니까?
  • [ ] 폴리머 수명 주기 메서드가 호출되지 않음

더 많은 땜질은 더 많은 통찰력으로 이어집니다. 수입 및 등록의 순서가 우리에게 혼란을 줄 수 있다고 생각합니다. Polymer 생성자가 호출된 후 const testApp = document.createElement('test-app'); 를 실행하면 createdready 메서드가 호출되지만 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 마이그레이션이 진행 중인 것 같습니다. 예를 들어 사양은 $ attachedCallback 기능과 쌍을 이루는 connectedCallback 를 호출합니다. 또한 해당 핸들러가 API의 일부가 아니므로 attached 라고 말할 때 attachedCallback 를 의미했다고 가정합니다. define()registerElement() 가 각 메서드마다 다른 콜백을 실행하는 것을 경험했습니다. 맞춤 요소 전략을 알아냈습니다. HTMLImports Domenic은 XMLHTTPRequest 패치를 사용하는 구현 전에 언급했습니다. 응답에서 직접 DocumentFragment 로 변환할 수 있다고 생각합니다. 그런 냄새는 "수입품"으로 스네이크 오일이 될 수 있습니다. "가짜" 수입품은 온전한 삶을 사는 곳일 수 있습니다.

또한 ES6 -> ES5에서 트랜스파일할 때 super()HTMLElement 에서 호출되는 약간의 장난이 있는 것 같으므로 주의하십시오. 나는 Rollup.js/Babel에서 이것을 경험했고 webcomponents 패키지에서 (경량) shim을 사용해야 했습니다.
https://developers.google.com/web/fundamentals/getting-started/primers/customelements

마지막으로 프로토타입 태그로 만들 때 (더) 성공하는 것 같습니다.

document.createElement('main', 'test-app')

@domenic 이 저에게 앞서 언급했듯이 우리는 GOOGLE이 하는 것만 하는 것이 아니라 가장 낮은 공통 분모 사양을 구현하는 데 신중하고 싶습니다. 웹 구성 요소로 인해 선이 흐려진 것 같습니다. 하지만 난 팬이야.

어떤 방법으로 작업 했습니까?

지금까지 저는 주로 webcomponents-lite 폴리필만 사용했고 Polymer < 2.0을 사용했습니다. 따라서 attached 방법을 언급할 때 attachedCallback 대신 사용하는 폴리머 수명 주기 방법을 의미했습니다. 또한 내가 아는 한 폴리필은 아직 새로운 v1 사용자 정의 요소 사양으로 전환되지 않았습니다. 그래서 제가 가지고 놀고 있는 모든 것은 Polymer가 현재 폴리필과 함께 작동하도록 하는 것뿐입니다.

@snuggs 지금 폴리필을 사용하고 있습니까 아니면 jsdom에서 실제 구현 작업을 하고 있습니까?

@lastmjs 80%를 얻을 필요가 없다고 느끼기 때문에 폴리필을 사용하지 않습니다. 플랫폼은 이제 약간의 사전 조정으로 기본 구성을 사용할 수 있을 정도로 충분히 성숙했습니다. 저는 프레임워크 대신 가벼운(보통 손으로 압연된) 도구를 사용하는 것을 좋아합니다. 그것은 대부분의 사람들이 아니라고 말했다. Domenic의 의도는 Custom Elements 👍 html imports 👎 인 것 같지만 문서의 fetching 를 처리하기 위해 XMLHTTPRequest를 확장하는 데 문제는 없습니다. 약 6개월 전의 일입니다. 구현 이후 많은 것이 변경되었습니다. 아마도 생각하고 있습니다. 그럼 @lastmjs는 어디서 끝내나요?

@snuggs 아마도 가장 합리적이고 미래에 대비할 수 있는 일은 jsdom에서 Custom Elements와 Shadow DOM에 대한 일류 지원을 구현하는 것입니다. 두 표준 모두 v1에 있으며 대부분의 주요 브라우저에서 이를 구현할 것으로 보입니다. 어떻게 해야 할까요? 지금은 시간이 제한되어 있지만 최소한 수행해야 할 작업을 배치할 수 있습니다. @domenic 이러한 구현을 진행하는 방법에 대한 제안 사항이나 하지 말아야 하는 이유가 있습니까?

사양을 구현하는 것 외에는 구체적인 제안이 없습니다. :)

나는 얼마 전에 이것을 작업한 지점이 있습니다(그 이후로 사양이 약간 변경되었습니다). CustomElementsRegistry를 구현하는 것은 충분히 쉬웠으며 사용자 정의 요소 반응을 코드베이스에 짜넣는 방법과 이러한 반응을 언제 어디서 호출해야 하는지 알아내는 데 어려움을 겪었습니다. 내가 이 백업(약속 없음)을 선택한다면 아마도 내가 집중할 것입니다.

@matthewp 도움이 될 것 같습니다. 해당 지점은 어디에서 찾을 수 있나요?

@matthewp 예, 좋을 것입니다.

내가 말했듯이 https://github.com/matthewp/jsdom/commits/custom-elements 그 이후로 사양이 변경되어 구식입니다. 그리고 이것은 가장 쉬운 부분이지만 누군가가 원한다면 시작점입니다. @snuggs @lastmjs

  • 템플릿은 모든 주요 브라우저에서 채택됩니다( W3C 사양 ).
  • 사용자 정의 요소가 곧 제공될 예정입니다( W3C 사양 )
  • HTML 가져오기는 브라우저 공급업체에서 조만간 구현하지 않을 예정입니다( W3C 사양 ).
  • Shadow dom은 그 사이에 있습니다...( W3c spec )

(http://jonrimmer.github.io/are-we-componentized-yet/)

개인적으로 단순히 Custom 요소를 지원하는 것만으로도 이미 훌륭할 것입니다.

(참고로 phantomJS 2.5는 최신 버전의 Webkit에서 이동할 때 최소한 Templates와 Custom 요소를 지원해야 하지만 어느 것이 맞는지 확실하지 않습니다.)

실제로 lib document-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

하지만 더 많은 사양을 작성하고 더 나은 느낌을 주기 위해 더 많은 것을 커버해야 합니다.

방법이 있다는 것이 신기합니다. 내가 폴리머를 좋아하는만큼 테스트 설정은 지옥이고 폴백으로 jsdom을 갖는 것이 좋습니다 ;) 작업을 넣어 주셔서 감사합니다

PR이 진행 중인 것 같습니다! https://github.com/tmpvar/jsdom/pull/1872

실제로 lib document-register-element @darlanmendonca 를 사용하여 customElements를 조롱합니다.

노드 전역에 jsdom 전역 연결에 대한 이 링크 를 읽어야 합니다. 안티 패턴입니다.

모두들 안녕,
JSDOM(Node.js 6.7.0 및 JSDOM 11.1.0 사용) 내에서 실행 중인 폴리머 상태에 대해 약간 혼란스럽습니다. 나는 혼합된 결과와 함께 여러 가지를 시도했습니다. 누군가 여기에서 나를 채워줄 수 있다면 정말 감사할 것입니다...

내가 지금까지 한 일:

1) 내 루트 디렉토리에서 http 서버를 시작했습니다.

./node_modules/http-server/bin/http-server --cors

2) 내 폴리머 구성 요소 중 하나를 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>

(웹 구성 요소 폴리필을 로드하기 위해 HTML 헤드를 추가했습니다.)

무엇을 관찰할 수 있습니까?

내가 이것을 실행할 때, 나는 본다

  • 웹 구성 요소 폴리필이 웹 서버에서 로드되고 있음
  • 콘솔에서 "자바스크립트가 실행되고 있습니다" 메시지

내가 보지 못하는 것

  • polymer.html 구성 요소가 웹 서버에서 로드되고 있음을 나타냅니다.
  • 콘솔의 "웹 구성 요소가 준비되었습니다" 메시지

이것은 WebComponentsReady 이벤트가 발생하지 않는다는 결론으로 ​​이어집니다(아마도 HTML 가져오기가 작동하지 않기 때문입니까?). 또한,
window.WebComponents{ flags: { log: {} } } 포함 -- ready 표시기가 없습니다.

나는 또한 조롱과 폴리필을 시도했다.

  window.Object = Object;
  window.Math = Math;
  require('document-register-element/pony')(window);

그러나 그것은 아무것도 변하지 않는 것 같았다.

자, 궁금합니다 :-) 이것이 전혀 작동해야 합니까? 그렇다면 왜 작동하지 않습니까? 그렇지 않은 경우 작동하기 위해 누락/필요한 것은 무엇입니까?

어떤 통찰력을 주셔서 감사합니다!

모인

나는 심지어 더 적은 성공으로 그것을 시도했고 여기에서 이 토론의 결과가 무엇인지 기다리는 것을 포기했습니다.

https://github.com/sebs/noframework/blob/master/test/configurator.js

해결책이 아니라 또 다른 실패한 시도일 뿐입니다. 같은 혼란 btw. 역시 같은 결론

jsdom에서 사용자 정의 요소를 폴리필하는 것은 매우 어려운 것으로 입증되었습니다. 누군가 jsdom에서 구현하는 문제를 나열할 수 있습니까? 그것을 얻기 위한 노력의 수준을 평가하려고 합니다.

근본적인 장애물은 jsdom이 생성자와 프로토타입을 공유 한다는 것입니다.

이것은 HTMLElement 생성자가 모든 창에서 공유되기 때문에 창당 사용자 정의 요소 레지스트리를 구현하는 것을 기본적으로 불가능하게 합니다. 따라서 사용자 정의 요소 생성자에서 super() 호출을 수행할 때 현재 실행 중인 HTMLElement 생성자는 어떤 창에서 조회해야 하는지 알지 못합니다. 이것은 짜증나는 일입니다.

좋은 중간 솔루션이 있는지 잘 모르겠습니다. 가장 큰 목표는 jsdom을 비공유 생성자/프로토타입을 허용하는 아키텍처로 옮기는 것입니다. 우리는 서로 다른 절충안을 가지고 몇 가지 방법으로 이것을 할 수 있습니다. 아마도 우리는 팀 및 커뮤니티와 논의하기 위해 전용 문제를 열고 싶지만 지금은 머리에서 가장 먼저 떠오르는 항목을 나열하겠습니다.

  • jsdom의 모든 항목에 [WebIDL2JSFactory] 를 사용하거나 최소한 HTMLElement 및 모든 하위 항목에 사용하십시오. [WebIDL2JSFactory] 가 상속과 함께 잘 작동하는지 확실하지 않지만 작동하도록 만들 수 있습니다. 이 대안을 사용하면 모든 사람이 추가 생성자/프로토타입 비용을 지불하게 되지만 사용자 지정 요소를 옵트인 기능으로 만드는 것보다 나을 수 있습니다.
  • jsdom이 vm 샌드박스 내에서 모든 클래스 정의 모듈을 실행하는 옵션이 있습니다. 예를 들어 jsdom에 모든 웹 API를 묶는 빌드 단계가 있고 새 창을 만들 때 새 샌드박스 전역 내에서 해당 번들로 vm.runScript() 를 수행합니다. 이것은 아마도 우리가 [WebIDL2JSFactory] 를 없애도록 할 것입니다.

또 다른 솔루션은 사용자 정의 요소 레지스트리가 Node.js 프로세스마다 전역적이라는 거대한 경고와 함께 사용자 정의 요소를 구현하는 것입니다. 그래도 끔찍해 보입니다.


그 초기 장애물 이후, 나머지는 사양을 따르는 측면에서 비교적 간단합니다. 가장 어려운 부분은 [CEReactions] 를 구현하고 모든 IDL 파일을 업데이트하여 적절한 위치에 있도록 하는 것이지만 그리 어렵지 않습니다.

별도의 프로토타입 버전도 갖고 싶다는 생각을 해왔습니다. 여기 내 생각이 있습니다.

[WebIDL2JSFactory] 가 상속과도 잘 작동하는지 확실하지 않지만 작동하도록 만들 수는 있습니다.

아니요, 정확히 어떻게 작동하는지 잘 모르겠습니다. 제 생각에는 두 번째 솔루션이 훨씬 더 간단합니다.

jsdom이 vm 샌드박스 내에서 모든 클래스 정의 모듈을 실행하는 옵션이 있습니다.

이것이 내가 선호하는 것입니다. 주요 문제는 초기화하는 동안 impl 클래스를 vm 샌드박스에 전달하는 것입니다. 하지만 이는 외부 컨텍스트의 모든 것을 하나의 전역 속성에 넣고 delete 이 완료된 후 해당 전역 속성에 넣어 수행할 수 있습니다. . 또한 [NamedConstructor] 및 몇 가지 다른 확장된 속성을 적절히 구현하고 누군가가 충분히 대담하다면 jsdom 환경을 위한 V8 시작 스냅샷을 생성할 수도 있습니다.

전체 [WebIDL2JSFactory] 사업은 처음부터 해킹이었고 가능한 한 빨리 그것을 없애고 싶습니다.

+1 댓글은 이 기능을 개발하는 데 도움이 되지 않으므로 최근 댓글을 하나 이상 삭제합니다.

안녕하세요, @TimothyGu 가 이 작업을 하고 있는지 몰랐습니다.
실제로 사용자 정의 요소 등록 및 생성 작업이 있습니다.
https://github.com/mraerino/jsdom/tree/custom-elements-spec

나는 가능한 한 최소한으로 침습적이며 가능한 한 사양에 가깝게 유지하려고 노력하고 있습니다.
사용자 정의 요소 레지스트리 웹 플랫폼 테스트가 통과되었습니다.

지난 밤에 이것을 해킹하는 동안 webIdl2JS를 수정하지 않고도 작동하는 솔루션을 찾았습니다.
여기를 참조하십시오: https://github.com/mraerino/jsdom/commit/592ad1236e9ca8f63f789d48e1887003305bc618

@TimothyGu 당신은 이것에 힘을 합칠 의향이 있습니까?

여기에 몇 가지 업데이트가 있습니다.
사양 구현에 대해 꽤 확신하고 있지만 현재 [HTMLConstructor] 확장 IDL 속성 때문에 막혀 있습니다. 그래서 https://github.com/jsdom/webidl2js/issues/87 을 열었습니다.

그 동안 나중에 쉽게 전환할 수 있도록 [Constructor] [HTMLConstructor] 알고리즘을 구현하겠습니다. (처음에는 window 에 모의 HTMLElement 클래스를 삽입하여 구현했지만 이것이 옳지 않은 것 같습니다.)

예, https://github.com/tmpvar/jsdom/issues/1030#issuecomment -333994158에서 언급했듯이 HTMLConstructor를 올바르게 구현하려면 jsdom 아키텍처를 근본적으로 변경해야 합니다.

귀하의 버전이 통과하는 웹 플랫폼 테스트의 수에 대한 정보가 있습니까?

지금은 customElementRegistry만 사용하고 진행 상황에 대해 완전히 틀릴 수 있습니다.

편집: 알겠습니다. 귀하의 의견을 다시 읽은 후 귀하가 의미하는 바를 알았습니다. 내 구현으로 시도하겠지만 @TimothyGu 도 분리 작업을 하는 것 같습니다.

나는 폴리머를 사용하므로 이 요청 기능에 대해 +1:입니다.

@dman777 @mraerino Slim.js 개발자도 마찬가지입니다. Slim은 기본 웹 구성 요소 API를 사용하며 jsdom에 대한 해킹 없이 HTMLElement를 상속할 수 없습니다.

이 호가 공개된 지 3년이 지났습니다. 대략 jsdom이 언제 사용자 정의 요소를 지원할 것인지 말할 수 있는 사람이 있습니까?

TL;DR

저는 지난 2주 동안 jsdom에서 사용자 정의 요소에 대한 지원을 추가하는 가능성을 평가하는 작업을 했습니다. 다음은 조사 결과입니다.

사용자 지정 요소의 사양 준수 구현은 https://github.com/jsdom/jsdom/compare/master...pmdartus :custom-elements?expand=1에서 찾을 수 있습니다. 여전히 여기 저기에 거친 가장자리가 있지만 적어도 대부분의 WPT 테스트는 통과 합니다. 나머지 실패한 테스트는 알려진 JSDOM 문제이거나 jsdom에서 실제 구현을 다룰 때 다룰 수 있는 사소한 문제입니다.

이제 좋은 소식이 있습니다. 이제 Shadow DOM이 기본적으로 사용자 정의 요소 분기와 돌연변이 관찰자와 함께 지원되므로 jsdom 🎉에서 최신 버전의 Polymer 3 hello world 애플리케이션 예제를 로드하고 렌더링할 수 있었습니다. 현재 상태에서 분기는 스텐실 애플리케이션을 로드할 수 없습니다(스텐실 개발 모드에는 module 와 같은 일부 지원되지 않는 기능이 필요하고 prod 모드는 알 수 없는 이유로 발생합니다).

행동 계획

다음은 실제 사용자 지정 요소 사양 구현을 시작하기 전에 먼저 발생해야 하는 변경 사항의 목록입니다. 목록의 각 항목은 독립적이며 병렬로 처리할 수 있습니다.

[CEReactions] IDL 확장 속성 지원

사용자 정의 요소에 대한 지원을 추가하기 위해 jsdom에서 기능적으로 누락된 핵심 중 하나는 [CEReactions] 속성입니다. 올바른 프로토타입 속성을 패치하여 이 문제를 부분적으로 해결할 수 있었습니다. 이 접근 방식은 모든 인터페이스 프로토타입이 공유되기 때문에 사용자 정의 요소 반응 스택이 전역적이며 관련된 유사 출처 브라우징 컨텍스트 단위가 아닌 한 작동합니다.

일부 인터페이스에는 인덱싱된 속성( HTMLOptionsCollection , DOMStringMap )과 연결된 [CEReactions] 속성이 있기 때문에 이 접근 방식에는 몇 가지 단점이 있습니다. 내부적으로 jsdom은 프록시를 사용하여 해당 속성에 대한 변형을 추적합니다. 이 경우 인터페이스의 프로토타입 패치가 작동하지 않습니다. 이 문제를 해결하기 위한 또 다른 접근 방식은 인터페이스(구현되지 않음) 대신 구현을 패치하는 것입니다.

나는 webidl2js 내부에 대해 충분히 익숙하지 않지만 [CEReactions] 에 대한 전역 후크를 추가하는 방법을 탐색해야 합니까?

변경 사항:

[HTMLConstructor] IDL 확장 속성 지원

위에서 @domenic이 설명 했듯이 [HTMLConstructor] 에 대한 지원을 추가하는 것은 여기에서 주요 방해 요소 중 하나입니다.

여기에서 각 탐색 컨텍스트에 대한 인터페이스 생성자를 패치하여 이 문제를 해결할 수 있었습니다. 인터페이스 생성자는 공유 프로토타입을 유지하면서 올바른 창과 문서 개체에 액세스할 수 있습니다. 이 접근 방식은 또한 각각의 새로운 브라우징 컨텍스트에 대해 인터페이스 프로토타입을 재평가하는 성능 오버헤드를 방지합니다.

이것이 최선의 접근 방식인지 확실하지 않지만 추가 성능 오버헤드를 도입하지 않고 요구 사항에 맞습니다.

변경 사항:

make 프래그먼트 구문 분석 알고리즘 사양을 준수하도록 합니다(#2522).

여기 에서 논의된 바와 같이 Element.innerHTMLElement.outerHTML 에 사용된 HTML 조각 구문 분석 알고리즘 구현이 올바르지 않습니다. 사용자 정의 요소 반응 콜백이 제대로 호출되려면 구문 분석 알고리즘을 리팩토링해야 합니다.

변경 사항:

요소 생성 알고리즘에 대한 인터페이스 조회 개선

내가 빠르게 발견한 문제 중 하나는 사용자 지정 요소 생성에 대한 지원을 추가할 때 새로운 순환 종속성의 도입이었습니다. CustomElementRegistry 및 요소 생성 알고리즘 모두 요소 인터페이스에 대한 액세스가 필요하므로 순환 종속성 악몽을 만듭니다.

분기에서 취한 접근 방식은 InterfaceCache 를 생성하여 요소 네임스페이스와 이름뿐 아니라 인터페이스 이름으로도 인터페이스 조회를 허용하는 것이었습니다. 인터페이스 모듈은 느리게 평가되고 평가되면 캐시됩니다. 이 접근 방식은 인터페이스가 더 이상 최상위 수준에서 필요하지 않기 때문에 순환 종속성을 제거합니다.

이것은 jsdom에서 이 오랜 문제를 해결하기 위한 하나의 접근 방식이며, 이 접근 방식의 이 문제 중 하나는 jsdom의 webpacked/browserified 버전(테스트되지 않음)을 손상시킬 수 있다는 것입니다.

변경 사항:

~Shadow DOM을 지원하도록 Element.isConnected 수정(https://github.com/jsdom/jsdom/pull/2424)~

이것은 요소가 섀도우 트리의 일부인 경우 isConnected가 false 를 반환하는 섀도우 DOM 의 도입과 함께 발생한 문제입니다. 이 동작을 확인하는 테스트가 없기 때문에 여기에 새로운 WPT 테스트를 추가해야 합니다.

변경 사항:

HTMLTemplateElement.templateContents 노드 문서 수정(#2426)

사양에 정의된 템플릿 콘텐츠 에는 HTMLTemplateElement 자체와 다른 노드 문서가 있습니다. jsdom은 오늘날 이 동작을 구현하지 않으며 HTMLTemplateElement와 템플릿 내용이 동일하게 공유됩니다.
문서 노드.

변경 사항:

  • HTMLTemplateElement-impl.js
  • htmltodom.js . 이 변경은 파서에 일부 다운스트림 효과도 있습니다. 컨텍스트 요소가 HTMLTemplateElement인 경우 HTML 조각 구문 분석 알고리즘은 요소 자체가 아니라 템플릿 콘텐츠에서 문서 노드를 선택해야 합니다.

HTMLTemplateElement 에 누락된 채택 단계 추가(#2426)

HTMLTemplateElement 는 다른 문서에 채택될 때 몇 가지 특정 단계를 실행해야 합니다. 내가 아는 한, 특별한 채택 단계가 있는 토양 인터페이스입니다. 이 채택 단계를 호출하려면 채택 노드 알고리즘 구현도 업데이트해야 합니다.

변경 사항:

parse5 직렬 변환기에서 isValue 조회 지원 추가

HTML 조각 을 직렬화하면 요소를 직렬화할 때 요소와 연결된 is 값 을 찾아 직렬화된 콘텐츠의 속성으로 반영합니다. getIsValue(element: Element): void | string 요소와 관련된 is 값을 조회하는 parse5 트리 어댑터에 다른 후크를 추가하는 것은 흥미로울 것입니다.

다른 접근 방식(구현되지 않음)은 요소에 연결된 is 값이 있는 경우 속성 목록에 is 값을 반환하도록 현재 getAttrList 후크의 동작을 변경하는 것입니다.

성능

성능 최적화를 수행하기 전에 분기에서 변경 사항의 성능도 확인하고 싶었습니다. 사용자 정의 요소를 추가하면 트리 돌연변이 벤치마크에 대한 마스터의 현재 결과와 비교하여 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년 된 것을 알면 웹 구성 요소가 지원되거나 지원될 예정입니까?

여기에 웹 구성 요소가 있으면 좋겠지만 누군가 알고 싶다면 대안으로 .... 헤드리스 크롬은 이제 노드에서 html 스팅 파일을 렌더링/빌드하는 데 사용할 수 있습니다.

이 스레드가 4년 된 것을 알면 웹 구성 요소가 지원되거나 지원될 예정입니까?

사양이 하나씩 구현되면서 작업이 진행 중입니다.

폴리필: 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에 사용자 정의 요소를 등록하고 생성하는 것이 완벽하게 작동합니다! 그러나 조각이 작동하지 않는 것 같습니다. (그런데 나는 그림자 돔을 사용하지 않습니다).

@tebanep 폴리 필이 대부분의 웹 구성 요소 작업에 적합하지 않다고 언급했듯이 Shadow DOM을 지원하지 않는 경우 이것이 달성하는 것과 실제로 비교되지 않습니다.

@tebanep 감사합니다. 나는 그림자 돔이 필요하지 않기 때문에 이것은 좋은 정보입니다.

이것이 구현될 것이라는 희망이 있습니까? 지금 우리는 많은 버그가 있는 jsdom-wc를 사용하고 있지만 더 나은 솔루션이 없습니다. 이 주제에 대한 나의 희망과 기도.

@dknight 나는 jsdom-wc가 일종의 작동을 일으키는 해킹이라는 것을 알고 있습니다. 내 개인 npm 범위에서 훨씬 더 나은 호환성으로 모듈을 게시했습니다. 다음을 사용하여 설치할 수 있습니다.

npm install @tbranyen/[email protected] --save-dev

안정적인 땅이 될 때까지 모든 JSDOM 웹 구성 요소 요구 사항에 대해 지금 이것을 사용합니다.

@tbranyen 포크 게시를 취소하셨나요? npm에서 찾을 수 없습니다.

@KingHenne dangit, "엔터프라이즈" 레지스트리에 등록된 것 같습니다. 방금 공개 npm에 게시했습니다. 미안합니다!

@ me, 하지만 우리는 puppeteer와 같은 실제 브라우저에서 웹 ui 코드를 테스트해서는 안 됩니다. 그러면 Shadow DOM/사용자 정의 요소 지원 문제가 사라집니다.

@Georgegriff가 되고 싶지 않다면 댓글을 게시하지 마십시오. 그것은 유효한 전략이지만 IPC를 효과적으로 수행하고 있기 때문에 다른 방식으로 느리고 버그가 있습니다. 예, 인형극을 사용하더라도 그렇습니다. 브라우저가 죽으면 많은 경우에 그 이유가 명확하지 않습니다. 꼭두각시 인형이 항상 최선의 아이디어가 아닌 이유를 맛보기 위해 농담으로 꼭두각시 문제를 디버그하고 시도하십시오.

개인적으로 나는 동기 테스트를 계속하고 동일한 스레드에서 테스트하고 싶습니다. 사양의 격리된 구현이 구성 요소 테스트를 위한 합리적인 런타임이 되지 않아야 할 이유가 없습니다. JSDOM은 현시점에서 효과적인 브라우저이지만 세 가지만큼 안정적이지 않습니다.

폴리필: 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에 사용자 정의 요소를 등록하고 생성하는 것이 완벽하게 작동합니다! 그러나 조각이 작동하지 않는 것 같습니다. (그런데 나는 그림자 돔을 사용하지 않습니다).

그것은 나를 위해 잘 작동하지만 connectedCallback 는 결코 호출되지 않습니다.

@FaBeyyy 나에게도 마찬가지입니다 :(

@FaBeyyy @majo44 문서에 구성 요소를 추가해야 합니다. document.body.appendChild(...) connectedCallback 해고 사양에 따르면 구성 요소가 Dom에 연결될 때 호출됩니다.

JSDOM은 현시점에서 효과적인 브라우저이지만 세 가지만큼 안정적이지 않습니다.

이 시점에서 Microsoft는 Windows만큼 오랫동안 함께 해 왔던 자사의 제품을 버리고 있기 때문에 큰 두 가지와 비슷합니다.

@FaBeyyy @majo44 문서에 구성 요소를 추가해야 합니다. document.body.appendChild(...) connectedCallback 해고 사양에 따르면 구성 요소가 Dom에 연결될 때 호출됩니다.

캡틴에게 감사를 표합니다. 하지만 그것은 여기서 문제가 아닙니다. 구성 요소 수명 주기가 어떻게 작동하는지 몰랐다면 아마도 테스트를 작성하지 않았을 것입니다 😄. 나중에 시간이 나면 repo 쇼케이스를 만들 것입니다.

@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에서 지원하는 웹 구성 요소 API에 대한 분석이 있습니까? Shadow DOM이 지원되지만 사용자 정의 요소는 지원되지 않는 것 같습니까(적어도 릴리스 분기/리포지토리에서)?

npm install @tbranyen/[email protected] --save-dev

@tbranyen 어딘가에서 사용할 수 있는 포크의 소스 코드가 있습니까? 차이점을 살펴보고 싶습니다 🙂

@majo44 가 제안한 것처럼 jest-environment-jsdom-fifteen과 babel-polyfill 및 document-register-element( @mgibas 답변 참조)를 사용하고 있습니다. 하지만 테스트를 위해 웹 구성 요소 섀도우 돔을 검색하려고 하면 여전히 오류가 발생합니다.

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] > *');
})

해결 방법에 대한 아이디어가 있습니까? 참고로 이미 Karma, Mocha, Chai & Jasmine으로 테스트되었습니다.

내 구성 요소에는 특별한 것이 없습니다.

customElements.define(
  'my-component',
  class extends HTMLElement {
    constructor() {
      super();

      const shadowRoot = this.attachShadow({ mode: 'open' });
      ...
  }
})

편집하다

내 문제를 더 잘 이해하기 위해 jsdom 15.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) (https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/living/nodes/Element-impl) 직후에 el.shadowDom 를 호출하면 .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 https://github.com/tbranyen/jsdom/tree/initial-custom-elements-impl 여기에서 @tbranyan 이 만든 포크의 소스 코드를 찾을 수 있습니다.

lit-html 및 lit-element로 만든 웹 구성 요소를 테스트하려고 하는데 요소를 만들 때 이 차이를 발견했습니다.

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를 구성하기 위해 setupFiles: ['document-register-element'] 인 폴리필 하나만 사용합니다.

myElem 의 render 메소드가 호출되지 않는 것 같습니다. 조금 더 디버깅하면 lit 요소에 있는 initialize 메서드가 호출되지 않는다는 것을 발견했습니다.
그래서 내가 할 경우 두 번째 예가 작동합니다.

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

웹 구성 요소를 지원하는 대체 DOM을 만들었습니다. 처음에는 PR을 하려고 했지만 JSDOM이 작동하는 방식으로 인해 그곳에서 제 요구 사항을 해결하기가 어려웠습니다. 당신은 그것을 사용하거나 코드를 볼 수 있습니다.

DOM:
https://www.npmjs.com/package/happy-dom

농담 환경:
https://www.npmjs.com/package/jest-environment-happy-dom

굉장해 보인다. 감사합니다.

2019년 10월 7일 월요일 오전 1:08 capricorn86 [email protected] 작성:

웹 구성 요소를 지원하는 대체 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=ACQ5ZD5QUEITPND4SXWOHW3QNILSRA5CNFSM4A4G5SF2YY3PNVWWK3TUL52HS4DFVEXG43VMXHJKTDNMV
또는 스레드 음소거
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:08 capricorn86 @ . * > 썼다: 웹 컴포넌트를 지원하는 대체 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을 .

@mots 감사합니다!

현재 JSDOM에서 지원하는 웹 구성 요소 API에 대한 분석이 있습니까? Shadow DOM이 지원되지만 사용자 정의 요소는 지원되지 않는 것 같습니까(적어도 릴리스 분기/리포지토리에서)?

이것도 관심이 가네요 :)

이 페이지가 도움이 되었나요?
0 / 5 - 0 등급