t2ym/i18n-element

[4.0] Remove dependency on Polymer library

Closed this issue · 4 comments

t2ym commented

[4.0] Remove dependency on Polymer library

Links to BundlePhobia

Minified + gzip package size

i18n-element@4.0.0 for i18n.js

i18n-element@4.0.0-core for i18n-core.js

Gzipped Bundled i18n-core.js - 15.26 kB including lit-html

Bundler Minifier Compression Size Notes
polymer-build minify: true - 93.19 kB
polymer-build minify: true gzip 19.13 kB
rollup babel-preset-minify - 67.84 kB
rollup babel-preset-minify gzip 15.26 kB
  • i18n-core.js is only for preprocessed sources
    • Use i18n-behavior/i18n-controller-core.js, which omits the preprocessor code
  • demo/gulpfile.js with const useI18nCoreJs = true can convert imports to i18n-core.js
    • "i18n-element" -> "i18n-element/i18n-core.js"
    • "i18n-element/i18n.js" -> "i18n-element/i18n-core.js"
    • "/node_modules/i18n-element/i18n.js" -> "/node_modules/i18n-element/i18n-core.js"

Gzipped Bundled i18n.js - 18.82 kB including lit-html

Bundler Minifier Compression Size Notes
polymer-build minify: true - 116.66 kB
polymer-build minify: true gzip 23.67 kB
rollup babel-preset-minify - 83.24 kB
rollup babel-preset-minify gzip 18.82 kB
rollup rollup-plugin-babel-minify - 106.43 kB LIMITED
rollup rollup-plugin-terser - 82.44 kB BROKEN
webpack babel-minify-webpack-plugin - 173.58 kB TRIAL
webpack babel-minify-webpack-plugin gzip 33.48 kB TRIAL
  • The current webpack.config.js is just a trial and may not be optimal
    • demo/preprocess/webpack.config.js seems to work but not tested well
    • Overhead modules in webpack
      • [20] ./node_modules/node-libs-browser/node_modules/buffer/index.js 47.7 KiB
      • [21] (webpack)/buildin/global.js 475 bytes
      • [22] ./node_modules/base64-js/index.js 3.41 KiB
      • [23] ./node_modules/ieee754/index.js 2.03 KiB
      • [24] ./node_modules/node-libs-browser/node_modules/isarray/index.js 131 bytes
  • An example app with webpack is configured at https://github.com/t2ym/pwa-i18n-webpack, based on https://github.com/vaadin-learning-center/lit-element-tutorial-pwa-and-offline
    • A minimal plugin @open-wc/webpack-import-meta-loader is applied
    • static get is() getters are explicitly declared to survive UglifyJS mangling for class names
    • The app is dependent on @vaadin/* elements, which depend on Polymer library, but the webpack configurations should be compatible even after they are removed from the app
  • rollup-plugin-babel can be configured with a preset babel-preset-minify
    • bundled-import-meta babel plugin can be configured with rollup-plugin-babel to convert import.meta meta properties to maintain their original values on bundling
    • rollup.config.js with bundled-import-meta is attached to this GitHub issue
  • The raw rollup-plugin-babel-minify minified version has limitations
    • LIMITATION Proper element-specific locales/ paths (not for bundle.*.json) cannot be found since import.meta meta-properties for the elements are left as they are in the bundle
      • In polymer-build, import.meta for each element is crafted to maintain the original intended value for the element so that importMeta() method can return the same expected value as that for the unbundled original version
    • i18n.js with rollup-plugin-babel-minify whose options following polymer-build itself is intact as it does not contain any import.meta for locales/ paths
      • rollup.config.js with the options is attached to this GitHub issue
  • CRITICAL MINIFICATION ISSUES seem to persist with rollup-plugin-terser for i18n.js
    • With demo/preprocess/clock.js, unexpected fetching of html-element.ja.json is observed, which indicates critical mal-minification by rollup-plugin-terser
      • Without the terser plugin, the issue is not observed
      • Root cause under investigation - It is difficult to pinpoint the issue origin since the minified code cannot be pretty-printed in DevTools
    • Since rollup-plugin-terser uses terser as a mangling engine, terser-mangled code should not have the issue with babel shown below
  • rollup is internally used in polymer build with calibrated options for Polymer 3

Bundled Modules and Packages

i18n.js - 25.29 KB (9.73%)
i18n-behavior - 105.34 KB (40.54%)
	i18n-controller.js - 82.14 KB (77.98%)
	i18n-attr-repo.js - 15.87 KB (15.07%)
	i18n-preference.js - 7.33 KB (6.95%)
lit-html - 43.51 KB (16.75%)
make-plural - 32.86 KB (12.65%)
i18n-format - 22.29 KB (8.58%)
i18n-number - 10.98 KB (4.23%)
wc-putty - 10.11 KB (3.89%)
deepcopy - 9.43 KB (3.63%)

Dependencies

i18n-element i18n-behavior Polymer lit-html
4.x 4.x 3.x (optional) 1.x
3.x 3.x 3.x (mandatory) 1.x
2.x 2.x 1.x-2.x -
- 1.x 1.x -

Notes

  • If Polymer elements have to be supported in i18n-element 4.x, the elements themselves must have @polymer/polymer as their NPM dependencies since i18n-element@4.x and i18n-behavior@4.x do NOT have @polymer/polymer as their own NPM package dependencies.
  • As it is unlikely that any Polymer elements have dependencies on i18n-element@4.x but have no dependencies on @polymer/polymer, the missing explicit NPM package dependencies on @polymer/polymer in i18n-element@4.x and i18n-behavior@4.x NPM packages should not become issues.

Status

  • WIP in lit-html branch
  • NPM package
    • npm install i18n-element@next or npm install i18n-element@4.0.0-pre.17
  • Try to keep external interfaces compatible with 3.0.0
    • i18n.js is independent of Polymer library
  • i18n-core.js for preprocessed sources
  • Use i18n-behavior@4.0.0-pre.19
    • i18n-behavior/i18n-controller-core.js omits the preprocessor code
    • Dependent i18n-format@4.0.0-pre.13 and i18n-number@4.0.0-pre.6 render with lit-html

Tasks

  • i18n-number
    • Render with lit-html instead of Polymer library
    • TBD
  • Create a separate npm module wc-putty for i18n-element/polyfill.js, which will be shared among i18n-number, i18n-format, and i18n-element
  • i18n-format
    • Render with lit-html instead of Polymer library
    • TBD
  • i18n-behavior
    • Convert <i18n-attr-repo> to a vanilla web component
    • Convert <i18n-preference> to a vanilla web component
    • Remove dependency on <iron-ajax> from I18nBehavior
    • Extract I18N interfaces independent of Polymer library
    • Keep I18nBehavior interface compatible with 3.x
    • TBD
  • Remove polyfill.js and use wc-putty/polyfill.js
  • Drop Safari 9 support
  • Tests
    • Tests for Polymer
      • mixin, base-element, legacy syntax tests pass
      • thin syntax tests failure
        • Fix: Use i18n-format@4.0.0-pre.10, which puts off assignment of DEFAULT_LANG until connectedCallback()
      • Tests time out on Safari 10 - Succeeded on weekends
    • Tests for lit-html
      • Pass with i18n-behavior@4.0.0-pre.3 - still dependent on Polymer library
  • Documentation
    • README
    • CHANGELOG
    • API Docs
    • TBD
  • TBD

3.0.0 with dependency on Polymer Library

  • Size of bundled and gzipped i18n.js ≈ 67.9KB

4.0.0 plan # 1 with no production dependency on Polymer Library

  • Roughly estimated size of bundled and gzipped i18n.js ≈ 23.5KB

Roughly Estimated Tasks on Plan # 1

  • i18n-number renders with lit-html, but has the same interfaces as 3.0.0
  • i18n-format renders with lit-html, but has the same interfaces as 3.0.0
  • i18n-attr-repo is a vanilla web component, but has the same interfaces as 3.0.0
  • i18n-preference is a vanilla web component, but has the same interfaces as 3.0.0, using localStorage API directly
  • i18n-controller.js is introduced for non-Polymer part of i18n-behavior.js, using XHR (or fetch with the help of whatwg-fetch on IE 11) directly
  • i18n-behavior.js contains only Polymer dependent part, depending on i18n-controller.js, but providing the same interfaces as 3.0.0
  • i18n-element.js and i18n-behavior.js are still supported if polymer is loaded
    • polymer is a devDependency of i18n-element and i18n-behavior npm packages
    • It is assumed that all the dependent modules of i18n-element.js depend on polymer and i18n-element and i18n-behaviro themselves do not have to depend on polymer as npm packages
  • live-localizer is still a Polymer element, but is required only in development phases and not required in production
  • LitElement has to be supported as well

Design Issues on Plan # 1

  • i18n-number, i18n-format, i18n-attr-repo, i18n-preference: Property/attribute management should be easier with LitElement
    • LitElement footprint overheads?
  • i18n-number, i18n-format can be implemented as vanilla web components but rendering with ShadyDOM may be more compatible and easier with lit-html
  • intl dependency can be dropped if Safari 9 support is dropped
  • Should base elements with lit-html other than LitElement be officially supported?
  • Can fetch API be used instead of XHR?
    • The fetch API can be polyfilled by whatwg-fetch on IE 11
t2ym commented

Log from npm run size with bundled-import-meta

i18n.js → test/build/i18n.bundled-not-usable-as-it-is.js...
i18n.js:
i18n-behavior - 105.34 KB (40.54%)
	i18n-controller.js - 82.14 KB (77.98%)
	i18n-attr-repo.js - 15.87 KB (15.07%)
	i18n-preference.js - 7.33 KB (6.95%)
lit-html - 43.51 KB (16.75%)
	lib/parts.js - 14.84 KB (34.10%)
	lib/template.js - 9.24 KB (21.23%)
	lib/template-instance.js - 4.07 KB (9.35%)
	lib/template-result.js - 3.42 KB (7.86%)
	lit-html.js - 2.52 KB (5.79%)
	lib/default-template-processor.js - 2.01 KB (4.61%)
	lib/template-factory.js - 1.9 KB (4.37%)
	lib/render.js - 1.78 KB (4.08%)
	lib/dom.js - 1.56 KB (3.58%)
	lib/directive.js - 1.37 KB (3.16%)
	lib/part.js - 834 B (1.87%)
make-plural - 32.86 KB (12.65%)
	es6/plurals.js - 32.86 KB (100.00%)
app - 25.29 KB (9.73%)
	i18n.js - 25.29 KB (100.00%)
i18n-format - 22.29 KB (8.58%)
	i18n-format.js - 22.29 KB (100.00%)
i18n-number - 10.98 KB (4.23%)
	i18n-number.js - 10.98 KB (100.00%)
wc-putty - 10.11 KB (3.89%)
	polyfill.js - 10.11 KB (100.00%)
deepcopy - 9.43 KB (3.63%)
	dist/deepcopy.js - 9.43 KB (100.00%)
┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Destination: test/build/i18n.bundled-not-usable-as-it-is.js   │
│   Bundle Size:  107.7 KB                                        │
│   Minified Size:  82.53 KB                                      │
│   Gzipped Size:  18.56 KB                                       │
│   Brotli size: 18.98 KB                                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
(!) rollup-plugin-sizes plugin: The ongenerate hook used by plugin rollup-plugin-sizes is deprecated. The generateBundle hook should be used instead.
created test/build/i18n.bundled-not-usable-as-it-is.js in 3.3s

> gulp size

[22:34:54] Using gulpfile ~/WebComponents/components/i18n-element/gulpfile.js
[22:34:54] Starting 'size'...
[22:34:54] gulp-debug: test/build/i18n.js
[22:34:54] gulp-debug: 1 item
[22:34:54] all files 115.25 kB
[22:34:54] gulp-debug: test/build/i18n.js.gz
[22:34:54] gulp-debug: 1 item
[22:34:54] all files 23.32 kB
[22:34:54] Finished 'size' after 35 ms
t2ym commented

rollup.config.js for bundled-import-meta

import resolve from 'rollup-plugin-node-resolve';
import sizes from 'rollup-plugin-sizes';
import filesize from 'rollup-plugin-filesize';
import babel from 'rollup-plugin-babel';
import minifyPreset from 'babel-preset-minify';

export default {
  input: 'i18n.js',
  output: {
    file: 'test/build/i18n.bundled-not-usable-as-it-is.js',
    format: 'esm',
  },
  plugins: [
    resolve({
      modulesOnly: true,
    }),
    babel({
      sourceMaps: false,
      comments: false,
      plugins: [
        '@babel/plugin-syntax-object-rest-spread',
        '@babel/plugin-syntax-async-generators',
        '@babel/plugin-syntax-dynamic-import',
        '@babel/plugin-syntax-import-meta',
        // rollup rewrites import.meta.url, but makes them point to the file location after bundling
        // we want the location before bundling
        [ 'bundled-import-meta', {
          'mappings': {
            'node_modules': '/node_modules'
          },
          'bundleDir': '.',
          'importStyle': 'esm',
        } ],
      ],
      presets: [
        minifyPreset({}, {
          // Options from polymer-build/src/js-transform.ts
          // Disable the minify-constant-folding plugin because it has a bug relating
          // to invalid substitution of constant values into export specifiers:
          // https://github.com/babel/minify/issues/820
          evaluate: false,

          // TODO(aomarks) Find out why we disabled this plugin.
          simplifyComparisons: false,

          // Prevent removal of things that babel thinks are unreachable, but sometimes
          // gets wrong: https://github.com/Polymer/tools/issues/724
          deadcode: false,

          // Disable the simplify plugin because it can eat some statements preceeding
          // loops. https://github.com/babel/minify/issues/824
          simplify: false,

          // This is breaking ES6 output. https://github.com/Polymer/tools/issues/261
          mangle: false,
        }),
      ],
    }),
    sizes({
      details: true,
    }),
    filesize({
      showBrotliSize: true,
    }),
  ]
}
t2ym commented

rollup.config.js for demo/preprocess/clock.js

deployed to build/esm-bundled/demo/preprocess/clock.js

Deployment

polymer build # set up build/esm-bundled/...
rollup -c # deploy build/esm-bundled/demo/preprocess/clock.js
cd build/esm-bundled
python -m SimpleHTTPServer 8081 # http://localhost:8081/demo/preprocess/

Verification

// On DevTools Console
customElements.get('world-clock').importMeta
{ url: 'http://localhost:8081/demo/preprocess/clock.js' }
customElements.get('i18n-format').importMeta
{ url: 'http://localhost:8081/node_modules/i18n-format/i18n-format.js' }

rollup.config.js

import resolve from 'rollup-plugin-node-resolve';
import sizes from 'rollup-plugin-sizes';
import filesize from 'rollup-plugin-filesize';
import babel from 'rollup-plugin-babel';
import minifyPreset from 'babel-preset-minify';

export default {
  input: 'demo/preprocess/clock.js',
  output: {
    file: 'build/esm-bundled/demo/preprocess/clock.js',
    format: 'esm',
  },
  plugins: [
    resolve({
      modulesOnly: true,
    }),
    babel({
      sourceMaps: false,
      comments: false,
      plugins: [
        '@babel/plugin-syntax-object-rest-spread',
        '@babel/plugin-syntax-async-generators',
        '@babel/plugin-syntax-dynamic-import',
        '@babel/plugin-syntax-import-meta',
        // rollup rewrites import.meta.url, but makes them point to the file location after bundling
        // we want the location before bundling
        [ 'bundled-import-meta', {
          'mappings': {
            'node_modules': '../../node_modules'
          },
          'bundleDir': 'demo/preprocess',
          'importStyle': 'esm',
        } ],
      ],
      presets: [
        minifyPreset({}, {
          // Options from polymer-build/src/js-transform.ts
          // Disable the minify-constant-folding plugin because it has a bug relating
          // to invalid substitution of constant values into export specifiers:
          // https://github.com/babel/minify/issues/820
          evaluate: false,

          // TODO(aomarks) Find out why we disabled this plugin.
          simplifyComparisons: false,

          // Prevent removal of things that babel thinks are unreachable, but sometimes
          // gets wrong: https://github.com/Polymer/tools/issues/724
          deadcode: false,

          // Disable the simplify plugin because it can eat some statements preceeding
          // loops. https://github.com/babel/minify/issues/824
          simplify: false,

          // This is breaking ES6 output. https://github.com/Polymer/tools/issues/261
          mangle: false,
        }),
      ],
    }),
    sizes({
      details: true,
    }),
    filesize({
      showBrotliSize: true,
    }),
  ]
}
t2ym commented

i18n-core.js part of log from npm run size

i18n-core.js → test/build/i18n-core.bundled-not-usable-as-it-is.js...
i18n-core.js:
i18n-behavior - 60.73 KB (29.08%)
	i18n-controller-core.js - 37.53 KB (61.80%)
	i18n-attr-repo.js - 15.87 KB (26.14%)
	i18n-preference.js - 7.33 KB (12.06%)
lit-html - 43.51 KB (20.83%)
	lib/parts.js - 14.84 KB (34.10%)
	lib/template.js - 9.24 KB (21.23%)
	lib/template-instance.js - 4.07 KB (9.35%)
	lib/template-result.js - 3.42 KB (7.86%)
	lit-html.js - 2.52 KB (5.79%)
	lib/default-template-processor.js - 2.01 KB (4.61%)
	lib/template-factory.js - 1.9 KB (4.37%)
	lib/render.js - 1.78 KB (4.08%)
	lib/dom.js - 1.56 KB (3.58%)
	lib/directive.js - 1.37 KB (3.16%)
	lib/part.js - 834 B (1.87%)
make-plural - 32.86 KB (15.73%)
	es6/plurals.js - 32.86 KB (100.00%)
i18n-format - 22.29 KB (10.67%)
	i18n-format.js - 22.29 KB (100.00%)
app - 18.95 KB (9.07%)
	i18n-core.js - 18.95 KB (100.00%)
i18n-number - 10.98 KB (5.26%)
	i18n-number.js - 10.98 KB (100.00%)
wc-putty - 10.11 KB (4.84%)
	polyfill.js - 10.11 KB (100.00%)
deepcopy - 9.43 KB (4.52%)
	dist/deepcopy.js - 9.43 KB (100.00%)