Skip to content

Instantly share code, notes, and snippets.

@yuchi
Last active February 5, 2020 20:40
Show Gist options
  • Save yuchi/f1225cab78cc67091f70 to your computer and use it in GitHub Desktop.
Save yuchi/f1225cab78cc67091f70 to your computer and use it in GitHub Desktop.
Runtime Front-end (Web) Resources and Dependencies

Runtime Front-end (Web) Resources and Dependencies

During Liferay DEVCON 2015 some interesting topics spurred out of the new Liferay Theme build process.

This document, and the expected following discussion, is here to understand how we can guarantee for web front-end engineers a friendly development environment and a de-facto standards adherent runtime.

Synopsis

At Liferay DEVCON 2015 a new way to build Liferay Themes has been announced. It is built around gulp tasks and offers the concept of Themelets, composable features distributable through npm. Themelets are then merged in your own theme at build time.

No explicit statement from the user (other than the installation of the Themelet itself) is required for the changes to take effect. The build process owns the concept of Themelets.

The discussion started around the fact that while Themelets are distributed through npm, they are not good npm citizens, we are using npm as a centralized storage system with a nice retrieval and versioning command-line util.

What a front-end engineer would expect is something different, and that’s dependency management. A small piece of code is worth a thousand words:

// liferay-themelet-sticky-dockbar/index.js
import Sticky from 'a-sticky-polyfill-on-npm';
export default new Sticky('#dockbar');

// liferay-themelet-sticky-dockbar/package.json
{
  "name": "liferay-themelet-sticky-dockbar",
  "version": "0.1.0",
  "dependencies": {
    "a-sticky-polyfill-on-npm": "^1.0.2"
  }
}

This is currently out of scope of the new Theme build process.

We think it should and that it should encompass the whole front-end development experience on Liferay.

Goals

  1. Let front-end engineers use their favorite package manager for Liferay Themes and Apps (aka Portlets) development.
  2. Provide a preferred distribution path for Liferay-oriented packages (e.g. Themelets.)
  3. Guarantee the support for current common scenarios (e.g. deduplication of packages, conflict management), common practices (polyfills, global-pulluting-things) and libraries/frameworks (e.g. Metal.js, Angular, React).
  4. Identify the module system to be used and define eventual compatibility paths.
  5. Define the boundaries for forward-compatibility of the designed system.

Prior art and references

Papers and specs:
Bundlers (and related stuff):
  • Webpack, web-oriented «module loader»
  • Browserify, «[…] lets you require('modules') in the browser by bundling up all of your dependencies»
  • Rollup, «Next-generation ES6 module bundler»
  • SystemJS Build Tool, «Provides a single-file build for SystemJS of mixed-dependency module trees»
  • module-deps, static analyzer that «walk[s] the dependency graph to generate json output […]»
Module loaders (and related stuff):

To know (for Java-ists)

This is an Hitchikers Guide to the JavaScript Galaxy for Java-ists.

Glossary

Module

In the JavaScript world the meaning of the term module is context-dependent.

Note: this is the de-facto standard, could diverge from what is officialy a CommonJS module.

A CommonJS module is a JavaScript file in which three APIs are injected by the system:

  • a module object with ultra-light info on the current module;
  • a module.exports property that will be used as the public result of requireing this module, defaults to an empty object;
  • a require( path ) function to obtain the module.exports of another module.

An AMD (Asynchronous Module Definition) module is a piece of JavaScript code that:

  • calls define([identifier,] requires, factory) passing
    • an optional identifier, to be used by other modules,
    • a requires array of other modules’ identifiers to be required,
    • a factory function which accepts as arguments the exports of the required modules.

An ECMAScript 2015 module is either

  • a function which is registered in the global module loader registry;
  • a file which is explicitly imported as a module in the context of a JavaScript runtime (ad example by specifying the module attribute in the <script> tag.)

Module Loader

A Module Loader is a piece of code that actually loads something and makes it available to whoever calls/requires it.

  • Node.js

    While not advertised as such it actually is (has) a Module Loader, one that supports both the CommonJS module standard and the CommonJS package standard, through its own package and module resolving algorithm.

    Obviously doesn’t work in the browser as the loading happens through the filesystem.

  • SystemJS

    An loader with a very solid support for multiple type of module definitions, even interspersed ones.

    Supports non-JS resources through plugins.

  • RequireJS

    An AMD loader, and probably the most known one.

    Supports non-JS resources through plugins

Module Bundler

A Module Bundler is a build process which assembles different JavaScript modules into a single module/file/script that can be executed where a Module Loader is not present.

  • Webpack

    A very powerful module bundler, which supports (among other things) multiple bundles, both AMD and CommonJS module definition and a huge list of plugins.

    As already said for Browserify it is not a Loader, but a Bundler.

    Supports non-JS resources through loader plugins.

  • Browserify through browser-pack

    A synchronous Module Loader that supports the CommonJS module standard. You push a JSON stream of objects in the form of {id, source, deps, entry} and it creates a bundle ready to be consumed as a single file, and therefore browser-consumable. The bundle automatically executes those ‘modules’ that are flagged as entry: true.

    The resulting code somewhat supports bundles sharding, where you can have more than one bundle loaded in order on the same global context (web page) and if in a lower bundle a module is not found it will ask the previous for it, and so on.

    Supports non-JS resources through transforms.

  • Rollup

    A Module Bundler oriented towards ES-modules (those that use import). Its flagship feature is automatic bundle thinning, where it loads pieces of modules only when explicitly used based on Static Analysis enforced by ES-syntax explicitness. Dead-code elimination on steroids.

    It create a single file, where module boundaries are lost and colliding names are mangled.

    Could support non-JS resources through plugins.

Package

In the JavaScript world the meaning of package is context-dependent and somewhat loosely defined.

In plain English you can read package as some shareable modules and stuff.

For actual definitions we need to go deeper.

TODO :)

Resolving algorithms

TODO :)

To not forget

Ideas and things that came out but are not yet in the discussion.

  • IMHO we should solve this issue in the Liferay scope first, and then in the wider OSGi scope.
  • For globally accessible packages and modules (a-la Angular) where you need a package to be loaded once, we could use peerDependencies in package.json. The issue is that those dependencies must provided (as in pom.xml) by the system or upper level packages.
@iods
Copy link

iods commented Feb 17, 2018

Summary of Request:

Brief description of request that all stakeholders can agree to.

Details of Request:

Customer list of the things necessary to accomplish the end result.

Affected Components:

What existing components are impacted by this request?

Work Involved:

High level definition of the desired API, classes, and SQL to be implemented.

Acceptance Testing:

Defined methodology for testing the items listed in the Details of Request section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment