The Polymer modulizer tool automates most of the changes you need to make to upgrade a project from 2.x to 3.x.
Polymer modulizer is still in pre-release, but has been fairly well-tested converting the Polymer library and elements. See the README for the latest updates.
Start with a clean repo. Before upgrading an element, make sure that any changes in your repo are committed, so you don't lose any previous changes, and can roll back to a previous state if you run into trouble.
Upgrade a project using modulizer
To upgrade a project:
-
Install the latest version of the Polymer CLI.
npm install -g polymer-cli
-
Install Polymer modulizer.
npm install -g polymer-modulizer
-
Make sure your bower dependencies are up to date.
bower cache clean && bower install
-
Run modulizer.
modulizer --import-style name --out .
The
--import-style name
option tells modulizer to write imports using package names instead of paths. You might want to omit this option when converting an application project.The
--out .
option tells modulizer to write its output to the current directory, overwriting its current contents. -
Test the project.
You may need to perform some manual fixes at this point. See Post-conversion cleanup for details.
Post-conversion cleanup
There are a few manual steps that may be required after converting a project using modulizer.
-
Fix strict mode and module errors.
Modules always run in strict mode, and with various other restrictions. A few examples include:
- Variables must be declared.
this
is undefined at the top level.document.write
doesn't work.document.currentScript
doesn't work.
Modulizer can't make these changes for you.
-
Convert
importHref
to dynamicimport
()For example, this:
const resolvedPageUrl = this.resolveUrl('my-page.html'); Polymer.importHref( resolvedPageUrl, null, this._showPage404.bind(this), true);
Becomes:
import('./my-page.js').then(null, this._showPage404.bind(this));
Note that
resolveUrl
is not required. Imports are always resolved relative to the current module. -
Fix imports that load polyfills.
In the past some elements have provided HTML imports for loading polyfills. For example, the neon-animation element provided an HTML import to load the web animations polyfill.
In the ES6 module world, the extra file is no longer required. Any required polyfills should be loaded at the application level.
For example, given a
some-polyfill.html
file that loads a polyfill script:<script src="./bower_components/some_polyfill/some_polyfill.js"></script>
Move the script into the main document.
Steps automated by modulizer
This section lists the changes made by Polymer modulizer. They may be helpful as an overview of what the Polymer modulizer is doing to convert your code.
Before upgrading an element, make sure that any changes in your repo are committed, so you don't lose any previous changes, and can roll back to a previous state if you run into trouble.
-
Rename the file from
.html
to.js
.mv my-el.html my-el.js
-
Convert HTML imports for ES6 module imports.
<link rel=import href="foo.html">
Becomes:
import './foo.js';
For importing resources that are part of the same project (for example, app-specific elements), use an absolute path (starting with
/
) or a relative path (starting with./
or../
) .For importing resources installed using npm, use a module specifier starting with the package name. For example, for Polymer imports, you'll usually replace a path like
/bower_components/polymer
with@polymer/polymer
.In many cases, you'll need to import specific symbols. For example, the standard Polymer element import:
<link rel="import" href="../bower_components/polymer/polymer-element.html">
Becomes:
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js'
-
Move the template from HTML into a static
template
getter.<html> <dom-module> <template>foo</template> </dom-module> <script> class A extends Polymer.Element { … } </script> </html>
Becomes:
import {PolymerElement, html} from '@polymer/polymer'; class A extends PolymerElement { static get template() { return html`foo`; } }
-
Replace namespaced references with imports.
<link rel="import" href="/bower_components/polymer/lib/utils/render-status.html">
And:
Polymer.RenderStatus.afterNextRender(callback);
Becomes:
import {afterNextRender} from '@polymer/polymer/lib/utils/render-status.js'; ... afterNextRender(callback);
The 3.x modules correspond to the 2.x HTML imports. See the 3.0 API docs for a list of exports from each module.
-
Remove any top-level IIFE wrappers.
Modules automatically encapsulate their contents.
(() => { let foo; })();
Becomes:
let foo;
-
Add
importMeta
static getter, if necessary.If you use the
importPath
property in your element's template, you must add a staticimportMeta
getter:class A extends PolymerElement { static get importMeta() { return import.meta; } }
-
Convert
polymer.json
Replace the
.html
filenames with their.js
equivalent.If you're using the polyfill loader (
webcomponents-loader.js
), update theextraDependencies
array to include the new webcomponents bundles:"extraDependencies": [ "node_modules/@webcomponents/webcomponentsjs/bundles/**" ],
-
Convert
bower.json
topackage.json
.Add all of your dependencies here. Note that
package.json
can only have one main file. -
Update to newer polyfills
Make sure you're depending on the v2 versions of the polyfills in
package.json
."@webcomponents/webcomponentsjs": "^2.0.0",
If you were using
webcomponents-lite.js
, replace it withwebcomponents-bundle.js
. -
Update top-level
.html
files.Certain HTML files stay as HTML—things like an index.html with content, or test documents. They usually just need to update
<link rel=import>
to<script type=module>
Some projects may also need the steps described in Less common upgrade tasks, which are typically performed by modulizer.
If you are converting by hand, see Post-conversion cleanup for the final steps.
Less common upgrade tasks
This section describes a few tasks which aren't required for every element, but which may be required for some elements or modules.
Move any non-template DOM into imperative code
If your module contains any DOM that's not part of the element template, you'll need to add imperative code to insert it into the main document. For example, if your 2.x HTML import includes a <custom-style>
tag, you could replace it with code like this:
const $_documentContainer = document.createElement('template');
$_documentContainer.innerHTML = `<custom-style>
<style>
html {
--theme-color: #eee;
}
</style>
</custom-style>`
document.head.appendChild($_documentContainer.content);
Replace namespace declarations with exports
If you have a module that added properties or functions to a global namespace, use exports to make the APIs available, instead.
For example:
const MyStuff = MyStuff || {};
MyStuff.MyMixin = (base) => class extends base { ... };
Instead of appending to the MyStuff
namespace, the module can simply export MyMixin
:
export const MyMixin = (base) => class extends base { ... };
Generally remove this
references that refer to the namespace object.
Foo.Bar = {
one() {
return this.two();
},
two() {
return 2;
}
}
Becomes:
export function one() {
return two();
}
export function two() {
return 2;
}
Likewise you don't need to bind functions where the this
value should be the namespace object.
Foo.Bar = {
one() {
el.addEventListener('click', this.onclick.bind(this));
},
onclick() {...}
}
Becomes:
export function one() {
el.addEventListener('click', onclick);
}
export function onclick() { … }