lit / lit-element

LEGACY REPO. This repository is for maintenance of the legacy LitElement library. The LitElement base class is now part of the Lit library, which is developed in the lit monorepo.
https://lit-element.polymer-project.org
BSD 3-Clause "New" or "Revised" License
4.49k stars 319 forks source link

Uncaught TypeError: Class constructor LitElement cannot be invoked without 'new' #54

Closed rjcorwin closed 5 years ago

rjcorwin commented 6 years ago

I'm trying out Lit Element in a vanilla Angular 6 project and running into Uncaught TypeError: Class constructor LitElement cannot be invoked without 'new' when trying out the hello world lit element example. This commit shows how I added webcomponentjs / litelement packages, allow custom schema in angular, declare the my-element LitElement example, and then use that element in the app. That commit was made right after running the ng new command.

screen shot 2018-05-11 at 3 33 16 pm

If you want to poke at it, run the following commands...

git clone https://github.com/rjsteinert/angular-with-lit-element.git
cd angular-with-list-element
npm install
npm start
jsilvermist commented 6 years ago

Is it being compiled to es5 without adding the custom-elements-es5-adapter.js file?

rjcorwin commented 6 years ago

@jsilvermist Good idea! Unfortunately, after adding it the same error persists.

TimvdLippe commented 6 years ago

@rjsteinert The imports you specified are not loaded at all. E.g. the webcomponents-loader as well as the adapter are never actually executed. You can see this by opening the website in Firefox, it will break.

Now, I tried to add the loader and adapter to index.html, but your node_modules/ are not in src/, so I was unable to load them via a script tag. I don't think the loader is compatible with ES modules. It has to be loaded via script tag.

rjcorwin commented 6 years ago

@TimvdLippe I found a ticket relared to ES modules in the webcomponentjs issue queue https://github.com/webcomponents/webcomponentsjs/issues/882

This not working in Firefox is definitely a concern and as you said related to the polyfills not loading. Does Chrome need the polyfills to make this work? I'm assuming not, and if that's the case then I wonder what is up with this error Class constructor LitElement cannot be invoked without 'new'.

TimvdLippe commented 6 years ago

@rjsteinert Chrome does not need the polyfill, but does require the adapter to be loaded. When I ran your project, I did not see the adapter being loaded. Therefore it would break in Chrome, as Chrome requires the adapter to be loaded if you define your custom elements in ES5.

hfjallemark commented 6 years ago

I'm getting the same error with the custom-elements-es5-adapter script loaded. I am trying to get lit-element working with Elm:

screen shot 2018-05-17 at 07 09 07

NicolasRannou commented 6 years ago

Same behavior with create-react-app.

index.html:

<head>
  <script src="./webcomponents-bundle.js"></script>
</head>
...

App.js:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

import {LitElement, html} from '@polymer/lit-element';

class MyElement extends LitElement {

  static get properties() { return { mood: String }}

  _render({mood}) {
    return html`<style> .mood { color: green; } </style>
      Web Components are <span class="mood">${mood}</span>!`;
  }

}

customElements.define('my-element', MyElement);

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        <my-element/>
      </div>
    );
  }
}

export default App;
moebiusmania commented 6 years ago

same here, building a pure vanilla/lit-element project, transpiled with Babel 6 (with preset env defaults) adding the custom-elements-es5-adapter before the polyfill results in:

Uncaught TypeError: Class constructor LitElement cannot be invoked without 'new'

it seems like the adapter is not... adapting.

I'm also using code-splitting via Webpack but trying to going back to single-bundle-file didn't changed the result.

any clue about this? thanks

web-padawan commented 6 years ago

Related to Polymer/tools#398

thosakwe commented 6 years ago

From what I can tell, the issue is years-old in Babel, and is a bug preventing the usage of extended native classes without new. So not really sure if this is a Polymer problem, so much as Babel.

dkundel commented 6 years ago

Hey @rjsteinert,

do you need it to be downtranspiled to ES5? I changed my tsconfig.json to target ES2015 and then using the polyfill without the adapter works. You can see an example here: https://github.com/dkundel/vote-my-talk/commit/8b7f29a2993076b05582914b594b9500d67d919b

Cheeers, Dominik

jkga commented 6 years ago

I experienced the same error when transpiling using webpack and babel-preset-env. And after a few hours of trying, using es2017 works for me.

npm install --save-dev babel-preset-es2017

then change the option in your webpack configuration to es2017

options: {
   presets: ['es2017']
}

or in .babelrc

{
  "presets": ["es2017"]
}
web-padawan commented 6 years ago

Fixed with babel/babel#8501 and included in Babel 7 release, so the issue can be closed. See https://github.com/web-padawan/polymer3-webpack-starter for proper config.

rjcorwin commented 6 years ago

@dkundel Changing Agnular's tsconfig.json target property to es2015 did the trick for Chrome, however Firefox has a strange error white screen of death now. Perhaps the babel fix that @web-padawan mentioned will make it into an angular-cli release sometime soon and all our problems will be solved? I was trying this on a fresh angular-cli install/fresh project.

ernsheong commented 6 years ago

What is the real resolution to this? So much confusion across the issues.

thosakwe commented 6 years ago

I’m assuming the new Babel update will fix this.

Waiting on support Babel 7 in Parcel, haha. Until then, I guess you could use Webpack?

ernsheong commented 6 years ago

I am already on Webpack actually :/

web-padawan commented 6 years ago

The 7.0.0 stable release of Babel works properly as I mentioned above. You can also find a config example here: https://github.com/web-padawan/lit-components

ernsheong commented 6 years ago

Thanks @web-padawan, that was helpful. I wasn't willing to let go of "preset-env" just yet.

In summary, the issue is... ES6 classes must only be called with new on instantiation. Transpiling to ES5 will not solve this issue. So one must also include custom-elements-es5-adapter.js somewhere to resolve that.

A suggested strategy is to have separate babel builds: 1) Targets modern browsers with ES6 class support (e.g. { targets: { chrome: 60 }}) 2) Targets IE11 with ES5 support + custom-elements-es5-adapter.js

Then in production server needs to figure out whether to serve 1 or 2, which is admittedly more complexity. (Or just tell your customers to download Chrome)

For now I'll go with 1 and configure 2 later.

Westbrook commented 6 years ago

@ernsheong if you’re interested in a world that combines 1 and 2 while making sure that users on each side get the best code for them, you might be interested in something like https://philipwalton.com/articles/deploying-es2015-code-in-production-today/ It’s looking really good for the applications I’m working on, which use polymer build to output multiple builds, but the webpack approach in the blog post looks useful if you’re into it and should be portable in some way to Rollup, etc as needed, not that I know that from experience.

ernsheong commented 6 years ago

@Westbrook thanks for sharing the article, definitely looks promising (and more doable)! I like this other article from the same author too: https://philipwalton.com/articles/loading-polyfills-only-when-needed/ (from which I got the idea of punishing older browsers rather than newer ones)

tonai commented 5 years ago

Hi, I'm trying to display LitElement components using storybook.

import './counter-element';

storiesOf('CounterElement', module) .add('default', () => <counter-element clicks="3" 1alue="1"></counter-element>);



But then I got the error `Class constructor LitElement cannot be invoked without 'new'` when trying to display the component.
I have read the discussion above, but I don't know I still got that issue.
Does someone know what is missing ?
(I haven't load the polyfills since I'm testing on Chrome 69)
moebiusmania commented 5 years ago

@tonai I'm using LitElement & Storybook with @storybook/polymer and it's going fine, had some issue with LitElement and @storybook/html.

tonai commented 5 years ago

@moebiusmania What version of @storybook/polymer do you use ?

I just switch my above story using @storybook/polymer (v4.0.7) and still have the same issue.

My storybook config file .storybook/config.js looks like this:

import { configure } from '@storybook/polymer';

const req = require.context('../src', true, /\.stories\.js$/);
function loadStories() {
  req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);

I also tried to initialize the storybook using the cli npx -p @storybook/cli sb init but still the same error...

moebiusmania commented 5 years ago

@tonai 4.0.2, you can check it out here https://github.com/bitrockteam/amber-components , hope it helps.

tonai commented 5 years ago

Ok thanks to @moebiusmania it finally works.

I works whenI use components with typescript and the following tscongif.json file (the same as the one in the @moebiusmania repository):

{
  "compilerOptions": {
    "target": "esnext",
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "module": "es6",
    "experimentalDecorators": true,
    "moduleResolution": "node"
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

Is there a special webpack config to use, or something, to make it works with js files ?

web-padawan commented 5 years ago

@tonai here is the example of the proper babel-loader config.

In case you don't use Polymer legacy (especially custom-style), you can leave it like this:

    {
      test: /\.js$/,
      loader: 'babel-loader',
      exclude: /node_modules\/(?!(lit-html|@polymer)\/).*/,
      options: {
        cacheDirectory: true
      }
    }
tonai commented 5 years ago

Ok it's fixed ! Thanks @web-padawan your comment sent me in the right direction.

Storybook comes with its own webpack configuration and at the beginning I was not seeing what was wrong in it. It finaly appear that the important part is the exclude section.

So I have added this rule in the storybook configuration by adding a .storybook/webpack.config.js file in my project with:

module.exports = (baseConfig, env, config) => {
  config.module.rules[0].exclude = /node_modules\/(?!(lit-html|@polymer)\/).*/;
  return config;
};

And it finally worked !... for the counter-element but not for my other components.

In my other components I am using the connect helper for Redux. So I updated the RegExp to: /node_modules\/(?!(lit-html|@polymer|pwa-helpers)\/).*/ And now it's completly operationnal ! :metal: :metal: :metal:

@web-padawan in the polymer3-webpack-starter and lit-components projects, the RegExp seems to be more complete : /node_modules\/(?!(@webcomponents\/shadycss|lit-html|@polymer|@vaadin|@lit)\/).*/

Do you think the @webcomponents\/shadycss, @vaadin and @lit parts may be needed in my case ? (I intend to create an issue on the storybook project to add this exclude part, but I wonder if I'm not forgetting something.)

Thanks

web-padawan commented 5 years ago

@tonai @webcomponents\/shadycss can only be needed because of the import of these lines: https://github.com/Polymer/polymer/blob/655a64640bfacfa25480d950d1c8092c3fcf9571/lib/elements/custom-style.js#L10

https://github.com/Polymer/polymer/blob/655a64640bfacfa25480d950d1c8092c3fcf9571/lib/legacy/legacy-element-mixin.js#L10

So, if you don't use <custom-style> element, or the Polymer legacy code, you can remove it.

Regarding @vaadin, in case you depend on some of Vaadin components, you should keep it. Otherwise, it is not needed. Same applies to e.g. @material for Material Web Components.

tonai commented 5 years ago

Thank you @web-padawan for the clarification.

daniel-nagy commented 5 years ago

I get this error when targeting es5 in a typescript project. I created a simple typescript project to demonstrate the issue.

git clone https://github.com/daniel-nagy/lit-element-test.git && cd lit-element-test
npm install
npm run start

Once Webpack is done you can visit http://localhost:5000 and you will see this error in the browser's console.

If you change the target to es2015 and restart the Webpack server the error will go away.

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "lib": ["dom", "es2017"],
    "moduleResolution": "node",
    "sourceMap": true,
    "strict": true,
-   "target": "es5"
+   "target": "es2015"
  }
}

Browsers tested

Chrome: 71.0.3578.80 Safari: 12.0.1 Firefox: 62.0.2, 63.0.3

sorvell commented 5 years ago

Closing as the issues here seem to have been addressed.

daniel-nagy commented 5 years ago

@sorvell how has this been addressed? Are you saying lit-element is limited to browsers that support es2015?

lkraav commented 5 years ago

Interestingly enough, I got Uncaught TypeError: Class constructor cannot be invoked without 'new' when I ran npm install for a local Vaadin component, which symlinks the directory.

webpack.config.js required resolve.symlinks: false and things started working.

daniel-nagy commented 5 years ago

To anyone else that ends up here, lit-element is distributed as es2015 so you must transpile it to es5 as part of your build process. If you're using Webpack add the following to your JavaScript rule:

{
  include: [
    // These packages are distributed as es2015 modules, therefore they need
    // to be transpiled to es5.
    /node_modules(?:\/|\\)lit-element|lit-html/
  ]
}
justinfagnani commented 5 years ago

To anyone else that ends up here, lit-element is distributed as es2015 so you must transpile it to es5 as part of your build process.

Note, this is only true if you're targeting older browsers like IE11. All modern browsers support classes. Most modern JS, and definitely LitElement & lit-html, will run faster if you send ES6+ to browsers.

jennasalau commented 5 years ago

If you are seeing this error, you are likely in need of two important babel plugins. Additionally if you have ES6 code inside node_modules you MUST include them in your babel loader. LitElement library itself is ES6.

I was having this issue with Gatsby. Gatsby 2 by default includes code from all node_modules so all I had to do was

Install these modules and add this to my .babelrc, the order is important.

"plugins": [
    ["@babel/plugin-proposal-class-properties", { "loose": true }],
    ["@babel/plugin-transform-classes", {"loose": true}]
]

For Gatsby, I did this by following the instructions at https://www.gatsbyjs.org/docs/babel/#how-to-use-a-custom-babelrc-file

ghost commented 5 years ago

To anyone else that ends up here, lit-element is distributed as es2015 so you must transpile it to es5 as part of your build process. If you're using Webpack add the following to your JavaScript rule:

{
  include: [
    // These packages are distributed as es2015 modules, therefore they need
    // to be transpiled to es5.
    /node_modules(?:\/|\\)lit-element|lit-html/
  ]
}

It solves the problem for me, but for some reason it prevents arrow functions from being transpiled. In other words, arrow syntax is not replaced neither in lit-element package nor in my app code.

Here's a snippet from package.json

    "@babel/core": "^7.4.3",
    "@babel/preset-env": "^7.4.3",
    "babel-loader": "^8.0.5",
mihaisavezi commented 5 years ago

If you are seeing this error, you are likely in need of two important babel plugins. Additionally if you have ES6 code inside node_modules you MUST include them in your babel loader. LitElement library itself is ES6.

I was having this issue with Gatsby. Gatsby 2 by default includes code from all node_modules so all I had to do was

Install these modules and add this to my .babelrc, the order is important.

"plugins": [
  ["@babel/plugin-proposal-class-properties", { "loose": true }],
  ["@babel/plugin-transform-classes", {"loose": true}]
]

For Gatsby, I did this by following the instructions at https://www.gatsbyjs.org/docs/babel/#how-to-use-a-custom-babelrc-file

THIS works, and the problem happens even on Chrome 75. So I don't see why this isn't included by default.

justinfagnani commented 5 years ago

@mihaisavezi Included where by default?

n-devr commented 5 years ago

Has anyone solved this issue yet?

I have tried everything in this thread and I'm still seeing this error in Chrome if I transpile my LitElements down to ES5 even when the custom-elements-es5-adapter.js is loaded.

I tried @Manidos 's Webpack config:

{
  include: [
    // These packages are distributed as es2015 modules, therefore they need
    // to be transpiled to es5.
    /node_modules(?:\/|\\)lit-element|lit-html/
  ]
}

and the error goes away in Chrome. But upon further inspection I believe that's because the include statement means the only things that will be transpiled are lit-element and lit-html within node_modules. This means that none of the actual LitElement component definitions within my application are transpiled, thus they work just fine in Chrome.

If you rewrite the config to the following: exclude: /node_modules\/(?!(lit-element|lit-html)\/).*/ it will exclude all node modules except lit-element and lit-html. In this case, the LitElement component definitions in the app are still transpiled, and the error persists.

I have tried including the es5-adapter in JavaScript as an import in my webpack entry file as well as in my HTML as a script tag but it doesn't seem like the adapter works at all. I know the adapter is being loaded because when I inspect the HTMLElement property of window I see that it has been modified: image

@web-padawan do you think this is a Babel issue again?

I am using the following config:

"devDependencies": {
    "@babel/core": "^7.6.4",
    "@babel/preset-env": "^7.6.3",
    "babel-loader": "^8.0.6",
    "webpack": "^4.41.1",
    "webpack-cli": "^3.3.9"
  }

and my .babelrc is as follows:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage",
                "corejs": 3,
                "debug": true
            }
        ]
    ]
}

Any guidance would be greatly appreciated. I have enjoyed my experience with LitElement so far and would really like to adopt it into some of my projects. I am currently just excluding the LitElements from my babel-loader in order to continue building, but I would love to put this issue to bed because at some point I will need to properly support IE11.

web-padawan commented 5 years ago

@n-devr consider using webpack-babel-multi-target-plugin which outputs ES module bundle + ES5 "nomodule" bundle for legacy browsers. We are using it and it works nice.

n-devr commented 5 years ago

Thanks for your reply @web-padawan - I did see that solution referenced in other issues and even found the open-wc compatibility docs as well. The concept sounds great and I like the idea of reducing the bundle size for modern browsers, but with the useBuiltIns option of babel working for me I am not adding much weight to my bundle size by transpiling down to ES5 and I'd much prefer to keep my Webpack config on the simpler side. Is there no way to use the es5-adapter to accomplish this without having to produce multiple bundles?

I'm happy to create a new repo to isolate the problem and make a new issue for it if that would help. It might also save people some time to know that it's not a viable option.

web-padawan commented 5 years ago

@n-devr you only need ES5 adapter if you transpile to ES5. Otherwise, you should not include it.

Also, you can set esmodules target for @babel/preset-env: https://babeljs.io/docs/en/babel-preset-env#targetsesmodules

