Repository: rijs/fullstack Branch: master Commit: 82f6e5ebe4a5 Files: 15 Total size: 64.6 KB Directory structure: gitextract__zv91krg/ ├── .gitattributes ├── .gitignore ├── .travis.yml ├── README.md ├── client/ │ ├── ripple.bundle.js │ └── ripple.js ├── index.js ├── package.json ├── rollup.config.js ├── rollup.pure.config.js └── tests/ ├── basic.test.js └── resources/ ├── _components/ │ ├── x-foo.css │ └── x-foo.js ├── components/ │ └── auto-loaded-component.js └── utils/ └── foo.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ dist/* -diff ================================================ FILE: .gitignore ================================================ node_modules coverage *.log ================================================ FILE: .travis.yml ================================================ sudo: false language: node_js node_js: - "stable" after_script: NODE_ENV=test istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage ================================================ FILE: README.md ================================================ # Ripple Fullstack On the server: **`index.js`** ```js const ripple = require('rijs')({ dir: __dirname }) ``` On the client: **`pages/index.html`** ```html ``` Run it: ``` $ node index.js ``` This starts up a server on a random port and statically serves your `/pages` directory. You can also specify a `port` to always use, or pass an existing HTTP `server` (e.g. from express). Clients will then just be streamed the fine-grained resources they are using (i.e. everything is lazy loaded, no bundling, no over-fetching). Ripple keeps clients/servers in sync by replicating an immutable log of actions in the background, and subsequently the view - or other modules - which are reactively updated when the local store is updated. That's it! No boilerplate necessary, no build pipeline, no special transpilation, no magical CLI. The basic API is: ```js ripple(name) // getter ripple(name, body) // setter ripple.on('change', (name, change) => { .. }) ```   ## Components Let's add a (Web) Component to the page: **`index.html`** ```diff + ``` Let's define the component: **`resources/my-app.js:`** ```js export default () => () ``` Ripple is agnostic to _how_ you write your components, they should just be idempotent: a single render function. This is fine: **`resources/my-app.js:`** ```js export default (node, data) => node.innerHTML = 'Hello World!' ``` Or using some DOM-diff helper: **`resources/my-app.js:`** ```js export default (node, data) => jsx(node)`

Hello World

` ``` Or using [once](https://github.com/utilise/once#once)/D3 joins: **`resources/my-app.js:`** ```js export default (node, data) => { once(node) ('h1', 1) .text('Hello World') }) ``` For more info about writing idempotent components, see [this spec](https://github.com/pemrouz/vanilla).   ## State/Data The first parameter of the component is the node to update. The second parameter contains all the state and data the component needs to render: ```js export default function component(node, data){ ... } ``` * You can inject data resources by adding the name of the resources to the data attribute: ```html ``` ```js export default function shop({ stock }){ ... } ``` Declaring the data needed on a component is used to reactively rerender it when the data changes. Alternatively, you can use `ripple.pull` directly to retrieve a resource, which has similar semantics to [dynamic `import()`](https://github.com/tc39/proposal-dynamic-import) (i.e. resolves from local cache or returns a single promise): ```js const dependency = await pull('dependency') ``` * The other option is to explicitly pass down data to the component from the parent: ```js once(node) ('my-shop', { stock }) ``` The helper function will set the state and redraw, so redrawing a parent will redraw it's children. If you want to do it yourself: ```js element.state = { stock } element.draw() ```   ## Defaults You can set defaults using the ES6 syntax: ```js export default function shop({ stock = [] }){ ... } ``` If you need to persist defaults on the component's state object, you can use a small [helper function](https://github.com/utilise/utilise#--defaults): ```js export default function shop(state){ const stock = defaults(state, 'stock', []) } ```   ## Updates #### Local state Whenever you need to update local state, just change the `state` and invoke a redraw (like a game loop): ```js export default function abacus(node, state){ const o = once(node) , { counter = 0 } = state o('span', 1) .text(counter) o('button', 1) .text('increment') .on('click.increment' d => { state.counter++ o.draw() }) } ``` #### Global state Whenever you need to update global state, you can simply compute the new value and register it again which will trigger an update: ```js ripple('stock', { apples: 10 , oranges: 20 , pomegranates: 30 }) ``` Or if you just want to change a part of the resource, use a [functional operator](https://github.com/utilise/utilise#--set) to apply a finer-grained diff and trigger an update: ```js update('pomegranates', 20)(ripple('stock')) // same as: set({ type: 'update', key: 'pomegranate', value: 20 })(ripple('stock')) ``` Using logs of atomic diffs combines the benefits of immutability with a saner way to synchronise state across a distributed environment. Components are rAF batched by default. You can access the list of all relevant changes since the last render in your component via `node.changes` to make it more performant if necessary.   ## Events Dispatch an event on the root element to communicate changes to parents (`node.dispatchEvent`).   ## Routing Routing is handled by your top-level component: Simply parse the URL to determine what children to render and invoke a redraw of your application when the route has changed: ```js export function app(node, data){ const o = once(node) , { pathname } = location o('page-dashboard', pathname == '/dashboard') o('page-login', pathname == '/login') once(window) .on('popstate.nav', d => o.draw()) } ``` This solution is not tied to any library, and you may not need one at all. For advanced uses cases, checkout [decouter](https://github.com/pemrouz/decouter).   ## Styling You can author your stylesheets assuming they are completely isolated, using the Web Component syntax (`:host` etc). They will either be inserted in the shadow root of the element, or scoped and added to the head if there is no shadow. By default, the CSS resource `component-name.css` will be automatically applied to the component `component-name`. But you can apply multiple stylesheets to a component too: just extend the `css` attribute.   ## Folder Convention All files in your `/resources` folder will be automatically registered (except tests etc). You can organise it as you like, but I recommend using the convention: a folder for each component (to co-locate JS, CSS and tests), and a `data` folder for the resources that make up your domain model. ``` resources ├── data │ ├── stock.js │ ├── order.js │ └── ... ├── my-app │ ├── my-app.js │ ├── my-app.css │ └── test.js ├── another-component │ ├── another-component.js │ ├── another-component.css │ └── test.js └── ... ``` Hot reloading works out of the box. Any changes to these files will be instantly reflected everywhere.   ## Loading Resources You can also get/set resources yourselves imperatively: ```js ripple(name) // getter ripple(name, body) // setter ``` Or for example import resources from other packages: ```js ripple .resource(require('external-module-1')) .resource(require('external-module-2')) .resource(require('external-module-3')) ``` You can also create resources that proxy to [fero](https://github.com/pemrouz/fero)) services too.   ## Offline Resources are currently cached in `localStorage`. This means even _before_ any network interaction, your application renders the last-known-good-state for a superfast startup. Then as resources are streamed in, the relevant parts of the application are updated. Note: Caching of resources will be improved by using ServiceWorkers under the hood instead soon ([#27](https://github.com/rijs/fullstack/issues/27))   ## Render Middleware By default the draw function just invokes the function on an element. You can extend this without any framework hooks using the explicit decorator pattern: ```js // in component export default function component(node, data){ middleware(node, data) } // around component export default middleware(function component(node, data){ }) // for all components ripple.draw = middleware(ripple.draw) ``` A few useful middleware included in this build are: ### Needs [This middleware](https://github.com/rijs/needs#ripple--needs) reads the `needs` header and applies the attributes onto the element. The component does not render until all dependencies are available. This is useful when a component needs to define its own dependencies. You can also supply a function to dynamically calculate the required resources. ```js export default { name: 'my-component' , body: function(){} , headers: { needs: '[css=..][data=..]' } } ``` ### Shadow If supported by the browser, a shadow root will be created for each component. The component will render into the shadow DOM rather than the light DOM. ### Perf (Optional) This one is not included by default, but you can use this to log out the time each component takes to render. Other debugging tips: * Check `ripple.resources` for a snapshot of your application. Resources are in the [tuple format](https://github.com/rijs/core#ripple--core) `{ name, body, headers }`. * Check `$0.state` on an element to see the state object it was last rendered with or manipulate it.   ## Sync You can define a `from` function in the resource headers which will process requests from the client: ```js const from = (req, res) => req.data.type == 'REGISTER' ? register(req, res) : req.data.type == 'FORGOT' ? forgot(req, res) : req.data.type == 'LOGOUT' ? logout(req, res) : req.data.type == 'RESET' ? reset(req, res) : req.data.type == 'LOGIN' ? login(req, res) : false module.exports = { name: 'users' , body: {} , headers: { from } } ``` This can return a single value, a promise or a stream. On the client you make requests with `ripple.send(name, type, value)`. This returns an awaitable [stream](https://github.com/utilise/emitterify/#emitterify). You can also use the `.subscribe` API to subscribe to all or part of a resource. The key can be arbitrarily deep, and multiple keys will be merged into a single object. ```js ripple.subscribe(name, key) ripple.subscribe(name, [keys]) ``` Subscriptions are automatically deduplicated are ref-counted, so components can indepedently subscribe to the data they need without worrying about this. Note that you can also use `ripple.get` instead of subscribe if you just want to get a single value and then automatically unsubscribe.   ## Ripple Minimal If you have don't have backend for your frontend, checkout [rijs/minimal](https://github.com/rijs/minimal) which is a client-side only build of Ripple. You can also adjust your own framework by [adding/removing modules](https://github.com/rijs/minimal/blob/master/src/index.js#L1-L11).   ## Docs See [rijs/docs](https://github.com/rijs/docs) for more guides, index of modules, API reference, etc ================================================ FILE: client/ripple.bundle.js ================================================ var rijs = (function () { 'use strict'; function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var client = typeof window != 'undefined'; var client$1 = /*#__PURE__*/Object.freeze({ default: client, __moduleExports: client }); var promise_1 = promise; function promise() { var resolve , reject , p = new Promise(function(res, rej){ resolve = res, reject = rej; }); arguments.length && resolve(arguments[0]); p.resolve = resolve; p.reject = reject; return p } var promise$1 = /*#__PURE__*/Object.freeze({ default: promise_1, __moduleExports: promise_1 }); var flatten = function flatten(p,v){ if (v instanceof Array) { v = v.reduce(flatten, []); } return (p = p || []), p.concat(v) }; var flatten$1 = /*#__PURE__*/Object.freeze({ default: flatten, __moduleExports: flatten }); var has = function has(o, k) { return k in o }; var has$1 = /*#__PURE__*/Object.freeze({ default: has, __moduleExports: has }); var has$2 = ( has$1 && has ) || has$1; var def = function def(o, p, v, w){ if (o.host && o.host.nodeName) { o = o.host; } if (p.name) { v = p, p = p.name; } !has$2(o, p) && Object.defineProperty(o, p, { value: v, writable: w }); return o[p] }; var def$1 = /*#__PURE__*/Object.freeze({ default: def, __moduleExports: def }); var promise$2 = ( promise$1 && promise_1 ) || promise$1; var flatten$2 = ( flatten$1 && flatten ) || flatten$1; var def$2 = ( def$1 && def ) || def$1; var noop = function(){}; var emitterify = function emitterify(body, hooks) { body = body || {}; hooks = hooks || {}; def$2(body, 'emit', emit, 1); def$2(body, 'once', once, 1); def$2(body, 'off', off, 1); def$2(body, 'on', on, 1); body.on['*'] = body.on['*'] || []; return body function emit(type, pm, filter) { var li = body.on[type.split('.')[0]] || [] , results = []; for (var i = 0; i < li.length; i++) { if (!li[i].ns || !filter || filter(li[i].ns)) { results.push(call(li[i].isOnce ? li.splice(i--, 1)[0] : li[i], pm)); } } for (var i = 0; i < body.on['*'].length; i++) { results.push(call(body.on['*'][i], [type, pm])); } return results.reduce(flatten$2, []) } function call(cb, pm){ return cb.next ? cb.next(pm) : pm instanceof Array ? cb.apply(body, pm) : cb.call(body, pm) } function on(type, opts, isOnce) { var id = type.split('.')[0] , ns = type.split('.')[1] , li = body.on[id] = body.on[id] || [] , cb = typeof opts == 'function' ? opts : 0; return !cb && ns ? (cb = body.on[id]['$'+ns]) ? cb : push(observable(body, opts)) : !cb && !ns ? push(observable(body, opts)) : cb && ns ? push((remove(li, body.on[id]['$'+ns] || -1), cb)) : cb && !ns ? push(cb) : false function push(cb){ cb.isOnce = isOnce; cb.type = id; if (ns) { body.on[id]['$'+(cb.ns = ns)] = cb; } li.push(cb) ;(hooks.on || noop)(cb); return cb.next ? cb : body } } function once(type, callback){ return body.on(type, callback, true) } function remove(li, cb) { var i = li.length; while (~--i) { if (cb == li[i] || cb == li[i].fn || !cb) { (hooks.off || noop)(li.splice(i, 1)[0]); } } } function off(type, cb) { remove((body.on[type] || []), cb); if (cb && cb.ns) { delete body.on[type]['$'+cb.ns]; } return body } function observable(parent, opts) { opts = opts || {}; var o = emitterify(opts.base || promise$2()); o.i = 0; o.li = []; o.fn = opts.fn; o.parent = parent; o.source = opts.fn ? o.parent.source : o; o.on('stop', function(reason){ o.type ? o.parent.off(o.type, o) : o.parent.off(o); return o.reason = reason }); o.each = function(fn) { var n = fn.next ? fn : observable(o, { fn: fn }); o.li.push(n); return n }; o.pipe = function(fn) { return fn(o) }; o.map = function(fn){ return o.each(function(d, i, n){ return n.next(fn(d, i, n)) }) }; o.filter = function(fn){ return o.each(function(d, i, n){ return fn(d, i, n) && n.next(d) }) }; o.reduce = function(fn, acc) { return o.each(function(d, i, n){ return n.next(acc = fn(acc, d, i, n)) }) }; o.unpromise = function(){ var n = observable(o, { base: {}, fn: function(d){ return n.next(d) } }); o.li.push(n); return n }; o.next = function(value) { o.resolve && o.resolve(value); return o.li.length ? o.li.map(function(n){ return n.fn(value, n.i++, n) }) : value }; o.until = function(stop){ return stop.each ? stop.each(o.stop) // TODO: check clean up on stop too : stop.then ? stop.then(o.stop) : stop.call ? o.filter(stop).map(o.stop) : 0 }; o.off = function(fn){ return remove(o.li, fn), o }; o.start = function(){ o.source.emit('start'); return o }; o.stop = function(reason){ return o.source.emit('stop', reason) }; o[Symbol.asyncIterator] = function(){ return { next: function(){ return o.wait = new Promise(function(resolve){ o.wait = true; o.map(function(d, i, n){ delete o.wait; o.off(n); resolve({ value: d, done: false }); }); o.emit('pull', o); }) } } }; return o } }; var emitterify$1 = /*#__PURE__*/Object.freeze({ default: emitterify, __moduleExports: emitterify }); var is_1 = is; is.fn = isFunction; is.str = isString; is.num = isNumber; is.obj = isObject; is.lit = isLiteral; is.bol = isBoolean; is.truthy = isTruthy; is.falsy = isFalsy; is.arr = isArray; is.null = isNull; is.def = isDef; is.in = isIn; is.promise = isPromise; is.stream = isStream; function is(v){ return function(d){ return d == v } } function isFunction(d) { return typeof d == 'function' } function isBoolean(d) { return typeof d == 'boolean' } function isString(d) { return typeof d == 'string' } function isNumber(d) { return typeof d == 'number' } function isObject(d) { return typeof d == 'object' } function isLiteral(d) { return d.constructor == Object } function isTruthy(d) { return !!d == true } function isFalsy(d) { return !!d == false } function isArray(d) { return d instanceof Array } function isNull(d) { return d === null } function isDef(d) { return typeof d !== 'undefined' } function isPromise(d) { return d instanceof Promise } function isStream(d) { return !!(d && d.next) } function isIn(set) { return function(d){ return !set ? false : set.indexOf ? ~set.indexOf(d) : d in set } } var is$1 = /*#__PURE__*/Object.freeze({ default: is_1, __moduleExports: is_1 }); var client$2 = ( client$1 && client ) || client$1; var is$2 = ( is$1 && is_1 ) || is$1; var colorfill_1 = colorfill(); function colorfill(){ /* istanbul ignore next */ ['red', 'green', 'bold', 'grey', 'strip'].forEach(function(color) { !is$2.str(String.prototype[color]) && Object.defineProperty(String.prototype, color, { get: function() { return String(this) } }); }); } var identity = function identity(d) { return d }; var identity$1 = /*#__PURE__*/Object.freeze({ default: identity, __moduleExports: identity }); var wrap = function wrap(d){ return function(){ return d } }; var wrap$1 = /*#__PURE__*/Object.freeze({ default: wrap, __moduleExports: wrap }); var keys = function keys(o) { return Object.keys(is$2.obj(o) || is$2.fn(o) ? o : {}) }; var keys$1 = /*#__PURE__*/Object.freeze({ default: keys, __moduleExports: keys }); var str = function str(d){ return d === 0 ? '0' : !d ? '' : is$2.fn(d) ? '' + d : is$2.obj(d) ? JSON.stringify(d) : String(d) }; var str$1 = /*#__PURE__*/Object.freeze({ default: str, __moduleExports: str }); var wrap$2 = ( wrap$1 && wrap ) || wrap$1; var keys$2 = ( keys$1 && keys ) || keys$1; var str$2 = ( str$1 && str ) || str$1; var key = function key(k, v){ var set = arguments.length > 1 , keys = is$2.fn(k) ? [] : str$2(k).split('.').filter(Boolean) , root = keys.shift(); return function deep(o, i){ var masked = {}; return !o ? undefined : !is$2.num(k) && !k ? (set ? replace(o, v) : o) : is$2.arr(k) ? (k.map(copy), masked) : o[k] || !keys.length ? (set ? ((o[k] = is$2.fn(v) ? v(o[k], i) : v), o) : (is$2.fn(k) ? k(o) : o[k])) : (set ? (key(keys.join('.'), v)(o[root] ? o[root] : (o[root] = {})), o) : key(keys.join('.'))(o[root])) function copy(k){ var val = key(k)(o); val = is$2.fn(v) ? v(val) : val == undefined ? v : val; if (val != undefined) { key(k, is$2.fn(val) ? wrap$2(val) : val)(masked); } } function replace(o, v) { keys$2(o).map(function(k){ delete o[k]; }); keys$2(v).map(function(k){ o[k] = v[k]; }); return o } } }; var key$1 = /*#__PURE__*/Object.freeze({ default: key, __moduleExports: key }); var key$2 = ( key$1 && key ) || key$1; var header = function header(header$1, value) { var getter = arguments.length == 1; return function(d){ return !d || !d.headers ? null : getter ? key$2(header$1)(d.headers) : key$2(header$1)(d.headers) == value } }; var header$1 = /*#__PURE__*/Object.freeze({ default: header, __moduleExports: header }); var datum = function datum(node){ return node.__data__ }; var datum$1 = /*#__PURE__*/Object.freeze({ default: datum, __moduleExports: datum }); var datum$2 = ( datum$1 && datum ) || datum$1; var from_1 = from; from.parent = fromParent; function from(o){ return function(k){ return key$2(k)(o) } } function fromParent(k){ return datum$2(this.parentNode)[k] } var from$1 = /*#__PURE__*/Object.freeze({ default: from_1, __moduleExports: from_1 }); var from$2 = ( from$1 && from_1 ) || from$1; var values = function values(o) { return !o ? [] : keys$2(o).map(from$2(o)) }; var values$1 = /*#__PURE__*/Object.freeze({ default: values, __moduleExports: values }); var to = { arr: toArray , obj: toObject }; function toArray(d){ return Array.prototype.slice.call(d, 0) } function toObject(d) { var by = 'id' ; return arguments.length == 1 ? (by = d, reduce) : reduce.apply(this, arguments) function reduce(p,v,i){ if (i === 0) { p = {}; } p[is$2.fn(by) ? by(v, i) : v[by]] = v; return p } } var to_1 = to.arr; var to_2 = to.obj; var to$1 = /*#__PURE__*/Object.freeze({ default: to, __moduleExports: to, arr: to_1, obj: to_2 }); var to$2 = ( to$1 && to ) || to$1; var za = function az() { return compare(to$2.arr(arguments)) }; function compare(keys){ return function(a, b){ if (!keys.length) { return 0 } var k = keys[0] , ka = key$2(k)(a) || '' , kb = key$2(k)(b) || ''; return ka < kb ? 1 : ka > kb ? -1 : compare(keys.slice(1))(a, b) } } var za$1 = /*#__PURE__*/Object.freeze({ default: za, __moduleExports: za }); var includes = function includes(pattern){ return function(d){ return d && d.indexOf && ~d.indexOf(pattern) } }; var includes$1 = /*#__PURE__*/Object.freeze({ default: includes, __moduleExports: includes }); var includes$2 = ( includes$1 && includes ) || includes$1; var text = { header: 'text/plain' , check: function check(res){ return !includes$2('.html')(res.name) && !includes$2('.css')(res.name) && is$2.str(res.body) } }; var text_1 = text.header; var text_2 = text.check; var text$1 = /*#__PURE__*/Object.freeze({ default: text, __moduleExports: text, header: text_1, check: text_2 }); var owner = client$2 ? /* istanbul ignore next */ window : global; var owner$1 = /*#__PURE__*/Object.freeze({ default: owner, __moduleExports: owner }); var owner$2 = ( owner$1 && owner ) || owner$1; var err = function err(ns){ return function(d){ if (!owner$2.console || !console.error.apply) { return d; } is$2.arr(arguments[2]) && (arguments[2] = arguments[2].length); var args = to$2.arr(arguments) , prefix = '[err][' + (new Date()).toISOString() + ']' + ns; args.unshift(prefix.red ? prefix.red : prefix); return console.error.apply(console, args), d } }; var err$1 = /*#__PURE__*/Object.freeze({ default: err, __moduleExports: err }); var log = function log(ns){ return function(d){ if (!owner$2.console || !console.log.apply) { return d; } is$2.arr(arguments[2]) && (arguments[2] = arguments[2].length); var args = to$2.arr(arguments) , prefix = '[log][' + (new Date()).toISOString() + ']' + ns; args.unshift(prefix.grey ? prefix.grey : prefix); return console.log.apply(console, args), d } }; var log$1 = /*#__PURE__*/Object.freeze({ default: log, __moduleExports: log }); var split = function split(delimiter){ return function(d){ return d.split(delimiter) } }; var split$1 = /*#__PURE__*/Object.freeze({ default: split, __moduleExports: split }); var split$2 = ( split$1 && split ) || split$1; var identity$2 = ( identity$1 && identity ) || identity$1; var DEBUG = strip((client$2 ? (owner$2.location.search.match(/debug=(.*?)(&|$)/) || [])[1] : key$2('process.env.DEBUG')(owner$2)) || '') , whitelist = DEBUG.split(',').map(split$2('/')); var deb = function deb(ns){ return DEBUG == '*' || whitelist.some(matches(ns)) ? out : identity$2 function out(d){ if (!owner$2.console || !console.log.apply) { return d; } is$2.arr(arguments[2]) && (arguments[2] = arguments[2].length); var args = to$2.arr(arguments) , prefix = '[deb][' + (new Date()).toISOString() + ']' + ns; args.unshift(prefix.grey ? prefix.grey : prefix); return console.log.apply(console, args), d } }; function matches(ns) { ns = strip(ns).split('/'); return function(arr){ return arr.length == 1 ? arr[0] == ns[0] : arr.length == 2 ? arr[0] == ns[0] && arr[1] == ns[1] : false } } function strip(str) { return str.replace(/(\[|\])/g, '') } var deb$1 = /*#__PURE__*/Object.freeze({ default: deb, __moduleExports: deb }); var emitterify$2 = ( emitterify$1 && emitterify ) || emitterify$1; var header$2 = ( header$1 && header ) || header$1; var values$2 = ( values$1 && values ) || values$1; var za$2 = ( za$1 && za ) || za$1; var text$2 = ( text$1 && text ) || text$1; var require$$0$1 = ( err$1 && err ) || err$1; var require$$0$2 = ( log$1 && log ) || log$1; var require$$2 = ( deb$1 && deb ) || deb$1; // ------------------------------------------- // API: Gets or sets a resource // ------------------------------------------- // ripple('name') - returns the resource body if it exists // ripple('name') - creates & returns resource if it doesn't exist // ripple('name', {}) - creates & returns resource, with specified name and body // ripple({ ... }) - creates & returns resource, with specified name, body and headers // ripple.resources - returns raw resources // ripple.register - alias for ripple // ripple.on - event listener for changes - all resources // ripple('name').on - event listener for changes - resource-specific var rijs_core = function core(ref){ if ( ref === void 0 ) ref = {}; var aliases = ref.aliases; if ( aliases === void 0 ) aliases = {}; log$2('creating'); ripple.resources = {}; ripple.link = link(ripple); ripple.register = ripple; ripple.types = types(); return linkify(emitterify$2(ripple), aliases) function ripple(name, body, headers){ return !name ? ripple : is$2.arr(name) ? name.map(ripple) : is$2.promise(name) ? name.then(ripple).catch(err$2) : is$2.obj(name) && !name.name ? ripple(values$2(name)) : is$2.fn(name) && name.resources ? ripple(values$2(name.resources)) : is$2.str(name) && !body && ripple.resources[name] ? ripple.resources[name].body : is$2.str(name) && !body && !ripple.resources[name] ? undefined : is$2.str(name) && body ? register(ripple)({ name: name, body: body, headers: headers }) : is$2.obj(name) ? register(ripple)(name) : (err$2('could not find or create resource', name), false) } }; var register = function (ripple) { return function (ref) { var name = ref.name; var body = ref.body; var headers = ref.headers; if ( headers === void 0 ) headers = {}; name = ripple.aliases.src[name] || name; if (is$2.promise(body)) { return body.then(function (body) { return register(ripple)({ name: name, body: body, headers: headers }); }).catch(err$2) } deb$2('registering', name); var res = normalise(ripple)({ name: name, body: body, headers: headers }); if (!res) { return err$2('failed to register', name), false } ripple.resources[name] = res; ripple.emit('change', [name, { type: 'update' , value: res.body , time: now(res) }]); return ripple.resources[name].body }; }; var normalise = function (ripple) { return function (res) { if (!header$2('content-type')(res)) { values$2(ripple.types).sort(za$2('priority')).some(contentType(res)); } if (!header$2('content-type')(res)) { return err$2('could not understand resource', res), false } return parse(ripple)(res) }; }; var parse = function (ripple) { return function (res) { var type = header$2('content-type')(res); if (!ripple.types[type]) { return err$2('could not understand type', type), false } return (ripple.types[type].parse || identity$2)(res) }; }; var contentType = function (res) { return function (type) { return type.check(res) && (res.headers['content-type'] = type.header); }; }; var types = function () { return [text$2].reduce(to$2.obj('header'), 1); }; var linkify = function (ripple, aliases) { ripple.aliases = { dst: {}, src: {} }; for (var name in aliases) { ripple.link(aliases[name], name); } return ripple }; var link = function (ripple) { return function (from, to) { ripple.aliases.src[from] = to; ripple.aliases.dst[to] = from; Object.defineProperty(ripple.resources, from, { get: function get(){ return ripple.resources[to] } , set: function set(value){ ripple.resources[to] = value; } }); }; }; var err$2 = require$$0$1('[ri/core]') , log$2 = require$$0$2('[ri/core]') , deb$2 = require$$2('[ri/core]') , now = function (d, t) { return (t = key$2('body.log.length')(d), is$2.num(t) ? t - 1 : t); }; var rijs_core$1 = /*#__PURE__*/Object.freeze({ default: rijs_core, __moduleExports: rijs_core }); // ------------------------------------------- // Exposes a convenient global instance // ------------------------------------------- var rijs_singleton = function singleton(ripple){ log$3('creating'); if (!owner$2.ripple) { owner$2.ripple = ripple; } return ripple }; var log$3 = require$$0$2('[ri/singleton]'); var rijs_singleton$1 = /*#__PURE__*/Object.freeze({ default: rijs_singleton, __moduleExports: rijs_singleton }); var copy = function copy(from, to){ return function(d){ return to[d] = from[d], d } }; var copy$1 = /*#__PURE__*/Object.freeze({ default: copy, __moduleExports: copy }); var copy$2 = ( copy$1 && copy ) || copy$1; var overwrite = function overwrite(to){ return function(from){ keys$2(from) .map(copy$2(from, to)); return to } }; var overwrite$1 = /*#__PURE__*/Object.freeze({ default: overwrite, __moduleExports: overwrite }); var not = function not(fn){ return function(){ return !fn.apply(this, arguments) } }; var not$1 = /*#__PURE__*/Object.freeze({ default: not, __moduleExports: not }); var not$2 = ( not$1 && not ) || not$1; var extend = function extend(to){ return function(from){ keys$2(from) .filter(not$2(is$2.in(to))) .map(copy$2(from, to)); return to } }; var extend$1 = /*#__PURE__*/Object.freeze({ default: extend, __moduleExports: extend }); var merge_1 = merge; function merge(to){ return function(from){ for (x in from) { is$2.obj(from[x]) && is$2.obj(to[x]) ? merge(to[x])(from[x]) : (to[x] = from[x]); } return to } } var merge$1 = /*#__PURE__*/Object.freeze({ default: merge_1, __moduleExports: merge_1 }); var attr = function attr(name, value) { var args = arguments.length; return !is$2.str(name) && args == 2 ? attr(arguments[1]).call(this, arguments[0]) : !is$2.str(name) && args == 3 ? attr(arguments[1], arguments[2]).call(this, arguments[0]) : function(el){ var ctx = this || {}; el = ctx.nodeName || is$2.fn(ctx.node) ? ctx : el; el = el.node ? el.node() : el; el = el.host || el; return args > 1 && value === false ? el.removeAttribute(name) : args > 1 ? (el.setAttribute(name, value), value) : el.attributes.getNamedItem(name) && el.attributes.getNamedItem(name).value } }; var attr$1 = /*#__PURE__*/Object.freeze({ default: attr, __moduleExports: attr }); var act = { add: add, update: update, remove: remove } , str$3 = JSON.stringify , parse$1 = JSON.parse; var set = function set(d, skipEmit) { return function(o, existing, max) { if (!is$2.obj(o) && !is$2.fn(o)) { return o } if (!is$2.obj(d)) { var log = existing || o.log || [] , root = o; if (!is$2.def(max)) { max = log.max || 0; } if (!max) { log = []; } if (max < 0) { log = log.concat(null); } if (max > 0) { var s = str$3(o); root = parse$1(s); log = log.concat({ type: 'update', value: parse$1(s), time: log.length }); } def$2(log, 'max', max); root.log ? (root.log = log) : def$2(emitterify$2(root, null), 'log', log, 1); return root } if (is$2.def(d.key)) { if (!apply(o, d.type, (d.key = '' + d.key).split('.').filter(Boolean), d.value)) { return false } } else { return false } if (o.log && o.log.max) { o.log.push((d.time = o.log.length, o.log.max > 0 ? d : null)); } if (!skipEmit && o.emit) { o.emit('change', d); } return o } }; function apply(body, type, path, value) { var next = path.shift(); if (!act[type]) { return false } if (path.length) { if (!(next in body)) { if (type == 'remove') { return true } else { body[next] = {}; } } return apply(body[next], type, path, value) } else { return !act[type](body, next, value) } } function add(o, k, v) { is$2.arr(o) ? o.splice(k, 0, v) : (o[k] = v); } function update(o, k, v) { if (!is$2.num(k) && !k) { if (!is$2.obj(v)) { return true } for (var x in o) { delete o[x]; } for (var x in v) { o[x] = v[x]; } } else { o[k] = v; } } function remove(o, k, v) { is$2.arr(o) ? o.splice(k, 1) : delete o[k]; } var set$1 = /*#__PURE__*/Object.freeze({ default: set, __moduleExports: set }); var overwrite$2 = ( overwrite$1 && overwrite ) || overwrite$1; var extend$2 = ( extend$1 && extend ) || extend$1; var merge$2 = ( merge$1 && merge_1 ) || merge$1; var attr$2 = ( attr$1 && attr ) || attr$1; var set$2 = ( set$1 && set ) || set$1; // ------------------------------------------- // Adds support for data resources // ------------------------------------------- var rijs_data = function data(ripple){ log$4('creating'); ripple .on('change.data') .filter(function (ref) { var name = ref[0]; var change = ref[1]; return header$2('content-type', 'application/data')(ripple.resources[name]); }) .filter(function (ref) { var name = ref[0]; var change = ref[1]; return change && change.key; }) .map(function (ref) { var name = ref[0]; var change = ref[1]; return ripple .resources[name] .body .emit('change', (change || null), not$2(is$2.in(['bubble']))); }); ripple.types['application/data'] = { header: 'application/data' , ext: '*.data.js' , selector: function (res) { return ("[data~=\"" + (res.name) + "\"]"); } , extract: function (el) { return (attr$2("data")(el) || '').split(' '); } , check: function (res) { return is$2.obj(res.body); } , load: function load(res) { var exported = commonjsRequire(res.headers.path); exported = exported.default || exported; exported = is$2.fn(exported) ? exported(ripple) : exported; res.headers['content-type'] = this.header; ripple(merge$2(res)(exported)); return ripple.resources[res.name] } , parse: function parse(res){ var existing = ripple.resources[res.name] || {}; extend$2(res.headers)(existing.headers); res.body = set$2()( res.body || [] , existing.body && existing.body.log , is$2.num(res.headers.log) ? res.headers.log : -1 ); overwrite$2(res.body.on)(listeners(existing)); res.body.on('change.bubble', function (change) { ripple.emit('change', ripple.change = [res.name, change], not$2(is$2.in(['data']))); delete ripple.change; }); if (res.headers.loaded && !res.headers.loading) { res.headers.loading = Promise.resolve(res.headers.loaded(ripple, res)) .then(function () { delete res.headers.loading; return res }); } return res } }; return ripple }; var log$4 = require$$0$2('[ri/types/data]') , listeners = key$2('body.on'); var rijs_data$1 = /*#__PURE__*/Object.freeze({ default: rijs_data, __moduleExports: rijs_data }); var djbx = function (str) { var hash = 5381 , i = str.length; while (i) { hash = (hash * 33) ^ str.charCodeAt(--i); } return hash >>> 0 }; var djbx$1 = /*#__PURE__*/Object.freeze({ default: djbx, __moduleExports: djbx }); var hash = ( djbx$1 && djbx ) || djbx$1; var client_1 = function(ripple) { return log$5("creating"), ripple.types["text/css"] = { header: "text/css", ext: "*.css", selector: function (res) { return ("[css~=\"" + (res.name) + "\"]"); }, extract: function (el) { return (attr$2("css")(el) || "").split(" "); }, check: function (res) { return includes$2(".css")(res.name); }, shortname: function (path) { return basename(path); }, load: !1, parse: function (res) { return (res.headers.hash = res.headers.hash || hash(res.body), res); } }, ripple; }; var log$5 = require$$0$2("[ri/types/css]"); var basename; var client$3 = /*#__PURE__*/Object.freeze({ default: client_1, __moduleExports: client_1 }); var lo = function lo(d){ return (d || '').toLowerCase() }; var lo$1 = /*#__PURE__*/Object.freeze({ default: lo, __moduleExports: lo }); var lo$2 = ( lo$1 && lo ) || lo$1; var client_1$1 = function(ripple, ref) { if ( ref === void 0 ) ref = {}; var dir = ref.dir; if ( dir === void 0 ) dir = "."; return log$6("creating"), ripple.require = (function (res) { return function (module) { if (module in res.headers.dependencies && ripple.resources[res.headers.dependencies[module]]) { return ripple(res.headers.dependencies[module]); } throw new Error(("Cannot find module: " + module + " for " + (res.name))); }; }), ripple.types["application/javascript"] = { header: header$3, selector: function (res) { return ((res.name) + ",[is~=\"" + (res.name) + "\"]"); }, extract: function (el) { return (attr$2("is")(el) || "").split(" ").concat(lo$2(el.nodeName)); }, ext: "*.js", shortname: function (path) { return basename$1(path).split(".").slice(0, -1).join("."); }, check: function (res) { return is$2.fn(res.body); }, load: !1, parse: function (res) { if ("cjs" == res.headers.format) { var m = { exports: {} }; res.body(m, m.exports, ripple.require(res), { env: {} }), res.body = m.exports; } return res; } }, ripple; }; var log$6 = require$$0$2("[ri/types/fn]"), header$3 = "application/javascript"; var basename$1; var client$4 = /*#__PURE__*/Object.freeze({ default: client_1$1, __moduleExports: client_1$1 }); var nanosocket = function(url){ if ( url === void 0 ) url = location.href.replace('http', 'ws'); var io = emitterify$2({ attempt: 0 }); io.ready = io.once('connected'); io.connect = connect(io, url); io.connect(); io.send = function (data) { return io.ready.then(function (socket) { return socket.send(data); }); }; return io }; var min = Math.min; var pow = Math.pow; var connect = function (io, url) { return function () { var WebSocket = window.WebSocket; var location = window.location; var setTimeout = window.setTimeout; var socket = new WebSocket(url); socket.onopen = function (d) { return io.emit('connected', socket); }; socket.onmessage = function (d) { return io.emit('recv', d.data); }; socket.onclose = function (d) { io.ready = io.once('connected'); io.emit('disconnected'); setTimeout(io.connect, backoff(++io.attempt)); }; }; }; var backoff = function (attempt, base, cap) { if ( base === void 0 ) base = 100; if ( cap === void 0 ) cap = 10000; return min(cap, base * pow(2, attempt)); }; var nanosocket$1 = /*#__PURE__*/Object.freeze({ default: nanosocket, __moduleExports: nanosocket }); var require$$0$3 = ( nanosocket$1 && nanosocket ) || nanosocket$1; var client$5 = function(ref){ if ( ref === void 0 ) ref = {}; var socket = ref.socket; if ( socket === void 0 ) socket = require$$0$3(); socket.id = 0; var server = emitterify$2({ socket: socket , send: send(socket) , get subscriptions(){ return values$2(socket.on) .map(function (d) { return d && d[0]; }) .filter(function (d) { return d && d.type && d.type[0] == '$'; }) } }); socket .once('disconnected') .map(function (d) { return socket .on('connected') .map(reconnect(server)); } ); socket .on('recv') .map(deserialise) .each(function (ref) { var id = ref.id; var data = ref.data; // TODO: check/warn if no sub var sink = socket.on[("$" + id)] && socket.on[("$" + id)][0]; data.exec ? data.exec(sink, data.value) : !id ? server.emit('recv', data) : socket.emit(("$" + id), data); }); return server }; var deserialise = function (input) { return (new Function(("return " + input)))(); }; var reconnect = function (server) { return function () { return server.subscriptions .map(function (ref) { var subscription = ref.subscription; return server.socket.send(subscription); }); }; }; var send = function (socket, type) { return function (data, meta) { if (data instanceof window.Blob) { return binary(socket, data, meta) } var id = str$2(++socket.id) , output = socket.on(("$" + id)) , next = function (data, count) { if ( count === void 0 ) count = 0; return socket .send(output.source.subscription = str$2({ id: id, data: data, type: type })) .then(function (d) { return output.emit('sent', { id: id, count: count }); }); }; data.next ? data.map(next).source.emit('start') : next(data); output .source .once('stop') .filter(function (reason) { return reason != 'CLOSED'; }) .map(function (d) { return send(socket, 'UNSUBSCRIBE')(id) // TODO: also force stop on close of server created sub (?) .filter(function (d, i, n) { return n.source.emit('stop', 'CLOSED'); }); } ); return output }; }; var binary = function (socket, blob, meta, start, blockSize) { if ( start === void 0 ) start = 0; if ( blockSize === void 0 ) blockSize = 1024; var output = emitterify$2().on('recv') , next = function (id) { return function () { return start >= blob.size ? output.emit('sent', { id: id }) : ( socket.send(blob.slice(start, start += blockSize)) , window.setTimeout(next(id)) ); }; }; send(socket, 'BINARY')({ size: blob.size, meta: meta }) .on('sent', function (ref) { var id = ref.id; return next(id)(); }) .on('progress', function (received) { return output.emit('progress', { received: received, total: blob.size }); }) .map(output.next) .source .until(output.once('stop')); return output }; var client$6 = /*#__PURE__*/Object.freeze({ default: client$5, __moduleExports: client$5 }); var time = function time(ms, fn) { return arguments.length === 1 ? setTimeout(ms) : setTimeout(fn, ms) }; var time$1 = /*#__PURE__*/Object.freeze({ default: time, __moduleExports: time }); var require$$0$4 = ( client$6 && client$5 ) || client$6; var time$2 = ( time$1 && time ) || time$1; var client$7 = function sync( ripple , ref , ref$1 ){ if ( ref === void 0 ) ref = {}; if ( ref$1 === void 0 ) ref$1 = {}; var xrs = ref$1.xrs; if ( xrs === void 0 ) xrs = require$$0$4; ripple.server = xrs(); ripple.send = send$1(ripple); ripple.subscribe = subscribe(ripple); ripple.subscriptions = {}; ripple.get = get(ripple); ripple.upload = upload(ripple); ripple.upload.id = 0; // TODO: other than cache pushes? ans: use server.type ripple .server .on('recv') .map(function (data, i, n) { return cache(ripple)(data, i, n); }); return ripple }; var send$1 = function (ref) { var server = ref.server; return function (name, type, value) { return name instanceof Blob ? server.send(name, type) : is$2.obj(name) ? server.send(name) : server.send({ name: name, type: type, value: value }); }; }; var get = function (ripple) { return function (name, k) { return ripple .subscribe(name, k) .filter(function (d, i, n) { return n.source.emit('stop'); }) .start(); }; }; var cache = function (ripple, n, k) { return function (change) { // if (name && change.name && name != change.name) ripple.link(name, change.name) var name = change.name = change.name || n; if (!change.type) { change.type = 'update'; } if (is$2.def(k)) { change.key = k + "." + (str$2(change.key)); } !change.key && change.type == 'update' ? ripple(body(change)) : set$2(change)(ripple.resources[name] ? ripple(name) : ripple(name, {})); ripple.change = change; return key$2(k)(ripple(name)) }; }; var subscribe = function (ripple) { return function (name, k) { if (is$2.arr(name)) { return merge$3(name.map(function (n) { return ripple.subscribe(n, k); })) .map(function (d) { return name.reduce(function (p, v, i) { return (p[v] = d[i], p); }, {}); }) } ripple.subscriptions[name] = ripple.subscriptions[name] || {}; if (is$2.arr(k)) { return merge$3(k.map(function (k) { return ripple.subscribe(name, k); })) .map(function (d) { return key$2(k)(ripple(name)); }) } var output = emitterify$2().on('subscription'); output .on('stop') .each(function (d, i, n) { raw.subs.splice(raw.subs.indexOf(output), 1); time$2(1000, function () { if (raw.subs.length) { return } raw.source.emit('stop'); ripple.subscriptions[name][k] = undefined; output.emit('end'); }); }); if (ripple.subscriptions[name][k]) { output .on('start') .map(function () { return key$2(k)(ripple(name)); }) .filter(is$2.def) .map(function (initial) { return output.next(initial); }); } var raw = ripple.subscriptions[name][k] = ripple.subscriptions[name][k] || ripple .send(name, 'SUBSCRIBE', k) .map(cache(ripple, name, k)) .each(function (value) { raw.subs.map(function (o) { return o.next(value); }); delete ripple.change; }); raw.subs = raw.subs || []; raw.subs.push(output); return output }; }; var upload = function (ripple) { return function (name, form) { var index = ++ripple.upload.id , fields = {} , size = 0 , next = function () { if (!files.length) { return true } var ref = files.shift(); var field = ref.field; var filename = ref.filename; var i = ref.i; var blob = ref.blob; return ripple .send(blob, { filename: filename, field: field, i: i, index: index }) .on('progress', function (ref) { var received = ref.received; var total = ref.total; return output.emit('progress', { total: size , received: size - (blob.size - received) - files.reduce(function (acc, d) { return (acc += d.blob.size); }, 0) }); }) .then(next) }; var files = keys$2(form) .map(function (field) { return (fields[field] = form[field], field); }) .filter(function (field) { return form[field] instanceof FileList; }) .map(function (field) { fields[field] = []; return to$2.arr(form[field]) .map(function (f) { return (size += f.size, f); }) .map(function (f, i) { return ({ field: field, filename: f.name, i: i, blob: f, sent: 0 }); }) }) .reduce(flatten$2, []); var output = ripple.send({ files: files.length , type: 'PREUPLOAD' , fields: fields , index: index , size: size , name: name }).once('sent', next); return output }; }; var body = function (ref) { var name = ref.name; var value = ref.value; var headers = ref.headers; return ({ name: name, headers: headers, body: value }); }; // TODO: factor out var merge$3 = function (streams) { var output = emitterify$2().on('merged'); output.streams = streams; streams.map(function (stream, i) { return stream.each(function (value) { stream.latest = value; var latest = streams.map(function (d) { return d.latest; }); if (latest.every(is$2.def)) { output.next(latest); } }); } ); output .once('start') .map(function (d) { return streams.map(function ($) { return $.source.emit('start'); }); }); output .once('stop') .map(function (d) { return streams.map(function ($) { return $.source.emit('stop'); }); }); return output }; var client$8 = /*#__PURE__*/Object.freeze({ default: client$7, __moduleExports: client$7 }); var ready = function ready(fn){ return document.body ? fn() : document.addEventListener('DOMContentLoaded', fn.bind(this)) }; var ready$1 = /*#__PURE__*/Object.freeze({ default: ready, __moduleExports: ready }); var _class = function (definition) { return assign$1( definition.class ? definition.class : !definition.prototype ? classed(definition) : definition.prototype.render ? definition : definition.prototype.connected ? definition : classed(definition) ); }; var assign$1 = Object.assign; var classed = function (render) { return render.class = render.class || class { render(){ render.apply(this, arguments); } }; }; var _class$1 = /*#__PURE__*/Object.freeze({ default: _class, __moduleExports: _class }); var event = function event(node, index) { node = node.host && node.host.nodeName ? node.host : node; if (node.on) { return } node.listeners = {}; var on = function (o) { var type = o.type.split('.').shift(); if (!node.listeners[type]) { node.addEventListener(type, node.listeners[type] = function (event) { return (!event.detail || !event.detail.emitted ? emit(type, event) : 0); } ); } }; var off = function (o) { if (!node.on[o.type].length) { node.removeEventListener(o.type, node.listeners[o.type]); delete node.listeners[o.type]; } }; emitterify$2(node, { on: on, off: off }); var emit = node.emit; node.emit = function(type, params){ var detail = { params: params, emitted: true } , event = new CustomEvent(type, { detail: detail, bubbles: false, cancelable: true }); node.dispatchEvent(event); return emit(type, event) }; }; var event$1 = /*#__PURE__*/Object.freeze({ default: event, __moduleExports: event }); var classed$1 = ( _class$1 && _class ) || _class$1; var event$2 = ( event$1 && event ) || event$1; var noop$1 = function () {} , HTMLElement = client$2 && window.HTMLElement || class {} , registry = client$2 && window.customElements || {}; var define = function define(name, component) { if (arguments.length == 1) { component = name, name = "anon-" + (registry.anon++); } if (component.wrapper) { return component.wrapper } if (!name.includes('-')) { return; } if (!client$2) { return wrap$3(classed$1(component)) } var wrapped = registry.get(name); if (wrapped) { if (wrapped.class == classed$1(component)) { return wrapped } wrapped.class = classed$1(component); var instances = Array.from(document.querySelectorAll(name)); instances.map(function (node) { node.disconnectedCallback(); node.methods.map(function (method) { delete node[method]; }); node.connectedCallback(); }); } else { registry.define(name, wrapped = wrap$3(classed$1(component))); } return wrapped }; var wrap$3 = function (component) { component.wrapper = component.wrapper || class extends HTMLElement { connectedCallback(){ var this$1 = this; var ref = component.wrapper.class; var prototype = ref.prototype; event$2(this); this.state = this.state || {}; this.methods = Object .getOwnPropertyNames(prototype) .filter(function (method) { return !(method in disallowed); }) .map(function (method) { return ((this$1[method] = prototype[method].bind(this$1)), method); }); return Promise.resolve((this.connected || noop$1).call(this, this, this.state)) .then(function (d) { this$1.initialised = true; this$1.render(); }) } render(){ var ref = component.wrapper.class; var prototype = ref.prototype; if (!this.initialised) { return } return prototype.render.call(this, this, this.state) } disconnectedCallback(){ (this.disconnected || noop$1).call(this, this, this.state); this.dispatchEvent(new CustomEvent('disconnected')); this.initialised = false; } }; component.wrapper.class = component; return component.wrapper }; var disallowed = { length: 1, prototype: 1, name: 1, render: 1 }; registry.anon = registry.anon || 1; var define$1 = /*#__PURE__*/Object.freeze({ default: define, __moduleExports: define }); var ready$2 = ( ready$1 && ready ) || ready$1; var define$2 = ( define$1 && define ) || define$1; var rijs_components = function components(ripple){ if (!client$2) { return ripple } log$7('creating'); // if no render is defined on a component, load up definition Node.prototype.render = function(){ var name = this.nodeName.toLowerCase(); if (name.includes('-')) { return this.fn$ = this.fn$ || ripple .subscribe(name) .map(function (component) { return define$2(name, component); }) } // TODO: test this works well across all instances // .until(new Promise(resolve => this.addEventListener('disconnected', () => { // if (!this.isConnected) resolve() // }))) }; // this is for backwards compatibility Node.prototype.draw = function(){ this.render(); }; ready$2(function () { return Array.from(document.querySelectorAll('*')) .filter(function (d) { return d.nodeName.includes('-'); }) .map(function (node) { return node.render(); }); } ); return ripple }; var log$7 = require$$0$2('[ri/components]'); var rijs_components$1 = /*#__PURE__*/Object.freeze({ default: rijs_components, __moduleExports: rijs_components }); var require$$0$5 = ( rijs_core$1 && rijs_core ) || rijs_core$1; var require$$1 = ( rijs_singleton$1 && rijs_singleton ) || rijs_singleton$1; var require$$2$1 = ( rijs_data$1 && rijs_data ) || rijs_data$1; var require$$3 = ( client$3 && client_1 ) || client$3; var require$$4 = ( client$4 && client_1$1 ) || client$4; var require$$5 = ( client$8 && client$7 ) || client$8; var require$$6 = ( rijs_components$1 && rijs_components ) || rijs_components$1; var ripple = createCommonjsModule(function (module) { function create(opts) { var ripple = require$$0$5(opts); return require$$1(ripple, opts), require$$2$1(ripple, opts), require$$3(ripple, opts), require$$4(ripple, opts), require$$5(ripple, opts), require$$6(ripple, opts), ripple; } !window.ripple && create(), module.exports = create; }); return ripple; }()); ================================================ FILE: client/ripple.js ================================================ const client = require("utilise/client"); function create(opts) { const ripple = require("rijs.core")(opts); return require("rijs.singleton")(ripple, opts), require("rijs.data")(ripple, opts), require("rijs.css")(ripple, opts), require("rijs.fn")(ripple, opts), require("rijs.sync")(ripple, opts), require("rijs.components")(ripple, opts), ripple; } !window.ripple && create(), module.exports = create; ================================================ FILE: index.js ================================================ const client = require('utilise/client') if (client) !window.ripple && create() module.exports = create function create(opts){ const ripple = require('rijs.core')(opts) require('rijs.singleton')(ripple, opts) require('rijs.data')(ripple, opts) require('rijs.css')(ripple, opts) require('rijs.fn')(ripple, opts) require('rijs.sync')(ripple, opts) require('rijs.components')(ripple, opts) if (!client) { const { dirname, resolve } = require('path') opts.dir = opts.dir || dirname(module.parent.filename) opts.serve = resolve(__dirname, 'client') require('rijs.sessions')(ripple, opts) require('rijs.serve')(ripple, opts) require('rijs.pages')(ripple, opts) require('rijs.resdir')(ripple, opts) } return ripple } ================================================ FILE: package.json ================================================ { "name": "rijs", "version": "0.9.1", "main": ".", "author": "Pedram Emrouznejad (https://github.com/pemrouz)", "license": "pemrouz.mit-license.org", "repository": { "type": "git", "url": "git://github.com/rijs/fullstack.git" }, "scripts": { "clean": "rm -rf ./client/ripple.*", "build": "npm run clean && npm run client && npm run rollup && npm run minify && npm run gzip", "client": "uglifyjs index.js -b -d client=true -c > ./client/ripple.js", "rollup": "rollup -c", "minify": "uglifyjs ./client/ripple.bundle.js -m -c keep_fargs=false > ./client/ripple.min.js", "gzip": "gzip -c ./client/ripple.min.js > ./client/ripple.min.js.gz", "version": "npm run build && git add -A", "postversion": "git push && git push --tags", "test": "tap ./tests/*.js" }, "dependencies": { "rijs.components": "*", "rijs.core": "*", "rijs.css": "*", "rijs.data": "*", "rijs.fn": "*", "rijs.pages": "*", "rijs.resdir": "*", "rijs.serve": "*", "rijs.sessions": "*", "rijs.singleton": "*", "rijs.sync": "*", "utilise": "*" }, "devDependencies": { "puppeteer": "^1.3.0", "rollup": "^0.58.0", "rollup-plugin-async": "*", "rollup-plugin-buble": "*", "rollup-plugin-commonjs": "*", "rollup-plugin-node-resolve": "*", "rollup-plugin-nodent": "*", "tap": "^10.7.3", "uglify-es": "^3.3.10" } } ================================================ FILE: rollup.config.js ================================================ import nodeResolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import async from 'rollup-plugin-async'; import buble from 'rollup-plugin-buble' export default { input: 'client/ripple.js' , output: { file: 'client/ripple.bundle.js' , format: 'iife' } , name: 'rijs' , plugins: [ nodeResolve({ browser: true }) , commonjs({ ignoreGlobal: true }) , async() , buble({ transforms: { generator: false , classes: false } }) ] } ================================================ FILE: rollup.pure.config.js ================================================ import nodeResolve from 'rollup-plugin-node-resolve' import commonjs from 'rollup-plugin-commonjs' import replace from 'rollup-plugin-replace' import buble from 'rollup-plugin-buble' export default { input: 'index.js' , output: { file: 'ripple.pure.js' , format: 'iife' } , name: 'rijs' , plugins: [ replace({ delimiters: ['',''] , values: { "require('utilise/emitterify')": "window.emitterify" , "require('utilise/overwrite')": "window.overwrite" , "require('utilise/colorfill')": "window.colorfill" , "require('utilise/includes')": "window.includes" , "require('utilise/identity')": "window.identity" , "require('utilise/debounce')": "window.debounce" , "require('utilise/flatten')": "window.flatten" , "require('utilise/replace')": "window.replace" , "require('utilise/header')": "window.header" , "require('utilise/extend')": "window.extend" , "require('utilise/append')": "window.append" , "require('utilise/values')": "window.values" , "require('utilise/ready')": "window.ready" , "require('utilise/proxy')": "window.proxy" , "require('utilise/split')": "window.split" , "require('utilise/clone')": "window.clone" , "require('utilise/group')": "window.group" , "require('utilise/parse')": "window.parse" , "require('utilise/attr')": "window.attr" , "require('utilise/keys')": "window.keys" , "require('utilise/time')": "window.time" , "require('utilise/noop')": "window.noop" , "require('utilise/from')": "window.from" , "require('utilise/all')": "window.all" , "require('utilise/raw')": "window.raw" , "require('utilise/log')": "window.log" , "require('utilise/not')": "window.not" , "require('utilise/key')": "window.key" , "require('utilise/set')": "window.set" , "require('utilise/err')": "window.err" , "require('utilise/str')": "window.str" , "require('utilise/is')": "window.is" , "require('utilise/by')": "window.by" , "require('utilise/el')": "window.el" , "require('utilise/to')": "window.to" , "require('utilise/lo')": "window.lo" , "require('utilise/fn')": "window.fn" , "require('utilise/za')": "window.za" , "require('utilise/owner')": "window" , "require('utilise/client')": "true" , "require('rijs.sessions')": "d => d" , "require('rijs.resdir')": "d => d" , "require('rijs.pages')": "d => d" , "require('rijs.serve')": "d => d" } }) , nodeResolve({ browser: true }) , commonjs({ ignoreGlobal: true }) , buble() ] } ================================================ FILE: tests/basic.test.js ================================================ (async () => { const puppeteer = require('puppeteer') , browser = await puppeteer.launch({ headless: process.env.HEADLESS !== 'false' }) , { test } = require('tap') await test('define, use component on page, with stylesheet, hot reload', async ({ plan, same }) => { plan(2) const { ripple, page } = await startup() // register component and css ripple .resource('web-component', node => node.innerHTML = 'foo') // append to page await page.evaluate(() => { foo = document.createElement('web-component') document.body.appendChild(foo) foo.render() }) // check rendered await page.waitFor('web-component') same('foo', await page.evaluate(() => foo.innerHTML)) // register new version of component ripple('web-component', node => node.innerHTML = 'boo') same('boo', await page.evaluate(() => foo.innerHTML)) await page.close() }) await test('auto load components, with dependencies', async ({ plan, same }) => { plan(1) const { ripple, page } = await startup(``) // check rendered await page.waitFor(() => component.innerHTML == 'foo') same(['./resources/utils/foo.js', 'auto-loaded-component'], await page.evaluate(() => Object.keys(ripple.resources))) await page.close() }) await browser.close() process.exit(0) async function startup(body = ''){ const ripple = require('..')({ port: 0, dir: __dirname }) ripple.server.express.use((req, res) => res.send(` ${body} `)) await ripple.server.once('listening') const page = await browser.newPage() await page.goto(`http://localhost:${ripple.server.port}`) if (process.env.DEBUG == 'true') page.on('console', (...args) => console.log('(CLIENT):', ...args)) return { ripple, page } } })() ================================================ FILE: tests/resources/_components/x-foo.css ================================================ :host { background: red } ================================================ FILE: tests/resources/_components/x-foo.js ================================================ const define = require('@compone/define') , style = require('@compone/style') module.exports = define('x-foo', async (node, state) => { await style(node, await ripple.get('x-foo.css')) node.innerHTML = await ripple.get('some-data') }) ================================================ FILE: tests/resources/components/auto-loaded-component.js ================================================ const foo = require('../utils/foo') module.exports = node => (node.innerHTML = foo) ================================================ FILE: tests/resources/utils/foo.js ================================================ module.exports = 'foo'