3

I don't think this is a duplicate issue. I only have @polymer/polymer installed as a dependency and imported into my vendor bundle (no @polymer/paper-input). I'm using v3.0.5 and I don't even see iron-meta in the dependency tree (via npm list) and my stack trace looks different - it points to polymer/lib/elements/dom-module.js


dom-module.js:178 Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry

The trace points to this line customElements.define('dom-module', DomModule);

at @polymer/polymer/lib/elements/dom-module.js?:178:16

I'm attempting to setup a basic Polymer 3 project. I'm using Webpack with babel-loader to compile to es5. Because I'm compiling to es5, I'm including the custom-elements-es5-adapter.js along with webcomponents-bundle.js per instructions on the webcomponentsjs repo. Those scripts are simply copied from node_modules to the output directory and the script tags are included in the html head.

As for my component code, I'm creating separate js chunks for each polymer component as well as a separate chunk for shared imports which currently only includes Polymer. The compilation and code splitting works without error and the resulting chunks are added to the html before the closing body tag.

The Webpack SplitChunks plugin pulls the @polymer/polymer imports into the separate chunk so that they are only included once.

The goal is to have all required vendor code pulled into a common script and each component in a tiny chunk of it's own that can be selectively included.

  • my-common.js (shared/common chunk)
  • my-button.js (component chunk)
  • my-tabs.js (component chunk)
  • ...more component chunks

With my current setup, the chunks appear to be created correctly.

In theory and based on what I've read so far, this should work but I'm completely stuck on this error.

If I bundle my component files together, everything works fine.


Here's an example of one of my very simple component files:

import { html, PolymerElement } from '@polymer/polymer';

export default class MyButton extends PolymerElement {
  constructor() {
    super();
  }

  static get template() {
    return html`
      <slot></slot>
    `;
  }

  static get properties() {
    return { }
  }
}

customElements.define('my-button', MyButton);

Here is the webpack config I've created for this proof of concept:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

const SRC_PATH = path.resolve(__dirname, './src');
const DIST_PATH = path.resolve(__dirname, './dist');

module.exports = {
  entry: {
    'my-button': `${SRC_PATH}/js/components/my-button.js`,
    'my-tabs': `${SRC_PATH}/js/components/my-tabs.js`
  },
  output: {
    filename: 'js/[name].js',
    path: DIST_PATH
  },
  resolve: {
    extensions: ['.js']
  },
  module: {
    rules: [{
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: [[
            'env',
            {
              targets: {
                browsers: [
                  'last 2 versions',
                  'ie > 10'
                ]
              },
              debug: true
            }
          ]]
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: `${SRC_PATH}/index.html`,
      filename: 'index.html',
      inject: 'head'
    }),
    new CopyWebpackPlugin([{
      from: './node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js',
      to: 'js/vendor',
      toType: 'dir'
    }, {
      from: './node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js',
      to: 'js/vendor',
      toType: 'dir'
    }, {
      from: './node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js',
      to: 'js/vendor',
      toType: 'dir'
    }]),
    new BundleAnalyzerPlugin()
  ],
  optimization: {
    splitChunks: {
      cacheGroups: {
        default: false,
        commons: {
          name: 'my-common',
          chunks: 'all',
          minChunks: 2
        }
      }
    },
    minimizer: [
      new UglifyJSPlugin({
        uglifyOptions: {
          ie8: false,
          safari10: false,
          compress: {
            warnings: false,
            drop_console: true
          },
          output: {
            ascii_only: true,
            beautify: false
          }
        }
      })
    ]
  },
  devServer: {
    contentBase: DIST_PATH,
    compress: false,
    overlay: {
      errors: true
    },
    port: 8080,
    host: '127.0.0.1'
  }
};

And here's the html:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
  <title>polymer-3-sandbox</title>
  <meta name="description" content="A polymer 3 sandbox">
  <link rel="manifest" href="/manifest.json">
  <script src="/js/vendor/webcomponents-bundle.js"></script>
  <script src="/js/vendor/custom-elements-es5-adapter.js"></script>
  <script type="text/javascript" src="js/my-common.js"></script> 
  <script type="text/javascript" src="js/my-button.js"></script>
  <script type="text/javascript" src="js/my-tabs.js"></script>
</head>

<body>    
  <p>
    <my-button>Learn More</my-button>
  </p>
</body>

</html>
Jbird
  • 2,839
  • 1
  • 21
  • 28
  • 1
    Possible duplicate of [Polymer 3.0 Uncaught DOM Exception When using Paper-Input](https://stackoverflow.com/questions/50355644/polymer-3-0-uncaught-dom-exception-when-using-paper-input) – tony19 Aug 10 '18 at 03:45
  • Thanks @tony19. Doesn't it seem like a different issue though? I only have @polymer/polymer installed as a dependency and imported into my vendor bundle (no @polymer/paper-input). I'm using v3.0.5 and I don't even see iron-meta in the dependency tree (via npm list) and my stack trace looks different - it points to `polymer/lib/elements/dom-module.js`. None the less, you're suggested shim works as expected. Ideally this issue could be resolved without it though so I'll keep looking into it this weekend. – Jbird Aug 10 '18 at 23:56
  • @Jbird did you eventually find a solution? I'm having the same issue right now. – Andre Oct 16 '18 at 22:52
  • 2
    @Andre Yes, if you add the following to a script before your polymer app code, it will ensure that custom elements are only declared once. const _customElementsDefine = window.customElements.define; window.customElements.define = (name, cl, conf) => { if (!customElements.get(name)) { _customElementsDefine.call(window.customElements, name, cl, conf); } }; – Jbird Oct 17 '18 at 16:22
  • ty, I actually found that as well in another stack overflow post, and it did solve the problem. A bit annoying because my base html template is now a mess of patches, polyfills and various babel helpers .... the joy of modern web development – Andre Oct 17 '18 at 19:47

1 Answers1

-2

We have solved this problem with a nested polymer removal script, check the original github issue here.

The trick is to get npm to run a preinstall.sh script by adding the following to your package.json file :

  "scripts": {
    "preinstall": "../preinstall.sh"
  }

Then run the following script which installs npm scriptlessly twice to get around install bugs :

#!/bin/bash
# Author: Flatmax developers
# Date : 2018 10 17
# License : free

npm i --ignore-scripts || true
if [ `ls node_modules/ | wc -l` -eq "0" ]; then
  zenity --error --text="ERROR : cb() never called\nrm node_modules and pacakge-lock.json and try again"
fi
npm i --ignore-scripts || true
if [ `ls node_modules/ | wc -l` -eq "0" ]; then
  zenity --error --text="ERROR : cb() never called\nrm node_modules and pacakge-lock.json and try again"
fi
. ../fixNestings.sh

Finally, the actual nestings removal script is like so :

#!/bin/bash
# Author: Flatmax developers
# Date : 2018 10 17
# License : free

# The following function will remove nested directories, where $1 exists like so
# node_modules/.*/node_modules/$1
# @param $1 the module name to remove nestings of
function rmNestedMod(){
  name=$1
  paths=`find -L node_modules -name $1 | sed "s|^node_modules/||;s|/\$name$||" | grep node_modules`
  for p in $paths; do
    echo rm -rf node_modules/$p/$name
    rm -rf node_modules/$p/$name
  done
}

# remove all nested polymer namespaces
namespaces=`ls node_modules/@polymer/`
for n in $namespaces; do
  rmNestedMod "$n"
done
Matt
  • 509
  • 2
  • 14
  • 1
    The solution I mention in a comment above is a much more simple solution (~4 lines) to solve this specific error and it’s harmless to the rest of your project. You should consider using it instead. Also, I’m not clear on what you mean by “...polymer modules which are nested in node_modules inside the top level node_modules...”. That comment is confusing and makes me think you’re dealing with a different issue than the one originally posted here. The ‘cannot define’ error can occur for a litany of reasons. Consider revisiting your answer to clarify how it actually solves the specific scenario. – Jbird Oct 18 '18 at 04:12
  • That solution requires changing the app code to fix this bug. It is likely that the nesting problem will be fixed upstream at some point - which would make an in-app solution redundant - as Anrae mentions above "my base html template is now a mess of patches ...". In my opinion it is important to avoid that. – Matt Oct 18 '18 at 22:35
  • That’s not correct. It requires zero change to your app code. It is an isolated shin. It’s only int the html template if you put it there. When it’s fixed “upstream”, you simply remove the shim. – Jbird Oct 18 '18 at 22:38