From 45f83ef9681de0c5559bed4d3ee16348af99e98b Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 01:37:43 +0200 Subject: [PATCH 01/79] remove hard dep on svelte --- package-lock.json | 6 ------ package.json | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10446926..7f74cb82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1243,12 +1243,6 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, - "svelte": { - "version": "3.0.0-beta.5", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.0.0-beta.5.tgz", - "integrity": "sha512-uPDvJ37NGCdIi/Nk2RvtYcrNuVOO34SA+T4ZDJMyb5JiRQ4xHXtbBpxl6ytzftHewfQqKGcEeB+T3LVLIwj6OQ==", - "dev": true - }, "svelte-dev-helper": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz", diff --git a/package.json b/package.json index ea71686d..bc13f726 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ "eslint-plugin-mocha": "^5.2.0", "mocha": "^5.2.0", "sinon": "^6.1.5", - "sinon-chai": "^3.2.0", - "svelte": "^3.0.0-beta.5" + "sinon-chai": "^3.2.0" }, "peerDependencies": { "svelte": ">1.44.0" From 23fa354900ef6033618e44ea632189f2ec891adf Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 01:38:08 +0200 Subject: [PATCH 02/79] style: remove trailing space --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 320a12ce..f0379d6b 100644 --- a/index.js +++ b/index.js @@ -106,7 +106,7 @@ module.exports = function(source, map) { const virtualModules = virtualModuleInstances.get(this._compiler); this.cacheable(); - + const options = Object.assign({}, getOptions(this)); const callback = this.async(); From eda88c6f8fa1266df2ba8392f8e99b1ab885340f Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 01:56:16 +0200 Subject: [PATCH 03/79] extract makeHot from index.js --- index.js | 33 +++------------------------------ lib/make-hot.js | 29 +++++++++++++++++++++++++++++ lib/posixify.js | 3 +++ 3 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 lib/make-hot.js create mode 100644 lib/posixify.js diff --git a/index.js b/index.js index f0379d6b..023989dc 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,7 @@ const { basename, extname, relative } = require('path'); const { getOptions } = require('loader-utils'); const VirtualModules = require('./lib/virtual'); - -const hotApi = require.resolve('./lib/hot-api.js'); +const posixify = require('./lib/posixify'); const { version } = require('svelte/package.json'); const major_version = +version[0]; @@ -10,6 +9,8 @@ const { compile, preprocess } = major_version >= 3 ? require('svelte/compiler') : require('svelte'); +const makeHot = require('./lib/make-hot'); + const pluginOptions = { externalDependencies: true, hotReload: true, @@ -25,34 +26,6 @@ const pluginOptions = { markup: true }; -function makeHot(id, code, hotOptions) { - const options = JSON.stringify(hotOptions); - const replacement = ` -if (module.hot) { - const { configure, register, reload } = require('${posixify(hotApi)}'); - - module.hot.accept(); - - if (!module.hot.data) { - // initial load - configure(${options}); - $2 = register(${id}, $2); - } else { - // hot update - $2 = reload(${id}, $2); - } -} - -export default $2; -`; - - return code.replace(/(export default ([^;]*));/, replacement); -} - -function posixify(file) { - return file.replace(/[/\\]/g, '/'); -} - function sanitize(input) { return basename(input) .replace(extname(input), '') diff --git a/lib/make-hot.js b/lib/make-hot.js new file mode 100644 index 00000000..99a021f1 --- /dev/null +++ b/lib/make-hot.js @@ -0,0 +1,29 @@ +const posixify = require('./posixify'); + +const hotApi = require.resolve('./hot-api.js'); + +function makeHot(id, code, hotOptions) { + const options = JSON.stringify(hotOptions); + const replacement = ` +if (module.hot) { + const { configure, register, reload } = require('${posixify(hotApi)}'); + + module.hot.accept(); + + if (!module.hot.data) { + // initial load + configure(${options}); + $2 = register(${id}, $2); + } else { + // hot update + $2 = reload(${id}, $2); + } +} + +export default $2; +`; + + return code.replace(/(export default ([^;]*));/, replacement); +} + +module.exports = makeHot; diff --git a/lib/posixify.js b/lib/posixify.js new file mode 100644 index 00000000..84d804b5 --- /dev/null +++ b/lib/posixify.js @@ -0,0 +1,3 @@ +module.exports = function posixify(file) { + return file.replace(/[/\\]/g, '/'); +}; From 5b568c8892a521069e9e92a5604225dd9a84de2d Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 02:19:26 +0200 Subject: [PATCH 04/79] add support for HMR with svelte 3 --- index.js | 4 +- lib/svelte3/hot/hot-api.js | 71 +++++ lib/svelte3/hot/proxy-adapter-dom.js | 99 +++++++ lib/svelte3/hot/proxy.js | 421 +++++++++++++++++++++++++++ lib/svelte3/hot/svelte-hooks.js | 73 +++++ lib/svelte3/make-hot.js | 19 ++ 6 files changed, 686 insertions(+), 1 deletion(-) create mode 100644 lib/svelte3/hot/hot-api.js create mode 100644 lib/svelte3/hot/proxy-adapter-dom.js create mode 100644 lib/svelte3/hot/proxy.js create mode 100644 lib/svelte3/hot/svelte-hooks.js create mode 100644 lib/svelte3/make-hot.js diff --git a/index.js b/index.js index 023989dc..027f24d0 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,9 @@ const { compile, preprocess } = major_version >= 3 ? require('svelte/compiler') : require('svelte'); -const makeHot = require('./lib/make-hot'); +const makeHot = major_version >= 3 + ? require('./lib/svelte3/make-hot') + : require('./lib/make-hot'); const pluginOptions = { externalDependencies: true, diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js new file mode 100644 index 00000000..4df8a25e --- /dev/null +++ b/lib/svelte3/hot/hot-api.js @@ -0,0 +1,71 @@ +import DomAdapter from './proxy-adapter-dom'; +import { initProxy } from './proxy'; + +const defaultHotOptions = { + noPreserveState: false, +}; + +const registry = new Map(); + +const createProxy = initProxy(DomAdapter); + +// One stop shop for HMR updates. Combines functionality of `configure`, +// `register`, and `reload`, based on current registry state. +// +// Additionaly does whatever it can to avoid crashing on runtime errors, +// and tries to decline HMR if that doesn't go well. +// +export function applyHMR(hotOptions, id, moduleHot, component) { + // resolve existing record + let record = registry.get(id); + let broken = false; + + hotOptions = Object.assign({}, defaultHotOptions, hotOptions); + + // (re)render + if (record) { + const success = record.reload({ component, hotOptions }); + if (success === false) { + broken = true; + } + } else { + record = createProxy(id, component, hotOptions); + registry.set(id, record); + } + + const proxy = record && record.proxy; + + if (!proxy) { + // well, endgame... we won't be able to render next updates, even + // successful, if we don't have proxies in svelte's tree + // + // full reload required + // + // we can't command a full reload from here + + // tell webpack our HMR is dead, so next update should trigger a full reload + moduleHot.decline(); + + // TODO report error on client + + // since we won't return the proxy and the app will expect a svelte + // component, it's gonna crash... so it's best to report the real cause + throw new Error(`Failed to create HMR proxy for Svelte component ${id}`); + } + + // Make sure we won't try to restore from an irrecuperable state. + // + // E.g. a component can partially render children to DOM from its + // constructor, then crash without even leaving a reference in a + // variable (since crash from the constructor). Maybe the compiler + // could handle the situation, but we can't (so, according to HMR + // Tao, better full reload than stale display). + // + if (broken) { + moduleHot.decline(); + } else { + moduleHot.accept(); + } + + return proxy; +} diff --git a/lib/svelte3/hot/proxy-adapter-dom.js b/lib/svelte3/hot/proxy-adapter-dom.js new file mode 100644 index 00000000..6fcddda3 --- /dev/null +++ b/lib/svelte3/hot/proxy-adapter-dom.js @@ -0,0 +1,99 @@ +/* global document */ + +const removeElement = el => el && el.parentNode && el.parentNode.removeChild(el); + +export default class ProxyAdapterDom { + constructor(instance) { + this.instance = instance; + this.insertionPoint = null; + + this.afterMount = this.afterMount.bind(this); + this.rerender = this.rerender.bind(this); + } + + dispose() { + // Component is being destroyed, detaching is not optional in Svelte3's + // component API, so we can dispose of the insertion point in every case. + if (this.insertionPoint) { + removeElement(this.insertionPoint); + this.insertionPoint = null; + } + } + + afterMount(target, anchor) { + const { + instance: { debugName }, + } = this; + // insertionPoint needs to be updated _only when the target changes_ -- + // i.e. when the component is mounted, i.e. (in svelte3) when the component + // is _created_, and svelte3 doesn't allow it to move afterward -- that + // is, insertionPoint only needs to be created once when the component is + // first mounted. + // + // DEBUG is it really true that components' elements cannot move in the + // DOM? what about keyed list? + // + if (!this.insertionPoint) { + this.insertionPoint = document.createComment(debugName); + target.insertBefore(this.insertionPoint, anchor); + } + } + + rerender() { + const { + instance: { refreshComponent }, + insertionPoint, + } = this; + if (!insertionPoint) { + throw new Error('Cannot rerender: Missing insertion point'); + } + refreshComponent(insertionPoint.parentNode, insertionPoint); + } + + renderError(err, target, anchor) { + const { + instance: { debugName }, + } = this; + const style = { + section: ` + border: 3px solid red; + background-color: #fee; + padding: 12px; + `, + h2: ` + margin: 0 0 12px; + color: red; + `, + pre: ` + background: #eee; + padding: 6px; + margin: 0; + border: 1px solid #ddd; + `, + }; + const title = debugName || err.moduleName || 'Error'; + const h2 = document.createElement('h2'); + h2.textContent = title; + const pre = document.createElement('pre'); + pre.textContent = err.stack || err; + const section = document.createElement('section'); + section.appendChild(h2); + section.appendChild(pre); + // style + section.style = style.section; + h2.style = style.h2; + pre.style = style.pre; + if (anchor) { + target.insertBefore(section, anchor); + } else { + if (!target) { + target = document.body; + section.style.posititon = 'absolute'; + } + target.appendChild(section); + } + return () => { + target.removeChild(section); + }; + } +} diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js new file mode 100644 index 00000000..0960f3b7 --- /dev/null +++ b/lib/svelte3/hot/proxy.js @@ -0,0 +1,421 @@ +import { hookComponent, restoreState, captureState } from './svelte-hooks'; + +const handledMethods = ['constructor', '$destroy']; +const forwardedMethods = ['$set', '$on']; + +const noop = () => {}; + +const logError = (...args) => console.error('[HMR][Svelte]', ...args); + +const posixify = file => file.replace(/[/\\]/g, '/'); + +const getBaseName = id => + id + .split('/') + .pop() + .split('.') + .shift(); + +const capitalize = str => str[0].toUpperCase() + str.slice(1); + +const getFriendlyName = id => capitalize(getBaseName(posixify(id))); + +const getDebugName = id => `<${getFriendlyName(id)}>`; + +const relayCalls = (getTarget, names, dest = {}) => { + for (const key of names) { + dest[key] = function(...args) { + const target = getTarget(); + if (!target) { + return; + } + return target[key] && target[key].call(this, ...args); + }; + } + return dest; +}; + +const copyComponentMethods = (proxy, cmp, debugName) => { + //proxy custom methods + const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(cmp)); + methods.forEach(method => { + if ( + !handledMethods.includes(method) && + !forwardedMethods.includes(method) + ) { + proxy[method] = function() { + if (cmp[method]) { + return cmp[method].apply(this, arguments); + } else { + // we can end up here when calling a method added by a previous + // version of the component, then removed (but still called + // somewhere else in the code) + // + // TODO we should possibly consider removing all the methods that + // have been added by a previous version of the component. This + // would be better memory-wise. Not so much so complexity-wise, + // though. And for now, we can't survive most runtime errors, so + // we will be reloaded often... + // + throw new Error( + `Called to undefined method on ${debugName}: ${method}` + ); + } + }; + } + }); +}; + +// TODO clean this extremely ad-hoc, coupled, & fragile code +// TODO native: this must respect Page/Frame interface... or need tolerance from SN +const createErrorComponent = (adapter, err, target, anchor) => { + const cmp = { + $destroy: noop, + $set: noop, + $$: { + fragment: { + c: noop, // create + l: noop, // claim + h: noop, // hydrate + m: (target, anchor) => { + cmp.$destroy = adapter.renderError(err, target, anchor); + }, // mount + p: noop, // update + i: noop, // intro + o: noop, // outro + d: noop, // destroy + }, + ctx: {}, + // state + props: [], + update: noop, + not_equal: noop, + bound: {}, + // lifecycle + on_mount: [], + on_destroy: [], + before_render: [], + after_render: [], + context: {}, + // everything else + callbacks: [], + dirty: noop, + }, + }; + if (target) { + cmp.$destroy = adapter.renderError(err, target, anchor); + } + return cmp; +}; + +// everything in the constructor! +// +// so we don't polute the component class with new members +// +// specificity & conformance with Svelte component constructor is achieved +// in the "component level" (as opposed "instance level") createRecord +// +class ProxyComponent { + constructor( + { + Adapter, + id, + debugName, + current, // { component, hotOptions: { noPreserveState, ... } } + register, + unregister, + reportError, + }, + options // { target, anchor, ... } + ) { + let cmp; + let disposed = false; + let restore; + let lastError = null; + + // it's better to restore props from the very beginning -- for example + // slots (yup, stored in props as $$slots) are broken if not present at + // component creation and later restored with $set + const restoreProps = restore => { + return restore && restore.props && { props: restore.props }; + }; + + const doCreateComponent = (target, anchor) => { + const { component } = current; + const opts = Object.assign( + {}, + options, + { target, anchor }, + restoreProps(restore) + ); + cmp = new component(opts); + }; + + const attachComponent = cmp => { + const onDestroy = () => { + if (cmp === getComponent()) { + afterDetach(); + } + }; + + hookComponent(this, cmp, { + onDestroy, + onMount: afterMount, + }); + + copyComponentMethods(this, cmp); + + restoreState(cmp, restore); + }; + + const createComponent = (target, anchor) => { + doCreateComponent(target, anchor); + attachComponent(cmp); + }; + + const destroyComponent = () => { + // destroyComponent is tolerant (don't crash on no cmp) because it + // is possible that reload/rerender is called after a previous + // createComponent has failed (hence we have a proxy, but no cmp) + if (cmp) { + const target = cmp; + // WARNING nullify BEFORE $destroy, or we'll consider we're destroying + // the final component instance, in the onDestroy hook + cmp = null; + target.$destroy(); + } + }; + + const refreshComponent = (target, anchor, conservativeDestroy) => { + if (lastError) { + clearError(); + } else if (conservativeDestroy) { + const prevCmp = cmp; + restore = captureState(cmp); + try { + createComponent(target, anchor); + prevCmp.$destroy(); + if (typeof conservativeDestroy === 'function') { + conservativeDestroy(); + } + } catch (err) { + logError( + `Failed to recreate ${debugName} instance: ${(err && err.stack) || + err}` + ); + cmp = prevCmp; + } + } else { + restore = captureState(cmp); + destroyComponent(); + try { + createComponent(target, anchor); + } catch (err) { + logError( + `Failed to recreate ${debugName} instance: ${(err && err.stack) || + err}` + ); + if (current.hotOptions.optimistic) { + setError(err, target, anchor); + } else { + throw err; + } + } + } + }; + + const setError = (err, target, anchor) => { + lastError = err; + destroyComponent(); + // create a noop comp to trap Svelte's calls + cmp = createErrorComponent(adapter, err, target, anchor); + }; + + const clearError = () => { + lastError = null; + adapter.rerender(); + }; + + const instance = { + hotOptions: current.hotOptions, + proxy: this, + id, + debugName, + refreshComponent, + }; + + if (current.hotOptions.noPreserveState) { + instance.captureState = noop; + instance.restoreState = noop; + } + + const adapter = new Adapter(instance); + + const { afterMount, rerender } = adapter; + + // $destroy is not called when a child component is disposed, so we + // need to hook from fragment. + const afterDetach = () => { + // NOTE do NOT call $destroy on the cmp from here; the cmp is already + // dead, this would not work + if (!disposed) { + disposed = true; + adapter.dispose(); + unregister(); + } + }; + + // ---- register proxy instance ---- + + register(rerender); + + // ---- augmented methods ---- + + this.$destroy = () => { + destroyComponent(); + afterDetach(); + }; + + // ---- forwarded methods ---- + + const getComponent = () => cmp; + + relayCalls(getComponent, forwardedMethods, this); + + // ---- create & mount target component instance --- + + { + const { component } = current; + const { target, anchor } = options; + + // copy statics before doing anything because a static prop/method + // could be used somewhere in the create/render call + copyStatics(component, this); + + try { + createComponent(target, anchor); + + // Svelte 3 creates and mount components from their constructor if + // options.target is present. + // + // This means that at this point, the component's `fragment.c` and, + // most notably, `fragment.m` will already have been called _from inside + // createComponent_. That is: before we have a chance to hook on it. + // + // Proxy's constructor + // -> createComponent + // -> component constructor + // -> component.$$.fragment.c(...) (or l, if hydrate:true) + // -> component.$$.fragment.m(...) + // + // -> you are here <- + // + // I've tried to move the responsibility for mounting the component here, + // by setting `$$inline` option to prevent Svelte from doing it itself. + // `$$inline` is normally used for child components, and their lifecycle + // is managed by their parent. But that didn't go too well. + // + // We want the proxied component to be mounted on the DOM anyway, so it's + // easier to let Svelte do its things and manually execute our `afterMount` + // hook ourself (will need to do the same for `c` and `l` hooks, if we + // come to need them here). + // + if (target) { + afterMount(target, anchor); + } + } catch (err) { + if (current.hotOptions.optimistic) { + logError( + `Failed to create ${debugName} instance: ${(err && err.stack) || + err}` + ); + setError(err, target, anchor); + } else { + throw err; + } + } + } + } +} + +const copyStatics = (component, proxy) => { + //forward static properties and methods + for (let key in component) { + proxy[key] = component[key]; + } +}; + +/* +creates a proxy object that +decorates the original component with trackers +and ensures resolution to the +latest version of the component +*/ +export const initProxy = Adapter => + function createProxy(id, component, hotOptions) { + const debugName = getDebugName(id); + const instances = []; + + // current object will be updated, proxy instances will keep a ref + const current = { + component, + hotOptions, + }; + + const name = `Proxy${debugName}`; + + // this trick gives the dynamic name Proxy to the concrete + // proxy class... unfortunately, this doesn't shows in dev tools, but + // it stills allow to inspect cmp.constructor.name to confirm an instance + // is a proxy + const proxy = { + [name]: class extends ProxyComponent { + constructor(options) { + super( + { + Adapter, + id, + debugName, + current, + register: rerender => { + instances.push(rerender); + }, + unregister: () => { + const i = instances.indexOf(this); + instances.splice(i, 1); + }, + }, + options + ); + } + }, + }[name]; + + // reload all existing instances of this component + const reload = ({ component, hotOptions }) => { + // update current references + Object.assign(current, { component, hotOptions }); + + // TODO delete props/methods previously added and of which value has + // not changed since + copyStatics(component, proxy); + + const errors = []; + + instances.forEach(rerender => { + try { + rerender(); + } catch (err) { + errors.push(err); + } + }); + + if (errors.length > 0) { + return false; + } + + return true; + }; + + return { id, proxy, reload }; + }; diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js new file mode 100644 index 00000000..a479809b --- /dev/null +++ b/lib/svelte3/hot/svelte-hooks.js @@ -0,0 +1,73 @@ +/** + * Emulates forthcoming HMR hooks in Svelte. + * + * All references to private compoonent state ($$) are now isolated in this + * module. + */ + +export const captureState = cmp => { + // sanity check: propper behaviour here is to crash noisily so that + // user knows that they're looking at something broken + if (!cmp) { + throw new Error('Missing component'); + } + if (!cmp.$$) { + throw new Error('Invalid component'); + } + const { + $$: { callbacks, bound, ctx: props }, + } = cmp; + return { props, callbacks, bound }; +}; + +export const restoreState = (cmp, restore) => { + if (!restore) { + return; + } + const { callbacks, bound } = restore; + if (callbacks) { + cmp.$$.callbacks = callbacks; + } + if (bound) { + cmp.$$.bound = bound; + } + // props, props.$$slots are restored at component creation (works + // better -- well, at all actually) +}; + +// emulates (forthcoming?) svelte dev hooks +// +// NOTE onDestroy must be called even if the call doesn't pass through the +// component's $destroy method (that we can hook onto by ourselves, since +// it's public API) -- this happens a lot in svelte's internals, that +// manipulates cmp.$$.fragment directly, often binding to fragment.d, +// for example +// +// NOTE onMount must provide target & anchor (for us to be able to determinate +// actual DOM insertion point) +// +// TODO should copyComponentMethods be done here? +// +export const hookComponent = (proxy, cmp, { onMount, onDestroy }) => { + if (onMount) { + const m = cmp.$$.fragment.m; + cmp.$$.fragment.m = (...args) => { + const result = m(...args); + onMount(...args); + return result; + }; + } + + if (onDestroy) { + cmp.$$.on_destroy.push(onDestroy); + } + + // WARNING the proxy MUST use the same $$ object as its component + // instance, because a lot of wiring happens during component + // initialisation... lots of references to $$ and $$.fragment have + // already been distributed around when the component constructor + // returns, before we have a chance to wrap them (and so we can't + // wrap them no more, because existing references would become + // invalid) + proxy.$$ = cmp.$$; +}; diff --git a/lib/svelte3/make-hot.js b/lib/svelte3/make-hot.js new file mode 100644 index 00000000..9d4408af --- /dev/null +++ b/lib/svelte3/make-hot.js @@ -0,0 +1,19 @@ +const posixify = require('../posixify'); + +const hotApi = require.resolve('./hot/hot-api.js'); + +const quote = JSON.stringify; + +function makeHot(id, code, hotOptions = {}) { + const options = JSON.stringify(hotOptions); + const replacement = ` + if (module.hot) { + const { applyHMR } = require('${posixify(hotApi)}'); + $2 = applyHMR(${options}, ${quote(id)}, module.hot, $2); + } + export default $2; + `; + return code.replace(/(export default ([^;]*));/, replacement); +} + +module.exports = makeHot; From 9dd5eec1c1f581ec9d1541749f6d0728753524cf Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 02:53:20 +0200 Subject: [PATCH 05/79] add support for HMR with svelte-native --- .../hot/patch-page-show-modal.js | 63 ++++ lib/svelte-native/hot/proxy-adapter-native.js | 304 ++++++++++++++++++ lib/svelte3/hot/hot-api.js | 8 +- native.js | 11 + 4 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 lib/svelte-native/hot/patch-page-show-modal.js create mode 100644 lib/svelte-native/hot/proxy-adapter-native.js create mode 100644 native.js diff --git a/lib/svelte-native/hot/patch-page-show-modal.js b/lib/svelte-native/hot/patch-page-show-modal.js new file mode 100644 index 00000000..6cb5bd8a --- /dev/null +++ b/lib/svelte-native/hot/patch-page-show-modal.js @@ -0,0 +1,63 @@ +/* global Symbol */ + +// This module monkey patches Page#showModal in order to be able to +// access from the HMR proxy data passed to `showModal` in svelte-native. +// +// Data are stored in a opaque prop accessible with `getModalData`. +// +// It also switches the `closeCallback` option with a custom brewed one +// in order to give the proxy control over when its own instance will be +// destroyed. +// +// Obviously this method suffer from extreme coupling with the target code +// in svelte-native. So it would be wise to recheck compatibility on SN +// version upgrades. +// +// Relevant code is there (last checked version): +// +// https://github.com/halfnelson/svelte-native/blob/08702e6b178644f43052f6ec0a789a51e800d21b/src/dom/svelte/StyleElement.ts +// + +// FIXME should we override ViewBase#showModal instead? +import { Page } from 'tns-core-modules/ui/page'; + +const prop = + typeof Symbol !== 'undefined' + ? Symbol('hmr_svelte_native_modal') + : '___HMR_SVELTE_NATIVE_MODAL___'; + +const sup = Page.prototype.showModal; + +let patched = false; + +export const patchShowModal = () => { + // guard: already patched + if (patched) return; + patched = true; + + Page.prototype.showModal = function(modalView, options) { + const modalData = { + originalOptions: options, + closeCallback: options.closeCallback, + }; + + modalView[prop] = modalData; + + // Proxies to a function that can be swapped on the fly by HMR proxy. + // + // The default is still to call the original closeCallback from svelte + // navtive, which will destroy the modal view & component. This way, if + // no HMR happens on the modal content, normal behaviour is preserved + // without the proxy having any work to do. + // + const closeCallback = (...args) => { + return modalData.closeCallback(...args); + }; + + const temperedOptions = Object.assign({}, options, { closeCallback }); + + return sup.call(this, modalView, temperedOptions); + }; +}; + +export const getModalData = modalView => modalView[prop]; diff --git a/lib/svelte-native/hot/proxy-adapter-native.js b/lib/svelte-native/hot/proxy-adapter-native.js new file mode 100644 index 00000000..91019f46 --- /dev/null +++ b/lib/svelte-native/hot/proxy-adapter-native.js @@ -0,0 +1,304 @@ +/* global document */ + +import ProxyAdapterDom from '../../svelte3/hot/proxy-adapter-dom'; + +import { getModalData } from './patch-page-show-modal'; + +// Svelte Native support +// ===================== +// +// Rerendering Svelte Native page proves challenging... +// +// In NativeScript, pages are the top level component. They are normally +// introduced into NativeScript's runtime by its `navigate` function. This +// is how Svelte Natives handles it: it renders the Page component to a +// dummy fragment, and "navigate" to the page element thus created. +// +// As long as modifications only impact child components of the page, then +// we can keep the existing page and replace its content for HMR. +// +// However, if the page component itself is modified (including its system +// title bar), things get hairy... +// +// Apparently, the sole way of introducing a new page in a NS application is +// to navigate to it (no way to just replace it in its parent "element", for +// example). This is how it is done in NS's own "core" HMR. +// +// Unfortunately the API they're using to do that is not public... Its various +// parts remain exposed though (but documented as private), so this exploratory +// work now relies on it. It might be fragile... +// +// The problem is that there is no public API that can navigate to a page and +// replacing (like location.replace) the current history entry. Actually there +// is an active issue at NS asking for that. Incidentally, members of +// NativeScript-Vue have commented on the issue to weight in for it -- they +// probably face some similar challenge. + +// svelte-native uses navigateFrom event + e.isBackNavigation to know +// when to $destroy the component -- but we don't want our proxy instance +// destroyed when we renavigate to the same page for navigation purposes! +const interceptPageNavigation = pageElement => { + const originalNativeView = pageElement.nativeView; + const { on } = originalNativeView; + const ownOn = originalNativeView.hasOwnProperty('on'); + // tricks svelte-native into giving us its handler + originalNativeView.on = function(type, handler, ...rest) { + if (type === 'navigatedFrom') { + this.navigatedFromHandler = handler; + if (ownOn) { + originalNativeView.on = on; + } else { + delete originalNativeView.on; + } + } else { + throw new Error( + 'Unexpected call: has underlying svelte-native code changed?' + ); + } + }; +}; + +const getNavTransition = ({ transition }) => { + if (typeof transition === 'string') { + transition = { name: transition }; + } + return transition ? { animated: true, transition } : { animated: false }; +}; + +export default class ProxyAdapterNative extends ProxyAdapterDom { + constructor(instance) { + super(instance); + + this.nativePageElement = null; + this.originalNativeView = null; + this.navigatedFromHandler = null; + + this.relayNativeNavigatedFrom = this.relayNativeNavigatedFrom.bind(this); + } + + dispose() { + super.dispose(); + this.releaseNativePageElement(); + } + + releaseNativePageElement() { + if (this.nativePageElement) { + // native cleaning will happen when navigating back from the page + this.nativePageElement = null; + } + } + + afterMount(target, anchor) { + // nativePageElement needs to be updated each time (only for page + // components, native component that are not pages follow normal flow) + // + // DEBUG quid of components that are initially a page, but then have the + // tag removed while running? or the opposite? + // + // insertionPoint needs to be updated _only when the target changes_ -- + // i.e. when the component is mount, i.e. (in svelte3) when the component + // is _created_, and svelte3 doesn't allow it to move afterward -- that + // is, insertionPoint only needs to be created once when the component is + // first mounted. + // + // DEBUG is it really true that components' elements cannot move in the + // DOM? what about keyed list? + // + const isNativePage = target.tagName === 'fragment'; + if (isNativePage) { + const nativePageElement = target.firstChild; + interceptPageNavigation(nativePageElement); + this.nativePageElement = nativePageElement; + } else { + // try to protect against components changing from page to no-page + // or vice versa -- see DEBUG 1 above. NOT TESTED so prolly not working + this.nativePageElement = null; + super.afterMount(target, anchor); + } + } + + rerender() { + const { nativePageElement } = this; + if (nativePageElement) { + this.rerenderNative(); + } else { + super.rerender(); + } + } + + rerenderNative() { + const { nativePageElement: oldPageElement } = this; + const nativeView = oldPageElement.nativeView; + const frame = nativeView.frame; + if (frame) { + return this.rerenderPage(frame, nativeView); + } + const modalParent = nativeView._modalParent; // FIXME private API + if (modalParent) { + return this.rerenderModal(modalParent, nativeView); + } + // wtf? hopefully a race condition with a destroyed component, so + // we have nothing more to do here + // + // for once, it happens when hot reloading dev deps, like this file + // + } + + rerenderPage(frame, previousPageView) { + const isCurrentPage = frame.currentPage === previousPageView; + if (isCurrentPage) { + const { + instance: { hotOptions }, + } = this; + const newPageElement = this.createPage(); + if (!newPageElement) { + throw new Error('Failed to create updated page'); + } + const isFirstPage = !frame.canGoBack(); + + if (isFirstPage) { + // The "replacePage" strategy does not work on the first page + // of the stack. + // + // Resulting bug: + // - launch + // - change first page => HMR + // - navigate to other page + // - back + // => actual: back to OS + // => expected: back to page 1 + // + // Fortunately, we can overwrite history in this case. + // + const nativeView = newPageElement.nativeView; + frame.navigate( + Object.assign( + {}, + { + create: () => nativeView, + clearHistory: true, + }, + getNavTransition(hotOptions) + ) + ); + } else { + // copied from TNS FrameBase.replacePage + // + // it is not public but there is a comment in there indicating + // it is for HMR (probably their own core HMR though) + // + // frame.navigationType = NavigationType.replace; + const currentBackstackEntry = frame._currentEntry; + frame.navigationType = 2; + frame.performNavigation({ + isBackNavigation: false, + entry: { + resolvedPage: newPageElement.nativeView, + // + // entry: currentBackstackEntry.entry, + entry: Object.assign( + currentBackstackEntry.entry, + getNavTransition(hotOptions) + ), + navDepth: currentBackstackEntry.navDepth, + fragmentTag: currentBackstackEntry.fragmentTag, + frameId: currentBackstackEntry.frameId, + }, + }); + } + } else { + const backEntry = frame.backStack.find( + ({ resolvedPage: page }) => page === previousPageView + ); + if (!backEntry) { + // well... looks like we didn't make it to history after all + return; + } + // replace existing nativeView + const newPageElement = this.createPage(); + if (newPageElement) { + backEntry.resolvedPage = newPageElement.nativeView; + } else { + throw new Error('Failed to create updated page'); + } + } + } + + // modalParent is the page on which showModal(...) was called + // oldPageElement is the modal content, that we're actually trying to reload + rerenderModal(modalParent, modalView) { + const modalData = getModalData(modalView); + + modalData.closeCallback = () => { + const nativePageElement = this.createPage(); + if (!nativePageElement) { + throw new Error('Failed to created updated modal page'); + } + const { nativeView } = nativePageElement; + const { originalOptions } = modalData; + // Options will get monkey patched again, the only work left for us + // is to try to reduce visual disturbances. + // + // FIXME Even that proves too much unfortunately... Apparently TNS + // does not respect the `animated` option in this context: + // https://docs.nativescript.org/api-reference/interfaces/_ui_core_view_base_.showmodaloptions#animated + // + const options = Object.assign({}, originalOptions, { animated: false }); + modalParent.showModal(nativeView, options); + }; + + modalView.closeModal(); + } + + createPage() { + const { + instance: { refreshComponent }, + } = this; + const { nativePageElement, relayNativeNavigatedFrom } = this; + const oldNativeView = nativePageElement.nativeView; + // rerender + const target = document.createElement('fragment'); + let haveNewPage = false; + //if the old page was destroyed then we can be sure we have a new page element. + refreshComponent(target, null, () => { + haveNewPage = true; + }); + if (!haveNewPage) return; + + this.releaseNativePageElement(); + this.afterMount(target); // udpates nativePageElement + const newPageElement = this.nativePageElement; + // update event proxy + oldNativeView.off('navigatedFrom', relayNativeNavigatedFrom); + nativePageElement.nativeView.on('navigatedFrom', relayNativeNavigatedFrom); + return newPageElement; + } + + relayNativeNavigatedFrom({ isBackNavigation }) { + const { originalNativeView, navigatedFromHandler } = this; + if (!isBackNavigation) { + return; + } + if (originalNativeView) { + const { off } = originalNativeView; + const ownOff = originalNativeView.hasOwnProperty('off'); + originalNativeView.off = function() { + this.navigatedFromHandler = null; + if (ownOff) { + originalNativeView.off = off; + } else { + delete originalNativeView.off; + } + }; + } + if (navigatedFromHandler) { + return navigatedFromHandler.apply(this, arguments); + } + } + + renderError(err, target, anchor) { + // TODO fallback on TNS error handler for now... at least our error + // is more informative + throw err; + } +} diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js index 4df8a25e..51427267 100644 --- a/lib/svelte3/hot/hot-api.js +++ b/lib/svelte3/hot/hot-api.js @@ -7,7 +7,13 @@ const defaultHotOptions = { const registry = new Map(); -const createProxy = initProxy(DomAdapter); +// Native overrides this with a global because we can't import anything from +// the native adapter, even dynamically, because the native adapter requires +// modules from NativeScript's core that are not resolvable for non-native +// users (and those missing modules would prevent webpack from building). +const ProxyAdapter = global.__SvelteLoader__ProxyAdapter || DomAdapter; + +const createProxy = initProxy(ProxyAdapter); // One stop shop for HMR updates. Combines functionality of `configure`, // `register`, and `reload`, based on current registry state. diff --git a/native.js b/native.js new file mode 100644 index 00000000..3463c79e --- /dev/null +++ b/native.js @@ -0,0 +1,11 @@ +// Native needs its own entry point because its proxy requires NativeScript +// modules that are not available in non-native project. +// +// This could also gives us the opportunity to better customize default loader +// configuration for native projects (in the future). + +global.__SvelteLoader__ProxyAdapter = require.resolve( + './lib/svelte-native/hot/proxy-adapter-native' +); + +module.exports = require('./index'); From 66b3061bd531d39b718b2f210c4a0de8df6447c2 Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 03:34:05 +0200 Subject: [PATCH 06/79] introduce cmp.$$.replace instead of capture_state & inject_state --- lib/svelte3/hot/hot-api.js | 6 +- lib/svelte3/hot/proxy.js | 174 ++++++++++---------------------- lib/svelte3/hot/svelte-hooks.js | 154 +++++++++++++++++++++------- 3 files changed, 175 insertions(+), 159 deletions(-) diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js index 51427267..b96520e8 100644 --- a/lib/svelte3/hot/hot-api.js +++ b/lib/svelte3/hot/hot-api.js @@ -21,7 +21,7 @@ const createProxy = initProxy(ProxyAdapter); // Additionaly does whatever it can to avoid crashing on runtime errors, // and tries to decline HMR if that doesn't go well. // -export function applyHMR(hotOptions, id, moduleHot, component) { +export function applyHMR(hotOptions, id, moduleHot, Component) { // resolve existing record let record = registry.get(id); let broken = false; @@ -30,12 +30,12 @@ export function applyHMR(hotOptions, id, moduleHot, component) { // (re)render if (record) { - const success = record.reload({ component, hotOptions }); + const success = record.reload({ Component, hotOptions }); if (success === false) { broken = true; } } else { - record = createProxy(id, component, hotOptions); + record = createProxy(id, Component, hotOptions); registry.set(id, record); } diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js index 0960f3b7..60398b84 100644 --- a/lib/svelte3/hot/proxy.js +++ b/lib/svelte3/hot/proxy.js @@ -1,4 +1,4 @@ -import { hookComponent, restoreState, captureState } from './svelte-hooks'; +import { createProxiedComponent } from './svelte-hooks'; const handledMethods = ['constructor', '$destroy']; const forwardedMethods = ['$set', '$on']; @@ -121,7 +121,7 @@ class ProxyComponent { Adapter, id, debugName, - current, // { component, hotOptions: { noPreserveState, ... } } + current, // { Component, hotOptions: { noPreserveState, ... } } register, unregister, reportError, @@ -130,59 +130,15 @@ class ProxyComponent { ) { let cmp; let disposed = false; - let restore; let lastError = null; - // it's better to restore props from the very beginning -- for example - // slots (yup, stored in props as $$slots) are broken if not present at - // component creation and later restored with $set - const restoreProps = restore => { - return restore && restore.props && { props: restore.props }; - }; - - const doCreateComponent = (target, anchor) => { - const { component } = current; - const opts = Object.assign( - {}, - options, - { target, anchor }, - restoreProps(restore) - ); - cmp = new component(opts); - }; - - const attachComponent = cmp => { - const onDestroy = () => { - if (cmp === getComponent()) { - afterDetach(); - } - }; - - hookComponent(this, cmp, { - onDestroy, - onMount: afterMount, - }); - - copyComponentMethods(this, cmp); - - restoreState(cmp, restore); - }; - - const createComponent = (target, anchor) => { - doCreateComponent(target, anchor); - attachComponent(cmp); - }; - const destroyComponent = () => { // destroyComponent is tolerant (don't crash on no cmp) because it // is possible that reload/rerender is called after a previous // createComponent has failed (hence we have a proxy, but no cmp) if (cmp) { - const target = cmp; - // WARNING nullify BEFORE $destroy, or we'll consider we're destroying - // the final component instance, in the onDestroy hook + cmp.$destroy(); cmp = null; - target.$destroy(); } }; @@ -190,30 +146,26 @@ class ProxyComponent { if (lastError) { clearError(); } else if (conservativeDestroy) { - const prevCmp = cmp; - restore = captureState(cmp); try { - createComponent(target, anchor); - prevCmp.$destroy(); - if (typeof conservativeDestroy === 'function') { - conservativeDestroy(); - } + cmp = cmp.$$.replace(current.Component, { + target, + anchor, + conservative: true, + }); } catch (err) { + const errString = String((err && err.stack) || err); logError( - `Failed to recreate ${debugName} instance: ${(err && err.stack) || - err}` + `Failed to recreate ${debugName} instance: ${errString}` ); - cmp = prevCmp; } } else { - restore = captureState(cmp); - destroyComponent(); try { - createComponent(target, anchor); + cmp = cmp.$$.replace(current.Component, { target, anchor }); } catch (err) { + cmp = null; + const errString = String((err && err.stack) || err); logError( - `Failed to recreate ${debugName} instance: ${(err && err.stack) || - err}` + `Failed to recreate ${debugName} instance: ${errString}` ); if (current.hotOptions.optimistic) { setError(err, target, anchor); @@ -224,6 +176,7 @@ class ProxyComponent { } }; + // TODO need to use cmp.$$.replace const setError = (err, target, anchor) => { lastError = err; destroyComponent(); @@ -244,18 +197,13 @@ class ProxyComponent { refreshComponent, }; - if (current.hotOptions.noPreserveState) { - instance.captureState = noop; - instance.restoreState = noop; - } - const adapter = new Adapter(instance); const { afterMount, rerender } = adapter; // $destroy is not called when a child component is disposed, so we // need to hook from fragment. - const afterDetach = () => { + const onDestroy = () => { // NOTE do NOT call $destroy on the cmp from here; the cmp is already // dead, this would not work if (!disposed) { @@ -273,7 +221,7 @@ class ProxyComponent { this.$destroy = () => { destroyComponent(); - afterDetach(); + onDestroy(); }; // ---- forwarded methods ---- @@ -284,55 +232,32 @@ class ProxyComponent { // ---- create & mount target component instance --- - { - const { component } = current; + try { + cmp = createProxiedComponent(current.Component, options, { + preserveState: !current.hotOptions.noPreserveState, + onDestroy, + onMount: afterMount, + onInstance: comp => { + // WARNING the proxy MUST use the same $$ object as its component + // instance, because a lot of wiring happens during component + // initialisation... lots of references to $$ and $$.fragment have + // already been distributed around when the component constructor + // returns, before we have a chance to wrap them (and so we can't + // wrap them no more, because existing references would become + // invalid) + this.$$ = comp.$$; + copyComponentMethods(this, comp); + }, + }); + } catch (err) { const { target, anchor } = options; - - // copy statics before doing anything because a static prop/method - // could be used somewhere in the create/render call - copyStatics(component, this); - - try { - createComponent(target, anchor); - - // Svelte 3 creates and mount components from their constructor if - // options.target is present. - // - // This means that at this point, the component's `fragment.c` and, - // most notably, `fragment.m` will already have been called _from inside - // createComponent_. That is: before we have a chance to hook on it. - // - // Proxy's constructor - // -> createComponent - // -> component constructor - // -> component.$$.fragment.c(...) (or l, if hydrate:true) - // -> component.$$.fragment.m(...) - // - // -> you are here <- - // - // I've tried to move the responsibility for mounting the component here, - // by setting `$$inline` option to prevent Svelte from doing it itself. - // `$$inline` is normally used for child components, and their lifecycle - // is managed by their parent. But that didn't go too well. - // - // We want the proxied component to be mounted on the DOM anyway, so it's - // easier to let Svelte do its things and manually execute our `afterMount` - // hook ourself (will need to do the same for `c` and `l` hooks, if we - // come to need them here). - // - if (target) { - afterMount(target, anchor); - } - } catch (err) { - if (current.hotOptions.optimistic) { - logError( - `Failed to create ${debugName} instance: ${(err && err.stack) || - err}` - ); - setError(err, target, anchor); - } else { - throw err; - } + if (current.hotOptions.optimistic && target) { + logError( + `Failed to create ${debugName} instance: ${(err && err.stack) || err}` + ); + setError(err, target, anchor); + } else { + throw err; } } } @@ -352,13 +277,13 @@ and ensures resolution to the latest version of the component */ export const initProxy = Adapter => - function createProxy(id, component, hotOptions) { + function createProxy(id, Component, hotOptions) { const debugName = getDebugName(id); const instances = []; // current object will be updated, proxy instances will keep a ref const current = { - component, + Component, hotOptions, }; @@ -392,13 +317,15 @@ export const initProxy = Adapter => }[name]; // reload all existing instances of this component - const reload = ({ component, hotOptions }) => { + const reload = ({ Component, hotOptions }) => { // update current references - Object.assign(current, { component, hotOptions }); + Object.assign(current, { Component, hotOptions }); + // copy statics before doing anything because a static prop/method + // could be used somewhere in the create/render call // TODO delete props/methods previously added and of which value has // not changed since - copyStatics(component, proxy); + copyStatics(Component, proxy); const errors = []; @@ -406,6 +333,9 @@ export const initProxy = Adapter => try { rerender(); } catch (err) { + logError( + `Failed to rerender ${debugName}: ${(err && err.stack) || err}` + ); errors.push(err); } }); diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index a479809b..0916088d 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -5,7 +5,7 @@ * module. */ -export const captureState = cmp => { +const captureState = cmp => { // sanity check: propper behaviour here is to crash noisily so that // user knows that they're looking at something broken if (!cmp) { @@ -20,7 +20,7 @@ export const captureState = cmp => { return { props, callbacks, bound }; }; -export const restoreState = (cmp, restore) => { +const restoreState = (cmp, restore) => { if (!restore) { return; } @@ -35,39 +35,125 @@ export const restoreState = (cmp, restore) => { // better -- well, at all actually) }; -// emulates (forthcoming?) svelte dev hooks -// -// NOTE onDestroy must be called even if the call doesn't pass through the -// component's $destroy method (that we can hook onto by ourselves, since -// it's public API) -- this happens a lot in svelte's internals, that -// manipulates cmp.$$.fragment directly, often binding to fragment.d, -// for example -// -// NOTE onMount must provide target & anchor (for us to be able to determinate -// actual DOM insertion point) -// -// TODO should copyComponentMethods be done here? -// -export const hookComponent = (proxy, cmp, { onMount, onDestroy }) => { - if (onMount) { - const m = cmp.$$.fragment.m; - cmp.$$.fragment.m = (...args) => { - const result = m(...args); - onMount(...args); - return result; - }; +export const createProxiedComponent = ( + Component, + options, + { + preserveState, + onInstance, + onMount, + onDestroy, } +) => { + let cmp; + let restore = null; - if (onDestroy) { - cmp.$$.on_destroy.push(onDestroy); - } + const isCurrent = _cmp => cmp === _cmp; + + // it's better to restore props from the very beginning -- for example + // slots (yup, stored in props as $$slots) are broken if not present at + // component creation and later restored with $set + const restoreProps = restore => { + return restore && restore.props && { props: restore.props }; + }; + + const overrideOptions = (target, anchor) => Object.assign( + {}, + options, + { target, anchor }, + restoreProps(restore) + ); + + const instrument = targetCmp => { + const createComponent = (Component, options, restore) => { + const comp = new Component(options); + restoreState(comp, restore); + instrument(comp); + return comp; + }; + + // `conservative: true` means we want to be sure that the new component has + // actually been successfuly created before destroying the old instance. + // This could be useful for preventing runtime errors in component init to + // bring down the whole HMR. Unfortunately the implementation bellow is + // broken (FIXME), but that remains an interesting target for when HMR hooks + // will actually land in Svelte itself. + targetCmp.$$.replace = (Component, { + target = options.target, + anchor = options.anchor, + conservative = false, + }) => { + const replaceOptions = overrideOptions(target, anchor); + restore = preserveState && captureState(targetCmp); + if (conservative) { + try { + cmp = createComponent(Component, replaceOptions, restore); + targetCmp.$destroy(); + } catch (err) { + cmp = targetCmp; + throw err; + } + } else { + cmp = null; // prevents on_destroy from firing on non-final cmp instance + targetCmp.$destroy(); + cmp = createComponent(Component, replaceOptions, restore); + } + return cmp; + }; + + // NOTE onMount must provide target & anchor (for us to be able to determinate + // actual DOM insertion point) + if (onMount) { + const m = targetCmp.$$.fragment.m; + targetCmp.$$.fragment.m = (...args) => { + const result = m(...args); + onMount(...args); + return result; + }; + } + + // NOTE onDestroy must be called even if the call doesn't pass through the + // component's $destroy method (that we can hook onto by ourselves, since + // it's public API) -- this happens a lot in svelte's internals, that + // manipulates cmp.$$.fragment directly, often binding to fragment.d, + // for example + if (onDestroy) { + targetCmp.$$.on_destroy.push(() => { + if (isCurrent(targetCmp)) { + onDestroy(); + } + }); + } + + if (onInstance) { + onInstance(targetCmp); + } + + // Svelte 3 creates and mount components from their constructor if + // options.target is present. + // + // This means that at this point, the component's `fragment.c` and, + // most notably, `fragment.m` will already have been called _from inside + // createComponent_. That is: before we have a chance to hook on it. + // + // Proxy's constructor + // -> createComponent + // -> component constructor + // -> component.$$.fragment.c(...) (or l, if hydrate:true) + // -> component.$$.fragment.m(...) + // + // -> you are here <- + // + if (onMount) { + const { target, anchor } = options; + if (target) { + onMount(target, anchor); + } + } + }; + + cmp = new Component(options); + instrument(cmp); - // WARNING the proxy MUST use the same $$ object as its component - // instance, because a lot of wiring happens during component - // initialisation... lots of references to $$ and $$.fragment have - // already been distributed around when the component constructor - // returns, before we have a chance to wrap them (and so we can't - // wrap them no more, because existing references would become - // invalid) - proxy.$$ = cmp.$$; + return cmp; }; From 87b4192ae3992f99ec0a92b51a41cc7e6850a07d Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 04:37:51 +0200 Subject: [PATCH 07/79] better name for $$.replace --- lib/svelte3/hot/proxy.js | 6 +++--- lib/svelte3/hot/svelte-hooks.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js index 60398b84..1235a9b4 100644 --- a/lib/svelte3/hot/proxy.js +++ b/lib/svelte3/hot/proxy.js @@ -147,7 +147,7 @@ class ProxyComponent { clearError(); } else if (conservativeDestroy) { try { - cmp = cmp.$$.replace(current.Component, { + cmp = cmp.$replace(current.Component, { target, anchor, conservative: true, @@ -160,7 +160,7 @@ class ProxyComponent { } } else { try { - cmp = cmp.$$.replace(current.Component, { target, anchor }); + cmp = cmp.$replace(current.Component, { target, anchor }); } catch (err) { cmp = null; const errString = String((err && err.stack) || err); @@ -176,7 +176,7 @@ class ProxyComponent { } }; - // TODO need to use cmp.$$.replace + // TODO need to use cmp.$replace const setError = (err, target, anchor) => { lastError = err; destroyComponent(); diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 0916088d..c0ffe522 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -78,7 +78,7 @@ export const createProxiedComponent = ( // bring down the whole HMR. Unfortunately the implementation bellow is // broken (FIXME), but that remains an interesting target for when HMR hooks // will actually land in Svelte itself. - targetCmp.$$.replace = (Component, { + targetCmp.$replace = (Component, { target = options.target, anchor = options.anchor, conservative = false, From 76ec3b875989ed6a67a1b0976ae1ff802acecc7d Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 22:27:12 +0200 Subject: [PATCH 08/79] fix restore props --- lib/svelte3/hot/svelte-hooks.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index c0ffe522..31ccb2ba 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -46,7 +46,6 @@ export const createProxiedComponent = ( } ) => { let cmp; - let restore = null; const isCurrent = _cmp => cmp === _cmp; @@ -57,7 +56,7 @@ export const createProxiedComponent = ( return restore && restore.props && { props: restore.props }; }; - const overrideOptions = (target, anchor) => Object.assign( + const overrideOptions = (target, anchor, restore) => Object.assign( {}, options, { target, anchor }, @@ -83,8 +82,8 @@ export const createProxiedComponent = ( anchor = options.anchor, conservative = false, }) => { - const replaceOptions = overrideOptions(target, anchor); - restore = preserveState && captureState(targetCmp); + const restore = preserveState && captureState(targetCmp); + const replaceOptions = overrideOptions(target, anchor, restore); if (conservative) { try { cmp = createComponent(Component, replaceOptions, restore); From c4d9f09775b46e1080decbdea0d138726be26bc7 Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 29 Jul 2019 22:27:36 +0200 Subject: [PATCH 09/79] style: autofix --- lib/svelte3/hot/svelte-hooks.js | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 31ccb2ba..9af25f62 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -38,12 +38,7 @@ const restoreState = (cmp, restore) => { export const createProxiedComponent = ( Component, options, - { - preserveState, - onInstance, - onMount, - onDestroy, - } + { preserveState, onInstance, onMount, onDestroy } ) => { let cmp; @@ -56,12 +51,8 @@ export const createProxiedComponent = ( return restore && restore.props && { props: restore.props }; }; - const overrideOptions = (target, anchor, restore) => Object.assign( - {}, - options, - { target, anchor }, - restoreProps(restore) - ); + const overrideOptions = (target, anchor, restore) => + Object.assign({}, options, { target, anchor }, restoreProps(restore)); const instrument = targetCmp => { const createComponent = (Component, options, restore) => { @@ -77,11 +68,10 @@ export const createProxiedComponent = ( // bring down the whole HMR. Unfortunately the implementation bellow is // broken (FIXME), but that remains an interesting target for when HMR hooks // will actually land in Svelte itself. - targetCmp.$replace = (Component, { - target = options.target, - anchor = options.anchor, - conservative = false, - }) => { + targetCmp.$replace = ( + Component, + { target = options.target, anchor = options.anchor, conservative = false } + ) => { const restore = preserveState && captureState(targetCmp); const replaceOptions = overrideOptions(target, anchor, restore); if (conservative) { From 21e0276cb8f85661e1da91b0f5fae5d8aed02062 Mon Sep 17 00:00:00 2001 From: halfnelson Date: Tue, 30 Jul 2019 23:04:08 +1000 Subject: [PATCH 10/79] update native support --- lib/svelte3/make-hot.js | 7 ++++++- native.js | 11 ----------- 2 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 native.js diff --git a/lib/svelte3/make-hot.js b/lib/svelte3/make-hot.js index 9d4408af..5a9ddc2b 100644 --- a/lib/svelte3/make-hot.js +++ b/lib/svelte3/make-hot.js @@ -2,17 +2,22 @@ const posixify = require('../posixify'); const hotApi = require.resolve('./hot/hot-api.js'); +const nativeApi = require.resolve('../svelte-native/hot/proxy-adapter-native.js') + const quote = JSON.stringify; function makeHot(id, code, hotOptions = {}) { const options = JSON.stringify(hotOptions); - const replacement = ` + + let replacement = ` if (module.hot) { + ${ hotOptions.native ? `global.__SvelteLoader__ProxyAdapter = require('${posixify(nativeApi)}').default` : ''} const { applyHMR } = require('${posixify(hotApi)}'); $2 = applyHMR(${options}, ${quote(id)}, module.hot, $2); } export default $2; `; + return code.replace(/(export default ([^;]*));/, replacement); } diff --git a/native.js b/native.js deleted file mode 100644 index 3463c79e..00000000 --- a/native.js +++ /dev/null @@ -1,11 +0,0 @@ -// Native needs its own entry point because its proxy requires NativeScript -// modules that are not available in non-native project. -// -// This could also gives us the opportunity to better customize default loader -// configuration for native projects (in the future). - -global.__SvelteLoader__ProxyAdapter = require.resolve( - './lib/svelte-native/hot/proxy-adapter-native' -); - -module.exports = require('./index'); From b2c1d2998ee19097a1f488231b0033852b3df21a Mon Sep 17 00:00:00 2001 From: rixo Date: Wed, 31 Jul 2019 00:01:14 +0200 Subject: [PATCH 11/79] remove global polution, now that it isn't needed anymore --- lib/svelte3/hot/hot-api.js | 20 +++---- lib/svelte3/hot/proxy.js | 115 ++++++++++++++++++------------------- lib/svelte3/make-hot.js | 19 ++++-- 3 files changed, 79 insertions(+), 75 deletions(-) diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js index 51427267..3fa379fd 100644 --- a/lib/svelte3/hot/hot-api.js +++ b/lib/svelte3/hot/hot-api.js @@ -1,5 +1,5 @@ import DomAdapter from './proxy-adapter-dom'; -import { initProxy } from './proxy'; +import { createProxy } from './proxy'; const defaultHotOptions = { noPreserveState: false, @@ -7,21 +7,19 @@ const defaultHotOptions = { const registry = new Map(); -// Native overrides this with a global because we can't import anything from -// the native adapter, even dynamically, because the native adapter requires -// modules from NativeScript's core that are not resolvable for non-native -// users (and those missing modules would prevent webpack from building). -const ProxyAdapter = global.__SvelteLoader__ProxyAdapter || DomAdapter; - -const createProxy = initProxy(ProxyAdapter); - // One stop shop for HMR updates. Combines functionality of `configure`, // `register`, and `reload`, based on current registry state. // // Additionaly does whatever it can to avoid crashing on runtime errors, // and tries to decline HMR if that doesn't go well. // -export function applyHMR(hotOptions, id, moduleHot, component) { +export function applyHMR( + hotOptions, + id, + moduleHot, + component, + ProxyAdapter = DomAdapter +) { // resolve existing record let record = registry.get(id); let broken = false; @@ -35,7 +33,7 @@ export function applyHMR(hotOptions, id, moduleHot, component) { broken = true; } } else { - record = createProxy(id, component, hotOptions); + record = createProxy(ProxyAdapter, id, component, hotOptions); registry.set(id, record); } diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js index 0960f3b7..c94dd00d 100644 --- a/lib/svelte3/hot/proxy.js +++ b/lib/svelte3/hot/proxy.js @@ -351,71 +351,70 @@ decorates the original component with trackers and ensures resolution to the latest version of the component */ -export const initProxy = Adapter => - function createProxy(id, component, hotOptions) { - const debugName = getDebugName(id); - const instances = []; - - // current object will be updated, proxy instances will keep a ref - const current = { - component, - hotOptions, - }; +export function createProxy(Adapter, id, component, hotOptions) { + const debugName = getDebugName(id); + const instances = []; + + // current object will be updated, proxy instances will keep a ref + const current = { + component, + hotOptions, + }; - const name = `Proxy${debugName}`; - - // this trick gives the dynamic name Proxy to the concrete - // proxy class... unfortunately, this doesn't shows in dev tools, but - // it stills allow to inspect cmp.constructor.name to confirm an instance - // is a proxy - const proxy = { - [name]: class extends ProxyComponent { - constructor(options) { - super( - { - Adapter, - id, - debugName, - current, - register: rerender => { - instances.push(rerender); - }, - unregister: () => { - const i = instances.indexOf(this); - instances.splice(i, 1); - }, + const name = `Proxy${debugName}`; + + // this trick gives the dynamic name Proxy to the concrete + // proxy class... unfortunately, this doesn't shows in dev tools, but + // it stills allow to inspect cmp.constructor.name to confirm an instance + // is a proxy + const proxy = { + [name]: class extends ProxyComponent { + constructor(options) { + super( + { + Adapter, + id, + debugName, + current, + register: rerender => { + instances.push(rerender); }, - options - ); - } - }, - }[name]; + unregister: () => { + const i = instances.indexOf(this); + instances.splice(i, 1); + }, + }, + options + ); + } + }, + }[name]; - // reload all existing instances of this component - const reload = ({ component, hotOptions }) => { - // update current references - Object.assign(current, { component, hotOptions }); + // reload all existing instances of this component + const reload = ({ component, hotOptions }) => { + // update current references + Object.assign(current, { component, hotOptions }); - // TODO delete props/methods previously added and of which value has - // not changed since - copyStatics(component, proxy); + // TODO delete props/methods previously added and of which value has + // not changed since + copyStatics(component, proxy); - const errors = []; + const errors = []; - instances.forEach(rerender => { - try { - rerender(); - } catch (err) { - errors.push(err); - } - }); - - if (errors.length > 0) { - return false; + instances.forEach(rerender => { + try { + rerender(); + } catch (err) { + errors.push(err); } + }); - return true; - }; + if (errors.length > 0) { + return false; + } - return { id, proxy, reload }; + return true; }; + + return { id, proxy, reload }; +} diff --git a/lib/svelte3/make-hot.js b/lib/svelte3/make-hot.js index 5a9ddc2b..2767a0f6 100644 --- a/lib/svelte3/make-hot.js +++ b/lib/svelte3/make-hot.js @@ -2,22 +2,29 @@ const posixify = require('../posixify'); const hotApi = require.resolve('./hot/hot-api.js'); -const nativeApi = require.resolve('../svelte-native/hot/proxy-adapter-native.js') +const nativeAdapter = require.resolve('../svelte-native/hot/proxy-adapter-native.js'); const quote = JSON.stringify; function makeHot(id, code, hotOptions = {}) { const options = JSON.stringify(hotOptions); - - let replacement = ` + + // NOTE Native adapter cannot be required in code (as opposed to this + // generated code) because it requires modules from NativeScript's code that + // are not resolvable for non-native users (and those missing modules would + // prevent webpack from building). + const adapter = hotOptions.native + ? `require('${posixify(nativeAdapter)}').default` + : 'undefined'; + + const replacement = ` if (module.hot) { - ${ hotOptions.native ? `global.__SvelteLoader__ProxyAdapter = require('${posixify(nativeApi)}').default` : ''} const { applyHMR } = require('${posixify(hotApi)}'); - $2 = applyHMR(${options}, ${quote(id)}, module.hot, $2); + $2 = applyHMR(${options}, ${quote(id)}, module.hot, $2, ${adapter}); } export default $2; `; - + return code.replace(/(export default ([^;]*));/, replacement); } From 81cfdd267a50dbb3a73946133b3977d6171052af Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 1 Aug 2019 11:36:45 +0200 Subject: [PATCH 12/79] style: remove trailing space --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 320a12ce..f0379d6b 100644 --- a/index.js +++ b/index.js @@ -106,7 +106,7 @@ module.exports = function(source, map) { const virtualModules = virtualModuleInstances.get(this._compiler); this.cacheable(); - + const options = Object.assign({}, getOptions(this)); const callback = this.async(); From 189a246903009d95292e6372424d2cf4eacefbbe Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 1 Aug 2019 11:42:05 +0200 Subject: [PATCH 13/79] refactor: extract svelte resolution to its own module --- index.js | 10 +++++----- lib/resolve-svelte.js | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 lib/resolve-svelte.js diff --git a/index.js b/index.js index f0379d6b..84686dbc 100644 --- a/index.js +++ b/index.js @@ -4,11 +4,11 @@ const VirtualModules = require('./lib/virtual'); const hotApi = require.resolve('./lib/hot-api.js'); -const { version } = require('svelte/package.json'); -const major_version = +version[0]; -const { compile, preprocess } = major_version >= 3 - ? require('svelte/compiler') - : require('svelte'); +const { + major_version, + compile, + preprocess, +} = require('./lib/resolve-svelte'); const pluginOptions = { externalDependencies: true, diff --git a/lib/resolve-svelte.js b/lib/resolve-svelte.js new file mode 100644 index 00000000..a37d7f06 --- /dev/null +++ b/lib/resolve-svelte.js @@ -0,0 +1,13 @@ +const { version } = require('svelte/package.json'); + +const major_version = +version[0]; + +const { compile, preprocess } = major_version >= 3 + ? require('svelte/compiler') + : require('svelte'); + +module.exports = { + major_version, + compile, + preprocess, +}; From 74636fbc48f981c4a94b14fe3dc51dde0891382c Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 1 Aug 2019 11:42:42 +0200 Subject: [PATCH 14/79] resolve svelte from application entrypoint - fixes #109 --- lib/resolve-svelte.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/resolve-svelte.js b/lib/resolve-svelte.js index a37d7f06..eb084f87 100644 --- a/lib/resolve-svelte.js +++ b/lib/resolve-svelte.js @@ -1,10 +1,10 @@ -const { version } = require('svelte/package.json'); +const { version } = require.main.require('svelte/package.json'); const major_version = +version[0]; const { compile, preprocess } = major_version >= 3 - ? require('svelte/compiler') - : require('svelte'); + ? require.main.require('svelte/compiler') + : require.main.require('svelte'); module.exports = { major_version, From 0089526a5c707083d0bd9838662a430e7b18df51 Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 1 Aug 2019 12:17:08 +0200 Subject: [PATCH 15/79] add support for overriding svelte location by env variable --- lib/resolve-svelte.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/resolve-svelte.js b/lib/resolve-svelte.js index eb084f87..bfdadf37 100644 --- a/lib/resolve-svelte.js +++ b/lib/resolve-svelte.js @@ -1,10 +1,34 @@ -const { version } = require.main.require('svelte/package.json'); +const path = require('path'); + +// Svelte location can be overriden by env variable. This can be needed +// to use some external tools targetting a whole app, during development +// of svelte-loader. + +const resolveSvelte = () => { + const { SVELTE } = process.env; + if (SVELTE) { + const ensureAbsolute = name => + path.isAbsolute(name) ? name : path.resolve(process.cwd(), name); + return { + req: require, + svelte: ensureAbsolute(SVELTE), + }; + } else { + return { + req: require.main.require.bind(require.main), + svelte: 'svelte', + }; + } +}; + +const { req, svelte } = resolveSvelte(); + +const { version } = req(`${svelte}/package.json`); const major_version = +version[0]; -const { compile, preprocess } = major_version >= 3 - ? require.main.require('svelte/compiler') - : require.main.require('svelte'); +const { compile, preprocess } = + major_version >= 3 ? req(`${svelte}/compiler`) : req(svelte); module.exports = { major_version, From 77876189728d56a0d7f0d797927a595db0f9162d Mon Sep 17 00:00:00 2001 From: rixo Date: Tue, 6 Aug 2019 17:54:40 +0200 Subject: [PATCH 16/79] refactor: move makeHot to resolveSvelte (because it depends on svelte version) --- index.js | 5 +---- lib/resolve-svelte.js | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index 2b826991..c2029fc5 100644 --- a/index.js +++ b/index.js @@ -7,12 +7,9 @@ const { major_version, compile, preprocess, + makeHot, } = require('./lib/resolve-svelte'); -const makeHot = major_version >= 3 - ? require('./lib/svelte3/make-hot') - : require('./lib/make-hot'); - const pluginOptions = { externalDependencies: true, hotReload: true, diff --git a/lib/resolve-svelte.js b/lib/resolve-svelte.js index bfdadf37..b6384961 100644 --- a/lib/resolve-svelte.js +++ b/lib/resolve-svelte.js @@ -1,37 +1,39 @@ const path = require('path'); -// Svelte location can be overriden by env variable. This can be needed -// to use some external tools targetting a whole app, during development -// of svelte-loader. - const resolveSvelte = () => { const { SVELTE } = process.env; + + const absolute = (name, base) => + path.isAbsolute(name) ? name : path.join(base, name); + if (SVELTE) { - const ensureAbsolute = name => - path.isAbsolute(name) ? name : path.resolve(process.cwd(), name); return { req: require, - svelte: ensureAbsolute(SVELTE), + base: absolute(SVELTE, process.cwd()), }; } else { return { req: require.main.require.bind(require.main), - svelte: 'svelte', + base: 'svelte', }; } }; -const { req, svelte } = resolveSvelte(); +const { req, base } = resolveSvelte(); -const { version } = req(`${svelte}/package.json`); +const { version } = req(`${base}/package.json`); const major_version = +version[0]; const { compile, preprocess } = - major_version >= 3 ? req(`${svelte}/compiler`) : req(svelte); + major_version >= 3 ? req(`${base}/compiler`) : req(`${base}`); + +const makeHot = + major_version >= 3 ? require('./svelte3/make-hot') : require('./make-hot'); module.exports = { major_version, compile, preprocess, + makeHot, }; From 3122272ed14b8073b9cab7666e8fd82e21877509 Mon Sep 17 00:00:00 2001 From: rixo Date: Wed, 7 Aug 2019 00:38:40 +0200 Subject: [PATCH 17/79] filter removed props using compilation infos --- index.js | 5 +++-- lib/svelte3/hot/hot-api.js | 11 ++++++++++- lib/svelte3/hot/svelte-hooks.js | 27 ++++++++++++++++++++++++++- lib/svelte3/make-hot.js | 15 +++++++++++++-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index c2029fc5..dae0432b 100644 --- a/index.js +++ b/index.js @@ -116,7 +116,8 @@ module.exports = function(source, map) { } } - let { js, css, warnings } = normalize(compile(processed.toString(), compileOptions)); + const compiled = compile(processed.toString(), compileOptions); + let { js, css, warnings } = normalize(compiled); if (major_version >= 3) { warnings.forEach( @@ -129,7 +130,7 @@ module.exports = function(source, map) { if (options.hotReload && !isProduction && !isServer) { const hotOptions = Object.assign({}, options.hotOptions); const id = JSON.stringify(relative(process.cwd(), compileOptions.filename)); - js.code = makeHot(id, js.code, hotOptions); + js.code = makeHot(id, js.code, hotOptions, compiled); } if (options.emitCss && css.code) { diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js index ab65fde6..39598723 100644 --- a/lib/svelte3/hot/hot-api.js +++ b/lib/svelte3/hot/hot-api.js @@ -18,7 +18,8 @@ export function applyHMR( id, moduleHot, Component, - ProxyAdapter = DomAdapter + ProxyAdapter = DomAdapter, + compileData ) { // resolve existing record let record = registry.get(id); @@ -26,6 +27,14 @@ export function applyHMR( hotOptions = Object.assign({}, defaultHotOptions, hotOptions); + // meta info from compilation (vars, things that could be inspected in AST...) + // can be used to help the proxy better emulate the proxied component (and + // better mock svelte hooks, in the wait for official support) + if (compileData) { + // NOTE we're making Component carry the load to minimize diff with base branch + Component.$$hmrCompileData = compileData; + } + // (re)render if (record) { const success = record.reload({ Component, hotOptions }); diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 9af25f62..9c842e98 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -35,12 +35,30 @@ const restoreState = (cmp, restore) => { // better -- well, at all actually) }; +const filterProps = (props, { vars }) => { + if (!vars) { + return props; + } + const previousProps = props; + const result = {}; + vars.filter(({ export_name }) => !!export_name).forEach(({ export_name }) => { + result[export_name] = previousProps[export_name]; + }); + Object.keys(previousProps) + .filter(name => name.substr(0, 2) === '$$') + .forEach(key => { + result[key] = previousProps[key]; + }); + return result; +}; + export const createProxiedComponent = ( Component, options, { preserveState, onInstance, onMount, onDestroy } ) => { let cmp; + let compileData; const isCurrent = _cmp => cmp === _cmp; @@ -48,7 +66,13 @@ export const createProxiedComponent = ( // slots (yup, stored in props as $$slots) are broken if not present at // component creation and later restored with $set const restoreProps = restore => { - return restore && restore.props && { props: restore.props }; + let props = restore && restore.props; + if (props) { + if (compileData && compileData.vars) { + props = filterProps(props, compileData); + } + return { props }; + } }; const overrideOptions = (target, anchor, restore) => @@ -72,6 +96,7 @@ export const createProxiedComponent = ( Component, { target = options.target, anchor = options.anchor, conservative = false } ) => { + compileData = Component.$$hmrCompileData; const restore = preserveState && captureState(targetCmp); const replaceOptions = overrideOptions(target, anchor, restore); if (conservative) { diff --git a/lib/svelte3/make-hot.js b/lib/svelte3/make-hot.js index 2767a0f6..38d83a93 100644 --- a/lib/svelte3/make-hot.js +++ b/lib/svelte3/make-hot.js @@ -6,9 +6,13 @@ const nativeAdapter = require.resolve('../svelte-native/hot/proxy-adapter-native const quote = JSON.stringify; -function makeHot(id, code, hotOptions = {}) { +function makeHot(id, code, hotOptions = {}, compiled) { const options = JSON.stringify(hotOptions); + const compileData = JSON.stringify({ + vars: compiled.vars + }); + // NOTE Native adapter cannot be required in code (as opposed to this // generated code) because it requires modules from NativeScript's code that // are not resolvable for non-native users (and those missing modules would @@ -20,7 +24,14 @@ function makeHot(id, code, hotOptions = {}) { const replacement = ` if (module.hot) { const { applyHMR } = require('${posixify(hotApi)}'); - $2 = applyHMR(${options}, ${quote(id)}, module.hot, $2, ${adapter}); + $2 = applyHMR( + ${options}, + ${quote(id)}, + module.hot, + $2, + ${adapter}, + ${compileData} + ); } export default $2; `; From 5fbd73bbfc444593952f7b1c90ef06f25046c83e Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 22 Aug 2019 17:41:05 +0200 Subject: [PATCH 18/79] fix hmr support for context --- lib/svelte3/hot/svelte-hooks.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 9c842e98..4e2b361a 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -4,6 +4,7 @@ * All references to private compoonent state ($$) are now isolated in this * module. */ +import { current_component, set_current_component } from 'svelte/internal'; const captureState = cmp => { // sanity check: propper behaviour here is to crash noisily so that @@ -20,6 +21,14 @@ const captureState = cmp => { return { props, callbacks, bound }; }; +// restoreState +// +// It is too late to restore context at this point because component instance +// function has already been called (and so context has already been read). +// Instead, we rely on setting current_component to the same value it has when +// the component was first rendered -- which fix support for context, and is +// also generally more respectful of normal operation. +// const restoreState = (cmp, restore) => { if (!restore) { return; @@ -41,9 +50,11 @@ const filterProps = (props, { vars }) => { } const previousProps = props; const result = {}; - vars.filter(({ export_name }) => !!export_name).forEach(({ export_name }) => { - result[export_name] = previousProps[export_name]; - }); + vars + .filter(({ export_name }) => !!export_name) + .forEach(({ export_name }) => { + result[export_name] = previousProps[export_name]; + }); Object.keys(previousProps) .filter(name => name.substr(0, 2) === '$$') .forEach(key => { @@ -58,6 +69,7 @@ export const createProxiedComponent = ( { preserveState, onInstance, onMount, onDestroy } ) => { let cmp; + let parentComponent; let compileData; const isCurrent = _cmp => cmp === _cmp; @@ -80,6 +92,7 @@ export const createProxiedComponent = ( const instrument = targetCmp => { const createComponent = (Component, options, restore) => { + set_current_component(parentComponent); const comp = new Component(options); restoreState(comp, restore); instrument(comp); @@ -166,6 +179,7 @@ export const createProxiedComponent = ( } }; + parentComponent = current_component; cmp = new Component(options); instrument(cmp); From 75bc4a3eeb719d83203f3da2f14fb1aeef4bfcc4 Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 22 Aug 2019 17:41:41 +0200 Subject: [PATCH 19/79] better debug name for file with multiple extensions --- lib/svelte3/hot/proxy.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js index c94dd00d..3bc90ae6 100644 --- a/lib/svelte3/hot/proxy.js +++ b/lib/svelte3/hot/proxy.js @@ -14,7 +14,8 @@ const getBaseName = id => .split('/') .pop() .split('.') - .shift(); + .slice(0, -1) + .join('.'); const capitalize = str => str[0].toUpperCase() + str.slice(1); From 67e98a84bb07195d0d50291c0b208fdc4f66c59a Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 19 Sep 2019 16:27:46 +0200 Subject: [PATCH 20/79] fix target / anchor in onMount when rendered to different ones (native) --- lib/svelte3/hot/svelte-hooks.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 4e2b361a..8f428df8 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -65,12 +65,13 @@ const filterProps = (props, { vars }) => { export const createProxiedComponent = ( Component, - options, + initialOptions, { preserveState, onInstance, onMount, onDestroy } ) => { let cmp; let parentComponent; let compileData; + let options = initialOptions; const isCurrent = _cmp => cmp === _cmp; @@ -87,11 +88,11 @@ export const createProxiedComponent = ( } }; - const overrideOptions = (target, anchor, restore) => - Object.assign({}, options, { target, anchor }, restoreProps(restore)); + const assignOptions = (target, anchor, restore) => + Object.assign(options, { target, anchor }, restoreProps(restore)); const instrument = targetCmp => { - const createComponent = (Component, options, restore) => { + const createComponent = (Component, restore) => { set_current_component(parentComponent); const comp = new Component(options); restoreState(comp, restore); @@ -105,16 +106,21 @@ export const createProxiedComponent = ( // bring down the whole HMR. Unfortunately the implementation bellow is // broken (FIXME), but that remains an interesting target for when HMR hooks // will actually land in Svelte itself. + // + // The goal would be to render an error inplace in case of error, to avoid + // losing the navigation stack (especially annoying in native, that is not + // based on URL navigation, so we lose the current page on each error). + // targetCmp.$replace = ( Component, { target = options.target, anchor = options.anchor, conservative = false } ) => { compileData = Component.$$hmrCompileData; const restore = preserveState && captureState(targetCmp); - const replaceOptions = overrideOptions(target, anchor, restore); + assignOptions(target, anchor, restore); if (conservative) { try { - cmp = createComponent(Component, replaceOptions, restore); + cmp = createComponent(Component, restore); targetCmp.$destroy(); } catch (err) { cmp = targetCmp; @@ -123,7 +129,7 @@ export const createProxiedComponent = ( } else { cmp = null; // prevents on_destroy from firing on non-final cmp instance targetCmp.$destroy(); - cmp = createComponent(Component, replaceOptions, restore); + cmp = createComponent(Component, restore); } return cmp; }; From 9026759f789db3f108e9e465935aa4f05150039e Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 19 Sep 2019 17:02:18 +0200 Subject: [PATCH 21/79] fix support for native, & TNS 6 --- lib/svelte-native/hot/proxy-adapter-native.js | 146 +++++++++++------- 1 file changed, 88 insertions(+), 58 deletions(-) diff --git a/lib/svelte-native/hot/proxy-adapter-native.js b/lib/svelte-native/hot/proxy-adapter-native.js index 91019f46..aac3b4a5 100644 --- a/lib/svelte-native/hot/proxy-adapter-native.js +++ b/lib/svelte-native/hot/proxy-adapter-native.js @@ -1,8 +1,12 @@ /* global document */ +import { NavigationType } from 'tns-core-modules/ui/frame/frame-common'; + import ProxyAdapterDom from '../../svelte3/hot/proxy-adapter-dom'; -import { getModalData } from './patch-page-show-modal'; +import { patchShowModal, getModalData } from './patch-page-show-modal'; + +patchShowModal(); // Svelte Native support // ===================== @@ -33,30 +37,8 @@ import { getModalData } from './patch-page-show-modal'; // is an active issue at NS asking for that. Incidentally, members of // NativeScript-Vue have commented on the issue to weight in for it -- they // probably face some similar challenge. - -// svelte-native uses navigateFrom event + e.isBackNavigation to know -// when to $destroy the component -- but we don't want our proxy instance -// destroyed when we renavigate to the same page for navigation purposes! -const interceptPageNavigation = pageElement => { - const originalNativeView = pageElement.nativeView; - const { on } = originalNativeView; - const ownOn = originalNativeView.hasOwnProperty('on'); - // tricks svelte-native into giving us its handler - originalNativeView.on = function(type, handler, ...rest) { - if (type === 'navigatedFrom') { - this.navigatedFromHandler = handler; - if (ownOn) { - originalNativeView.on = on; - } else { - delete originalNativeView.on; - } - } else { - throw new Error( - 'Unexpected call: has underlying svelte-native code changed?' - ); - } - }; -}; +// +// https://github.com/NativeScript/NativeScript/issues/6283 const getNavTransition = ({ transition }) => { if (typeof transition === 'string') { @@ -65,6 +47,56 @@ const getNavTransition = ({ transition }) => { return transition ? { animated: true, transition } : { animated: false }; }; +// copied from TNS FrameBase.replacePage +// +// it is not public but there is a comment in there indicating it is for +// HMR (probably their own core HMR though) +// +// NOTE this "worked" in TNS 5, but not anymore in TNS 6: updated version bellow +// +// eslint-disable-next-line no-unused-vars +const replacePage_tns5 = (frame, newPageElement, hotOptions) => { + const currentBackstackEntry = frame._currentEntry; + frame.navigationType = 2; + frame.performNavigation({ + isBackNavigation: false, + entry: { + resolvedPage: newPageElement.nativeView, + // + // entry: currentBackstackEntry.entry, + entry: Object.assign( + currentBackstackEntry.entry, + getNavTransition(hotOptions) + ), + navDepth: currentBackstackEntry.navDepth, + fragmentTag: currentBackstackEntry.fragmentTag, + frameId: currentBackstackEntry.frameId, + }, + }); +}; + +// Updated for TNS v6 +// +// https://github.com/NativeScript/NativeScript/blob/6.1.1/tns-core-modules/ui/frame/frame-common.ts#L656 +const replacePage = (frame, newPageElement) => { + const currentBackstackEntry = frame._currentEntry; + const newPage = newPageElement.nativeView; + const newBackstackEntry = { + entry:currentBackstackEntry.entry, + resolvedPage: newPage, + navDepth: currentBackstackEntry.navDepth, + fragmentTag: currentBackstackEntry.fragmentTag, + frameId: currentBackstackEntry.frameId, + }; + const navigationContext = { + entry: newBackstackEntry, + isBackNavigation: false, + navigationType: NavigationType.replace + }; + frame._navigationQueue.push(navigationContext); + frame._processNextNavigationEntry(); +}; + export default class ProxyAdapterNative extends ProxyAdapterDom { constructor(instance) { super(instance); @@ -88,6 +120,30 @@ export default class ProxyAdapterNative extends ProxyAdapterDom { } } + // svelte-native uses navigateFrom event + e.isBackNavigation to know + // when to $destroy the component -- but we don't want our proxy instance + // destroyed when we renavigate to the same page for navigation purposes! + interceptPageNavigation(pageElement) { + const originalNativeView = pageElement.nativeView; + const { on } = originalNativeView; + const ownOn = originalNativeView.hasOwnProperty('on'); + // tricks svelte-native into giving us its handler + originalNativeView.on = function(type, handler, ...rest) { + if (type === 'navigatedFrom') { + this.navigatedFromHandler = handler; + if (ownOn) { + originalNativeView.on = on; + } else { + delete originalNativeView.on; + } + } else { + throw new Error( + 'Unexpected call: has underlying svelte-native code changed?' + ); + } + }; + } + afterMount(target, anchor) { // nativePageElement needs to be updated each time (only for page // components, native component that are not pages follow normal flow) @@ -107,7 +163,7 @@ export default class ProxyAdapterNative extends ProxyAdapterDom { const isNativePage = target.tagName === 'fragment'; if (isNativePage) { const nativePageElement = target.firstChild; - interceptPageNavigation(nativePageElement); + this.interceptPageNavigation(nativePageElement); this.nativePageElement = nativePageElement; } else { // try to protect against components changing from page to no-page @@ -182,29 +238,7 @@ export default class ProxyAdapterNative extends ProxyAdapterDom { ) ); } else { - // copied from TNS FrameBase.replacePage - // - // it is not public but there is a comment in there indicating - // it is for HMR (probably their own core HMR though) - // - // frame.navigationType = NavigationType.replace; - const currentBackstackEntry = frame._currentEntry; - frame.navigationType = 2; - frame.performNavigation({ - isBackNavigation: false, - entry: { - resolvedPage: newPageElement.nativeView, - // - // entry: currentBackstackEntry.entry, - entry: Object.assign( - currentBackstackEntry.entry, - getNavTransition(hotOptions) - ), - navDepth: currentBackstackEntry.navDepth, - fragmentTag: currentBackstackEntry.fragmentTag, - frameId: currentBackstackEntry.frameId, - }, - }); + replacePage(frame, newPageElement, hotOptions); } } else { const backEntry = frame.backStack.find( @@ -258,15 +292,11 @@ export default class ProxyAdapterNative extends ProxyAdapterDom { const oldNativeView = nativePageElement.nativeView; // rerender const target = document.createElement('fragment'); - let haveNewPage = false; - //if the old page was destroyed then we can be sure we have a new page element. - refreshComponent(target, null, () => { - haveNewPage = true; - }); - if (!haveNewPage) return; - - this.releaseNativePageElement(); - this.afterMount(target); // udpates nativePageElement + // not using conservative for now, since there's nothing in place here to + // leverage it (yet?) -- and it might be easier to miss breakages in native + // only code paths + refreshComponent(target, null); + // this.nativePageElement is updated in afterMount, triggered by proxy / hooks const newPageElement = this.nativePageElement; // update event proxy oldNativeView.off('navigatedFrom', relayNativeNavigatedFrom); From e69f4051e327cdd98efadbda08164344d374c9d4 Mon Sep 17 00:00:00 2001 From: rixo Date: Sat, 28 Sep 2019 17:38:00 +0200 Subject: [PATCH 22/79] backport fixes from rollup plugin --- lib/svelte3/hot/proxy-adapter-dom.js | 12 ++-------- lib/svelte3/hot/svelte-hooks.js | 36 +++++++++++++++++++--------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/lib/svelte3/hot/proxy-adapter-dom.js b/lib/svelte3/hot/proxy-adapter-dom.js index 6fcddda3..fe433ae9 100644 --- a/lib/svelte3/hot/proxy-adapter-dom.js +++ b/lib/svelte3/hot/proxy-adapter-dom.js @@ -20,23 +20,15 @@ export default class ProxyAdapterDom { } } + // NOTE afterMount CAN be called multiple times (e.g. keyed list) afterMount(target, anchor) { const { instance: { debugName }, } = this; - // insertionPoint needs to be updated _only when the target changes_ -- - // i.e. when the component is mounted, i.e. (in svelte3) when the component - // is _created_, and svelte3 doesn't allow it to move afterward -- that - // is, insertionPoint only needs to be created once when the component is - // first mounted. - // - // DEBUG is it really true that components' elements cannot move in the - // DOM? what about keyed list? - // if (!this.insertionPoint) { this.insertionPoint = document.createComment(debugName); - target.insertBefore(this.insertionPoint, anchor); } + target.insertBefore(this.insertionPoint, anchor); } rerender() { diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 8f428df8..857871ff 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -6,7 +6,7 @@ */ import { current_component, set_current_component } from 'svelte/internal'; -const captureState = cmp => { +const captureState = (cmp, captureLocalState = true) => { // sanity check: propper behaviour here is to crash noisily so that // user knows that they're looking at something broken if (!cmp) { @@ -18,7 +18,11 @@ const captureState = cmp => { const { $$: { callbacks, bound, ctx: props }, } = cmp; - return { props, callbacks, bound }; + const result = { props, callbacks, bound }; + if (cmp.$capture_state) { + result.state = cmp.$capture_state({ captureLocalState }); + } + return result; }; // restoreState @@ -33,13 +37,16 @@ const restoreState = (cmp, restore) => { if (!restore) { return; } - const { callbacks, bound } = restore; + const { callbacks, bound, state } = restore; if (callbacks) { cmp.$$.callbacks = callbacks; } if (bound) { cmp.$$.bound = bound; } + if (state && cmp.$inject_state) { + cmp.$inject_state(state); + } // props, props.$$slots are restored at component creation (works // better -- well, at all actually) }; @@ -81,10 +88,16 @@ export const createProxiedComponent = ( const restoreProps = restore => { let props = restore && restore.props; if (props) { - if (compileData && compileData.vars) { - props = filterProps(props, compileData); + // $capture_state is not present in some cases on components. Also, it + // does not preserves slots. So for now we need fallbacks. + if (restore.state) { + return { $$slots: props.$$slots }; + } else { + if (compileData && compileData.vars) { + props = filterProps(props, compileData); + } + return { props }; } - return { props }; } }; @@ -92,8 +105,8 @@ export const createProxiedComponent = ( Object.assign(options, { target, anchor }, restoreProps(restore)); const instrument = targetCmp => { - const createComponent = (Component, restore) => { - set_current_component(parentComponent); + const createComponent = (Component, restore, previousCmp) => { + set_current_component(parentComponent || previousCmp); const comp = new Component(options); restoreState(comp, restore); instrument(comp); @@ -116,11 +129,12 @@ export const createProxiedComponent = ( { target = options.target, anchor = options.anchor, conservative = false } ) => { compileData = Component.$$hmrCompileData; - const restore = preserveState && captureState(targetCmp); + const restore = captureState(targetCmp, preserveState); assignOptions(target, anchor, restore); + const previous = cmp; if (conservative) { try { - cmp = createComponent(Component, restore); + cmp = createComponent(Component, restore, previous); targetCmp.$destroy(); } catch (err) { cmp = targetCmp; @@ -129,7 +143,7 @@ export const createProxiedComponent = ( } else { cmp = null; // prevents on_destroy from firing on non-final cmp instance targetCmp.$destroy(); - cmp = createComponent(Component, restore); + cmp = createComponent(Component, restore, previous); } return cmp; }; From d6e4a1a61a5ba981f2564bc374a19e96d254dc9c Mon Sep 17 00:00:00 2001 From: rixo Date: Sat, 28 Sep 2019 17:52:29 +0200 Subject: [PATCH 23/79] mock $capture_state to support preserving local state --- lib/svelte3/hot/svelte-hooks.js | 35 ++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 857871ff..e3fd5504 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -6,6 +6,34 @@ */ import { current_component, set_current_component } from 'svelte/internal'; +const isWritable = v => !v.module && v.writable; + +const isProp = v => !v.module && v.export_name != null; + +// Core $capture_state should be able to capture either only props or the whole +// local state (i.e. any `let` value). The best behaviour regarding HMR varies +// between projects, and even situations of what you're currently working on... +// It's better to leave it as an option to the end user. +const $capture_state = (cmp, { captureLocalState }) => { + const compileData = cmp.constructor.$$hmrCompileData; + if (compileData && compileData.vars) { + const state = {}; + const filter = captureLocalState ? isWritable : isProp; + const vars = compileData.vars.filter(filter); + const ctx = cmp.$$.ctx; + for (const { name } of vars) { + state[name] = ctx[name]; + } + return state; + } + // fallback on actual $capture_state + if (cmp.$capture_state) { + // NOTE the captureLocalState option is fantasy for now + return cmp.$capture_state({ captureLocalState }); + } + // else nothing, state won't be used for restore... +}; + const captureState = (cmp, captureLocalState = true) => { // sanity check: propper behaviour here is to crash noisily so that // user knows that they're looking at something broken @@ -18,11 +46,8 @@ const captureState = (cmp, captureLocalState = true) => { const { $$: { callbacks, bound, ctx: props }, } = cmp; - const result = { props, callbacks, bound }; - if (cmp.$capture_state) { - result.state = cmp.$capture_state({ captureLocalState }); - } - return result; + const state = $capture_state(cmp, { captureLocalState }); + return { props, callbacks, bound, state }; }; // restoreState From 91cfef76290ab4e0553df0545c9831213ab041f4 Mon Sep 17 00:00:00 2001 From: rixo Date: Sat, 28 Sep 2019 18:02:06 +0200 Subject: [PATCH 24/79] respect noPreserveState option --- lib/svelte3/hot/proxy.js | 2 +- lib/svelte3/hot/svelte-hooks.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js index 485d8166..79a9ee08 100644 --- a/lib/svelte3/hot/proxy.js +++ b/lib/svelte3/hot/proxy.js @@ -231,7 +231,7 @@ class ProxyComponent { try { cmp = createProxiedComponent(current.Component, options, { - preserveState: !current.hotOptions.noPreserveState, + noPreserveState: current.hotOptions.noPreserveState, onDestroy, onMount: afterMount, onInstance: comp => { diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index e3fd5504..1875b80a 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -98,7 +98,7 @@ const filterProps = (props, { vars }) => { export const createProxiedComponent = ( Component, initialOptions, - { preserveState, onInstance, onMount, onDestroy } + { noPreserveState, onInstance, onMount, onDestroy } ) => { let cmp; let parentComponent; @@ -154,7 +154,7 @@ export const createProxiedComponent = ( { target = options.target, anchor = options.anchor, conservative = false } ) => { compileData = Component.$$hmrCompileData; - const restore = captureState(targetCmp, preserveState); + const restore = captureState(targetCmp, !noPreserveState); assignOptions(target, anchor, restore); const previous = cmp; if (conservative) { From 0e5b2091400fb18cc1f2a4dbdac2e7e4d59ce92e Mon Sep 17 00:00:00 2001 From: rixo Date: Sat, 28 Sep 2019 18:13:37 +0200 Subject: [PATCH 25/79] implement full reload on failure --- lib/svelte3/hot/hot-api.js | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js index 39598723..26735709 100644 --- a/lib/svelte3/hot/hot-api.js +++ b/lib/svelte3/hot/hot-api.js @@ -48,16 +48,29 @@ export function applyHMR( const proxy = record && record.proxy; + const reloadOrDecline = () => { + // NOTE window.location.reload is not be available in all supported runtimes + /* global window */ + if ( + // DEBUG TODO doccument noReload + !hotOptions.noReload && + typeof window !== 'undefined' && + window.location && + window.location.reload + ) { + window.location.reload(); + } else { + moduleHot.decline(); + } + }; + if (!proxy) { // well, endgame... we won't be able to render next updates, even // successful, if we don't have proxies in svelte's tree // // full reload required // - // we can't command a full reload from here - - // tell webpack our HMR is dead, so next update should trigger a full reload - moduleHot.decline(); + reloadOrDecline(); // TODO report error on client @@ -75,7 +88,7 @@ export function applyHMR( // Tao, better full reload than stale display). // if (broken) { - moduleHot.decline(); + reloadOrDecline(); } else { moduleHot.accept(); } From 4c322c232901efbd027ddb40d16ebb9f1bb10e3d Mon Sep 17 00:00:00 2001 From: rixo Date: Sat, 28 Sep 2019 18:28:38 +0200 Subject: [PATCH 26/79] simpler error recuperation (not broken also) --- lib/svelte3/hot/proxy.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js index 79a9ee08..68fffbd3 100644 --- a/lib/svelte3/hot/proxy.js +++ b/lib/svelte3/hot/proxy.js @@ -145,7 +145,8 @@ class ProxyComponent { const refreshComponent = (target, anchor, conservativeDestroy) => { if (lastError) { - clearError(); + lastError = null; + adapter.rerender(); } else if (conservativeDestroy) { try { cmp = cmp.$replace(current.Component, { @@ -161,7 +162,6 @@ class ProxyComponent { try { cmp = cmp.$replace(current.Component, { target, anchor }); } catch (err) { - cmp = null; const errString = String((err && err.stack) || err); logError(`Failed to recreate ${debugName} instance: ${errString}`); if (current.hotOptions.optimistic) { @@ -176,14 +176,9 @@ class ProxyComponent { // TODO need to use cmp.$replace const setError = (err, target, anchor) => { lastError = err; - destroyComponent(); + // destroyComponent(); // create a noop comp to trap Svelte's calls - cmp = createErrorComponent(adapter, err, target, anchor); - }; - - const clearError = () => { - lastError = null; - adapter.rerender(); + // cmp = createErrorComponent(cmp, adapter, err, target, anchor); }; const instance = { From 8d5701a4b0d0eda5ebc0a63736a335acad79018d Mon Sep 17 00:00:00 2001 From: rixo Date: Sat, 28 Sep 2019 22:14:26 +0200 Subject: [PATCH 27/79] exclude store subscriptions from preserved local state --- lib/svelte3/hot/svelte-hooks.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js index 1875b80a..cdfe4306 100644 --- a/lib/svelte3/hot/svelte-hooks.js +++ b/lib/svelte3/hot/svelte-hooks.js @@ -6,9 +6,12 @@ */ import { current_component, set_current_component } from 'svelte/internal'; -const isWritable = v => !v.module && v.writable; +// NOTE excludes store subscriptions because it causes crashes (and also +// probably not intented to restore stores states -- stores lives outside of +// the HMR'd component normally) +const isWritable = v => !v.module && v.writable && v.name.substr(0, 1) !== '$'; -const isProp = v => !v.module && v.export_name != null; +const isProp = v => isWritable(v) && v.export_name != null; // Core $capture_state should be able to capture either only props or the whole // local state (i.e. any `let` value). The best behaviour regarding HMR varies From 1ae9f836474ad84b2e49578b457b620ef8de14a6 Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 30 Sep 2019 01:53:12 +0200 Subject: [PATCH 28/79] use svelte-hmr --- .../hot/patch-page-show-modal.js | 63 ---- lib/svelte-native/hot/proxy-adapter-native.js | 334 ----------------- lib/svelte3/hot-api.js | 36 ++ lib/svelte3/hot/hot-api.js | 97 ----- lib/svelte3/hot/proxy-adapter-dom.js | 91 ----- lib/svelte3/hot/proxy.js | 342 ------------------ lib/svelte3/hot/svelte-hooks.js | 235 ------------ lib/svelte3/make-hot.js | 41 +-- 8 files changed, 39 insertions(+), 1200 deletions(-) delete mode 100644 lib/svelte-native/hot/patch-page-show-modal.js delete mode 100644 lib/svelte-native/hot/proxy-adapter-native.js create mode 100644 lib/svelte3/hot-api.js delete mode 100644 lib/svelte3/hot/hot-api.js delete mode 100644 lib/svelte3/hot/proxy-adapter-dom.js delete mode 100644 lib/svelte3/hot/proxy.js delete mode 100644 lib/svelte3/hot/svelte-hooks.js diff --git a/lib/svelte-native/hot/patch-page-show-modal.js b/lib/svelte-native/hot/patch-page-show-modal.js deleted file mode 100644 index 6cb5bd8a..00000000 --- a/lib/svelte-native/hot/patch-page-show-modal.js +++ /dev/null @@ -1,63 +0,0 @@ -/* global Symbol */ - -// This module monkey patches Page#showModal in order to be able to -// access from the HMR proxy data passed to `showModal` in svelte-native. -// -// Data are stored in a opaque prop accessible with `getModalData`. -// -// It also switches the `closeCallback` option with a custom brewed one -// in order to give the proxy control over when its own instance will be -// destroyed. -// -// Obviously this method suffer from extreme coupling with the target code -// in svelte-native. So it would be wise to recheck compatibility on SN -// version upgrades. -// -// Relevant code is there (last checked version): -// -// https://github.com/halfnelson/svelte-native/blob/08702e6b178644f43052f6ec0a789a51e800d21b/src/dom/svelte/StyleElement.ts -// - -// FIXME should we override ViewBase#showModal instead? -import { Page } from 'tns-core-modules/ui/page'; - -const prop = - typeof Symbol !== 'undefined' - ? Symbol('hmr_svelte_native_modal') - : '___HMR_SVELTE_NATIVE_MODAL___'; - -const sup = Page.prototype.showModal; - -let patched = false; - -export const patchShowModal = () => { - // guard: already patched - if (patched) return; - patched = true; - - Page.prototype.showModal = function(modalView, options) { - const modalData = { - originalOptions: options, - closeCallback: options.closeCallback, - }; - - modalView[prop] = modalData; - - // Proxies to a function that can be swapped on the fly by HMR proxy. - // - // The default is still to call the original closeCallback from svelte - // navtive, which will destroy the modal view & component. This way, if - // no HMR happens on the modal content, normal behaviour is preserved - // without the proxy having any work to do. - // - const closeCallback = (...args) => { - return modalData.closeCallback(...args); - }; - - const temperedOptions = Object.assign({}, options, { closeCallback }); - - return sup.call(this, modalView, temperedOptions); - }; -}; - -export const getModalData = modalView => modalView[prop]; diff --git a/lib/svelte-native/hot/proxy-adapter-native.js b/lib/svelte-native/hot/proxy-adapter-native.js deleted file mode 100644 index aac3b4a5..00000000 --- a/lib/svelte-native/hot/proxy-adapter-native.js +++ /dev/null @@ -1,334 +0,0 @@ -/* global document */ - -import { NavigationType } from 'tns-core-modules/ui/frame/frame-common'; - -import ProxyAdapterDom from '../../svelte3/hot/proxy-adapter-dom'; - -import { patchShowModal, getModalData } from './patch-page-show-modal'; - -patchShowModal(); - -// Svelte Native support -// ===================== -// -// Rerendering Svelte Native page proves challenging... -// -// In NativeScript, pages are the top level component. They are normally -// introduced into NativeScript's runtime by its `navigate` function. This -// is how Svelte Natives handles it: it renders the Page component to a -// dummy fragment, and "navigate" to the page element thus created. -// -// As long as modifications only impact child components of the page, then -// we can keep the existing page and replace its content for HMR. -// -// However, if the page component itself is modified (including its system -// title bar), things get hairy... -// -// Apparently, the sole way of introducing a new page in a NS application is -// to navigate to it (no way to just replace it in its parent "element", for -// example). This is how it is done in NS's own "core" HMR. -// -// Unfortunately the API they're using to do that is not public... Its various -// parts remain exposed though (but documented as private), so this exploratory -// work now relies on it. It might be fragile... -// -// The problem is that there is no public API that can navigate to a page and -// replacing (like location.replace) the current history entry. Actually there -// is an active issue at NS asking for that. Incidentally, members of -// NativeScript-Vue have commented on the issue to weight in for it -- they -// probably face some similar challenge. -// -// https://github.com/NativeScript/NativeScript/issues/6283 - -const getNavTransition = ({ transition }) => { - if (typeof transition === 'string') { - transition = { name: transition }; - } - return transition ? { animated: true, transition } : { animated: false }; -}; - -// copied from TNS FrameBase.replacePage -// -// it is not public but there is a comment in there indicating it is for -// HMR (probably their own core HMR though) -// -// NOTE this "worked" in TNS 5, but not anymore in TNS 6: updated version bellow -// -// eslint-disable-next-line no-unused-vars -const replacePage_tns5 = (frame, newPageElement, hotOptions) => { - const currentBackstackEntry = frame._currentEntry; - frame.navigationType = 2; - frame.performNavigation({ - isBackNavigation: false, - entry: { - resolvedPage: newPageElement.nativeView, - // - // entry: currentBackstackEntry.entry, - entry: Object.assign( - currentBackstackEntry.entry, - getNavTransition(hotOptions) - ), - navDepth: currentBackstackEntry.navDepth, - fragmentTag: currentBackstackEntry.fragmentTag, - frameId: currentBackstackEntry.frameId, - }, - }); -}; - -// Updated for TNS v6 -// -// https://github.com/NativeScript/NativeScript/blob/6.1.1/tns-core-modules/ui/frame/frame-common.ts#L656 -const replacePage = (frame, newPageElement) => { - const currentBackstackEntry = frame._currentEntry; - const newPage = newPageElement.nativeView; - const newBackstackEntry = { - entry:currentBackstackEntry.entry, - resolvedPage: newPage, - navDepth: currentBackstackEntry.navDepth, - fragmentTag: currentBackstackEntry.fragmentTag, - frameId: currentBackstackEntry.frameId, - }; - const navigationContext = { - entry: newBackstackEntry, - isBackNavigation: false, - navigationType: NavigationType.replace - }; - frame._navigationQueue.push(navigationContext); - frame._processNextNavigationEntry(); -}; - -export default class ProxyAdapterNative extends ProxyAdapterDom { - constructor(instance) { - super(instance); - - this.nativePageElement = null; - this.originalNativeView = null; - this.navigatedFromHandler = null; - - this.relayNativeNavigatedFrom = this.relayNativeNavigatedFrom.bind(this); - } - - dispose() { - super.dispose(); - this.releaseNativePageElement(); - } - - releaseNativePageElement() { - if (this.nativePageElement) { - // native cleaning will happen when navigating back from the page - this.nativePageElement = null; - } - } - - // svelte-native uses navigateFrom event + e.isBackNavigation to know - // when to $destroy the component -- but we don't want our proxy instance - // destroyed when we renavigate to the same page for navigation purposes! - interceptPageNavigation(pageElement) { - const originalNativeView = pageElement.nativeView; - const { on } = originalNativeView; - const ownOn = originalNativeView.hasOwnProperty('on'); - // tricks svelte-native into giving us its handler - originalNativeView.on = function(type, handler, ...rest) { - if (type === 'navigatedFrom') { - this.navigatedFromHandler = handler; - if (ownOn) { - originalNativeView.on = on; - } else { - delete originalNativeView.on; - } - } else { - throw new Error( - 'Unexpected call: has underlying svelte-native code changed?' - ); - } - }; - } - - afterMount(target, anchor) { - // nativePageElement needs to be updated each time (only for page - // components, native component that are not pages follow normal flow) - // - // DEBUG quid of components that are initially a page, but then have the - // tag removed while running? or the opposite? - // - // insertionPoint needs to be updated _only when the target changes_ -- - // i.e. when the component is mount, i.e. (in svelte3) when the component - // is _created_, and svelte3 doesn't allow it to move afterward -- that - // is, insertionPoint only needs to be created once when the component is - // first mounted. - // - // DEBUG is it really true that components' elements cannot move in the - // DOM? what about keyed list? - // - const isNativePage = target.tagName === 'fragment'; - if (isNativePage) { - const nativePageElement = target.firstChild; - this.interceptPageNavigation(nativePageElement); - this.nativePageElement = nativePageElement; - } else { - // try to protect against components changing from page to no-page - // or vice versa -- see DEBUG 1 above. NOT TESTED so prolly not working - this.nativePageElement = null; - super.afterMount(target, anchor); - } - } - - rerender() { - const { nativePageElement } = this; - if (nativePageElement) { - this.rerenderNative(); - } else { - super.rerender(); - } - } - - rerenderNative() { - const { nativePageElement: oldPageElement } = this; - const nativeView = oldPageElement.nativeView; - const frame = nativeView.frame; - if (frame) { - return this.rerenderPage(frame, nativeView); - } - const modalParent = nativeView._modalParent; // FIXME private API - if (modalParent) { - return this.rerenderModal(modalParent, nativeView); - } - // wtf? hopefully a race condition with a destroyed component, so - // we have nothing more to do here - // - // for once, it happens when hot reloading dev deps, like this file - // - } - - rerenderPage(frame, previousPageView) { - const isCurrentPage = frame.currentPage === previousPageView; - if (isCurrentPage) { - const { - instance: { hotOptions }, - } = this; - const newPageElement = this.createPage(); - if (!newPageElement) { - throw new Error('Failed to create updated page'); - } - const isFirstPage = !frame.canGoBack(); - - if (isFirstPage) { - // The "replacePage" strategy does not work on the first page - // of the stack. - // - // Resulting bug: - // - launch - // - change first page => HMR - // - navigate to other page - // - back - // => actual: back to OS - // => expected: back to page 1 - // - // Fortunately, we can overwrite history in this case. - // - const nativeView = newPageElement.nativeView; - frame.navigate( - Object.assign( - {}, - { - create: () => nativeView, - clearHistory: true, - }, - getNavTransition(hotOptions) - ) - ); - } else { - replacePage(frame, newPageElement, hotOptions); - } - } else { - const backEntry = frame.backStack.find( - ({ resolvedPage: page }) => page === previousPageView - ); - if (!backEntry) { - // well... looks like we didn't make it to history after all - return; - } - // replace existing nativeView - const newPageElement = this.createPage(); - if (newPageElement) { - backEntry.resolvedPage = newPageElement.nativeView; - } else { - throw new Error('Failed to create updated page'); - } - } - } - - // modalParent is the page on which showModal(...) was called - // oldPageElement is the modal content, that we're actually trying to reload - rerenderModal(modalParent, modalView) { - const modalData = getModalData(modalView); - - modalData.closeCallback = () => { - const nativePageElement = this.createPage(); - if (!nativePageElement) { - throw new Error('Failed to created updated modal page'); - } - const { nativeView } = nativePageElement; - const { originalOptions } = modalData; - // Options will get monkey patched again, the only work left for us - // is to try to reduce visual disturbances. - // - // FIXME Even that proves too much unfortunately... Apparently TNS - // does not respect the `animated` option in this context: - // https://docs.nativescript.org/api-reference/interfaces/_ui_core_view_base_.showmodaloptions#animated - // - const options = Object.assign({}, originalOptions, { animated: false }); - modalParent.showModal(nativeView, options); - }; - - modalView.closeModal(); - } - - createPage() { - const { - instance: { refreshComponent }, - } = this; - const { nativePageElement, relayNativeNavigatedFrom } = this; - const oldNativeView = nativePageElement.nativeView; - // rerender - const target = document.createElement('fragment'); - // not using conservative for now, since there's nothing in place here to - // leverage it (yet?) -- and it might be easier to miss breakages in native - // only code paths - refreshComponent(target, null); - // this.nativePageElement is updated in afterMount, triggered by proxy / hooks - const newPageElement = this.nativePageElement; - // update event proxy - oldNativeView.off('navigatedFrom', relayNativeNavigatedFrom); - nativePageElement.nativeView.on('navigatedFrom', relayNativeNavigatedFrom); - return newPageElement; - } - - relayNativeNavigatedFrom({ isBackNavigation }) { - const { originalNativeView, navigatedFromHandler } = this; - if (!isBackNavigation) { - return; - } - if (originalNativeView) { - const { off } = originalNativeView; - const ownOff = originalNativeView.hasOwnProperty('off'); - originalNativeView.off = function() { - this.navigatedFromHandler = null; - if (ownOff) { - originalNativeView.off = off; - } else { - delete originalNativeView.off; - } - }; - } - if (navigatedFromHandler) { - return navigatedFromHandler.apply(this, arguments); - } - } - - renderError(err, target, anchor) { - // TODO fallback on TNS error handler for now... at least our error - // is more informative - throw err; - } -} diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js new file mode 100644 index 00000000..9fdc9ddd --- /dev/null +++ b/lib/svelte3/hot-api.js @@ -0,0 +1,36 @@ +import { doApplyHMR } from 'svelte-hmr/runtime'; + +export function applyHMR( + targetModule, + id, + hotOptions, + Component, + ProxyAdapter, + compileData +) { + try { + const { proxy, error } = doApplyHMR( + hotOptions, + id, + Component, + ProxyAdapter, + compileData + ); + + if (error && !hotOptions.optimistic) { + targetModule.hot.decline(); + } else { + targetModule.hot.accept(); + } + + return proxy; + } catch (err) { + targetModule.hot.decline(); + // since we won't return the proxy and the app will expect a svelte + // component, it's gonna crash... so it's best to report the real cause + const errString = (err && err.stack) || err; + throw new Error( + `Failed to create HMR proxy for Svelte component ${id}: ${errString}` + ); + } +} diff --git a/lib/svelte3/hot/hot-api.js b/lib/svelte3/hot/hot-api.js deleted file mode 100644 index 26735709..00000000 --- a/lib/svelte3/hot/hot-api.js +++ /dev/null @@ -1,97 +0,0 @@ -import DomAdapter from './proxy-adapter-dom'; -import { createProxy } from './proxy'; - -const defaultHotOptions = { - noPreserveState: false, -}; - -const registry = new Map(); - -// One stop shop for HMR updates. Combines functionality of `configure`, -// `register`, and `reload`, based on current registry state. -// -// Additionaly does whatever it can to avoid crashing on runtime errors, -// and tries to decline HMR if that doesn't go well. -// -export function applyHMR( - hotOptions, - id, - moduleHot, - Component, - ProxyAdapter = DomAdapter, - compileData -) { - // resolve existing record - let record = registry.get(id); - let broken = false; - - hotOptions = Object.assign({}, defaultHotOptions, hotOptions); - - // meta info from compilation (vars, things that could be inspected in AST...) - // can be used to help the proxy better emulate the proxied component (and - // better mock svelte hooks, in the wait for official support) - if (compileData) { - // NOTE we're making Component carry the load to minimize diff with base branch - Component.$$hmrCompileData = compileData; - } - - // (re)render - if (record) { - const success = record.reload({ Component, hotOptions }); - if (success === false) { - broken = true; - } - } else { - record = createProxy(ProxyAdapter, id, Component, hotOptions); - registry.set(id, record); - } - - const proxy = record && record.proxy; - - const reloadOrDecline = () => { - // NOTE window.location.reload is not be available in all supported runtimes - /* global window */ - if ( - // DEBUG TODO doccument noReload - !hotOptions.noReload && - typeof window !== 'undefined' && - window.location && - window.location.reload - ) { - window.location.reload(); - } else { - moduleHot.decline(); - } - }; - - if (!proxy) { - // well, endgame... we won't be able to render next updates, even - // successful, if we don't have proxies in svelte's tree - // - // full reload required - // - reloadOrDecline(); - - // TODO report error on client - - // since we won't return the proxy and the app will expect a svelte - // component, it's gonna crash... so it's best to report the real cause - throw new Error(`Failed to create HMR proxy for Svelte component ${id}`); - } - - // Make sure we won't try to restore from an irrecuperable state. - // - // E.g. a component can partially render children to DOM from its - // constructor, then crash without even leaving a reference in a - // variable (since crash from the constructor). Maybe the compiler - // could handle the situation, but we can't (so, according to HMR - // Tao, better full reload than stale display). - // - if (broken) { - reloadOrDecline(); - } else { - moduleHot.accept(); - } - - return proxy; -} diff --git a/lib/svelte3/hot/proxy-adapter-dom.js b/lib/svelte3/hot/proxy-adapter-dom.js deleted file mode 100644 index fe433ae9..00000000 --- a/lib/svelte3/hot/proxy-adapter-dom.js +++ /dev/null @@ -1,91 +0,0 @@ -/* global document */ - -const removeElement = el => el && el.parentNode && el.parentNode.removeChild(el); - -export default class ProxyAdapterDom { - constructor(instance) { - this.instance = instance; - this.insertionPoint = null; - - this.afterMount = this.afterMount.bind(this); - this.rerender = this.rerender.bind(this); - } - - dispose() { - // Component is being destroyed, detaching is not optional in Svelte3's - // component API, so we can dispose of the insertion point in every case. - if (this.insertionPoint) { - removeElement(this.insertionPoint); - this.insertionPoint = null; - } - } - - // NOTE afterMount CAN be called multiple times (e.g. keyed list) - afterMount(target, anchor) { - const { - instance: { debugName }, - } = this; - if (!this.insertionPoint) { - this.insertionPoint = document.createComment(debugName); - } - target.insertBefore(this.insertionPoint, anchor); - } - - rerender() { - const { - instance: { refreshComponent }, - insertionPoint, - } = this; - if (!insertionPoint) { - throw new Error('Cannot rerender: Missing insertion point'); - } - refreshComponent(insertionPoint.parentNode, insertionPoint); - } - - renderError(err, target, anchor) { - const { - instance: { debugName }, - } = this; - const style = { - section: ` - border: 3px solid red; - background-color: #fee; - padding: 12px; - `, - h2: ` - margin: 0 0 12px; - color: red; - `, - pre: ` - background: #eee; - padding: 6px; - margin: 0; - border: 1px solid #ddd; - `, - }; - const title = debugName || err.moduleName || 'Error'; - const h2 = document.createElement('h2'); - h2.textContent = title; - const pre = document.createElement('pre'); - pre.textContent = err.stack || err; - const section = document.createElement('section'); - section.appendChild(h2); - section.appendChild(pre); - // style - section.style = style.section; - h2.style = style.h2; - pre.style = style.pre; - if (anchor) { - target.insertBefore(section, anchor); - } else { - if (!target) { - target = document.body; - section.style.posititon = 'absolute'; - } - target.appendChild(section); - } - return () => { - target.removeChild(section); - }; - } -} diff --git a/lib/svelte3/hot/proxy.js b/lib/svelte3/hot/proxy.js deleted file mode 100644 index 68fffbd3..00000000 --- a/lib/svelte3/hot/proxy.js +++ /dev/null @@ -1,342 +0,0 @@ -import { createProxiedComponent } from './svelte-hooks'; - -const handledMethods = ['constructor', '$destroy']; -const forwardedMethods = ['$set', '$on']; - -const noop = () => {}; - -const logError = (...args) => console.error('[HMR][Svelte]', ...args); - -const posixify = file => file.replace(/[/\\]/g, '/'); - -const getBaseName = id => - id - .split('/') - .pop() - .split('.') - .slice(0, -1) - .join('.'); - -const capitalize = str => str[0].toUpperCase() + str.slice(1); - -const getFriendlyName = id => capitalize(getBaseName(posixify(id))); - -const getDebugName = id => `<${getFriendlyName(id)}>`; - -const relayCalls = (getTarget, names, dest = {}) => { - for (const key of names) { - dest[key] = function(...args) { - const target = getTarget(); - if (!target) { - return; - } - return target[key] && target[key].call(this, ...args); - }; - } - return dest; -}; - -const copyComponentMethods = (proxy, cmp, debugName) => { - //proxy custom methods - const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(cmp)); - methods.forEach(method => { - if ( - !handledMethods.includes(method) && - !forwardedMethods.includes(method) - ) { - proxy[method] = function() { - if (cmp[method]) { - return cmp[method].apply(this, arguments); - } else { - // we can end up here when calling a method added by a previous - // version of the component, then removed (but still called - // somewhere else in the code) - // - // TODO we should possibly consider removing all the methods that - // have been added by a previous version of the component. This - // would be better memory-wise. Not so much so complexity-wise, - // though. And for now, we can't survive most runtime errors, so - // we will be reloaded often... - // - throw new Error( - `Called to undefined method on ${debugName}: ${method}` - ); - } - }; - } - }); -}; - -// TODO clean this extremely ad-hoc, coupled, & fragile code -// TODO native: this must respect Page/Frame interface... or need tolerance from SN -const createErrorComponent = (adapter, err, target, anchor) => { - const cmp = { - $destroy: noop, - $set: noop, - $$: { - fragment: { - c: noop, // create - l: noop, // claim - h: noop, // hydrate - m: (target, anchor) => { - cmp.$destroy = adapter.renderError(err, target, anchor); - }, // mount - p: noop, // update - i: noop, // intro - o: noop, // outro - d: noop, // destroy - }, - ctx: {}, - // state - props: [], - update: noop, - not_equal: noop, - bound: {}, - // lifecycle - on_mount: [], - on_destroy: [], - before_render: [], - after_render: [], - context: {}, - // everything else - callbacks: [], - dirty: noop, - }, - }; - if (target) { - cmp.$destroy = adapter.renderError(err, target, anchor); - } - return cmp; -}; - -// everything in the constructor! -// -// so we don't polute the component class with new members -// -// specificity & conformance with Svelte component constructor is achieved -// in the "component level" (as opposed "instance level") createRecord -// -class ProxyComponent { - constructor( - { - Adapter, - id, - debugName, - current, // { Component, hotOptions: { noPreserveState, ... } } - register, - unregister, - reportError, - }, - options // { target, anchor, ... } - ) { - let cmp; - let disposed = false; - let lastError = null; - - const destroyComponent = () => { - // destroyComponent is tolerant (don't crash on no cmp) because it - // is possible that reload/rerender is called after a previous - // createComponent has failed (hence we have a proxy, but no cmp) - if (cmp) { - cmp.$destroy(); - cmp = null; - } - }; - - const refreshComponent = (target, anchor, conservativeDestroy) => { - if (lastError) { - lastError = null; - adapter.rerender(); - } else if (conservativeDestroy) { - try { - cmp = cmp.$replace(current.Component, { - target, - anchor, - conservative: true, - }); - } catch (err) { - const errString = String((err && err.stack) || err); - logError(`Failed to recreate ${debugName} instance: ${errString}`); - } - } else { - try { - cmp = cmp.$replace(current.Component, { target, anchor }); - } catch (err) { - const errString = String((err && err.stack) || err); - logError(`Failed to recreate ${debugName} instance: ${errString}`); - if (current.hotOptions.optimistic) { - setError(err, target, anchor); - } else { - throw err; - } - } - } - }; - - // TODO need to use cmp.$replace - const setError = (err, target, anchor) => { - lastError = err; - // destroyComponent(); - // create a noop comp to trap Svelte's calls - // cmp = createErrorComponent(cmp, adapter, err, target, anchor); - }; - - const instance = { - hotOptions: current.hotOptions, - proxy: this, - id, - debugName, - refreshComponent, - }; - - const adapter = new Adapter(instance); - - const { afterMount, rerender } = adapter; - - // $destroy is not called when a child component is disposed, so we - // need to hook from fragment. - const onDestroy = () => { - // NOTE do NOT call $destroy on the cmp from here; the cmp is already - // dead, this would not work - if (!disposed) { - disposed = true; - adapter.dispose(); - unregister(); - } - }; - - // ---- register proxy instance ---- - - register(rerender); - - // ---- augmented methods ---- - - this.$destroy = () => { - destroyComponent(); - onDestroy(); - }; - - // ---- forwarded methods ---- - - const getComponent = () => cmp; - - relayCalls(getComponent, forwardedMethods, this); - - // ---- create & mount target component instance --- - - try { - cmp = createProxiedComponent(current.Component, options, { - noPreserveState: current.hotOptions.noPreserveState, - onDestroy, - onMount: afterMount, - onInstance: comp => { - // WARNING the proxy MUST use the same $$ object as its component - // instance, because a lot of wiring happens during component - // initialisation... lots of references to $$ and $$.fragment have - // already been distributed around when the component constructor - // returns, before we have a chance to wrap them (and so we can't - // wrap them no more, because existing references would become - // invalid) - this.$$ = comp.$$; - copyComponentMethods(this, comp); - }, - }); - } catch (err) { - const { target, anchor } = options; - if (current.hotOptions.optimistic && target) { - logError( - `Failed to create ${debugName} instance: ${(err && err.stack) || err}` - ); - setError(err, target, anchor); - } else { - throw err; - } - } - } -} - -const copyStatics = (component, proxy) => { - //forward static properties and methods - for (let key in component) { - proxy[key] = component[key]; - } -}; - -/* -creates a proxy object that -decorates the original component with trackers -and ensures resolution to the -latest version of the component -*/ -export function createProxy(Adapter, id, Component, hotOptions) { - const debugName = getDebugName(id); - const instances = []; - - // current object will be updated, proxy instances will keep a ref - const current = { - Component, - hotOptions, - }; - - const name = `Proxy${debugName}`; - - // this trick gives the dynamic name Proxy to the concrete - // proxy class... unfortunately, this doesn't shows in dev tools, but - // it stills allow to inspect cmp.constructor.name to confirm an instance - // is a proxy - const proxy = { - [name]: class extends ProxyComponent { - constructor(options) { - super( - { - Adapter, - id, - debugName, - current, - register: rerender => { - instances.push(rerender); - }, - unregister: () => { - const i = instances.indexOf(this); - instances.splice(i, 1); - }, - }, - options - ); - } - }, - }[name]; - - // reload all existing instances of this component - const reload = ({ Component, hotOptions }) => { - // update current references - Object.assign(current, { Component, hotOptions }); - - // copy statics before doing anything because a static prop/method - // could be used somewhere in the create/render call - // TODO delete props/methods previously added and of which value has - // not changed since - copyStatics(Component, proxy); - - const errors = []; - - instances.forEach(rerender => { - try { - rerender(); - } catch (err) { - logError( - `Failed to rerender ${debugName}: ${(err && err.stack) || err}` - ); - errors.push(err); - } - }); - - if (errors.length > 0) { - return false; - } - - return true; - }; - - return { id, proxy, reload }; -} diff --git a/lib/svelte3/hot/svelte-hooks.js b/lib/svelte3/hot/svelte-hooks.js deleted file mode 100644 index cdfe4306..00000000 --- a/lib/svelte3/hot/svelte-hooks.js +++ /dev/null @@ -1,235 +0,0 @@ -/** - * Emulates forthcoming HMR hooks in Svelte. - * - * All references to private compoonent state ($$) are now isolated in this - * module. - */ -import { current_component, set_current_component } from 'svelte/internal'; - -// NOTE excludes store subscriptions because it causes crashes (and also -// probably not intented to restore stores states -- stores lives outside of -// the HMR'd component normally) -const isWritable = v => !v.module && v.writable && v.name.substr(0, 1) !== '$'; - -const isProp = v => isWritable(v) && v.export_name != null; - -// Core $capture_state should be able to capture either only props or the whole -// local state (i.e. any `let` value). The best behaviour regarding HMR varies -// between projects, and even situations of what you're currently working on... -// It's better to leave it as an option to the end user. -const $capture_state = (cmp, { captureLocalState }) => { - const compileData = cmp.constructor.$$hmrCompileData; - if (compileData && compileData.vars) { - const state = {}; - const filter = captureLocalState ? isWritable : isProp; - const vars = compileData.vars.filter(filter); - const ctx = cmp.$$.ctx; - for (const { name } of vars) { - state[name] = ctx[name]; - } - return state; - } - // fallback on actual $capture_state - if (cmp.$capture_state) { - // NOTE the captureLocalState option is fantasy for now - return cmp.$capture_state({ captureLocalState }); - } - // else nothing, state won't be used for restore... -}; - -const captureState = (cmp, captureLocalState = true) => { - // sanity check: propper behaviour here is to crash noisily so that - // user knows that they're looking at something broken - if (!cmp) { - throw new Error('Missing component'); - } - if (!cmp.$$) { - throw new Error('Invalid component'); - } - const { - $$: { callbacks, bound, ctx: props }, - } = cmp; - const state = $capture_state(cmp, { captureLocalState }); - return { props, callbacks, bound, state }; -}; - -// restoreState -// -// It is too late to restore context at this point because component instance -// function has already been called (and so context has already been read). -// Instead, we rely on setting current_component to the same value it has when -// the component was first rendered -- which fix support for context, and is -// also generally more respectful of normal operation. -// -const restoreState = (cmp, restore) => { - if (!restore) { - return; - } - const { callbacks, bound, state } = restore; - if (callbacks) { - cmp.$$.callbacks = callbacks; - } - if (bound) { - cmp.$$.bound = bound; - } - if (state && cmp.$inject_state) { - cmp.$inject_state(state); - } - // props, props.$$slots are restored at component creation (works - // better -- well, at all actually) -}; - -const filterProps = (props, { vars }) => { - if (!vars) { - return props; - } - const previousProps = props; - const result = {}; - vars - .filter(({ export_name }) => !!export_name) - .forEach(({ export_name }) => { - result[export_name] = previousProps[export_name]; - }); - Object.keys(previousProps) - .filter(name => name.substr(0, 2) === '$$') - .forEach(key => { - result[key] = previousProps[key]; - }); - return result; -}; - -export const createProxiedComponent = ( - Component, - initialOptions, - { noPreserveState, onInstance, onMount, onDestroy } -) => { - let cmp; - let parentComponent; - let compileData; - let options = initialOptions; - - const isCurrent = _cmp => cmp === _cmp; - - // it's better to restore props from the very beginning -- for example - // slots (yup, stored in props as $$slots) are broken if not present at - // component creation and later restored with $set - const restoreProps = restore => { - let props = restore && restore.props; - if (props) { - // $capture_state is not present in some cases on components. Also, it - // does not preserves slots. So for now we need fallbacks. - if (restore.state) { - return { $$slots: props.$$slots }; - } else { - if (compileData && compileData.vars) { - props = filterProps(props, compileData); - } - return { props }; - } - } - }; - - const assignOptions = (target, anchor, restore) => - Object.assign(options, { target, anchor }, restoreProps(restore)); - - const instrument = targetCmp => { - const createComponent = (Component, restore, previousCmp) => { - set_current_component(parentComponent || previousCmp); - const comp = new Component(options); - restoreState(comp, restore); - instrument(comp); - return comp; - }; - - // `conservative: true` means we want to be sure that the new component has - // actually been successfuly created before destroying the old instance. - // This could be useful for preventing runtime errors in component init to - // bring down the whole HMR. Unfortunately the implementation bellow is - // broken (FIXME), but that remains an interesting target for when HMR hooks - // will actually land in Svelte itself. - // - // The goal would be to render an error inplace in case of error, to avoid - // losing the navigation stack (especially annoying in native, that is not - // based on URL navigation, so we lose the current page on each error). - // - targetCmp.$replace = ( - Component, - { target = options.target, anchor = options.anchor, conservative = false } - ) => { - compileData = Component.$$hmrCompileData; - const restore = captureState(targetCmp, !noPreserveState); - assignOptions(target, anchor, restore); - const previous = cmp; - if (conservative) { - try { - cmp = createComponent(Component, restore, previous); - targetCmp.$destroy(); - } catch (err) { - cmp = targetCmp; - throw err; - } - } else { - cmp = null; // prevents on_destroy from firing on non-final cmp instance - targetCmp.$destroy(); - cmp = createComponent(Component, restore, previous); - } - return cmp; - }; - - // NOTE onMount must provide target & anchor (for us to be able to determinate - // actual DOM insertion point) - if (onMount) { - const m = targetCmp.$$.fragment.m; - targetCmp.$$.fragment.m = (...args) => { - const result = m(...args); - onMount(...args); - return result; - }; - } - - // NOTE onDestroy must be called even if the call doesn't pass through the - // component's $destroy method (that we can hook onto by ourselves, since - // it's public API) -- this happens a lot in svelte's internals, that - // manipulates cmp.$$.fragment directly, often binding to fragment.d, - // for example - if (onDestroy) { - targetCmp.$$.on_destroy.push(() => { - if (isCurrent(targetCmp)) { - onDestroy(); - } - }); - } - - if (onInstance) { - onInstance(targetCmp); - } - - // Svelte 3 creates and mount components from their constructor if - // options.target is present. - // - // This means that at this point, the component's `fragment.c` and, - // most notably, `fragment.m` will already have been called _from inside - // createComponent_. That is: before we have a chance to hook on it. - // - // Proxy's constructor - // -> createComponent - // -> component constructor - // -> component.$$.fragment.c(...) (or l, if hydrate:true) - // -> component.$$.fragment.m(...) - // - // -> you are here <- - // - if (onMount) { - const { target, anchor } = options; - if (target) { - onMount(target, anchor); - } - } - }; - - parentComponent = current_component; - cmp = new Component(options); - instrument(cmp); - - return cmp; -}; diff --git a/lib/svelte3/make-hot.js b/lib/svelte3/make-hot.js index 38d83a93..73d6a116 100644 --- a/lib/svelte3/make-hot.js +++ b/lib/svelte3/make-hot.js @@ -1,42 +1,7 @@ -const posixify = require('../posixify'); +const { createMakeHot } = require('svelte-hmr'); -const hotApi = require.resolve('./hot/hot-api.js'); +const hotApi = require.resolve('./hot-api.js'); -const nativeAdapter = require.resolve('../svelte-native/hot/proxy-adapter-native.js'); - -const quote = JSON.stringify; - -function makeHot(id, code, hotOptions = {}, compiled) { - const options = JSON.stringify(hotOptions); - - const compileData = JSON.stringify({ - vars: compiled.vars - }); - - // NOTE Native adapter cannot be required in code (as opposed to this - // generated code) because it requires modules from NativeScript's code that - // are not resolvable for non-native users (and those missing modules would - // prevent webpack from building). - const adapter = hotOptions.native - ? `require('${posixify(nativeAdapter)}').default` - : 'undefined'; - - const replacement = ` - if (module.hot) { - const { applyHMR } = require('${posixify(hotApi)}'); - $2 = applyHMR( - ${options}, - ${quote(id)}, - module.hot, - $2, - ${adapter}, - ${compileData} - ); - } - export default $2; - `; - - return code.replace(/(export default ([^;]*));/, replacement); -} +const makeHot = createMakeHot(hotApi); module.exports = makeHot; From 3f54b181988bc9d7a03a5048103904ca06d444d9 Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 30 Sep 2019 02:13:24 +0200 Subject: [PATCH 29/79] add missing dep --- package-lock.json | 5 +++++ package.json | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 86e50a8d..7c64c688 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1248,6 +1248,11 @@ "resolved": "https://registry.npmjs.org/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz", "integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=" }, + "svelte-hmr": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.1.tgz", + "integrity": "sha512-3p1STe7GIuxmotpZ3UoGDQlqrVJmxgY5q2IBjHF5XQOHwzvzMGG2sJNQ+vlqq0fksbJwgLEByWAgyxHMCodWdw==" + }, "symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", diff --git a/package.json b/package.json index 6ea3b99a..6f5532f2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ ], "dependencies": { "loader-utils": "^1.1.0", - "svelte-dev-helper": "^1.1.9" + "svelte-dev-helper": "^1.1.9", + "svelte-hmr": "0.0.1" }, "devDependencies": { "chai": "^4.1.2", From fadd9b4f1fee2751e7661b4b200b934dfbfb18e9 Mon Sep 17 00:00:00 2001 From: rixo Date: Mon, 30 Sep 2019 11:48:00 +0200 Subject: [PATCH 30/79] upgrade to last svelte-hmr, with error management --- lib/svelte3/hot-api.js | 43 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js index 9fdc9ddd..53a9eda0 100644 --- a/lib/svelte3/hot-api.js +++ b/lib/svelte3/hot-api.js @@ -1,36 +1,15 @@ -import { doApplyHMR } from 'svelte-hmr/runtime'; +import { doApplyHmr } from 'svelte-hmr/runtime'; -export function applyHMR( - targetModule, - id, - hotOptions, - Component, - ProxyAdapter, - compileData -) { - try { - const { proxy, error } = doApplyHMR( - hotOptions, - id, - Component, - ProxyAdapter, - compileData - ); +export function applyHmr(args) { + const { m } = args; - if (error && !hotOptions.optimistic) { - targetModule.hot.decline(); - } else { - targetModule.hot.accept(); - } + const decline = () => { + m.hot.decline(); + }; - return proxy; - } catch (err) { - targetModule.hot.decline(); - // since we won't return the proxy and the app will expect a svelte - // component, it's gonna crash... so it's best to report the real cause - const errString = (err && err.stack) || err; - throw new Error( - `Failed to create HMR proxy for Svelte component ${id}: ${errString}` - ); - } + const accept = () => { + m.hot.accept(); + }; + + return doApplyHmr(Object.assign({}, args, { accept, decline })); } From 4e6f38a728c1ef7b08c9f1393d1d68856bfe2f33 Mon Sep 17 00:00:00 2001 From: rixo Date: Tue, 1 Oct 2019 10:31:31 +0200 Subject: [PATCH 31/79] use last svelte-hmr --- package-lock.json | 7 ++++--- package.json | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c64c688..fed4c137 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1249,9 +1249,10 @@ "integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=" }, "svelte-hmr": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.1.tgz", - "integrity": "sha512-3p1STe7GIuxmotpZ3UoGDQlqrVJmxgY5q2IBjHF5XQOHwzvzMGG2sJNQ+vlqq0fksbJwgLEByWAgyxHMCodWdw==" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.6.tgz", + "integrity": "sha512-1S7rNCmT7bvdBdYfkwt9ocyNuePxuMg74JrcqDIanyWv68hKuLVXkd8rwalFUrOzNpxqlTExnE7u6f7CjY/vug==", + "dev": true }, "symbol-observable": { "version": "1.0.1", diff --git a/package.json b/package.json index 6f5532f2..53122bf0 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,7 @@ ], "dependencies": { "loader-utils": "^1.1.0", - "svelte-dev-helper": "^1.1.9", - "svelte-hmr": "0.0.1" + "svelte-dev-helper": "^1.1.9" }, "devDependencies": { "chai": "^4.1.2", @@ -25,7 +24,8 @@ "eslint-plugin-mocha": "^5.2.0", "mocha": "^5.2.0", "sinon": "^6.1.5", - "sinon-chai": "^3.2.0" + "sinon-chai": "^3.2.0", + "svelte-hmr": "0.0.6" }, "peerDependencies": { "svelte": ">1.44.0" From 765d64d5d9b06a496ec1cc0b015190960582299b Mon Sep 17 00:00:00 2001 From: rixo Date: Wed, 2 Oct 2019 21:53:45 +0200 Subject: [PATCH 32/79] fix svelte-hmr incorrectly registered as a dev dep --- package-lock.json | 3 +-- package.json | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index fed4c137..6782aa5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1251,8 +1251,7 @@ "svelte-hmr": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.6.tgz", - "integrity": "sha512-1S7rNCmT7bvdBdYfkwt9ocyNuePxuMg74JrcqDIanyWv68hKuLVXkd8rwalFUrOzNpxqlTExnE7u6f7CjY/vug==", - "dev": true + "integrity": "sha512-1S7rNCmT7bvdBdYfkwt9ocyNuePxuMg74JrcqDIanyWv68hKuLVXkd8rwalFUrOzNpxqlTExnE7u6f7CjY/vug==" }, "symbol-observable": { "version": "1.0.1", diff --git a/package.json b/package.json index 53122bf0..ccee6528 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ ], "dependencies": { "loader-utils": "^1.1.0", - "svelte-dev-helper": "^1.1.9" + "svelte-dev-helper": "^1.1.9", + "svelte-hmr": "^0.0.6" }, "devDependencies": { "chai": "^4.1.2", @@ -24,8 +25,7 @@ "eslint-plugin-mocha": "^5.2.0", "mocha": "^5.2.0", "sinon": "^6.1.5", - "sinon-chai": "^3.2.0", - "svelte-hmr": "0.0.6" + "sinon-chai": "^3.2.0" }, "peerDependencies": { "svelte": ">1.44.0" From 2204a8f7e66e441f5e0ce6e70a5fa0f9e6bb2103 Mon Sep 17 00:00:00 2001 From: rixo Date: Fri, 25 Oct 2019 17:39:57 +0200 Subject: [PATCH 33/79] upgrade to last version of svelte-hmr --- lib/svelte3/hot-api.js | 61 ++++++++++++++++++++++++++++++++++++------ package-lock.json | 6 ++--- package.json | 2 +- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js index 53a9eda0..4572aba9 100644 --- a/lib/svelte3/hot-api.js +++ b/lib/svelte3/hot-api.js @@ -1,15 +1,60 @@ -import { doApplyHmr } from 'svelte-hmr/runtime'; +import { makeApplyHmr } from 'svelte-hmr/runtime'; -export function applyHmr(args) { +export const applyHmr = makeApplyHmr(args => { const { m } = args; - const decline = () => { - m.hot.decline(); + let currentAcceptHandlers = m.hot.data && m.hot.data.acceptHandlers || []; + let acceptHandlers = []; + let currentCatchHandlers = m.hot.data && m.hot.data.catchHandlers || []; + + m.hot.dispose(data => { + data.acceptHandlers = acceptHandlers; + }); + + const dispose = (...args) => m.hot.dispose(...args); + + const accept = handler => { + if (acceptHandlers.length === 0) { + m.hot.accept(err => { + const handlers = currentAcceptHandlers; + if (handlers.length > 0) { + currentAcceptHandlers = []; + handlers.forEach(handler => { + handler(err); + }); + } + }); + } + acceptHandlers.push(handler); }; - const accept = () => { - m.hot.accept(); + setTimeout(() => { + if (currentAcceptHandlers.length > 0) { + const handlers = currentAcceptHandlers; + currentAcceptHandlers = []; + try { + handlers.forEach(handler => { + handler(null); + }); + } catch (err) { + const handlers = currentCatchHandlers; + currentCatchHandlers = []; + if (handlers.length > 0) { + handlers.forEach(handler => { + handler(err); + }); + } else { + throw err; + } + } + } + }); + + const hot = { + data: m.hot.data, + dispose, + accept, }; - return doApplyHmr(Object.assign({}, args, { accept, decline })); -} + return Object.assign({}, args, { hot }); +}); diff --git a/package-lock.json b/package-lock.json index 6782aa5d..8f94171a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1249,9 +1249,9 @@ "integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=" }, "svelte-hmr": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.6.tgz", - "integrity": "sha512-1S7rNCmT7bvdBdYfkwt9ocyNuePxuMg74JrcqDIanyWv68hKuLVXkd8rwalFUrOzNpxqlTExnE7u6f7CjY/vug==" + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.9.tgz", + "integrity": "sha512-I2Zz+hKxusDcnq8896YVOAfxVpAeSWyfCSXO3a9FZKZIK3vjqK4L7ybifyx+N2GPn9yvmRj95Ou5T6CB3NW70g==" }, "symbol-observable": { "version": "1.0.1", diff --git a/package.json b/package.json index ccee6528..65944951 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "dependencies": { "loader-utils": "^1.1.0", "svelte-dev-helper": "^1.1.9", - "svelte-hmr": "^0.0.6" + "svelte-hmr": "0.0.9" }, "devDependencies": { "chai": "^4.1.2", From dfbdb6f002f2b8f7463d9c6f2c5e1709fdc81a93 Mon Sep 17 00:00:00 2001 From: rixo Date: Fri, 25 Oct 2019 22:23:29 +0200 Subject: [PATCH 34/79] cleaner hot-api adapter (also, fix svhs tests) --- lib/svelte3/hot-api.js | 52 +++++++++++++----------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js index 4572aba9..64146c50 100644 --- a/lib/svelte3/hot-api.js +++ b/lib/svelte3/hot-api.js @@ -1,53 +1,33 @@ import { makeApplyHmr } from 'svelte-hmr/runtime'; +const runAcceptHandlers = acceptHandlers => + Promise.resolve().then(() => + Promise.all(acceptHandlers.map(handler => handler(null))) + ); + export const applyHmr = makeApplyHmr(args => { - const { m } = args; + const { m, reload } = args; - let currentAcceptHandlers = m.hot.data && m.hot.data.acceptHandlers || []; - let acceptHandlers = []; - let currentCatchHandlers = m.hot.data && m.hot.data.catchHandlers || []; + let acceptHandlers = (m.hot.data && m.hot.data.acceptHandlers) || []; + let nextAcceptHandlers = []; m.hot.dispose(data => { - data.acceptHandlers = acceptHandlers; + data.acceptHandlers = nextAcceptHandlers; }); const dispose = (...args) => m.hot.dispose(...args); const accept = handler => { - if (acceptHandlers.length === 0) { - m.hot.accept(err => { - const handlers = currentAcceptHandlers; - if (handlers.length > 0) { - currentAcceptHandlers = []; - handlers.forEach(handler => { - handler(err); - }); - } - }); + if (nextAcceptHandlers.length === 0) { + m.hot.accept(); } - acceptHandlers.push(handler); + nextAcceptHandlers.push(handler); }; - setTimeout(() => { - if (currentAcceptHandlers.length > 0) { - const handlers = currentAcceptHandlers; - currentAcceptHandlers = []; - try { - handlers.forEach(handler => { - handler(null); - }); - } catch (err) { - const handlers = currentCatchHandlers; - currentCatchHandlers = []; - if (handlers.length > 0) { - handlers.forEach(handler => { - handler(err); - }); - } else { - throw err; - } - } - } + runAcceptHandlers(acceptHandlers).catch(err => { + const msg = '[HMR][Svelte] Failed to accept update'; + console.error(msg, (err && err.stack) || err); + reload(); }); const hot = { From 251588edd6ca797bbffbda4a968ae40d2d9e4898 Mon Sep 17 00:00:00 2001 From: rixo Date: Tue, 5 Nov 2019 13:05:02 +0100 Subject: [PATCH 35/79] run accept handlers serially to prevent race conditions --- lib/svelte3/hot-api.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js index 64146c50..8a86d118 100644 --- a/lib/svelte3/hot-api.js +++ b/lib/svelte3/hot-api.js @@ -1,9 +1,17 @@ import { makeApplyHmr } from 'svelte-hmr/runtime'; -const runAcceptHandlers = acceptHandlers => - Promise.resolve().then(() => - Promise.all(acceptHandlers.map(handler => handler(null))) - ); +const runAcceptHandlers = acceptHandlers => { + const queue = [...acceptHandlers]; + const next = () => { + const cur = queue.shift(); + if (cur) { + return cur(null).then(next); + } else { + return Promise.resolve(); + } + }; + return next(); +}; export const applyHmr = makeApplyHmr(args => { const { m, reload } = args; @@ -25,8 +33,8 @@ export const applyHmr = makeApplyHmr(args => { }; runAcceptHandlers(acceptHandlers).catch(err => { - const msg = '[HMR][Svelte] Failed to accept update'; - console.error(msg, (err && err.stack) || err); + const errString = (err && err.stack) || err; + console.error('[HMR:Svelte] Failed to accept update', errString); reload(); }); From 3bc4671995c5c101e625c159760277a35db1c59f Mon Sep 17 00:00:00 2001 From: rixo Date: Tue, 5 Nov 2019 13:24:21 +0100 Subject: [PATCH 36/79] add info message when svelte loc is overriden by env --- lib/resolve-svelte.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/resolve-svelte.js b/lib/resolve-svelte.js index b6384961..359c8b83 100644 --- a/lib/resolve-svelte.js +++ b/lib/resolve-svelte.js @@ -7,9 +7,14 @@ const resolveSvelte = () => { path.isAbsolute(name) ? name : path.join(base, name); if (SVELTE) { + const base = absolute(SVELTE, process.cwd()); + console.log( + 'ℹ 「svelte-loader」:', + `Use Svelte location from SVELTE env variable: ${base}` + ); return { req: require, - base: absolute(SVELTE, process.cwd()), + base, }; } else { return { From 327814803f17ff056f61a8ef27949159211e88de Mon Sep 17 00:00:00 2001 From: rixo Date: Tue, 5 Nov 2019 15:58:52 +0100 Subject: [PATCH 37/79] fix async accept handlers race condition --- lib/svelte3/hot-api.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js index 8a86d118..f4eb4d2c 100644 --- a/lib/svelte3/hot-api.js +++ b/lib/svelte3/hot-api.js @@ -32,10 +32,20 @@ export const applyHmr = makeApplyHmr(args => { nextAcceptHandlers.push(handler); }; - runAcceptHandlers(acceptHandlers).catch(err => { - const errString = (err && err.stack) || err; - console.error('[HMR:Svelte] Failed to accept update', errString); - reload(); + const check = status => { + if (status === 'idle') { + runAcceptHandlers(acceptHandlers).catch(err => { + const errString = (err && err.stack) || err; + console.error('[HMR:Svelte] Failed to accept update', errString); + reload(); + }); + } + }; + + m.hot.addStatusHandler(check); + + m.hot.dispose(() => { + m.hot.removeStatusHandler(check); }); const hot = { From 2be1bd7e0f433c14930f97ce4a0e02ccbd20f8f6 Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 7 Nov 2019 08:47:49 +0100 Subject: [PATCH 38/79] add webpack specific HMR done message --- lib/svelte3/hot-api.js | 59 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/svelte3/hot-api.js b/lib/svelte3/hot-api.js index f4eb4d2c..e98a8862 100644 --- a/lib/svelte3/hot-api.js +++ b/lib/svelte3/hot-api.js @@ -1,5 +1,49 @@ import { makeApplyHmr } from 'svelte-hmr/runtime'; +// eslint-disable-next-line no-undef +const g = typeof window !== 'undefined' ? window : global; + +const globalKey = + typeof Symbol !== 'undefined' + ? Symbol('SVELTE_LOADER_HOT') + : '__SVELTE_LOADER_HOT'; + +if (!g[globalKey]) { + // do updating refs counting to know when a full update has been applied + let updatingCount = 0; + + const notifyStart = () => { + updatingCount++; + }; + + const notifyError = reload => err => { + const errString = (err && err.stack) || err; + // eslint-disable-next-line no-console + console.error( + '[HMR] Failed to accept update (nollup compat mode)', + errString + ); + reload(); + notifyEnd(); + }; + + const notifyEnd = () => { + updatingCount--; + if (updatingCount === 0) { + // NOTE this message is important for timing in tests + // eslint-disable-next-line no-console + console.log('[HMR:Svelte] Up to date'); + } + }; + + g[globalKey] = { + hotStates: {}, + notifyStart, + notifyError, + notifyEnd, + }; +} + const runAcceptHandlers = acceptHandlers => { const queue = [...acceptHandlers]; const next = () => { @@ -7,13 +51,14 @@ const runAcceptHandlers = acceptHandlers => { if (cur) { return cur(null).then(next); } else { - return Promise.resolve(); + return Promise.resolve(null); } }; return next(); }; export const applyHmr = makeApplyHmr(args => { + const { notifyStart, notifyError, notifyEnd } = g[globalKey]; const { m, reload } = args; let acceptHandlers = (m.hot.data && m.hot.data.acceptHandlers) || []; @@ -33,12 +78,12 @@ export const applyHmr = makeApplyHmr(args => { }; const check = status => { - if (status === 'idle') { - runAcceptHandlers(acceptHandlers).catch(err => { - const errString = (err && err.stack) || err; - console.error('[HMR:Svelte] Failed to accept update', errString); - reload(); - }); + if (status === 'ready') { + notifyStart(); + } else if (status === 'idle') { + runAcceptHandlers(acceptHandlers) + .then(notifyEnd) + .catch(notifyError(reload)); } }; From 4a20c9991b1713f0e32be142c0377162c24b42a6 Mon Sep 17 00:00:00 2001 From: rixo Date: Thu, 7 Nov 2019 11:55:00 +0100 Subject: [PATCH 39/79] adapt for next version of svelte-hmr --- lib/svelte3/make-hot.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/svelte3/make-hot.js b/lib/svelte3/make-hot.js index 73d6a116..72293fd8 100644 --- a/lib/svelte3/make-hot.js +++ b/lib/svelte3/make-hot.js @@ -2,6 +2,8 @@ const { createMakeHot } = require('svelte-hmr'); const hotApi = require.resolve('./hot-api.js'); -const makeHot = createMakeHot(hotApi); +const makeHot = createMakeHot(hotApi, { + meta: 'module', +}); module.exports = makeHot; From ed4e5edd0dcec6c506574f7191e7daef1fe5e585 Mon Sep 17 00:00:00 2001 From: rixo Date: Fri, 15 Nov 2019 03:58:42 +0100 Subject: [PATCH 40/79] bump svelte-hmr --- package-lock.json | 1358 --------------------------------------------- package.json | 89 +-- yarn.lock | 887 +++++++++++++++++++++++++++++ 3 files changed, 937 insertions(+), 1397 deletions(-) delete mode 100644 package-lock.json create mode 100644 yarn.lock diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8f94171a..00000000 --- a/package-lock.json +++ /dev/null @@ -1,1358 +0,0 @@ -{ - "name": "svelte-loader", - "version": "2.13.6", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@sinonjs/commons": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.0.2.tgz", - "integrity": "sha512-WR3dlgqJP4QNrLC4iXN/5/2WaLQQ0VijOOkmflqFGVJ6wLEpbSjo7c0ZeGIdtY8Crk7xBBp87sM6+Mkerz7alw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "@sinonjs/samsam": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.0.0.tgz", - "integrity": "sha512-D7VxhADdZbDJ0HjUTMnSQ5xIGb4H2yWpg8k9Sf1T08zfFiQYlaxM8LZydpR4FQ2E6LZJX8IlabNZ5io4vdChwg==", - "dev": true - }, - "acorn": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.2.tgz", - "integrity": "sha512-cJrKCNcr2kv8dlDnbw+JPUGjHZzo4myaxOLmpOX8a+rgX94YeTcTMv/LFJUSByRpc+i4GgVnnhLxvMu/2Y+rqw==", - "dev": true - }, - "acorn-jsx": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", - "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", - "dev": true, - "requires": { - "acorn": "^5.0.3" - } - }, - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", - "dev": true, - "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", - "dev": true, - "requires": { - "color-name": "1.1.1" - } - }, - "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", - "dev": true - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.4.0.tgz", - "integrity": "sha512-UIpL91XGex3qtL6qwyCQJar2j3osKxK9e3ano3OcGEIRM4oWIpCkDg9x95AXEC2wMs7PnxzOkPZ2gq+tsMS9yg==", - "dev": true, - "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.2", - "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" - } - }, - "eslint-plugin-mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-5.2.0.tgz", - "integrity": "sha512-4VTX/qIoxUFRnXLNm6bEhEJyfGnGagmQzV4TWXKzkZgIYyP2FSubEdCjEFTyS/dGwSVRWCWGX7jO7BK8R0kppg==", - "dev": true, - "requires": { - "ramda": "^0.25.0" - } - }, - "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", - "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", - "dev": true, - "requires": { - "acorn": "^5.6.0", - "acorn-jsx": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - }, - "just-extend": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", - "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" - } - }, - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lolex": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz", - "integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==", - "dev": true - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", - "dev": true - }, - "nise": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.3.tgz", - "integrity": "sha512-cg44dkGHutAY+VmftgB1gHvLWxFl2vwYdF8WpbceYicQwylESRJiAAKgCRJntdoEbMiUzywkZEUzjoDWH0JwKA==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^2.0.0", - "just-extend": "^1.1.27", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "ramda": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz", - "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", - "dev": true - }, - "regexpp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", - "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sinon": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.5.tgz", - "integrity": "sha512-TcbRoWs1SdY6NOqfj0c9OEQquBoZH+qEf8799m1jjcbfWrrpyCQ3B/BpX7+NKa7Vn33Jl+Z50H4Oys3bzygK2Q==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.0.1", - "@sinonjs/formatio": "^2.0.0", - "@sinonjs/samsam": "^2.0.0", - "diff": "^3.5.0", - "lodash.get": "^4.4.2", - "lolex": "^2.7.1", - "nise": "^1.4.2", - "supports-color": "^5.4.0", - "type-detect": "^4.0.8" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "sinon-chai": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.2.0.tgz", - "integrity": "sha512-Z72B4a0l0IQe5uWi9yzcqX/Ml6K9e1Hp03NmkjJnRG3gDsKTX7KvLFZsVUmCaz0eqeXLLK089mwTsP1P1W+DUQ==", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "svelte-dev-helper": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz", - "integrity": "sha1-fRh9tcbNu9ZNdaMvkbiZi94yc8M=" - }, - "svelte-hmr": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.0.9.tgz", - "integrity": "sha512-I2Zz+hKxusDcnq8896YVOAfxVpAeSWyfCSXO3a9FZKZIK3vjqK4L7ybifyx+N2GPn9yvmRj95Ou5T6CB3NW70g==" - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, - "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - } - } -} diff --git a/package.json b/package.json index 65944951..14a1fb42 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,52 @@ { - "name": "svelte-loader", - "version": "2.13.6", - "author": "Nico Rehwaldt ", - "description": "A webpack loader for svelte", - "license": "MIT", - "scripts": { - "all": "npm run lint && npm run test", - "test": "mocha --harmony --full-trace --check-leaks", - "lint": "eslint index.js lib/*.js test/**/*.js" - }, - "keywords": [ - "svelte", - "sveltejs", - "webpack-loader" - ], - "dependencies": { - "loader-utils": "^1.1.0", - "svelte-dev-helper": "^1.1.9", - "svelte-hmr": "0.0.9" - }, - "devDependencies": { - "chai": "^4.1.2", - "eslint": "^5.4.0", - "eslint-plugin-mocha": "^5.2.0", - "mocha": "^5.2.0", - "sinon": "^6.1.5", - "sinon-chai": "^3.2.0" - }, - "peerDependencies": { - "svelte": ">1.44.0" - }, - "repository": { - "type": "git", - "url": "git@github.com:sveltejs/svelte-loader.git" - }, - "files": [ - "lib", - "index.js" - ] + "name": "svelte-loader-hot", + "version": "0.0.0", + "author": "Nico Rehwaldt ", + "maintainers": [ + { + "name": "rixo", + "email": "rixo@rixo.fr" + } + ], + "description": "Webpack loader for Svelte with HMR support", + "license": "MIT", + "keywords": [ + "svelte", + "sveltejs", + "webpack-loader", + "hmr" + ], + "repository": { + "type": "git", + "url": "git@github.com:rixo/svelte-loader-hot.git" + }, + "homepage": "https://github.com/rixo/svelte-loader-hot", + "bugs": { + "url": "https://github.com/rixo/svelte-loader-hot/issues" + }, + "dependencies": { + "loader-utils": "^1.1.0", + "svelte-dev-helper": "^1.1.9", + "svelte-hmr": "~0.1.0" + }, + "devDependencies": { + "chai": "^4.1.2", + "eslint": "^5.4.0", + "eslint-plugin-mocha": "^5.2.0", + "mocha": "^5.2.0", + "sinon": "^6.1.5", + "sinon-chai": "^3.2.0" + }, + "peerDependencies": { + "svelte": ">1.44.0" + }, + "files": [ + "lib", + "index.js" + ], + "scripts": { + "all": "npm run lint && npm run test", + "test": "mocha --harmony --full-trace --check-leaks", + "lint": "eslint index.js lib/*.js test/**/*.js" + } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..55de2559 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,887 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/highlight@^7.0.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.3.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" + dependencies: + type-detect "4.0.8" + +"@sinonjs/formatio@^3.0.0", "@sinonjs/formatio@^3.2.1": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^3.1.0" + +"@sinonjs/samsam@^2.1.2": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.3.tgz#62cf2a9b624edc795134135fe37fc2ae8ea36be3" + +"@sinonjs/samsam@^3.1.0": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" + dependencies: + "@sinonjs/commons" "^1.3.0" + array-from "^2.1.1" + lodash "^4.17.15" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + +acorn-jsx@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + +acorn@^6.0.7: + version "6.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" + +ajv@^6.10.2, ajv@^6.9.1: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +array-from@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + +chai@^4.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +debug@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + dependencies: + ms "^2.1.1" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +diff@3.5.0, diff@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + dependencies: + esutils "^2.0.2" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eslint-plugin-mocha@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-5.3.0.tgz#cf3eb18ae0e44e433aef7159637095a7cb19b15b" + dependencies: + ramda "^0.26.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + +eslint@^5.4.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + dependencies: + flat-cache "^2.0.1" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2, glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + +import-fresh@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-yaml@^3.13.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + dependencies: + minimist "^1.2.0" + +just-extend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +loader-utils@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + +lolex@^2.7.5: + version "2.7.5" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733" + +lolex@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@0.5.1, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + +nise@^1.4.5: + version "1.5.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" + dependencies: + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + lolex "^4.1.0" + path-to-regexp "^1.7.0" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + dependencies: + callsites "^3.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + dependencies: + isarray "0.0.1" + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +ramda@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + dependencies: + glob "^7.1.3" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +rxjs@^6.4.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" + dependencies: + tslib "^1.9.0" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +semver@^5.5.0, semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +sinon-chai@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.3.0.tgz#8084ff99451064910fbe2c2cb8ab540c00b740ea" + +sinon@^6.1.5: + version "6.3.5" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-6.3.5.tgz#0f6d6a5b4ebaad1f6e8e019395542d1d02c144a0" + dependencies: + "@sinonjs/commons" "^1.0.2" + "@sinonjs/formatio" "^3.0.0" + "@sinonjs/samsam" "^2.1.2" + diff "^3.5.0" + lodash.get "^4.4.2" + lolex "^2.7.5" + nise "^1.4.5" + supports-color "^5.5.0" + type-detect "^4.0.8" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + dependencies: + ansi-regex "^4.1.0" + +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + +supports-color@^5.3.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +svelte-dev-helper@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/svelte-dev-helper/-/svelte-dev-helper-1.1.9.tgz#7d187db5c6cdbbd64d75a32f91b8998bde3273c3" + +svelte-hmr@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.1.0.tgz#8cfed36e596f939cb1e78bf105543f09186bc8c7" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + dependencies: + mkdirp "^0.5.1" From e7156744a76664a48e23764862c28578fda39f31 Mon Sep 17 00:00:00 2001 From: rixo Date: Fri, 15 Nov 2019 17:52:30 +0100 Subject: [PATCH 41/79] make README hot --- README.md | 240 ++++++++++-------------------------------------------- 1 file changed, 44 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index 3812cb46..cc96196c 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,33 @@ -# svelte-loader +# svelte-loader-hot -[![Build Status](https://travis-ci.org/sveltejs/svelte-loader.svg?branch=master)](https://travis-ci.org/sveltejs/svelte-loader) +This is a copy of official [svelte-loader](https://github.com/sveltejs/svelte-loader) with added HMR support. -A [webpack](https://webpack.js.org) loader for [svelte](https://svelte.technology). +HMR is not officially supported by Svelte 3 yet. Progress can be tracked in [this issue](https://github.com/sveltejs/svelte/issues/3632). +Meanwhile, please report your issues regarding HMR (with Webpack) in this project's [issue tracker](https://github.com/rixo/rollup-plugin-svelte-hot/issues). By the way, now is a very good time to share your ideas & suggestions about Svelte HMR, since this is all very much a work in progress. Mere feedback is welcome, too. -## Usage +## Templates -Configure inside your `webpack.config.js`: +To quickly bootstrap a new project, or for example purpose, you can use the following templates. They are copies of the official templates, with the bare minimum added to support HMR with this plugin. -```javascript - ... - resolve: { - // see below for an explanation - alias: { - svelte: path.resolve('node_modules', 'svelte') - }, - extensions: ['.mjs', '.js', '.svelte'], - mainFields: ['svelte', 'browser', 'module', 'main'] - }, - module: { - rules: [ - ... - { - test: /\.(html|svelte)$/, - exclude: /node_modules/, - use: 'svelte-loader' - } - ... - ] - } - ... -``` - -Check out the [example project](https://github.com/sveltejs/template-webpack). - -### resolve.alias +- [svelte-template-hot](https://github.com/rixo/svelte-template-webpack-hot): hot version of the official Svelte template for Webpack. -The [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) option is used to make sure that only one copy of the Svelte runtime is bundled in the app, even if you are `npm link`ing in dependencies with their own copy of the `svelte` package. Having multiple copies of the internal scheduler in an app, besides being inefficient, can also cause various problems. +- [sapper-template-hot](https://github.com/rixo/sapper-template-hot/tree/webpack): Sapper + Webpack template with HMR. -### resolve.mainFields +## Installation -Webpack's [`resolve.mainFields`](https://webpack.js.org/configuration/resolve/#resolve-mainfields) option determines which fields in package.json are used to resolve identifiers. If you're using Svelte components installed from npm, you should specify this option so that your app can use the original component source code, rather than consuming the already-compiled version (which is less efficient). - -### Extracting CSS - -If your Svelte components contain `