Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Illegal constructor on Safari #8

Closed
Javarome opened this issue Feb 18, 2022 · 5 comments
Closed

Illegal constructor on Safari #8

Javarome opened this issue Feb 18, 2022 · 5 comments

Comments

@Javarome
Copy link

I'm using the polyfill to use built-in web components on Safari, which works fine when using:

<script src="//unpkg.com/@ungap/custom-elements"></script>

However when using it as an imported package, I get the following error at component's creation (not explicit creation through JS, but implicit through the element in HTML code).

TypeError: Illegal constructor
(anonymous function)  MyComponent.ts:24
MyComponent  MyComponent.ts:24
_handle  index.js:423
notifier  index.js:164
loop  index.js:91
loop  index.js:99
(fonction anonyme)  index.js:110

Component's code is basically:

class MyComponent extends window.HTMLInputElement {
  constructor() {
    super()
  } 
}

Notes:

  • Env is macOS 12.2.1 with Safari 15.3 (17612.4.9.1.8) ;
  • The polyfill is imported at first statement, using import "@ungap/custom-elements". At runtime it is there and executed, and the component has been property registered (using window.customElements.define("my-input", MyComponent, {extends: "input"})) as the error occurs in the polyfill code ;
  • This issue looks similar to this stackoverflow post which has no answer.
@Javarome
Copy link
Author

Javarome commented Feb 18, 2022

Error occurs here after observer detected the <input is="my-input"> in the DOM:

var _handle = function _handle(element, connected, selector) {
      var proto = _prototypes.get(selector);

      if (connected && !proto.isPrototypeOf(element)) {
        var redefine = expando(element);
        _override = setPrototypeOf(element, proto);

        try {
          new proto.constructor(); <== here where proto is class MyComponent
        } finally {
          _override = null;
          redefine();
        }
      }

      var method = "".concat(connected ? '' : 'dis', "connectedCallback");
      if (method in proto) element[method]();
    };

So it looks like it is forbidden to instantiate an HTMLInputElement programmatically (which I was aware of) but then I don't get:

  • how such a WC polyfill could work (while trying to allow an forbidden extends)
  • how this polyfill can work when loaded as a script.

I must be missing something essential.

@Javarome
Copy link
Author

Javarome commented Feb 18, 2022

It looks that the script alternative triggers another part of the polyfill which doesn't fail in this case:

var _override = null;
    getOwnPropertyNames(self).filter(function (k) {
      return /^HTML/.test(k);
    }).forEach(function (k) {
      var HTMLElement = self[k];

      function HTMLBuiltIn() {                      <== MyComponent.super() calls this
        var constructor = this.constructor;
        if (!_classes.has(constructor)) throw new TypeError('Illegal constructor');

        var _classes$get = _classes.get(constructor),
            is = _classes$get.is,
            tag = _classes$get.tag;

        if (is) {
          if (_override) return _augment(_override, is);    <== works down here

          var element = _createElement.call(document$1, tag);

          element.setAttribute('is', is);
          return _augment(setPrototypeOf(element, constructor.prototype), is);
        } else return construct.call(this, HTMLElement, [], constructor);
      }


      defineProperty(HTMLBuiltIn.prototype = HTMLElement.prototype, 'constructor', {
        value: HTMLBuiltIn
      });
      defineProperty(self, k, {
        value: HTMLBuiltIn
      });
    });

@WebReflection
Copy link
Member

beside the fact this polyfill works already, this issue is missig how and/or when you import the polyfill, and the SO issue (nobody mentioned me in there, how can I even help?!?) is also importing a default, as if this module actually export anything at all.

So ...

  • how are you importing this module?
  • when are you importing this module?
  • are your builtin custom elements inside Shadow DOM and, if that's the case, are you importing any other library before the polyfill?
  • last, but not least, have you tried vanilla elements too, which allows you through Safari detection adn import maps changed at runtime to define the right export so that every other standard browser won't ever need to download a libarry that makes no sense outside safari?

@Javarome
Copy link
Author

Javarome commented Feb 18, 2022

Indeed this was an import order issue.

Doing:

import "MyComponent"
import "@ungap/custom-elements"

fails, but doing:

import "@ungap/custom-elements"
import "MyComponent"

works.

Thanks for the hint!

@WebReflection
Copy link
Member

@Javarome for what is worth it as a hint, every single polyfill should always be loaded before anything else ... we (polyfils authors) can't fight the unknown all libraries internals do, so bring the polyfill always before everything else and code happily ever after 🥳

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants