SystemJS 2.0.0 has just been released, a significant simplification of the project building on the principle of a focused hackable core with a minimal footprint.
The project now provides two base builds:
- s.js - a 1.5KB minimal loader supporting the full semantics of System.register loading only. This is an ideal loader for Rollup code-splitting workflows.
- system.js - a 3KB loader supporting global script loading for third-party library support, package name maps, WASM and loading in Web Workers.
On top of these builds, pluggable extras can be dropped in for additional functionality including AMD support, named exports support for legacy modules and source transform support for in-browser Babel compilation.
Both loaders support IE11+ provided a Promise polyfill is available.
Many of the previous features of SystemJS 0.21 should be possible to implement within the extras and extensions model of SystemJS 2.0. If you are interested in a specific feature here, please share your work by listing your extensions in the README of the project. In addition we can integrate whatever extras turn out to be the most useful for the community. This final set of extras, and what can be done through loader extensibility, is up to you.
SystemJS 0.21 will continue to be maintained, and will be in the long-term. There is no pressing need to upgrade.
SystemJS 2.0 Goals
The 2.0 SystemJS release is a full departure from this loader specification model, instead focusing on building an optimal and hackable browser loader, doubling down on the goals of:
- To support existing module workflows in legacy browsers through the s.js build.
- To provide current implementations of the future native modules specifications in browsers today through the system.js build.
- To provide a flexible hackable loader core that can be extended through core extras or community extensions.
Being able to radically simplify the project will help to adapt to the workflows needed going forward.
Modular Workflows in Legacy Browsers (s.js build)
With Rollup code-splitting soon to be released as stable, we now have viable production workflows for deploying native ES modules in browsers in a way that optimizes loading with code-sharing between different entry points and dynamic imports.
The availability of native modules in browsers has continued to grow, to the point that 75% of web users are now running native-modules-compatible environments. Yet we still need to cater to that other 25%.
SystemJS provides a path to support these modern module workflows in legacy browsers, by guaranteeing that code that works in browsers supporting native modules will also work when compiled into System.register and run through SystemJS.
Having a dedicated minimal s.js build gets this overhead down to just a 1.5KB loader ensuring a fast production path.
Implementations of Future Native Modules Features (system.js build)
Supporting these future features in SystemJS allows you, as the project has always strived to do, to use these upcoming features in browsers today, allowing development workflows to align with what browsers will support.
Package Name Maps
Most of the features of the previous SystemJS map configuration are still supported in package maps - for example there is support for both base-level mappings as well as scoped mappings, and
baseURL becomes the package map
Package name maps are an early spec draft and will still change before their implemention in browsers.
Web Assembly Integration
.wasm files, these are now automatically passed to WebAssembly in the browser (if available), including support for imports from WASM running through the SystemJS resolver.
While Web Assembly has been previously available through a configuration option in SystemJS, enabling it by default for the
.wasm file extension provides a much simpler integration. While this isn't quite how browsers will work (likely detecting the MIME type), servers will likely set that MIME type from the file extension anyway.
Hackable Loader Core
Released from the constraints of trying to follow the former loader specification, we can now expose a much simpler loader hooks mechanism.
The resolver can be directly intercepted through
System.resolve. Even things like the properties available on
import.meta can be customized. The SystemJS extras all follow this same hooks model themselves.
Extras like the transform extra, allow an easy to use
System.transform for still supporting those in-browser Babel workflows.
For this early implementation, the current (in-progress) deprecation list is included below:
System.config: There is currently no configuration mechanism apart from package maps. Depending on the needs of extensions, we could provide a base-level configuration extension. All configuration options are currently deprecated -
paths(replaced by package name maps),
depCache(replaced by build techniques that inline deep dependencies, as Rollup does),
bundles(replaced by the fact that named System.register is no longer supported).
formatdetection between System.register, globals and AMD is handled automatically. Rather, we can add back configuration paths for what we need based on workflow feedback.
SystemJSglobal: We originally used the
SystemJSglobal to avoid conflict with
global.Systemdue to some JS specs using the
Systemglobal name. Since there are currently no active specifications using the
Systemglobal, SystemJS can continue to use this global.
System.registry: To align with changes to the loader spec, the registry was moved to
System.registry. Since there is currently no active registry specification, the registry API has been simplified to just
System.delete(in the system.js build only). This provides enough functionality for most use cases like hot-reloading.
System.register: Originally System.register followed the AMD build model of named defines in builds where multiple modules get joined into a single file under aliased names. This model has proven outdated in the face of deeper build analyis like that done by Rollup.
System.register('name', ...)is no longer supported as Rollup output will join modules together without any wrappers, while also supporting deeper code optimizations, relying on the fact that ES modules have a composibility property down to scope transformations, while retaining exact semantics. This shift was what I previously discussed in my 2016 DotJS talk on the topic, before becoming a core contributor to Rollup in order to work on this code splitting feature.
- CommonJS loading: There is currently no CommonJS loader, as the base-level Systrem.register loader, with global script support in system.js and the AMD extra provides most external loading use cases. If you do have a specific need for a CommonJS loader, splitting out the previous implementation into a third-party extension could certainly be useful.
- Global Snapshots: When loading global scripts, the loader no longer supports multiple global detection, or global detection when two libraries use the same global name as it now uses a more optimized
Object.keys(Window)diff instead of a full value diff iterating the entire global object.
If you disagree with any of these directions, please do share your thoughts or implementation work - nothing here is set in stone at this point. Many of these features could still be integrated as extras or extensions.
Going forward, s.js will continue to remain a minimal optimal loader, while system.js will continue to track the latest module specification progress, using the extras model for early experimental work, moving this work into core builds on major release milestones.
All other features can be supported through core extras or ecosystem extensions, and the hope is to better showcase community projects here.
Eventually though, once browser support for modules is strong enough, the optimized s.js loader should no longer be necessary. New techniques, like polyfilling package maps on native modules, or similarly for whatever new module workflows we have in future, will take its place.
SystemJS still aims to facilitate loading any modular package dynamically in the browser. The new jspm.io CDN supports loading any package from npm. Using the s.js build,
System.import('//system-dev.jspm.io/[pkgName]') will load any package on-demand with Browserify-style compatibility. In the short time since it has been released this CDN is already serving 2 million users per month. Tentative work is still in progress on a jspm 2.0 to bring these same workflows available locally so that such CDN workflows can remain complementary and in alignment with a wider overall development workflow.
A huge thank you to Canopy Tax for their sponsorship of the project.
In addition thank you to all companies that have previously sponsored the project, including One.com. It is thanks to the forward-thinking teams like these that the project has been able to keep going at all.
It has been a big change for the project dealing with the difference in modules specifications from where modules were heading to where they are now heading. Maintenance has definitely suffered in the process, and my apologies for this, but I am only one person with limited time for the project. Over the past years I have spent countless hours resolving over 1500 project issues single-handedly, and have fallen behind on this recently. I will do my best to continue maintaining both branches, and any help at all is very welcome.
Thanks again to you all for your continued support.