This PR is a breaking change, affecting the syntax for loading Tangram via <script> tags, and is intended for a v0.16.0 release.
ES6/ES2015+ and ES5 bundles
This PR changes the Tangram build process to produce two bundles for distribution:
An ES6 (or more accurately, ES2015+) code bundle, without any code transpiling or polyfills (for now, though some polyfills may make sense to include in this bundle in the future). This bundle intended for use on most browsers (it should work for all browsers officially supported by Tangram, except for IE11). This bundle is available as tangram.debug.js/tangram.min.js.
An ES5 code bundle, with code transpiled and features polyfilled to work on older browsers (specifically, IE11). This bundle is similar to the one previously used to publish Tangram (and continues to be forward-compatible with newer browsers). This bundle is available as tangram.es5.debug.js/tangram.es5.min.js.
Browsers have evolved since the early days of Tangram to natively support a much greater range of JS syntax and features. By skipping the transpiling process, we can produce a bundle that is both smaller (no extra transpiled code or unnecessary polyfills) and faster (less code for the browser to load over the network and parse). The minified ES6 bundle on this branch is 560k (157k gzipped), vs. 652k (180k gzipped) for the ES5 build; that's about 15% less code that users have to download and run.
We can use the module/nomodule pattern to automatically load the correct bundle for the browser, using this syntax:
<script type="module" src="dist/tangram.debug.js"></script> <!-- modern browsers load the ES6 bundle -->
<script nomodule src="dist/tangram.es5.debug.js"></script> <!-- older browsers (e.g. IE11) load the ES5 bundle -->
Browsers that support ES modules (type="module" syntax) will load the ES6 script and ignore the ES5 one. Those that don't will ignore the ES6 script, and load the ES5 one, marked with nomodule. Read more about this pattern here:
Additionally, because scripts with the module type automatically load in "deferred" mode, users must also include the defer keyword for any scripts that depend on Tangram (so that they won't run until Tangram is finished loading). For example, if your app code is in index.js, load it like this (anywhere after the Tangram <script> tag):
<script defer src="index.js"></script>
This is also a breaking change, however, using defer to load scripts is considered a best practice.
Worker code
As a side effect of moving to ES6 code, the method in which Tangram's web workers are instantiated has also changed. Scripts loaded with type="module" always execute in strict mode. Tangram previously used a custom method of creating its web worker code (in build/quine.js), which used arguments.callee.toString() to access its own source code. However, the callee property isn't allowed in strict mode.
Instead, workers are now created using the Browserify webworkify plugin, which uses Browserify's API to resolve dependencies and generate worker code at run-time (while remaining strict-mode compliant). This has the advantage of removing bespoke code that was also brittle (and had been known to sometimes conflict with other build systems and bundlers). The downside is that worker threads instantiated with webworkify do not work with source maps, which can make debugging worker code more difficult; however, this is somewhat mitigated by the removal of transpiled/polyfilled code from the main bundle, which was one of the main barriers to readability when debugging. (Note that an earlier branch experimenting with a Webpack-based build did have run-time generated workers with proper source maps; Webpack can be reconsidered in the future, but was outside the scope of this PR).
Babel 7
This branch also upgrades to Babel 7, and the preset-env plugin that is now the best practice when using Babel. Together, these provide a succinct method of configuring our two builds (see babel.config.js). The two most notable features here are the esmodules: true syntax, to target browsers that support ES modules (for the ES6 build), and useBuiltIns: usage, which provides a new automatic polyfilling mode, which selectively adds polyfills to the build based on detecting which features are used in code (for the ES5 build). Automatic polyfilling simplifies Tangram, replacing handwritten code for importing a limited set of polyfills.
Terser
Finally, this branch switches from uglify for minification, to terser, an uglify fork that adds support for ES6 code. (Revisiting minification tooling may also make sense in the future, but for now, terser provides a drop-in replacement that is compatible with the rest of the new build pipeline.)
This PR is a breaking change, affecting the syntax for loading Tangram via
<script>
tags, and is intended for a v0.16.0 release.ES6/ES2015+ and ES5 bundles
This PR changes the Tangram build process to produce two bundles for distribution:
tangram.debug.js
/tangram.min.js
.tangram.es5.debug.js
/tangram.es5.min.js
.Browsers have evolved since the early days of Tangram to natively support a much greater range of JS syntax and features. By skipping the transpiling process, we can produce a bundle that is both smaller (no extra transpiled code or unnecessary polyfills) and faster (less code for the browser to load over the network and parse). The minified ES6 bundle on this branch is 560k (157k gzipped), vs. 652k (180k gzipped) for the ES5 build; that's about 15% less code that users have to download and run.
We can use the
module
/nomodule
pattern to automatically load the correct bundle for the browser, using this syntax:Browsers that support ES modules (
type="module"
syntax) will load the ES6 script and ignore the ES5 one. Those that don't will ignore the ES6 script, and load the ES5 one, marked withnomodule
. Read more about this pattern here:https://developers.google.com/web/fundamentals/primers/modules#browser https://philipwalton.com/articles/deploying-es2015-code-in-production-today/ https://jakearchibald.com/2017/es-modules-in-browsers/#nomodule-for-backwards-compatibility
Additionally, because scripts with the
module
type automatically load in "deferred" mode, users must also include thedefer
keyword for any scripts that depend on Tangram (so that they won't run until Tangram is finished loading). For example, if your app code is inindex.js
, load it like this (anywhere after the Tangram<script>
tag):<script defer src="index.js"></script>
This is also a breaking change, however, using
defer
to load scripts is considered a best practice.Worker code
As a side effect of moving to ES6 code, the method in which Tangram's web workers are instantiated has also changed. Scripts loaded with
type="module"
always execute in strict mode. Tangram previously used a custom method of creating its web worker code (inbuild/quine.js
), which usedarguments.callee.toString()
to access its own source code. However, thecallee
property isn't allowed in strict mode.Instead, workers are now created using the Browserify
webworkify
plugin, which uses Browserify's API to resolve dependencies and generate worker code at run-time (while remaining strict-mode compliant). This has the advantage of removing bespoke code that was also brittle (and had been known to sometimes conflict with other build systems and bundlers). The downside is that worker threads instantiated withwebworkify
do not work with source maps, which can make debugging worker code more difficult; however, this is somewhat mitigated by the removal of transpiled/polyfilled code from the main bundle, which was one of the main barriers to readability when debugging. (Note that an earlier branch experimenting with a Webpack-based build did have run-time generated workers with proper source maps; Webpack can be reconsidered in the future, but was outside the scope of this PR).Babel 7
This branch also upgrades to Babel 7, and the
preset-env
plugin that is now the best practice when using Babel. Together, these provide a succinct method of configuring our two builds (seebabel.config.js
). The two most notable features here are theesmodules: true
syntax, to target browsers that support ES modules (for the ES6 build), anduseBuiltIns: usage
, which provides a new automatic polyfilling mode, which selectively adds polyfills to the build based on detecting which features are used in code (for the ES5 build). Automatic polyfilling simplifies Tangram, replacing handwritten code for importing a limited set of polyfills.Terser
Finally, this branch switches from
uglify
for minification, toterser
, anuglify
fork that adds support for ES6 code. (Revisiting minification tooling may also make sense in the future, but for now,terser
provides a drop-in replacement that is compatible with the rest of the new build pipeline.)