Regarding useBuiltIns, I haven't tried how it works with corejs 3. But AFAIK it should not affect custom elements classes when they aren't transpiled.

n-devr commented 5 years ago

Thanks for your time and quick reply @web-padawan !

My goal is to have a single bundle transpiled to ES5 that I serve to all browsers. Sorry if the useBuiltIns piece confused the matter, I was trying to explain that I wouldn't see much benefit in serving separate modern and legacy bundles because the bundle sizes would be almost the same size. I believe I am bundling and transpiling everything correctly, and my LitElements are rendering just fine when I transpile them except when I try to view them in Chrome. I am including the es5-adapter in that case but I don't think it is working properly. Is that use case simply not supported by LitElement any more?

sdykae commented 5 years ago

@n-devr Sir Did you found any solution? with webpack targeting ie 11?

n-devr commented 5 years ago

@sdyalor my issue is not the IE11 support in and of itself - if you use the webpack and babel config I referenced above you should be able to generate a bundle that is transpiled down to ES5 and works in IE11. Unfortunately, that same bundle does not work in Chrome, and the custom-elements-es5-adapter.js script that is supposed to fix this no longer seems to be working. While I haven't found a solution as of yet, @web-padawan has offered a workaround that does work, but it requires you to generate two different bundles. You can combine a plugin like: webpack-babel-multi-target-plugin with this approach: Deploying ES2015+ Code in Production Today. While it does work, I don't think it should be absolutely necessary in order to adopt lit-elements and support IE.

@web-padawan I also want to reiterate my offer to create a public repo with a simple project to demonstrate the issue. It seems like there are a good number of people who are having this issue, and if you think it would help the team to debug and potentially solve the problem I'm happy to help.

sdykae commented 5 years ago

@n-devr So, excluding Internet Explorer and knowing that custom-elements-es5-adapter.js does not work when targeting babel to ie: 11

What the formula to cover wide range of browsers is?

When I target to esmodules: true, things works well but when this is done, Do I need to load webcomponents polyfill? <script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script> with corejs3

web-padawan commented 5 years ago

@n-devr if you create an example repository, I could take a look.

However, please keep in mind the following problems:

  1. IE11 does not work well with web components because of ShadyDOM performance issues:
  1. Also, ShadyDOM breaks DevTools in IE11 (the issue is closed but not fixed):
  1. Finally, by transpiling to ES5 you also send more code to evergreen browsers.

As a result, developer experience with IE11 is horrible and the apps are too slow. This BTW is a reason why Salesforce uses own polyfill for LWC.

sslotsky commented 4 years ago

Sorry to add to a closed thread but what is the solution to this for TypeScript and Rollup? Most of what I see here is for babel and webpack. I've tried adding this to my tsconfig

    "target": "ES2015"

Rollup config looks like this

import typescript from '@rollup/plugin-typescript';
import resolve from 'rollup-plugin-node-resolve';

export default {
  input: 'src/client/main.ts',
  output: [
    {
      file: 'src/server/dist/bundle.js',
      format: 'iife',
      sourcemap: false,
    },
    {
      file: 'src/server/dist/bundle.esm.js',
      format: 'esm',
      sourcemap: false,
    },
  ],
  plugins: [
    resolve(),
    typescript({
      lib: ['es5', 'es6', 'dom'],
      target: 'es5',
    }),
  ],
};

What should I change?