How to use CustomElement v1 polyfill in webpack / babel build?
I am having trouble getting this WebComponents polyfill + native-shim to work right on all devices though webpack.
Some details about my setup: * Webpack2 + babel-6 * application is written in ES6, rewriting ES5 * imports a package node_module
written in ES6 that defines / registers to CustomElement
be used in the application
So the corresponding webpack dev config looks something like this:
const config = webpackMerge(baseConfig, {
entry: [
'webpack/hot/only-dev-server',
'@webcomponents/custom-elements/src/native-shim',
'@webcomponents/custom-elements',
'<module that uses CustomElements>/dist/src/main',
'./src/client',
],
output: {
path: path.resolve(__dirname, './../dist/assets/'),
filename: 'app.js',
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
include: [
path.join(NODE_MODULES_DIR, '<module that uses CustomElements>'),
path.join(__dirname, '../src'),
],
},
],
},
...
the key takes: * I need the CustomElement to be loaded before <module that uses CustomElements>
* I need to <module that uses CustomElements>
load before my application * <module that uses CustomElements>
is ES6, so we drag and drop it (thus include in babel-loader
).
The above works are expected in modern ES6 browsers (IE desktop browser), HOWEVER
it doesn't work in older browsers. I am getting the following error in older browsers like iOS 8:
SyntaxError: Unexpected token ')'
pointing to the anonymous open function in the stuffed pollyfill:
(() => {
'use strict';
// Do nothing if `customElements` does not exist.
if (!window.customElements) return;
const NativeHTMLElement = window.HTMLElement;
const nativeDefine = window.customElements.define;
const nativeGet = window.customElements.get;
It seems to me that native-shim
needs to be translated to ES5:
include: [
+ path.join(NODE_MODULES_DIR, '@webcomponents/custom-elements/src/native-shim'),
path.join(NODE_MODULES_DIR, '<module that uses CustomElements>'),
path.join(__dirname, '../src'),
],
... but now this breaks both Chrome and iOS 8 with the following error:
app.js:1 Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
at new StandInElement (native-shim.js:122)
at HTMLDocument.createElement (<anonymous>:1:1545)
at ReactDOMComponent.mountComponent (ReactDOMComponent.js:504)
at Object.mountComponent (ReactReconciler.js:46)
at ReactCompositeComponentWrapper.performInitialMount (ReactCompositeComponent.js:371)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:258)
at Object.mountComponent (ReactReconciler.js:46)
at Object.updateChildren (ReactChildReconciler.js:121)
at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:208)
at ReactDOMComponent._updateChildren (ReactMultiChild.js:312)
.. which brings me to this line constructor()
in native-shim:
window.customElements.define = (tagname, elementClass) => {
const elementProto = elementClass.prototype;
const StandInElement = class extends NativeHTMLElement {
constructor() {
Uf. So it's very unclear to me how we actually include this in a webpack based build where the dependency using the CustomElements is ES6 (and needs forwarding).
- Passing inline step to es5 doesn't work.
- using inline-shim as-is at the top of the package entry point doesn't work for iOS 8, but for Chrome
- not including native-shim breaks both Chrome and iOS
I am very frustrated with web components at the moment. I just want to use this dependency which is generated with web components. How can I get it to work correctly in webpack build and work on all devices? Am I missing something obvious here?
Config my .babelrc for posterity (most suitable dev option):
{
"presets": [
["es2015", { "modules": false }],
"react"
],
"plugins": [
"transform-custom-element-classes",
"transform-object-rest-spread",
"transform-object-assign",
"transform-exponentiation-operator"
],
"env": {
"test": {
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "./cfg/test.js" } ]
]
},
"dev": {
"plugins": [
"react-hot-loader/babel",
[ "babel-plugin-webpack-alias", { "config": "./cfg/dev.js" } ]
]
},
"dist": {
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "./cfg/dist.js" } ],
"transform-react-constant-elements",
"transform-react-remove-prop-types",
"minify-dead-code-elimination",
"minify-constant-folding"
]
},
"production": {
"plugins": [
[ "babel-plugin-webpack-alias", { "config": "./cfg/server.js" } ],
"transform-react-constant-elements",
"transform-react-remove-prop-types",
"minify-dead-code-elimination",
"minify-constant-folding"
]
}
}
}
source to share
I was able to achieve something similar with the .babelrc
plugin below. It looks like the only differences are https://babeljs.io/docs/plugins/transform-es2015-classes/ and https://babeljs.io/docs/plugins/transform-es2015-classes/ , but I honestly I can't remember which problems are being solved specifically:
{
"plugins": [
"transform-runtime",
["babel-plugin-transform-builtin-extend", {
"globals": ["Error", "Array"]
}],
"syntax-async-functions",
"transform-async-to-generator",
"transform-custom-element-classes",
"transform-es2015-classes"
]
}
source to share