Lit-element: Webpack : Lit Element new usage

Created on 22 May 2018  Â·  17Comments  Â·  Source: Polymer/lit-element

Hi everyone, what's up?

As Polymer 3 came out, I try to use it along with Webpack in order to ease my frontend logic, the problem is, if I try to "compile" the Polymer Lit Element with Webpack, I receive this error :

LitElement

Here's my element class :

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

class HomeComponent extends LitElement {

    static get properties() {
        return {
            foo: String,
        }
    }

    _render(props) {
        return html`
            <div>Hello From Polymer !</div>
        `;
    }
}

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

And here's my Webpack configuration :

const Encore = require('@symfony/webpack-encore');

Encore
    .setOutputPath('public/build/')
    .setPublicPath('/build')
    .cleanupOutputBeforeBuild()
    .enableSourceMaps(!Encore.isProduction())
    // uncomment to create hashed filenames (e.g. app.abc123.css)
    // .enableVersioning(Encore.isProduction())
    .enableTypeScriptLoader()
    .enableVueLoader()
    .enableSassLoader()
    .addLoader(
        {
            test: /\.scss$/,
            use: [
                {
                    loader: 'sass-loader',
                    options: {
                        importer: function(url, prev) {
                            if(url.indexOf('@material') === 0) {
                                const filePath = url.split('@material')[1];
                                const nodeModulePath = `./node_modules/@material/${filePath}`;
                                return {
                                    file: require('path').resolve(nodeModulePath)
                                };
                            }
                            return {
                                file: url
                            };
                        }
                    }
                }
            ]
        }
    )
    .addLoader(
        {
            test: /\.js$/,
            loader: 'babel-loader',
            query: {
                presets: ['es2015']
            }
        }
    )

    // Style
    .addStyleEntry('core', './assets/scss/public/core.scss')
    .addStyleEntry('registration', './assets/scss/public/registration.scss')

    // Javascript
    .addEntry('form', './assets/javascript/components/form.js')
    .addEntry('snackbar', './assets/javascript/components/form/snackbar.js')

    // PWA
    .addEntry('serviceWorker', './assets/javascript/pwa/app.js')
    .addEntry('sw', './assets/javascript/pwa/sw.js')

    // Vue
    .addEntry('vue', './assets/vue/public/index.js')

    // Polymer
    .addEntry('home', './assets/javascript/polymer/home.js')
;

module.exports = Encore.getWebpackConfig();

_Information:_ Encore is a PHP/Symfony library, that's just a wrapper around Webpack so the call stay easily the same.

Does anyone have more information about the Webpack usage with lit-Element ?

Thanks for the help :)

Most helpful comment

The webcomponents spec mandate that components are created as classes and the new operator is called. If you are transpiling to es5, the components are no longer classes and thus the browsers that implement native web components will complain.

The webcomponentsjs project contains an adapter to handle this: https://github.com/webcomponents/webcomponentsjs/blob/master/custom-elements-es5-adapter.js the polymer build tools add this adapter automatically, with webpack you'd need to add it yourself next to where you're loading the polyfill. You just need to make sure you only add this adapter if you're transpiling to es5.

All 17 comments

The webcomponents spec mandate that components are created as classes and the new operator is called. If you are transpiling to es5, the components are no longer classes and thus the browsers that implement native web components will complain.

The webcomponentsjs project contains an adapter to handle this: https://github.com/webcomponents/webcomponentsjs/blob/master/custom-elements-es5-adapter.js the polymer build tools add this adapter automatically, with webpack you'd need to add it yourself next to where you're loading the polyfill. You just need to make sure you only add this adapter if you're transpiling to es5.

Ok, I saw, I've just tried to compile to es2016 and a new error is thrown :

ES2016

This seems related to #54 but I don't really know how to get rid of this error as the loader isn't called during the compilation phase and the node_modules folder isn't publicly accessible throught the front controller of my application.

Hi,
i use webpack to build a "lit-element project" that way (no babel), it copy/bundle app files in the build directory.

const webpack = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");

module.exports = {
  entry: {
    app: "./src/my-app.js"
  },
  output: {
    filename: "./build/app/[name].js"
  },
  plugins: [
    new CopyWebpackPlugin([
      { from: "index.html", to: "build/" },
      { from: "robots.txt", to: "build/" },
      { from: "img", to: "build/img" },
...
      {
        from: "node_modules/@webcomponents/webcomponentsjs/*js",
        to: "build/webcomponentsjs/",
        flatten: true
      }
    ])
  ]
};

@eskan Ok, just tried your configuration (using es2015) and my components is correctly instantiated and the DOM display the shadow-root element, really impressive trick, many thanks :).

@LarsDenBakker Maybe it could be a great idea to update the documentation and add a Webpack example ?

Related to Polymer/tools#398

@Guikingone I am not familiar with WebPack nor its configuration, so please bare with me with the following questions :joy:

I have been reading through the WebPack project and spotted https://github.com/webpack/webpack#browser-compatibility If I understand correctly, this requirement is for the runtime it provides as well as any code you write. Is there any way WebPack can be configured to NOT transpile and keep the ES2015?

Also it appears to be using Babel, but that is a whole different transpilation method. I am not sure whether this is an issue with Babel or with WebPack. Could you clarify if you are using Babel and if so which package is breaking the polyfills?

All in all, the adapter you use should be used on browsers that DO support ES6 to load ES5. E.g. if you are on the latest version of Chrome, load the ES6 adapter with the ES5 element definition and it should work. We somehow have to tell WebPack to not touch the adapter code and leave it alone (or just not transpile to ES5 at all, which is the best situation). This is elaborated upon in more detail in https://github.com/webcomponents/webcomponentsjs#custom-elements-es5-adapterjs

I've been experimenting with using Webpack as an alternative build step for pwa-starter-kit in its webpack branch. The tricky part is making sure the webcomponentsjs polyfills get copied without transpilation and writing a custom html plugin to handle transforming index.html. Currently it's configured for es2015 transpilation, but you can set es2015 = false at the top of webpack.config.js to skip that.

This code is _an experiment_ (read: not production ready) as it only handles the current state of pwa-stater-kit, but should be a good starting point until I get more time to work on this.

I managed to create a working example with Polymer 3, Webpack 4 and Babel 7 today, after struggling with various issues and implementing the workarounds, especially some of those by polymer-build:

  • pin @babel/plugin-transform-classes to v7.0.0-beta.35 (Polymer/tools#216)
  • list needed plugins explicitly, instead of using preset-env
  • pass some options to babel-preset-minify
  • use @babel/plugin-external-helpers, add babelHelpers generated by polymer-build manually

Also I had to workaround the issue with latest babel-loader beta not picking up .babelrc. The overall feeling so far is that Babel betas are still not ready enough, and the Good Luck With That Public License would be a really good fit here. I might even create a separate repo for that purpose 😆

Here is the result: https://github.com/web-padawan/polymer3-webpack-starter

Fixed with babel/babel#8501 and included in Babel 7 release, so the issue can be closed.
I have updated my starter from the above comment in case anyone wants to try.

Is not working for me

                {
                    test: /\.js$/,
                    exclude: config.babel.exclude,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            plugins: [
                                '@babel/plugin-syntax-dynamic-import',
                                '@babel/plugin-transform-classes',
                                '@babel/plugin-transform-react-jsx',
                                '@babel/plugin-proposal-object-rest-spread'
                            ],
                            presets: [['@babel/preset-env', {
                                modules: false,
                            }]],
                            cacheDirectory: true
                        }
                    }
                }
        "@babel/core": "7.0.0",
        "@babel/plugin-proposal-object-rest-spread": "7.0.0",
        "@babel/plugin-syntax-dynamic-import": "7.0.0",
        "@babel/plugin-transform-react-jsx": "7.0.0",
        "@babel/polyfill": "7.0.0",
        "@babel/preset-env": "7.0.0“,
        "@babel/plugin-transform-classes": "7.0.0",

@Hanterdro note that you also need to include proper babel helpers.
See web-padawan/polymer3-webpack-starter@6142d59

@web-padawan
Thanks for your response.

Did I ge this right, to get lit-elements working with webpack and babel7 I have to do some hacky stuff with some „babel-external-helpers“?
Sorry, but I wouldn’t call this bug as resolved. :(
It may be resolved for the polymer3-webpack-starter repo, but not for the rest of the world.

If I use lit-elements in a default webpack setup with babel and the es5-adapater for webcomponents I get the

Class constructor LitElement cannot be invoked without ‚new‘

I think it should work with the @babel/preset-env setup I posted above to consider it as fixed.

However I tried your babel-external-helpers workaround, but I didn’t it to work in my setup.

It’s not easy to figure out what to do and what is relevant after

https://babeljs.io/docs/en/babel-plugin-external-helpers

has no docu what this plugin is doing.

I recognized that you have a node script to bundle some helper files from the white list which you register in the extenal-helper plugin.

What I assume what is happening, in the babel-env is a plugin/helper file which cause the new error for lit-element and you excluded this one in your whitelist.

We should be able to make working setup with @babel/preset-env. Will try and inform you here.

Thanks that would be very nice!
Try to figure out a working setup, currently too. But I really don’t know what I’m doing.
I just play try and error ;)

I think I found the issue!

I used in my babel config the following exclude pattern:

  exclude: /node_modules\/(?!(([^\/]+?\/){1,2}(src|es6)))/

No file in the node_modules dir was babeled, expect the src or es6 directory of a module.
The problem is, that lit-elements does not bundle a es5 version (unlike most other libs).

After I change my babel behavior in way that lit-elements got babeled everything seems working. I replaced my exclude with this include:

include: [/node_modules\/*\/src/, /node_modules\/@polymer/, /node_modules\/lit-html/, /src/],

It works with the @babel/preset-env config I posted above.

It would be great if lit-elements would also distribute a es5 version.

Hello guys
@Hanterdro I think i have the same configuration than you but still not working

Closing as the issues here seem to have been resolved.

Was this page helpful?
0 / 5 - 0 ratings