├── .editorconfig ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── gulpfile.js ├── lib ├── client.js ├── core.js ├── plugins │ ├── babel.js │ ├── common.js │ ├── env.js │ ├── json.js │ ├── jsx.js │ ├── resolve.js │ └── text.js └── sw.js ├── package.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # RNA 2 | # EditorConfig is awesome: http://EditorConfig.org 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | insert_final_newline = true 12 | 13 | # Matches multiple files with brace expansion notation 14 | # Set default charset 15 | [*.{js,json,html,css,scss,sass}] 16 | charset = utf-8 17 | 18 | # 4 space indentation 19 | [*.{js,html,css,scss,sass}] 20 | indent_style = space 21 | indent_size = 4 22 | 23 | # 2 space indentation 24 | [*.{json,yml,yaml}] 25 | indent_style = space 26 | indent_size = 2 27 | 28 | [Makefile] 29 | indent_style = tab 30 | indent_size = 4 31 | 32 | # RNA -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | # RNA 2 | extends: 3 | - eslint:recommended 4 | 5 | globals: 6 | process: true 7 | 8 | env: 9 | es6: true 10 | browser: true 11 | 12 | parserOptions: 13 | ecmaVersion: 6 14 | sourceType: module 15 | ecmaFeatures: 16 | jsx: true 17 | generators: false 18 | objectLiteralDuplicateProperties: false 19 | 20 | rules: 21 | quotes: 22 | - 1 23 | - single 24 | semi: 25 | - 1 26 | - always 27 | indent: 28 | - 1 29 | - 4 30 | - SwitchCase: 1 31 | func-names: 0 32 | prefer-const: 0 33 | space-before-function-paren: 34 | - 1 35 | - never 36 | no-proto: 0 37 | no-param-reassign: 0 38 | quote-props: 39 | - 1 40 | - consistent-as-needed 41 | radix: 0 42 | no-new-func: 0 43 | arrow-body-style: 44 | - 2 45 | - as-needed 46 | arrow-parens: 0 47 | arrow-spacing: 48 | - 2 49 | - before: true 50 | after: true 51 | comma-dangle: 52 | - 1 53 | - always-multiline 54 | constructor-super: 0 55 | generator-star-spacing: 0 56 | no-class-assign: 0 57 | no-confusing-arrow: 58 | - 2 59 | - allowParens: true 60 | no-const-assign: 2 61 | no-new-symbol: 2 62 | no-restricted-globals: 0 63 | no-restricted-imports: 0 64 | no-this-before-super: 0 65 | no-var: 2 66 | no-useless-constructor: 2 67 | object-shorthand: 68 | - 1 69 | - always 70 | prefer-arrow-callback: 2 71 | prefer-spread: 0 72 | prefer-reflect: 0 73 | prefer-rest-params: 2 74 | prefer-template: 1 75 | require-yield: 0 76 | sort-imports: 0 77 | template-curly-spacing: 2 78 | yield-star-spacing: 79 | - 2 80 | - after 81 | max-depth: 82 | - 0 83 | - 4 84 | max-params: 85 | - 0 86 | - 3 87 | max-statements: 88 | - 0 89 | - 10 90 | no-bitwise: 0 91 | no-plusplus: 0 92 | no-unused-vars: 93 | - 1 94 | - varsIgnorePattern: (IDOM|process) 95 | no-console: 96 | - 1 97 | 98 | # RNA 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | /dist 4 | npm-debug.log 5 | .npmrc 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | /node_modules 3 | /coverage 4 | /test 5 | gulpfile.js 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Edoardo Cavazza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unchained 2 | 3 | [![npm](https://img.shields.io/npm/v/unchained-js.svg)](https://www.npmjs.com/package/unchained-js) 4 | 5 | Unchained takes advantage from browsers support for ES6 modules and Service Workers in order to load a full web application without using a bundler like Webpack or Rollup. 6 | 7 | ☢️ *This project is just a research about web technologies.* 8 | 9 | DO NOT use it in production. 10 | 11 | ## Why 12 | 13 | * Since Safari, Firefox and Chrome started to support ES6 modules syntax, I started to look for a good practise to load my applications. 14 | 15 | * Bundlers are great, and I will continue to use them for working/production environments, but I felt nostalgic about the times where I used to build application without installing ~1000 node modules just to start. 16 | 17 | Read more on Medium: https://medium.com/@edoardo.cavazza/a-study-about-how-to-improve-frontend-dev-experience-without-a-bundler-1b4c3a461a35 18 | 19 | ## How it works 20 | 21 | Native ES6 modules syntax accepts relative paths only (so, support for dependencies installed by NPM/Yarn is missing). Also, it doesn't work with other source formats rather than javascript (JSON, texts, styles...) or syntaxes (like JSX). 22 | 23 | Today, those issues are resolved on dev environment side by bundlers (Webpack, Rollup, Browserify) and transpilers (Babel, Traceur). 24 | 25 | The idea is to intercept import calls and transform the source in a ServiceWorker context, using the magninificent Babel standalone distribution to manipulate sources and resolve NPM dependencies. 26 | 27 | ![Unchained concept](https://docs.google.com/drawings/d/e/2PACX-1vQdqQI38CpJUSRT7diAH9dQOb-N8fGmp8LpOIdmJ6WbebEeDuzenx5wuZNtD0sPCpkYQ3INe3LsRHqM/pub?w=1362&h=1437) 28 | 29 | 30 | ## Usage 31 | 32 | Install from NPM: 33 | ```sh 34 | $ npm install unchained-js 35 | # OR 36 | $ yarn add unchained-js 37 | ``` 38 | 39 | Use the Unchained client helper to register a ServiceWorker and to import the main application file. 40 | 41 | **index.html** 42 | ```html 43 | 44 | 50 | ``` 51 | 52 | **sw.js** 53 | ```js 54 | // import Unchained core and plugins 55 | self.importScripts('node_modules/unchained-js/dist/unchained.sw.js'); 56 | ``` 57 | 58 | **index.js** 59 | ```js 60 | import { Component, h, render } from 'preact'; 61 | 62 | class App extends Component { 63 | render() { 64 | return

Hello world!

; 65 | } 66 | } 67 | 68 | render(document.body, ); 69 | ``` 70 | 71 | ## Configuration 72 | 73 | The Unchained object can be configured with a set of plugins, through the `Unchained.resolve` method. 74 | 75 | ### Plugins 76 | 77 | An array of Plugin constructors *or* Plugin instances *or* Plugin names. 78 | 79 | ```js 80 | { 81 | plugins: [ 82 | // constrcutor 83 | Unchained.TextPlugin, 84 | // instance 85 | new Unchained.ResolvePlugin(), 86 | // name 87 | 'env', 88 | // constructor|instance|name with options 89 | ['jsx', { pragram: 'h' }] 90 | ] 91 | } 92 | ``` 93 | 94 | > The Plugin name may be registered via the `Unchained.registerPlugin(name, constructor)` method. 95 | 96 | A list of available plugins can be found [here](https://github.com/edoardocavazza/unchained/wiki/Plugins). 97 | 98 | ### Via querystring 99 | 100 | You may also configure Unchained via querystring in the service worker registration url: 101 | 102 | ```js 103 | navigator.serviceWorker.register(`sw.js?unchained={"plugins":["env", "text"]}`); 104 | ``` 105 | 106 | The equivalent can be written using the third parameter of the `Unchained.register` helper method: 107 | ```js 108 | Unchained.register('sw.js', { scope: '/' }, { 109 | plugins: ['env', 'text'], 110 | }); 111 | ``` 112 | 113 | ## Browsers support ☢️ 114 | 115 | Support for [Service Workers](https://caniuse.com/#feat=serviceworkers), [ES6 syntax](https://kangax.github.io/compat-table/es6) and [ES6 modules](https://caniuse.com/#feat=es6-module) is required. 116 | 117 | Manually tested on Chrome v63.0.3239.84. 118 | 119 | ## Resources 120 | 121 | **ES6 modules** 122 | - [`import`](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Statements/import) | [`export`](https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Statements/export) 123 | - [Dynamic `import()`](https://developers.google.com/web/updates/2017/11/dynamic-import) 124 | - [`