Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

diary - implementation thoughts #5

Open
sashafirsov opened this issue Apr 4, 2018 · 17 comments
Open

diary - implementation thoughts #5

sashafirsov opened this issue Apr 4, 2018 · 17 comments
Labels
diary question Further information is requested

Comments

@sashafirsov
Copy link
Member

<embed-page> overall nuances.

@sashafirsov
Copy link
Member Author

sashafirsov commented Apr 4, 2018

ES5 implementation could give more syntax sugar than ES6 classes. In ES6 the private data need to be routed by accessor methods defined as closure in constructor. Which anyway are not private and insulated just symbolically by '_' prefix. The trouble is in need to preserve the slot and other internal variables which do not meant to be exposed for security reasons.

The simulation of class definition by function (ES5 approach) from closure could give the private variables shared across methods without exposing those via accessor methods.

Unfortunately arrow functions and other nice ES6 features would require either compiled version or abandon the legacy browsers.

At the moment leaving the compilation to application. To make it simpler the embed-page.js will be in UMD format compatible with Babel and WebPack.

Links:

@sashafirsov
Copy link
Member Author

HTML or JS for deployment?
Import of WebComponent via <link rel="import" href="..."/> uses HTML format for WC definition. While it is a fist and IMO most convenient way to define complex WC, it is not applicable for embed-page which has a primitive dom structure and no other WC dependencies. The embed-page code is 99% JS and there is no need for HTML as independent layer.

The complete functionality of embed-page would be available as UMD-wrapped JS. The embed-page.html would be kept for backward compatibility and will serve only JS inclusion purpose.

@sashafirsov sashafirsov added diary question Further information is requested labels Apr 4, 2018
@sashafirsov
Copy link
Member Author

sashafirsov commented Apr 14, 2018

In POC inherited from HTMLElement(not Polymer.Element) Still could not catch the event on Polymer binding update to substitute the string presentation with secured dom. <iron-ajax last-response="{{htmlContent}}"/><embed-page>[[htmlContent]]</embed-page>

So far tried

  • Polymer.dom(this).observeNodes() - works on own manipulation, does not on binding

with slot=shadowRoot.querySelector( '#slotted slot' ) :

  • slot.addEventListener('slotchange',...) - triggered only once during creation, not on each binding change
  • events DOMSubtreeModified DOMCharacterDataModified DOMNodeInsertedIntoDocument DOMNodeRemovedFromDocument DOMNodeInserted DOMNodeRemoved and MutationObserver on slot and slot.assignedNodes()[0] - not triggered on binding changes

Also tried to encapsulate binding with div <embed-page><div id='watched'>[[htmlContent]]</div></embed-page> and watch DIV with MutationObserver and events - not triggered on binding changes.

At the moment leaving <embed-page>[[htmlContent]]</embed-page> unimplemented as there is a way around via <embed-page html="[[htmlContent]]"></embed-page>

@sashafirsov
Copy link
Member Author

sashafirsov commented Apr 15, 2018

Along with removing Polymer dependencies, moving embed-page from html-based web component to ES6 module, with following publishing on NPM and Bower.

@sashafirsov
Copy link
Member Author

sashafirsov commented Apr 15, 2018

Will UMD provide any additional value if the embed-page code will be in ES6 ?

  1. WebPack makes any module format acceptable. Giving ability to use embed-page in any format. Its babel config also supports AMD output for ES5 target if needed.
  2. In AMD environment the import of ES6 module will produce a valid outcome ( registering embed-page web component ) except of undefined module value. Which is still accessible via customElements.get('embed-page') making the 'embed-page' aliasing simple.
  3. ES6 module export class EmbedPage will work just fine.

As the last is the least complex, it is chosen as format for embed-page. UMD/AMD have a fall back which need to be reflected in docs.

@sashafirsov
Copy link
Member Author

sashafirsov commented Apr 15, 2018

Apparently ES6 module needs <script type="module"> which sets the strict mode, which is interfering with a with operator used in current code for scope insulation.

For enabling embed-page in page the usual <script src="../embed-page.js" ></script> is applied instead of importing the html file via <link rel="import" href="../embed-page.html">. For compatibility reasons the last is still functional importing same JS.

@sashafirsov
Copy link
Member Author

sashafirsov commented Apr 15, 2018

Apparently Polymer.Element gives quite a bit of features to keep it as dependency. Properties definition and binding in particular. Keeping EmbedPage0 directly inherited from HTMLElement around till decoupling from Polymer.

@sashafirsov
Copy link
Member Author

sashafirsov commented May 13, 2018

Eat your own dog food.

  • Menu with demo pages links is a perfect sample of JS-less content as client-side include.
    It would need implementation of scope="none" to prevent the links targetting same embed-page component.
  • Test runner is another sample of running the UI+code loaded via embed-page in the page scope.

CC: @rianby64 , @TakayoshiKochi

@sashafirsov
Copy link
Member Author

The content of <embed-page> is html text taken either from url, html attribute, or inline template. Implementation makes this html text treated as web component including features like css :host or <slot>.

While the CSS is web component compliant and could be used in full extent, the <slot> has no use.

A slot tag presents one of template abilities most of web component based frameworks serve. While the <embed-page> is not a framework per se, some template capabilities could be handy. For example if the page runs in the context of <embed-page> a variable value could be evaluated before html rendering.

<embed-page>
     <template>
          <h1>${location.pathname}</h1>   

The mock above is using JS string literal syntax which is reflecting one time evaluation. If the page is using own templating framework, it could conflict with <embed-page> one as by syntax as by execution/life cycle. Which is given the need in handy ability to apply some transformations optionally and in defined sequence. The "beforeload" event handler, perhaps?

The templating in such manner could be made in various ways from treating XML with XSLT, converting JSON to XML and than transforming into SVG, and so on.

The transformation and trigger different transformation according to content-type or another condition need to be thought-through. Browser default behavior in such cases already given some hints: XML for example is transformed into collapsible tree. Seems such behavior for embed-page would be expected out of the box.

@sashafirsov
Copy link
Member Author

Loading state API

Considered options

  1. State via class “initial” vs “loading” vs “loaded” – no events, could be checked via unusual string parsing ( i.e. fr.className.split(‘ ‘).includes(‘loaded’)
  2. Notification via load/beforeload event. If state not changed, the event will not be fired
  3. New. Loaded Promise property. It is unambiguous value as reassigning fr.src would require breaking the initial Promise if frame is not loaded yet AND replacing with new Promise making the watchdog handler complex( react on failed promise and re-assign itself to new one ). Also could add extra fragility as promise chain could be easily broken in the middle by malfunctioned handler.

Most popular use cases

While each app could be unique, it is expected the order of use cases by popularity would be

  1. Fr with src defined and do not change its value
  2. Default content defined inline
  3. Inline content with src defined( inline serves as fall-back and loading UI )
  4. Src is re-assigned either by embedded app or by page. Meaning the state would trigger between “loading” and “loaded”
    The attribute+property could serve the easy loading status check, also could serve CSS trigger

Failure handling

Q. Should the “failed” status set when content load failed upon re-assigning src ?
Q. Should the inline content be used when loading from src failed? With initially defined src that would be out of the box behavior, what about several passed but last fail, should be previous screen kept or default inline content shown instead?

Browser in IFRAME shows the browsing error message with error code. In case of load error the default content could be re-rendered with error status available in JS and rendered by default app.
Decision

  • Css class – nothing on undefined or loaded state, a ‘loading’ as transitional state.
  • Error class on load from src failed. The trouble is that default page as error handler could be a valid gracefully worked state. Workaround is to define #xxx as url

@sashafirsov
Copy link
Member Author

Css contain released by FF and Chrome, it is good rendering optimisation flag which could have some use in embed-page. While it defies the purpose of EPA to be customizable by container, in some cases it is justified to insulate content. Need to think through the effect.

@sashafirsov
Copy link
Member Author

sashafirsov commented Sep 30, 2019

use
As container web component, embed-page gives ability to define and re-set content. Via:

  • src attribute
  • html attribute
  • embedded content
  • innerHTML

<slot> itself would act in same fashion as html embedding. Is there a need for explicit slot use? Perhaps not. It would interfere with internal use of slot and with embedding html without slot.

From another hand, implementation could gain from encasing the content html within slot element.

Is there a potential for multiple slots? As a candidates:

  • title ( to reflect html title )
  • url ( editable? )
  • load error ( to customize error UI )
  • body ( default )
  • status
  • console( debug support ? )
  • history/navigation
  • favorites( bar?)

Those slots reflect browser as application peaces. Hidden by default, show default UI if slot mentioned, replaced with custom content when defined.

@sashafirsov
Copy link
Member Author

sashafirsov commented Dec 1, 2019

fix node_modules/wct-browser-legacy/browser.js

lodash produces Uncaught ReferenceError: module is not defined

Solution: Replace 'lodash/index.js' with 'lodash/lodash.js'.
Polymer/tools#3237

With current polymer-cli the error seems gone.

@sashafirsov
Copy link
Member Author

sashafirsov commented Jan 26, 2020

CDN for es6 modules
I have tried how to make base framework served from common URL( not even a CDN but from same server) using ES6 imports. So far have not found any guide, not to mention CDN with compliant code. Thinking how much effort it could be. Will it fit into 2 nights?

WebPack does not support exporting ES6 modules. Only build stack which is capable is RollUp and polymer-cli. There is an outdated plugin for replacing rel path to URL. Which is almost sufficient for client code. The missing part is a library build and distribution: plugin which would produce @vendor/module@rev/ into build. And of course CDN to host libs.

also rollup-plugin-esm-import-to-url

Related issues:

@sashafirsov
Copy link
Member Author

sashafirsov commented May 19, 2021

Time for v1.0

As proof of concept embed-page has done its work. It is given the demo ability for microapplication container, provided most of functionality via standard browser API, shown full 2020 browser set compatibility from IE to embedded Safari.

It is a time to move on with current 2021 browser set and JS stack making POC the actual product.

Changes

Same API

As initially designed, basic functionality would be available via same API compatible with IFRAME, import maps, etc.

Implementation changes

Native web components, ES6 classes & async.

  • no external dependencies except of parser. Parser embedding TBD.
  • modular implementation as opposite to single JS
  • binary in dist/
  • test as separate proj to avoid false positives on dependencies scans.
  • test proj would embed the main proj, how TBD, npm link as an option.
  • sample SPA as micro-app with front and backend as vanilla JS, Angular, React, Ionic. Link to demo registry.
  • JS parser based on XXX AST replacing
  • css, JS, IMG, ... using import maps. Samples of CDN based modules.
  • web components insulation TBD.

@sashafirsov
Copy link
Member Author

Web components

Would need insulation withing microapplication container as default scope is global.

Could be solved by namespacing in XmlDocument. Default namespace would be defined on document and documentElement level.
Trick is to map html name to local namespaced name in queries API, and in css files/style tags.

@sashafirsov
Copy link
Member Author

sashafirsov commented May 20, 2021

virtual DOM vs polyfill

XmlDocument

XmlDocument could be used as virtual DOM capable of HtmlDocument functionality. All changes to virtual document could be reflected in microapplication container.

Since there is no direct dom and document exposure, it is a most comprehensive insulation method.

JS polyfill

Is working in embed-page. The window and document API are massaged and routed to matching container. Hence JS is working on page DOM directly without extra memory and the need for virtual dom synchronization.
While polyfill gives most performant implementation, it gives a space for tricks and access to container from microapplication. The normal use and most obvious holes are patched though.

hybrid approach

Would permit to place the trusted microapplications in polyfill based containers and keep untrusted content within virtual dom.

As development of additional implementation is costly, it would be either in V2 or in commercial implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
diary question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant