├── .gitignore ├── src ├── components.js ├── render.js └── main.js ├── dist-esm ├── components.js ├── render.js └── main.js ├── app.html ├── dist-system ├── components.js ├── render.js └── main.js ├── package.json ├── modules-bootstrap.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /src/components.js: -------------------------------------------------------------------------------- 1 | export function homepageComponent (opts) { 2 | return `

${opts.message}

`; 3 | } 4 | -------------------------------------------------------------------------------- /dist-esm/components.js: -------------------------------------------------------------------------------- 1 | export function homepageComponent (opts) { 2 | return `

${opts.message}

`; 3 | } 4 | -------------------------------------------------------------------------------- /src/render.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Eat your heart out React. 3 | */ 4 | export function render (html) { 5 | document.body.innerHTML = html; 6 | } 7 | -------------------------------------------------------------------------------- /dist-esm/render.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Eat your heart out React. 3 | */ 4 | export function render (html) { 5 | document.body.innerHTML = html; 6 | } 7 | -------------------------------------------------------------------------------- /app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { homepageComponent } from './components.js'; 2 | import { render } from './render.js'; 3 | 4 | render(homepageComponent({ 5 | message: `This application is rendered using 6 | ${self.__supportsScriptTypeModule ? 'ES modules' : ' System.register scripts'}.` 7 | })); 8 | -------------------------------------------------------------------------------- /dist-esm/main.js: -------------------------------------------------------------------------------- 1 | import { homepageComponent } from './components.js'; 2 | import { render } from './render.js'; 3 | 4 | render(homepageComponent({ 5 | message: `This application is rendered using 6 | ${self.__supportsScriptTypeModule ? 'ES modules' : ' System.register scripts'}.` 7 | })); 8 | -------------------------------------------------------------------------------- /dist-system/components.js: -------------------------------------------------------------------------------- 1 | System.register([], function (_export, _context) { 2 | "use strict"; 3 | 4 | function homepageComponent(opts) { 5 | return `

${ opts.message }

`; 6 | } 7 | 8 | _export("homepageComponent", homepageComponent); 9 | 10 | return { 11 | setters: [], 12 | execute: function () {} 13 | }; 14 | }); -------------------------------------------------------------------------------- /dist-system/render.js: -------------------------------------------------------------------------------- 1 | System.register([], function (_export, _context) { 2 | "use strict"; 3 | 4 | /* 5 | * Eat your heart out React. 6 | */ 7 | function render(html) { 8 | document.body.innerHTML = html; 9 | } 10 | 11 | _export("render", render); 12 | 13 | return { 14 | setters: [], 15 | execute: function () {} 16 | }; 17 | }); -------------------------------------------------------------------------------- /dist-system/main.js: -------------------------------------------------------------------------------- 1 | System.register(['./components.js', './render.js'], function (_export, _context) { 2 | "use strict"; 3 | 4 | var homepageComponent, render; 5 | return { 6 | setters: [function (_componentsJs) { 7 | homepageComponent = _componentsJs.homepageComponent; 8 | }, function (_renderJs) { 9 | render = _renderJs.render; 10 | }], 11 | execute: function () { 12 | 13 | render(homepageComponent({ 14 | message: `This application is rendered using 15 | ${ self.__supportsScriptTypeModule ? 'ES modules' : ' System.register scripts' }.` 16 | })); 17 | } 18 | }; 19 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modules-iso", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npm run copySrcToDist && npm run compileSrcToSystem", 8 | "copySrcToDist": "rm -r dist-esm ; cp -r src dist-esm", 9 | "compileSrcToSystem": "rm -r dist-system ; babel src --out-dir dist-system --plugins transform-es2015-modules-systemjs" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "systemjs": "^0.20.0-rc.1" 15 | }, 16 | "devDependencies": { 17 | "babel-cli": "^6.18.0", 18 | "babel-plugin-transform-es2015-modules-systemjs": "^6.19.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules-bootstrap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SystemJS Modules bootstrap 3 | * This file is designed to run as both a module, a script, a worker module, and a worker script 4 | * 5 | * For use with it should be loaded via: 6 | * 7 | * 8 | * 9 | * 10 | * Works by checking if we are in a modules environment or not (feature-detectable by this === undefined) 11 | * If in a modules environment, it dynamically loads the modules main, otherwise it loads SystemJS and 12 | * then System.import loads the system module format main. 13 | */ 14 | (function (topLevelThis) { 15 | 16 | var isModule = topLevelThis === undefined; 17 | var global = self; 18 | 19 | // this is needed for modules since document.currentScript doesn't seem supported 20 | // implementation just returns the first 31 | // -> load the main.js 32 | if (isModule) { 33 | var module = document.createElement('script'); 34 | module.type = 'module'; 35 | module.src = getCurrentModule().getAttribute('main'); 36 | document.head.appendChild(module); 37 | global.__supportsScriptTypeModule = true; 38 | } 39 | // running as a 40 | // -> load SystemJS and then System.import('main.js') 41 | else if (!global.__supportsScriptTypeModule) { 42 | var systemScript = document.createElement('script'); 43 | systemScript.src = document.currentScript.getAttribute('systemjs') || 'system.js'; 44 | 45 | var mainSrc = document.currentScript.getAttribute('main'); 46 | 47 | systemScript.addEventListener('load', load, false); 48 | document.head.appendChild(systemScript); 49 | 50 | function load () { 51 | systemScript.removeEventListener('load', load, false); 52 | document.head.removeChild(systemScript); 53 | 54 | SystemJS.import(mainSrc); 55 | } 56 | } 57 | })(this); 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # <script type="module"> Isomorphic browser modules workflow 2 | 3 | This project was created as a follow-up to discussions around how to load ES modules 4 | in modern browsers, while providing a fallback for older browsers that only 5 | support scripts. 6 | 7 | The approach here is to use a `modules-bootstrap.js` that is first attempted to load 8 | as a module and second as a script. 9 | 10 | Custom attributes on the scripts can then be used to point to the correct actual 11 | main entry points to be loaded once we know which loading mechanism to use. 12 | 13 | ### Demo 14 | 15 | Clone the repo and open `app.html` here when running a web server. 16 | 17 | In the latest Safari Technology Preview release with ES modules support, it will use 18 | the ES modules code, while in older browsers it will fallback to SystemJS production and 19 | System.register module format loading. 20 | 21 | To change the code and run a new build use `npm install && npm run build`. 22 | 23 | ### Bootstrap Explained 24 | 25 | The file `app.html` uses the following HTML for this bootstrap: 26 | 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | When `