UniteJS — The Road To Polymer 3

October 31, 2017 0 Comments

UniteJS — The Road To Polymer 3

 

 

Trying to support all the modern frameworks with UniteJS is not an easy task. Polymer is no exception, in fact we have waited until Polymer 3 (still in preview) before we attempted it.

One of the main concerns with earlier versions was the use of bower as a package manager (bower themselves even recommend Yarn on their homepage). But with v3 of Polymer they have switched to npm, one hurdle out of the way.

To create our bootstrap app with the same functionality as all of our other supported frameworks( routing, decorators, templates, styling) the polymer packages we are using are:

https://github.com/Polymer/polymer/tree/3.0-preview
https://github.com/Polymer/polymer-decorators
https://github.com/webcomponents/webcomponentsjs
https://github.com/PolymerElements/app-route
https://github.com/PolymerElements/iron-pages

All the repos and the npm packages contain only an ES6 version of the code which means some sort of transpilation is needed in the build workflow.

These days the task of transpilation is often offloaded to webpack but since we want to support other module loaders/bundlers we need another solution. Also having the bundler do this transpilation repeatedly is a waste of time and resources.

Since none of our current frameworks/libraries were purely ES6 a new build step was needed, we have added a new process “build-transpile-modules”. Client packages now have options for transpilation which the new task will use to create a version of the module for the module loader/bundler you have configured. If the transpiled version already exists it is not rebuilt, saving lots of time during the complete build process (an update to your unite configuration will delete the transpiled version in case your new options affect the transpilation).

With this new step in place we could create versions of the packages in a new location, well nearly. As is the way with pure ES6 code it does not rely on a module loader to locate other packages, instead the includes for other packages like webcomponents use deep nested relative paths e.g.

import '../../../../@webcomponents/shadycss/entrypoints/apply-shim.js';

Since polymer is targeting modern web browsers which load the modules directly and have no concept of named resolution it has to import everything in this manner. However this does not work when trying to use the module as a package and since the webcomponents have been transpiled to another location the relative paths fail :(

To solve this our new build task was extended to allow for some transpile transforms. In this case

import '../../../../@webcomponents/......js';

is replaced with

import '@webcomponents/......js';

You can just set a list of modules to tranform their imports in this manner and not worry about how the transform itself works.

Browsers that can load modules in the new way also need the import statements to include the .js extension, but other module loaders fail with the .js included, so another transform was introduced to strip the extensions for use with the module loaders. Just set the transpile.stripExt flag for the clientPackage.

With these transforms in place the code compiled and ran successfully in all the different module loader/bundler combinations. You can see the unite.json config for all the clientPackages below

A component consists of code, template and styling and we wanted to support these in the way we do all other frameworks, keeping them independent to allow for other operations on them, like styling from sass, less, stylus or minifying html etc

To keep a consistency between the JavaScript and TypeScript versions we wanted to use the polymer class decorators. However they are written in TypeScript which of course would not work with our JavaScript classes, so as you will see in the clientPackages example above we transpiled that library as well.

So using polymer decorators and sticking with imports for the style and template we produce the following for our JavaScript components.

To support TypeScript there were another couple of issues that had to be tackled. The current Polymer definitions are not up to date, a new automated process from the Polymer team is in place for these to improve long term.

Using imports for our style/template we needed to let TypeScript know about the css and html files so we introduce a a .d.ts file with some definitions.

Secondly the inclusion of the polymer-element did not get recognised as part of the current TypeScript definitions, so we introduce our own based on the library definitions, this will probably be removed when the official definitions are updated:

With these extra definitions in place we can successfully compile our TypeScript version of the component, which looks almost identical to the JavaScript version.

When it came to unit testing the only engine that we could currently use from those in UniteJS was ChromeHeadless, both PhantomJS and JSDom were lacking some modern browser features required by web components. Maybe these features could be polyfilled but ideally they will be added to the engines (unlikely with PhantomJS as it is end of line and they recommend ChromeHeadless).

E2E testing using Protractor or Webdriver both suffer from the same issue and that is the state of flux that ShadowDOM is in.

Eventually both of the E2E runners and the associated browser drivers will support ShadowDOM but for now we needed a solution.

To allow for ShadowDom lookups the code from here https://github.com/angular/protractor/issues/4367 was incorporated into our own plugins for both Protractor and Webdriver.

E2E test lookups can now work even for elements deep in the ShadowDOM using the customShadowRoot locator from the plugins.

Now with all of this in place you can use Polymer with UniteJS just like any other framework, for example using the TypeScript profile:

unite configure --packageName=polymer-ts --title="Polymer TypeScript" --profile=PolymerTypeScript --outputDirectory=polymer-ts

or the JavaScript profile:

unite configure --packageName=polymer-js --title="Polymer JavaScript" --profile=PolymerJavaScript --outputDirectory=polymer-js

You can always try out some of the other options using the command line, or make your life easier and use the online generator http://unitejs.com/#/generator

After a lot of head scratching and some new requirements Polymer 3 is now supported by UniteJS. As you can see in the screen grab below we have a page that contains polymer elements which have been routed to a child element with the ShadowDOM clearly visible.

Unit tests and E2E tests work, as do the rest of the UniteJS features like themes, bundling and packaging.

With that in mind how about Polymer 3 packaged as an Electron app, we can do that.

Now which framework to add to UniteJS next ?

Martyn Janes @martynjanes
Senior Developer UniteJS

For more information, documentation and the online CLI generator visit the web site at http://unitejs.com
Npm: https://www.npmjs.com/package/unitejs-cli
GitHub: https://github.com/unitejs
Gitter Chat: https://gitter.im/unitejs/discuss
Twitter: https://twitter.com/UniteJS


Tag cloud