From bba5d9e488b70fd97533744b070e88a6c990f85c Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Thu, 18 Jul 2019 01:22:59 -0700 Subject: [PATCH] Major updates for v0.11.0 --- README.md | 21 +- babel.config.js | 9 +- dom-expressions.config.js | 37 +- package-lock.json | 441 ++++++++++-------- package.json | 4 +- runtime.d.ts | 6 - template/runtime.d.ts | 8 +- template/runtime.ejs | 841 ++++++++++++---------------------- test/createComponent.spec.jsx | 27 ++ test/each.spec.jsx | 376 --------------- test/insert.spec.js | 25 +- test/portal.spec.jsx | 52 --- test/provide.spec.jsx | 31 -- test/suspend.spec.jsx | 127 ----- test/switchWhen.spec.jsx | 36 -- test/when.spec.jsx | 90 ---- 16 files changed, 601 insertions(+), 1530 deletions(-) delete mode 100644 test/each.spec.jsx delete mode 100644 test/portal.spec.jsx delete mode 100644 test/provide.spec.jsx delete mode 100644 test/suspend.spec.jsx delete mode 100644 test/switchWhen.spec.jsx delete mode 100644 test/when.spec.jsx diff --git a/README.md b/README.md index 698c6ed2..3270ac5f 100644 --- a/README.md +++ b/README.md @@ -34,32 +34,19 @@ module.exports = { output: 'path-to-output/filename.js', variables: { imports: [ `import S from 's-js'` ], - computed: 'S', - sample: 'S.sample', - root: 'S.root', - cleanup: 'S.cleanup' + declarations: { + wrap: 'S', + } } } ``` These symbols should reference an observable API with the following functionality: -### computed(fn) : void +### wrap(fn) : void This is used to wrap all expressions in computations. Your wrap method is expected to call fn with the previously evaluated value if the arity is 1 to allow for reducing computations. -### root(fn) : any - -This indicates a new disposable context. The fn should be provided a dispose method that can be called to free all computations in the context. - -### sample(fn) : any - -A method that causes dependencies within not to be tracked. - -### cleanup(fn) : void - -This method should register a cleanup method to be called when the context is released. - Then you run the cli command: ```sh > dom-expressions diff --git a/babel.config.js b/babel.config.js index 15941ca4..5d056196 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,8 @@ -// babel.config.js module.exports = { - presets: [['@babel/preset-env', {targets: {node: 'current'}}]], - plugins: [['babel-plugin-jsx-dom-expressions', {moduleName: './runtime.js'}]] + env: { + test: { + presets: [['@babel/preset-env', {targets: {node: 'current'}}]], + plugins: [['babel-plugin-jsx-dom-expressions', {moduleName: './runtime.js', alwaysCreateComponents: true}]] + } + } }; \ No newline at end of file diff --git a/dom-expressions.config.js b/dom-expressions.config.js index 931c3759..a9c12c26 100644 --- a/dom-expressions.config.js +++ b/dom-expressions.config.js @@ -2,40 +2,9 @@ module.exports = { output: 'test/runtime.js', variables: { imports: [ `import { - comp as wrap, sample, root, cleanup, getContextOwner as currentContext, - setContext, makeDataNode, value + comp as wrap, getContextOwner as currentContext, } from '@ryansolid/s-js'` ], - declarations: { - memo: `(fn) => { - const s = value(fn()); - wrap(() => s(fn())); - return s; - }`, - SuspenseContext: `{ - id: 'suspense', initFn: () => { - let counter = 0; - const s = makeDataNode(), - store = { - increment: () => ++counter === 1 && !store.initializing && s.next(), - decrement: () => --counter === 0 && s.next(), - suspended: () => { - s.current(); - return counter; - }, - initializing: true - } - return store; - } - }`, - registerSuspense: `(fn) => { - wrap(() => { - const c = SuspenseContext.initFn(); - setContext(SuspenseContext.id, c); - fn(c); - c.initializing = false; - }); - }` - }, - includeContext: true + includeContext: true, + classComponents: true } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d34d9e4e..4293a24f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dom-expressions", - "version": "0.10.3", + "version": "0.11.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -36,12 +36,12 @@ } }, "@babel/generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.4.tgz", - "integrity": "sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.0.tgz", + "integrity": "sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA==", "dev": true, "requires": { - "@babel/types": "^7.4.4", + "@babel/types": "^7.5.0", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", @@ -241,20 +241,20 @@ } }, "@babel/helpers": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.4.tgz", - "integrity": "sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.4.tgz", + "integrity": "sha512-6LJ6xwUEJP51w0sIgKyfvFMJvIb9mWAfohJp0+m6eHJigkFdcH8duZ1sfhn0ltJRzwUIT/yqqhdSfRpCpL7oow==", "dev": true, "requires": { "@babel/template": "^7.4.4", - "@babel/traverse": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/traverse": "^7.5.0", + "@babel/types": "^7.5.0" } }, "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { "chalk": "^2.0.0", @@ -263,9 +263,9 @@ } }, "@babel/parser": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.5.tgz", - "integrity": "sha512-9mUqkL1FF5T7f0WDFfAoDdiMVPWsdD1gZYzSnaXsxUCUqzuch/8of9G3VUSNiZmMBoRxT3neyVsqeiL/ZPcjew==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { @@ -279,6 +279,16 @@ "@babel/plugin-syntax-async-generators": "^7.2.0" } }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz", + "integrity": "sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", @@ -290,9 +300,9 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz", - "integrity": "sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.4.tgz", + "integrity": "sha512-KCx0z3y7y8ipZUMAEEJOyNi11lMb/FOPUjjB113tfowgw0c16EGYos7worCKBcUAh2oG+OBnoUhsnTSoLpV9uA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", @@ -329,6 +339,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", + "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", @@ -375,9 +394,9 @@ } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz", - "integrity": "sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.5.0.tgz", + "integrity": "sha512-mqvkzwIGkq0bEF1zLRRiTdjfomZJDV33AH3oQzHVGkI2VzEmXLpKKOBvEVaFZBJdN0XTyH38s9j/Kiqr68dggg==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -430,9 +449,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz", - "integrity": "sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.5.0.tgz", + "integrity": "sha512-YbYgbd3TryYYLGyC7ZR+Tq8H/+bCmwoaxHfJHupom5ECstzbRLTch6gOQbhEY9Z4hiCNHEURgq06ykFv9JZ/QQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" @@ -450,9 +469,9 @@ } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", - "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz", + "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0" @@ -506,34 +525,37 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", - "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz", + "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz", - "integrity": "sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz", + "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.4.4", "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" + "@babel/helper-simple-access": "^7.1.0", + "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz", - "integrity": "sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.5.0.tgz", + "integrity": "sha512-Q2m56tyoQWmuNGxEtUyeEkm6qJYFqs4c+XyXH5RAuYxObRNz9Zgj/1g2GMnjYp2EUyEy7YTrxliGCXzecl/vJg==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.4.4", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.0.0", + "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-umd": { @@ -671,39 +693,41 @@ } }, "@babel/preset-env": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.4.5.tgz", - "integrity": "sha512-f2yNVXM+FsR5V8UwcFeIHzHWgnhXg3NpRmy0ADvALpnhB0SLbCvrCRr4BLOUYbQNLS+Z0Yer46x9dJXpXewI7w==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.5.4.tgz", + "integrity": "sha512-hFnFnouyRNiH1rL8YkX1ANCNAUVC8Djwdqfev8i1415tnAG+7hlA5zhZ0Q/3Q5gkop4HioIPbCEWAalqcbxRoQ==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-dynamic-import": "^7.5.0", "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.4.4", + "@babel/plugin-proposal-object-rest-spread": "^7.5.4", "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", "@babel/plugin-syntax-async-generators": "^7.2.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-json-strings": "^7.2.0", "@babel/plugin-syntax-object-rest-spread": "^7.2.0", "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.4.4", + "@babel/plugin-transform-async-to-generator": "^7.5.0", "@babel/plugin-transform-block-scoped-functions": "^7.2.0", "@babel/plugin-transform-block-scoping": "^7.4.4", "@babel/plugin-transform-classes": "^7.4.4", "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.4.4", + "@babel/plugin-transform-destructuring": "^7.5.0", "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", + "@babel/plugin-transform-duplicate-keys": "^7.5.0", "@babel/plugin-transform-exponentiation-operator": "^7.2.0", "@babel/plugin-transform-for-of": "^7.4.4", "@babel/plugin-transform-function-name": "^7.4.4", "@babel/plugin-transform-literals": "^7.2.0", "@babel/plugin-transform-member-expression-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.4.4", - "@babel/plugin-transform-modules-systemjs": "^7.4.4", + "@babel/plugin-transform-modules-amd": "^7.5.0", + "@babel/plugin-transform-modules-commonjs": "^7.5.0", + "@babel/plugin-transform-modules-systemjs": "^7.5.0", "@babel/plugin-transform-modules-umd": "^7.2.0", "@babel/plugin-transform-named-capturing-groups-regex": "^7.4.5", "@babel/plugin-transform-new-target": "^7.4.4", @@ -718,7 +742,7 @@ "@babel/plugin-transform-template-literals": "^7.4.4", "@babel/plugin-transform-typeof-symbol": "^7.2.0", "@babel/plugin-transform-unicode-regex": "^7.4.4", - "@babel/types": "^7.4.4", + "@babel/types": "^7.5.0", "browserslist": "^4.6.0", "core-js-compat": "^3.1.1", "invariant": "^2.2.2", @@ -738,26 +762,26 @@ } }, "@babel/traverse": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.5.tgz", - "integrity": "sha512-Vc+qjynwkjRmIFGxy0KYoPj4FdVDxLej89kMHFsWScq999uX+pwcX4v9mWRjW0KcAYTPAuVQl2LKP1wEVLsp+A==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.0.tgz", + "integrity": "sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.4", + "@babel/generator": "^7.5.0", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.4.5", - "@babel/types": "^7.4.4", + "@babel/parser": "^7.5.0", + "@babel/types": "^7.5.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" } }, "@babel/types": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz", - "integrity": "sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.0.tgz", + "integrity": "sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -1004,9 +1028,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", - "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -1072,23 +1096,23 @@ }, "dependencies": { "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz", + "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", "dev": true } } }, "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "dev": true }, "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -1239,6 +1263,15 @@ "slash": "^2.0.0" } }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, "babel-plugin-istanbul": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", @@ -1260,9 +1293,9 @@ } }, "babel-plugin-jsx-dom-expressions": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.10.0.tgz", - "integrity": "sha512-qbfoW1sYsLlVtp46z4iODDsWESibcPqQzYFH1+Uv5N7IIQt1oQI6q3VDIfzRdz8zB0ozXhDSzq1E3r9kqS+p7w==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.11.0.tgz", + "integrity": "sha512-DTzmWYwirGyR9e0pDBUPzO4mb3NrPjKqNFV6Dxf4Unm7CHm7gjsvlA0OEcWI70382Y58CpAQEyDTT+1eCtIDEw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -1412,20 +1445,20 @@ } }, "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.6.tgz", + "integrity": "sha512-D2Nk3W9JL9Fp/gIcWei8LrERCS+eXu9AM5cfXA8WEZ84lFks+ARnZ0q/R69m2SV3Wjma83QDDPxsNKXUwdIsyA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" + "caniuse-lite": "^1.0.30000984", + "electron-to-chromium": "^1.3.191", + "node-releases": "^1.1.25" } }, "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", + "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", "dev": true, "requires": { "node-int64": "^0.4.0" @@ -1467,9 +1500,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30000971", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000971.tgz", - "integrity": "sha512-TQFYFhRS0O5rdsmSbF1Wn+16latXYsQJat66f7S7lizXW1PVpWJeZw9wqqVLIjuxDRz7s7xRUj13QCfd8hKn6g==", + "version": "1.0.30000984", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000984.tgz", + "integrity": "sha512-n5tKOjMaZ1fksIpQbjERuqCyfgec/m9pferkFQbLmWtqLUdmt12hNhjSwsmPdqeiG2NkITOQhr1VYIwWSAceiA==", "dev": true }, "capture-exit": { @@ -1636,28 +1669,28 @@ "dev": true }, "core-js-compat": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.2.tgz", - "integrity": "sha512-X0Ch5f6itrHxhg5HSJucX6nNLNAGr+jq+biBh6nPGc3YAWz2a8p/ZIZY8cUkDzSRNG54omAuu3hoEF8qZbu/6Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.1.4.tgz", + "integrity": "sha512-Z5zbO9f1d0YrJdoaQhphVAnKPimX92D6z8lCGphH89MNRxlL1prI9ExJPqVwP0/kgkQCv8c4GJGT8X16yUncOg==", "dev": true, "requires": { - "browserslist": "^4.6.0", - "core-js-pure": "3.1.2", - "semver": "^6.0.0" + "browserslist": "^4.6.2", + "core-js-pure": "3.1.4", + "semver": "^6.1.1" }, "dependencies": { "semver": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz", - "integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", "dev": true } } }, "core-js-pure": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.2.tgz", - "integrity": "sha512-5ckIdBF26B3ldK9PM177y2ZcATP2oweam9RskHSoqfZCrJ2As6wVg8zJ1zTriFsZf6clj/N1ThDFRGaomMsh9w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.1.4.tgz", + "integrity": "sha512-uJ4Z7iPNwiu1foygbcZYJsJs1jiXrTTCvxfLDXNhI/I+NHbSIEyr548y4fcsCEyWY0XgfAG/qqaunJ1SThHenA==", "dev": true }, "core-util-is": { @@ -1694,15 +1727,15 @@ } }, "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", "dev": true, "requires": { "cssom": "0.3.x" @@ -1861,9 +1894,9 @@ "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==" }, "electron-to-chromium": { - "version": "1.3.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.137.tgz", - "integrity": "sha512-kGi32g42a8vS/WnYE7ELJyejRT7hbr3UeOOu0WeuYuQ29gCpg9Lrf6RdcTQVXSt/v0bjCfnlb/EWOOsiKpTmkw==", + "version": "1.3.191", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.191.tgz", + "integrity": "sha512-jasjtY5RUy/TOyiUYM2fb4BDaPZfm6CXRFeJDMfFsXYADGxUN49RBqtgB7EL2RmJXeIRUk9lM1U6A5yk2YJMPQ==", "dev": true }, "end-of-stream": { @@ -2267,7 +2300,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2288,12 +2322,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2308,17 +2344,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2435,7 +2474,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2447,6 +2487,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2461,6 +2502,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2468,12 +2510,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2492,6 +2536,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2572,7 +2617,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2584,6 +2630,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2669,7 +2716,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2705,6 +2753,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2724,6 +2773,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2767,12 +2817,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -2833,9 +2885,9 @@ "dev": true }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", + "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", "dev": true }, "growl": { @@ -3001,9 +3053,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "invariant": { @@ -3248,9 +3300,9 @@ }, "dependencies": { "semver": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz", - "integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", "dev": true } } @@ -3444,9 +3496,9 @@ "dev": true }, "jest-haste-map": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.0.tgz", - "integrity": "sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ==", + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.1.tgz", + "integrity": "sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==", "dev": true, "requires": { "@jest/types": "^24.8.0", @@ -3916,9 +3968,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "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.sortby": { @@ -4071,9 +4123,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -4109,9 +4161,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "nan": { @@ -4184,9 +4236,9 @@ } }, "node-releases": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.21.tgz", - "integrity": "sha512-TwnURTCjc8a+ElJUjmDqU6+12jhli1Q61xOQmdZ7ECZVBZuQpN/1UnembiIHDM1wCcfLvh5wrWXUF5H6ufX64Q==", + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.25.tgz", + "integrity": "sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==", "dev": true, "requires": { "semver": "^5.3.0" @@ -4286,6 +4338,18 @@ "isobject": "^3.0.0" } }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, "object.getownpropertydescriptors": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", @@ -4544,9 +4608,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "prompts": { @@ -4560,9 +4624,9 @@ } }, "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==", "dev": true }, "pump": { @@ -4673,9 +4737,9 @@ } }, "regexp-tree": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.10.tgz", - "integrity": "sha512-K1qVSbcedffwuIslMwpe6vGlj+ZXRnGkvjAtFHfDZZZuEdA/h0dxljAPu9vhUo6Rrx2U2AwJ+nSQ6hK+lrP5MQ==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.11.tgz", + "integrity": "sha512-7/l/DgapVVDzZobwMCCgMlqiqyLFJ0cduo/j+3BcDJIB+yJdsYCfKuI3l/04NV+H/rfNRdPIDbXNZHM9XvQatg==", "dev": true }, "regexpu-core": { @@ -4794,9 +4858,9 @@ "dev": true }, "resolve": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -4839,9 +4903,9 @@ } }, "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, "safe-buffer": { @@ -4901,9 +4965,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -4951,9 +5015,9 @@ "dev": true }, "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.2.tgz", + "integrity": "sha512-ZcYcZcT69nSLAR2oLN2JwNmLkJEKGooFMCdvOkFrToUt/WfcRWqhIg4P4KwY4dmLbuyXIx4o4YmPsvMRJYJd/w==", "dev": true }, "slash": { @@ -5154,9 +5218,9 @@ } }, "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "split-string": { @@ -5318,9 +5382,9 @@ } }, "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, "test-exclude": { @@ -5453,9 +5517,9 @@ } }, "uglify-js": { - "version": "3.5.15", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.15.tgz", - "integrity": "sha512-fe7aYFotptIddkwcm6YuA0HmknBZ52ZzOsUxZEdhhkSsz7RfjHDX2QDxwKTiv4JQ5t5NhfmpgAK+J7LiDhKSqg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", "dev": true, "optional": true, "requires": { @@ -5501,38 +5565,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unset-value": { diff --git a/package.json b/package.json index 7733a6f2..237d71e1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dom-expressions", "description": "A Fine-Grained Runtime for Performant DOM Rendering", - "version": "0.10.3", + "version": "0.11.0", "author": "Ryan Carniato", "license": "MIT", "repository": { @@ -24,7 +24,7 @@ "devDependencies": { "@babel/core": "7.4.5", "@babel/preset-env": "^7.4.5", - "babel-plugin-jsx-dom-expressions": "0.10.0", + "babel-plugin-jsx-dom-expressions": "0.11.0", "coveralls": "3.0.4", "jest": "24.8.0", "@ryansolid/s-js": "~0.4.14" diff --git a/runtime.d.ts b/runtime.d.ts index 5cc1f2f8..2d2a8b9b 100644 --- a/runtime.d.ts +++ b/runtime.d.ts @@ -8,10 +8,4 @@ declare module "dom-expressions-runtime" { export function spread(node: HTMLElement, accessor: any): void; export function classList(node: HTMLElement, value: { [k: string]: boolean; }): void; export function currentContext(): any; - export function when(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; - export function switchWhen(parent: Node, conditions: object[], expr: null, options: any, marker?: Node): void; - export function each(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; - export function suspend(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; - export function portal(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; - export function provide(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; } \ No newline at end of file diff --git a/template/runtime.d.ts b/template/runtime.d.ts index 33516c75..8ee2d8c7 100644 --- a/template/runtime.d.ts +++ b/template/runtime.d.ts @@ -6,10 +6,4 @@ export function delegateEvents(eventNames: string[]): void; export function clearDelegatedEvents(): void; export function spread(node: HTMLElement, accessor: any): void; export function classList(node: HTMLElement, value: { [k: string]: boolean; }): void; -export function currentContext(): any; -export function when(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; -export function switchWhen(parent: Node, conditions: object[], expr: null, options: any, marker?: Node): void; -export function each(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; -export function suspend(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; -export function portal(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; -export function provide(parent: Node, accessor: () => any, expr: (...args: any[]) => any, options: any, marker?: Node): void; \ No newline at end of file +export function currentContext(): any; \ No newline at end of file diff --git a/template/runtime.ejs b/template/runtime.ejs index f76f4949..0d6a6376 100644 --- a/template/runtime.ejs +++ b/template/runtime.ejs @@ -9,149 +9,73 @@ import { Attributes } from 'dom-expressions'; })(); %> -const GROUPING = '__rGroup', - FORWARD = 'nextSibling', - BACKWARD = 'previousSibling'; -let groupCounter = 0; +const eventRegistry = new Set(); -export { wrap<%-locals.includeContext && ', currentContext' %> }; +export { wrap<%-locals.includeContext ? ', currentContext' : '' %> }; export function template(html) { const t = document.createElement('template'); t.innerHTML = html; + if (t.innerHTML !== html) throw new Error(`Template html does not match input:\n${t.innerHTML}\n${html}`); return t; } -function normalizeIncomingArray(normalized, array) { - for (let i = 0, len = array.length; i < len; i++) { - let item = array[i]; - if (item instanceof Node) { - if (item.nodeType === 11) { - normalizeIncomingArray(normalized, item.childNodes) - } else normalized.push(item); - } else if (item == null || item === true || item === false) { // matches null, undefined, true or false - // skip - } else if (Array.isArray(item)) { - normalizeIncomingArray(normalized, item); - } else if (typeof item === 'string') { - normalized.push(document.createTextNode(item)); - } else { - normalized.push(document.createTextNode(item.toString())); - } +export function createComponent(Comp, props, dynamicKeys) { + if (dynamicKeys) { + for (let i = 0; i < dynamicKeys.length; i++) dynamicProp(props, dynamicKeys[i]); } - return normalized; +<% if (locals.classComponents) { %> + if (Comp.prototype && Comp.prototype.isClassComponent) { + const comp = new Comp(props); + return comp.render(); + } +<% } %> + return Comp(props); } -function addNode(parent, node, afterNode, counter, register) { - if (Array.isArray(node)) { - if (!node.length) return; - node = normalizeIncomingArray([], node); - let mark = node[0]; - if (node.length !== 1) mark[GROUPING] = node[node.length - 1][GROUPING] = counter; - for (let i = 0; i < node.length; i++) - afterNode ? parent.insertBefore(node[i], afterNode) : parent.appendChild(node[i]); - return mark; - } - let mark, t = typeof node; - if (node == null || t === 'string' || t === 'number') node = document.createTextNode(node || ''); - else if (t === 'function') { - wrap(() => { - const rendered = node(); - if (rendered) { - let next = afterNode; - if (mark) { - next = mark.nextSibling; - parent.removeChild(mark); - } - mark = addNode(parent, rendered, next, counter, register); - } - }); - if (!mark) { - mark = document.createTextNode(''); - afterNode ? parent.insertBefore(mark, afterNode) : parent.appendChild(mark); +export function delegateEvents(eventNames) { + for (let i = 0, l = eventNames.length; i < l; i++) { + const name = eventNames[i]; + if (!eventRegistry.has(name)) { + eventRegistry.add(name); + document.addEventListener(name, eventHandler); } - return mark; - } else if (node.nodeType === 11 && (mark = node.firstChild) && mark !== node.lastChild) { - mark[GROUPING] = node.lastChild[GROUPING] = counter; } - - afterNode ? parent.insertBefore(node, afterNode) : parent.appendChild(node); - register && register(mark || node); - return mark || node; } -function step(node, direction, inner) { - const key = node[GROUPING]; - if (key) { - node = node[direction]; - while(node && node[GROUPING] !== key) node = node[direction]; - } - return inner ? node : node[direction]; +export function clearDelegatedEvents() { + for (let name of eventRegistry.keys()) document.removeEventListener(name, eventHandler); + eventRegistry.clear(); } -function removeNodes(parent, node, end) { - let tmp; - while(node !== end) { - tmp = node.nextSibling; - parent.removeChild(node); - node = tmp; +export function classList(node, value) { + const classKeys = Object.keys(value); + for (let i = 0; i < classKeys.length; i++) { + const key = classKeys[i], + classNames = key.split(/\s+/); + for (let j = 0; j < classNames.length; j++) + node.classList.toggle(classNames[j], value[key]); } } -function clearAll(parent, current, marker, startNode) { - if (current == null || current === '') return ''; - if (marker === undefined) return parent.textContent = ''; - if (Array.isArray(current)) startNode = current[0]; - else if (startNode == null) { - startNode = step((marker && marker.previousSibling) || parent.lastChild, BACKWARD, true); - } - startNode && removeNodes(parent, startNode, marker); - return ''; +export function spread(node, accessor) { + if (typeof accessor === 'function') { + wrap(() => spreadExpression(node, accessor())); + } else spreadExpression(node, accessor); } -function insertExpression(parent, value, current, marker, beforeNode) { - if (value === current) return current; - parent = (marker && marker.parentNode) || (beforeNode && beforeNode.parentNode) || parent; - const t = typeof value; - if (t === 'string' || t === 'number') { - if (t === 'number') value = value.toString(); - if (marker !== undefined) { - const startNode = (marker && marker.previousSibling) || (beforeNode && beforeNode.nextSibling) || parent.lastChild; - if (value === '') clearAll(parent, current, marker) - else if (current !== '' && typeof current === 'string') { - startNode.data = value; - } else { - const node = document.createTextNode(value); - if (current !== '' && current != null) { - parent.replaceChild(node, startNode); - } else parent.insertBefore(node, marker); - } - current = value; - } else { - if (current !== '' && typeof current === 'string') { - current = parent.firstChild.data = value; - } else current = parent.textContent = value; - } - } else if (value == null || t === 'boolean') { - current = clearAll(parent, current, marker); - } else if (value instanceof Node || Array.isArray(value)) { - if (current !== '' && current != null) clearAll(parent, current, marker); - addNode(parent, value, marker || (beforeNode && beforeNode.nextSibling), ++groupCounter); - current = value; +export function insert(parent, accessor, marker, initial) { + if (marker !== undefined && !initial) initial = []; + if (typeof accessor === 'function') + wrap((current = initial) => insertExpression(parent, accessor(), current, marker)); + else if (Array.isArray(accessor) && checkDynamicArray(accessor)) { + wrap((current = initial) => current = insertExpression(parent, accessor, current, marker)); } else { - throw new Error("content must be Node, stringable, or array of same"); + return insertExpression(parent, accessor, initial, marker); } - - return current; -} - -export function insert(parent, accessor, marker, initial) { - if (typeof accessor !== 'function') return insertExpression(parent, accessor, initial, marker); - let beforeNode; - if (marker !== undefined) beforeNode = marker ? marker.previousSibling : parent.lastChild; - wrap((current = initial) => insertExpression(parent, accessor(), current, marker, beforeNode)); } +// Internal Functions function dynamicProp(props, key) { const src = props[key]; Object.defineProperty(props, key, { @@ -160,14 +84,6 @@ function dynamicProp(props, key) { }); } -export function createComponent(Comp, props, dynamicKeys) { - if (dynamicKeys) { - for (let i = 0; i < dynamicKeys.length; i++) dynamicProp(props, dynamicKeys[i]); - } - return Comp(props); -} - -const eventRegistry = new Set(); function lookup(el) { return el && (el.model || lookup(el.host || el.parentNode)); } @@ -200,31 +116,6 @@ function eventHandler(e) { } } -export function delegateEvents(eventNames) { - for (let i = 0, l = eventNames.length; i < l; i++) { - const name = eventNames[i]; - if (!eventRegistry.has(name)) { - eventRegistry.add(name); - document.addEventListener(name, eventHandler); - } - } -} - -export function clearDelegatedEvents() { - for (let name of eventRegistry.keys()) document.removeEventListener(name, eventHandler); - eventRegistry.clear(); -} - -export function classList(node, value) { - const classKeys = Object.keys(value); - for (let i = 0; i < classKeys.length; i++) { - const key = classKeys[i], - classNames = key.split(/\s+/); - for (let j = 0; j < classNames.length; j++) - node.classList.toggle(classNames[j], value[key]); - } -} - function spreadExpression(node, props) { let info; for (const prop in props) { @@ -243,80 +134,281 @@ function spreadExpression(node, props) { } } -export function spread(node, accessor) { - if (typeof accessor === 'function') { - wrap(() => spreadExpression(node, accessor())); - } else spreadExpression(node, accessor); +function normalizeIncomingArray(normalized, array) { + for (let i = 0, len = array.length; i < len; i++) { + let item = array[i], t; + if (item instanceof Node) { + normalized.push(item); + } else if (item == null || item === true || item === false) { // matches null, undefined, true or false + // skip + } else if (Array.isArray(item)) { + normalizeIncomingArray(normalized, item); + } else if ((t = typeof item) === 'string') { + normalized.push(document.createTextNode(item)); + } else if (t === 'function') { + const idx = item(); + normalizeIncomingArray(normalized, Array.isArray(idx) ? idx : [idx]); + } else normalized.push(document.createTextNode(item.toString())); + } + return normalized; } -export function when(parent, accessor, expr, options, marker) { - let beforeNode, current; - const { afterRender, fallback } = options, - condition = memo(accessor); - - if (marker !== undefined) beforeNode = marker ? marker.previousSibling : parent.lastChild; - wrap(() => { - const value = condition(); - sample(() => { - parent = (marker && marker.parentNode) || (beforeNode && beforeNode.parentNode) || parent; - clearAll(parent, current, marker !== undefined ? marker || (current ? step(current, FORWARD) : null) : marker, beforeNode && beforeNode.nextSibling); - current = null; - if (value == null || value === false) { - afterRender && afterRender(current, marker); - if (fallback) { - addNode(parent, fallback(), marker || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => current = node); - } - } else addNode(parent, expr(value), marker || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => (current = node, afterRender && afterRender(node, marker))); - }); - }); +function appendNodes(parent, array, marker) { + for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker); } -export function switchWhen(parent, conditions, _, options, marker) { - let beforeNode, current; - const { fallback } = options, - evalConditions = memo(() => { - for (let i = 0; i < conditions.length; i++) { - if (conditions[i].condition()) return i; - } - return -1; - }); - - if (marker !== undefined) beforeNode = marker ? marker.previousSibling : parent.lastChild; - wrap(() => { - const index = evalConditions(); - sample(() => { - parent = (marker && marker.parentNode) || (beforeNode && beforeNode.parentNode) || parent; - clearAll(parent, current, marker !== undefined ? marker || (current ? step(current, FORWARD) : null) : marker, beforeNode && beforeNode.nextSibling); - current = null; - if (index < 0) { - if (fallback) { - addNode(parent, fallback(), marker || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => current = node); - } - } else { - const afterRender = conditions[index].options && conditions[index].options.afterRender; - addNode(parent, conditions[index].render(), marker || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => current = node, afterRender && afterRender(node, marker)); - } - }); - }); +function clearAll(parent, current, multi, replacement) { + if (!multi) return parent.textContent = ''; + if (current.length) { + let node; + for (let i = 0; i < current.length - 1; i++) parent.removeChild(current[i]); + parent.replaceChild(node = (replacement || document.createTextNode('')), current[current.length - 1]); + return [node]; + } + return current; } -function insertNodes(parent, node, end, target) { - let tmp; - while (node !== end) { - tmp = node.nextSibling; - parent.insertBefore(node, target); - node = tmp; +function checkDynamicArray(array) { + for (let i = 0, len = array.length; i < len; i++) { + const item = array[i]; + if (Array.isArray(item) && checkDynamicArray(item) || typeof item === 'function') return true; } + return false; } -function cleanNode(disposables, node) { - let disposable; - (disposable = disposables.get(node)) && disposable(); - disposables.delete(node); +function insertExpression(parent, value, current, marker) { + if (value === current) return current; + const t = typeof value, + multi = marker !== undefined; + parent = (multi && current[0] && current[0].parentNode) || parent; + + if (t === 'string' || t === 'number') { + if (t === 'number') value = value.toString(); + if (multi) { + let good = null; + const test = current[0]; + if (test && test.nodeType === 3) { + test.data = value; + good = test; + } else { + value = document.createTextNode(value); + if (test) parent.replaceChild(value, test); + else parent.insertBefore(value, marker); + current[0] = good = value; + } + let end = current[current.length - 1], i = 0; + while (good !== end) { + test = end; + end = test.previousSibling; + parent.removeChild(test); + i++; + } + current.length = current.length - i; + } else { + if (current !== '' && typeof current === 'string') { + current = parent.firstChild.data = value; + } else current = parent.textContent = value; + } + } else if (value == null || t === 'boolean') { + current = clearAll(parent, current, multi); + } else if (t === 'function') { + wrap(function() { current = insertExpression(parent, value(), current, marker); }); + } else if (Array.isArray(value)) { + const array = normalizeIncomingArray([], value); + if (array.length === 0) { + current = clearAll(parent, current, multi); + if (multi) return current; + } else { + if (Array.isArray(current)) { + if (current.length === 0) { + appendNodes(parent, array, marker); + } else reconcileArrays(parent, current, array); + } else if (current == null || current === '') { + appendNodes(parent, array); + } else { + reconcileArrays(parent, (multi && current) || [parent.firstChild], array); + } + } + current = array; + } else if (value instanceof Node) { + if (Array.isArray(current)) { + if (current.length === 0) { + parent.insertBefore(value, marker); + } else if (current.length === 1) { + parent.replaceChild(value, current[0]); + } else clearAll(parent, current, multi, value); + } else if (current == null || current === '') { + parent.insertBefore(value, marker); + } else parent.replaceChild(value, parent.firstChild); + current = value; + } else { + throw new Error("content must be Node, stringable, or array of same"); + } + // first time fallback + if (multi && !current.length) parent.insertBefore(current = document.createTextNode(''), marker); + + return current; } // Picked from // https://github.com/adamhaile/surplus/blob/master/src/runtime/content.ts#L368 +var NOMATCH = -1 + +// reconcile the content of parent from ns to us +// see ivi's excellent writeup of diffing arrays in a vdom library: +// https://github.com/ivijs/ivi/blob/2c81ead934b9128e092cc2a5ef2d3cabc73cb5dd/packages/ivi/src/vdom/implementation.ts#L1187 +// this code isn't identical, since we're diffing real dom nodes to nodes-or-strings, +// but the core methodology of trimming ends and reversals, matching nodes, then using +// the longest increasing subsequence to minimize DOM ops is inspired by ivi. +function reconcileArrays(parent, ns, us) { + var ulen = us.length, + // n = nodes, u = updates + // ranges defined by min and max indices + nmin = 0, + nmax = ns.length - 1, + umin = 0, + umax = ulen - 1, + // start nodes of ranges + n = ns[nmin], + u = us[umin], + // end nodes of ranges + nx = ns[nmax], + ux = us[umax], + // node, if any, just after ux, used for doing .insertBefore() to put nodes at end + ul = nx.nextSibling, + i, j, k, + loop = true; + + // scan over common prefixes, suffixes, and simple reversals + fixes: while (loop) { + loop = false; + + // common prefix, u === n + while (u === n) { + umin++; + nmin++; + if (umin > umax || nmin > nmax) break fixes; + u = us[umin]; + n = ns[nmin]; + } + + // common suffix, ux === nx + while (ux === nx) { + ul = nx; + umax--; + nmax--; + if (umin > umax || nmin > nmax) break fixes; + ux = us[umax]; + nx = ns[nmax]; + } + + // reversal u === nx, have to swap node forward + while (u === nx) { + loop = true; + parent.insertBefore(nx, n); + umin++; + nmax--; + if (umin > umax || nmin > nmax) break fixes; + u = us[umin]; + nx = ns[nmax]; + } + + // reversal ux === n, have to swap node back + while (ux === n) { + loop = true; + if (ul === null) parent.appendChild(n); + else parent.insertBefore(n, ul); + ul = n; + umax--; + nmin++; + if (umin > umax || nmin > nmax) break fixes; + ux = us[umax]; + n = ns[nmin]; + } + } + + // if that covered all updates, just need to remove any remaining nodes and we're done + if (umin > umax) { + // remove any remaining nodes + while (nmin <= nmax) { + parent.removeChild(ns[nmax]); + nmax--; + } + return; + } + + // if that covered all current nodes, just need to insert any remaining updates and we're done + if (nmin > nmax) { + // insert any remaining nodes + while (umin <= umax) { + parent.insertBefore(us[umin], ul); + umin++; + } + return; + } + + // Positions for reusing nodes from current DOM state + const P = new Array(umax - umin + 1); + for(let i = umin; i <= umax; i++) P[i] = NOMATCH; + + // Index to resolve position from current to new + const I = new Map(); + for(let i = umin; i <= umax; i++) I.set(us[i], i); + + let reusingNodes = umin + us.length - 1 - umax, + toRemove = [] + + for(let i = nmin; i <= nmax; i++) { + if (I.has(ns[i])) { + P[I.get(ns[i])] = i + reusingNodes++ + } else toRemove.push(i) + } + + // Fast path for full replace + if (reusingNodes === 0) { + if (n !== parent.firstChild || nx !== parent.lastChild) { + for (i = nmin; i <= nmax; i++) parent.removeChild(ns[i]); + while (umin <= umax) { + parent.insertBefore(us[umin], ul); + umin++ + } + return; + } + // no nodes preserved, use fast clear and append + parent.textContent = ''; + while (umin <= umax) { + parent.appendChild(us[umin]); + umin++; + } + return; + } + + // find longest common sequence between ns and us, represented as the indices + // of the longest increasing subsequence in src + var lcs = longestPositiveIncreasingSubsequence(P, umin), + nodes = [], + tmp = ns[nmin], lisIdx = lcs.length - 1, tmpB;; + + // Collect nodes to work with them + for(let i = nmin; i <= nmax; i++) { + nodes[i] = tmp; + tmp = tmp.nextSibling; + } + + for(let i = 0; i < toRemove.length; i++) parent.removeChild(nodes[toRemove[i]]) + + for(let i = umax; i >= umin; i--) { + if(lcs[lisIdx] === i) { + ul = nodes[P[lcs[lisIdx]]] + lisIdx-- + } else { + tmpB = (P[i] === NOMATCH) ? us[i] : nodes[P[i]]; + parent.insertBefore(tmpB, ul) + ul = tmpB + } + } +} // return an array of the indices of ns that comprise the longest increasing subsequence within ns function longestPositiveIncreasingSubsequence(ns, newStart) { @@ -350,14 +442,14 @@ function longestPositiveIncreasingSubsequence(ns, newStart) { function findGreatestIndexLEQ(seq, n) { // invariant: lo is guaranteed to be index of a value <= n, hi to be > // therefore, they actually start out of range: (-1, last + 1) - let lo = -1, + var lo = -1, hi = seq.length; // fast path for simple increasing sequences if (hi > 0 && seq[hi - 1] <= n) return hi - 1; while (hi - lo > 1) { - let mid = Math.floor((lo + hi) / 2); + var mid = Math.floor((lo + hi) / 2); if (seq[mid] > n) { hi = mid; } else { @@ -366,327 +458,4 @@ function findGreatestIndexLEQ(seq, n) { } return lo; -} - -// This is almost straightforward implementation of reconciliation algorithm -// based on ivi documentation: -// https://github.com/localvoid/ivi/blob/2c81ead934b9128e092cc2a5ef2d3cabc73cb5dd/packages/ivi/src/vdom/implementation.ts#L1366 -// With some fast paths from Surplus implementation: -// https://github.com/adamhaile/surplus/blob/master/src/runtime/content.ts#L86 -// And working with data directly from Stage0: -// https://github.com/Freak613/stage0/blob/master/reconcile.js -// This implementation is tailored for fine grained change detection and adds support for fragments -export function each(parent, accessor, expr, options, afterNode) { - let disposables = new Map(), isFallback = false, beforeNode; - const { afterRender, fallback } = options; - if (afterNode !== undefined) beforeNode = afterNode ? afterNode.previousSibling : parent.lastChild; - - function createFn(item, i, afterNode) { - return root(disposer => - addNode(parent, expr(item, i), afterNode, ++groupCounter, node => disposables.set(node, disposer)) - ); - } - - function after() { - afterRender && afterRender( - beforeNode ? beforeNode.nextSibling : parent.firstChild, afterNode - ); - } - - function disposeAll() { - for (let i of disposables.keys()) disposables.get(i)(); - disposables.clear(); - } - - cleanup(disposeAll); - wrap((renderedValues = []) => { - const data = accessor() || []; - return sample(() => { - parent = (afterNode && afterNode.parentNode) || (beforeNode && beforeNode.parentNode) || parent; - const length = data.length; - - // Fast path for clear - if (length === 0 || isFallback) { - if (afterNode !== undefined) { - let node = beforeNode ? beforeNode.nextSibling : parent.firstChild; - removeNodes(parent, node, afterNode ? afterNode : null); - } else parent.textContent = ""; - disposeAll(); - if (length === 0) { - after(); - if (fallback) { - isFallback = true; - root(disposer => - addNode(parent, fallback(), afterNode || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => disposables.set(node, disposer)) - ); - } - return []; - } else isFallback = false; - } - - // Fast path for create - if (renderedValues.length === 0) { - for (let i = 0; i < length; i++) createFn(data[i], i, afterNode); - after(); - return data.slice(0); - } - - let prevStart = 0, - newStart = 0, - loop = true, - prevEnd = renderedValues.length-1, newEnd = length-1, - a, b, - prevStartNode = beforeNode ? beforeNode.nextSibling : parent.firstChild, - newStartNode = prevStartNode, - prevEndNode = afterNode ? afterNode.previousSibling : parent.lastChild, - newAfterNode = afterNode; - - fixes: while(loop) { - loop = false; - let _node; - - // Skip prefix - a = renderedValues[prevStart], b = data[newStart]; - while(a === b) { - prevStart++; - newStart++; - newStartNode = prevStartNode = step(prevStartNode, FORWARD); - if (prevEnd < prevStart || newEnd < newStart) break fixes; - a = renderedValues[prevStart]; - b = data[newStart]; - } - - // Skip suffix - a = renderedValues[prevEnd], b = data[newEnd]; - while(a === b) { - prevEnd--; - newEnd--; - newAfterNode = step(prevEndNode, BACKWARD, true); - prevEndNode = newAfterNode.previousSibling; - if (prevEnd < prevStart || newEnd < newStart) break fixes; - a = renderedValues[prevEnd]; - b = data[newEnd]; - } - - // Fast path to swap backward - a = renderedValues[prevEnd], b = data[newStart]; - while(a === b) { - loop = true; - let mark = step(prevEndNode, BACKWARD, true); - _node = mark.previousSibling; - if (newStartNode !== mark) { - insertNodes(parent, mark, prevEndNode.nextSibling, newStartNode) - prevEndNode = _node; - } - newStart++; - prevEnd--; - if (prevEnd < prevStart || newEnd < newStart) break fixes; - a = renderedValues[prevEnd]; - b = data[newStart]; - } - - // Fast path to swap forward - a = renderedValues[prevStart], b = data[newEnd]; - while(a === b) { - loop = true; - _node = step(prevStartNode, FORWARD); - if (prevStartNode !== newAfterNode) { - let mark = _node.previousSibling; - insertNodes(parent, prevStartNode, _node, newAfterNode); - newAfterNode = mark; - prevStartNode = _node; - } - prevStart++; - newEnd--; - if (prevEnd < prevStart || newEnd < newStart) break fixes; - a = renderedValues[prevStart]; - b = data[newEnd]; - } - } - - // Fast path for shrink - if (newEnd < newStart) { - if (prevStart <= prevEnd) { - let next, node; - while(prevStart <= prevEnd) { - node = step(prevEndNode, BACKWARD, true); - next = node.previousSibling; - removeNodes(parent, node, prevEndNode.nextSibling); - cleanNode(disposables, node); - prevEndNode = next; - prevEnd--; - } - } - after(); - return data.slice(0); - } - - // Fast path for add - if (prevEnd < prevStart) { - if (newStart <= newEnd) { - while(newStart <= newEnd) { - createFn(data[newStart], newStart, newAfterNode); - newStart++; - } - } - after(); - return data.slice(0); - } - - // Positions for reusing nodes from current DOM state - const P = new Array(newEnd + 1 - newStart); - for(let i = newStart; i <= newEnd; i++) P[i] = -1; - - // Index to resolve position from current to new - const I = new Map(); - for(let i = newStart; i <= newEnd; i++) I.set(data[i], i); - - let reusingNodes = 0, toRemove = []; - for(let i = prevStart; i <= prevEnd; i++) { - if (I.has(renderedValues[i])) { - P[I.get(renderedValues[i])] = i; - reusingNodes++; - } else toRemove.push(i); - } - - // Fast path for full replace - if (reusingNodes === 0) { - const doRemove = prevStartNode !== parent.firstChild || prevEndNode !== parent.lastChild; - let node = prevStartNode, mark; - newAfterNode = prevEndNode.nextSibling; - while(node !== newAfterNode) { - mark = step(node, FORWARD); - cleanNode(disposables, node); - doRemove && removeNodes(parent, node, mark); - node = mark; - prevStart++; - } - !doRemove && (parent.textContent = ""); - - for(let i = newStart; i <= newEnd; i++) createFn(data[i], i, newAfterNode); - after(); - return data.slice(0); - } - - // What else? - const longestSeq = longestPositiveIncreasingSubsequence(P, newStart), - nodes= []; - let tmpC = prevStartNode, lisIdx = longestSeq.length - 1, tmpD; - - // Collect nodes to work with them - for(let i = prevStart; i <= prevEnd; i++) { - nodes[i] = tmpC; - tmpC = step(tmpC, FORWARD); - } - - for(let i = 0; i < toRemove.length; i++) { - let index = toRemove[i], - node = nodes[index]; - removeNodes(parent, node, step(node, FORWARD)); - cleanNode(disposables, node); - } - - for(let i = newEnd; i >= newStart; i--) { - if(longestSeq[lisIdx] === i) { - newAfterNode = nodes[P[longestSeq[lisIdx]]]; - lisIdx--; - } else { - if (P[i] === -1) { - tmpD = createFn(data[i], i, newAfterNode); - } else { - tmpD = nodes[P[i]]; - insertNodes(parent, tmpD, step(tmpD, FORWARD), newAfterNode); - } - newAfterNode = tmpD; - } - } - - after(); - return data.slice(0); - }); - }); -} - -export function suspend(parent, accessor, expr, options, marker) { - let beforeNode, disposable, current; - const { fallback } = options, - doc = document.implementation.createHTMLDocument(); - - if (marker !== undefined) beforeNode = marker ? marker.previousSibling : parent.lastChild; - for (let name of eventRegistry.keys()) doc.addEventListener(name, eventHandler); - Object.defineProperty(doc.body, 'host', { get() { return (marker && marker.parentNode) || (beforeNode && beforeNode.parentNode) || parent; } }); - cleanup(function dispose() { disposable && disposable(); }); - - function suspense(options) { - const rendered = sample(expr); - wrap(cached => { - const value = !!options.suspended(); - if (value === cached) return cached; - let node; - parent = (marker && marker.parentNode) || (beforeNode && beforeNode.parentNode) || parent; - if (value) { - if (options.initializing) addNode(doc.body, rendered, undefined, ++groupCounter); - else { - node = marker !== undefined ? step(marker.previousSibling, BACKWARD, true) : parent.firstChild; - while (node && node !== marker) { - const next = node.nextSibling; - doc.body.appendChild(node); - node = next; - } - } - if (fallback) { - sample(() => root(disposer => { - disposable = disposer; - addNode(parent, fallback(), marker || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => current = node); - })); - } - return value; - } - if (options.initializing) addNode(parent, rendered, marker || (beforeNode && beforeNode.nextSibling), ++groupCounter, node => current = node); - else { - if (disposable) { - clearAll(parent, current, marker !== undefined ? marker || (current ? step(current, FORWARD) : null) : marker, beforeNode && beforeNode.nextSibling); - disposable(); - } - while (node = doc.body.firstChild) parent.insertBefore(node, marker); - } - return value; - }); - } -<% if(locals.includeContext) { %> - if (accessor) { - const config = { suspended: accessor, initializing: true } - suspense(config); - config.initializing = false; - } else registerSuspense(suspense); -<% } else { %> - const config = { suspended: accessor, initializing: true } - suspense(config); - config.initializing = false; -<% } %> -} -<% if(locals.includeContext) { %> -export function provide(parent, accessor, expr, options, marker) { - const Context = accessor(), - { value } = options; - wrap(() => { - sample(() => { - setContext(Context.id, Context.initFn ? Context.initFn(value) : value); - addNode(parent, expr(), marker, ++groupCounter); - }); - }); -} -<% } %> -export function portal(parent, accessor, expr, options, marker) { - const { useShadow } = options, - container = document.createElement('div'), - anchor = (accessor && sample(accessor)) || document.body, - renderRoot = (useShadow && container.attachShadow) ? container.attachShadow({ mode: 'open' }) : container; - let beforeNode; - if (marker !== undefined) beforeNode = marker ? marker.previousSibling : parent.lastChild; - Object.defineProperty(container, 'host', { get() { return (marker && marker.parentNode) || (beforeNode && beforeNode.parentNode) || parent; } }); - const nodes = sample(() => expr(container)); - addNode(renderRoot, nodes, undefined, ++groupCounter); - anchor.appendChild(container); - cleanup(() => anchor.removeChild(container)); } \ No newline at end of file diff --git a/test/createComponent.spec.jsx b/test/createComponent.spec.jsx index 7e9dcdd8..cdaff59d 100644 --- a/test/createComponent.spec.jsx +++ b/test/createComponent.spec.jsx @@ -21,4 +21,31 @@ describe('create component with dynamic expressions', () => { expect(span.textContent).toBe('John loves Nissan R35 GTR'); disposer(); }); +}); + +describe('create component with class syntax', () => { + class Component {} + Component.prototype.isClassComponent = true; + + it('should properly create component', () => { + let ref, disposer; + + class MyComponent extends Component { + constructor() { + super(); + this.favoriteCar = S.data('Porsche 911 Turbo'); + } + render() { + return
John loves {( this.favoriteCar() )}
+ } + } + + S.root(dispose => { + disposer = dispose; + + }); + + expect(ref.textContent).toBe('John loves Porsche 911 Turbo'); + disposer(); + }); }); \ No newline at end of file diff --git a/test/each.spec.jsx b/test/each.spec.jsx deleted file mode 100644 index f9e1f9f7..00000000 --- a/test/each.spec.jsx +++ /dev/null @@ -1,376 +0,0 @@ -import * as S from '@ryansolid/s-js'; - -describe('Testing an only child each control flow', () => { - let div, disposer; - const n1 = 'a', - n2 = 'b', - n3 = 'c', - n4 = 'd'; - const list = S.data([n1, n2, n3, n4]); - const Component = () => -
<$ each={list()}>{ item => item }
- - function apply(array) { - list(array); - expect(div.innerHTML).toBe(array.join('')); - list([n1, n2, n3, n4]) - expect(div.innerHTML).toBe('abcd'); - } - - test('Create each control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe('abcd'); - }); - - test('1 missing', () => { - apply([ n2, n3, n4]); - apply([n1, n3, n4]); - apply([n1, n2, n4]); - apply([n1, n2, n3 ]); - }); - - test('2 missing', () => { - apply([ n3, n4]); - apply([ n2, n4]); - apply([ n2, n3 ]); - apply([n1, n4]); - apply([n1, n3 ]); - apply([n1, n2, ]); - }); - - test('3 missing', () => { - apply([n1 ]); - apply([ n2 ]); - apply([ n3 ]); - apply([ n4]); - }); - - test('all missing', () => { - apply([ ]); - }); - - test('swaps', () => { - apply([n2, n1, n3, n4]); - apply([n3, n2, n1, n4]); - apply([n4, n2, n3, n1]); - }); - - test('rotations', () => { - apply([n2, n3, n4, n1]); - apply([n3, n4, n1, n2]); - apply([n4, n1, n2, n3]); - }); - - test('reversal', () => { - apply([n4, n3, n2, n1]); - }); - - test('full replace', () => { - apply(['e', 'f', 'g', 'h']); - }); - - test('swap backward edge', () => { - list(["milk", "bread", "chips", "cookie", "honey"]); - list(["chips", "bread", "cookie", "milk", "honey"]); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an multi child each control flow', () => { - const div = document.createElement('div'); - const n1 = 'a', - n2 = 'b', - n3 = 'c', - n4 = 'd'; - const list = S.data([n1, n2, n3, n4]); - const Component = () => <$ each={list()}>{ item => item } - let disposer; - - function apply(array) { - list(array); - expect(div.innerHTML).toBe(array.join('')); - list([n1, n2, n3, n4]) - expect(div.innerHTML).toBe('abcd'); - } - - test('Create each control flow', () => { - S.root(dispose => { - disposer = dispose; - div.appendChild() - }); - - expect(div.innerHTML).toBe('abcd'); - }); - - test('1 missing', () => { - apply([ n2, n3, n4]); - apply([n1, n3, n4]); - apply([n1, n2, n4]); - apply([n1, n2, n3 ]); - }); - - test('2 missing', () => { - apply([ n3, n4]); - apply([ n2, n4]); - apply([ n2, n3 ]); - apply([n1, n4]); - apply([n1, n3 ]); - apply([n1, n2, ]); - }); - - test('3 missing', () => { - apply([n1 ]); - apply([ n2 ]); - apply([ n3 ]); - apply([ n4]); - }); - - test('all missing', () => { - apply([ ]); - }); - - test('swaps', () => { - apply([n2, n1, n3, n4]); - apply([n3, n2, n1, n4]); - apply([n4, n2, n3, n1]); - }); - - test('rotations', () => { - apply([n2, n3, n4, n1]); - apply([n3, n4, n1, n2]); - apply([n4, n1, n2, n3]); - }); - - test('reversal', () => { - apply([n4, n3, n2, n1]); - }); - - test('full replace', () => { - apply(['e', 'f', 'g', 'h']); - }); - - test('swap backward edge', () => { - list(["milk", "bread", "chips", "cookie", "honey"]); - list(["chips", "bread", "cookie", "milk", "honey"]); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an only child each control flow with fragment children', () => { - let div, disposer; - const n1 = 'a', - n2 = 'b', - n3 = 'c', - n4 = 'd'; - const list = S.data([n1, n2, n3, n4]); - const Component = () => -
<$ each={list()}>{ item => <>{item}{item}}
- - function apply(array) { - list(array); - expect(div.innerHTML).toBe(array.map(p => `${p}${p}`).join('')); - list([n1, n2, n3, n4]) - expect(div.innerHTML).toBe('aabbccdd'); - } - - test('Create each control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe('aabbccdd'); - }); - - test('1 missing', () => { - apply([ n2, n3, n4]); - apply([n1, n3, n4]); - apply([n1, n2, n4]); - apply([n1, n2, n3 ]); - }); - - test('2 missing', () => { - apply([ n3, n4]); - apply([ n2, n4]); - apply([ n2, n3 ]); - apply([n1, n4]); - apply([n1, n3 ]); - apply([n1, n2, ]); - }); - - test('3 missing', () => { - apply([n1 ]); - apply([ n2 ]); - apply([ n3 ]); - apply([ n4]); - }); - - test('all missing', () => { - apply([ ]); - }); - - test('swaps', () => { - apply([n2, n1, n3, n4]); - apply([n3, n2, n1, n4]); - apply([n4, n2, n3, n1]); - }); - - test('rotations', () => { - apply([n2, n3, n4, n1]); - apply([n3, n4, n1, n2]); - apply([n4, n1, n2, n3]); - }); - - test('reversal', () => { - apply([n4, n3, n2, n1]); - }); - - test('full replace', () => { - apply(['e', 'f', 'g', 'h']); - }); - - test('swap backward edge', () => { - list(["milk", "bread", "chips", "cookie", "honey"]); - list(["chips", "bread", "cookie", "milk", "honey"]); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an only child each control flow with array children', () => { - let div, disposer; - const n1 = 'a', - n2 = 'b', - n3 = 'c', - n4 = 'd'; - const list = S.data([n1, n2, n3, n4]); - const Component = () => -
<$ each={list()}>{ item => [item, item] }
- - function apply(array) { - list(array); - expect(div.innerHTML).toBe(array.map(p => `${p}${p}`).join('')); - list([n1, n2, n3, n4]) - expect(div.innerHTML).toBe('aabbccdd'); - } - - test('Create each control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe('aabbccdd'); - }); - - test('1 missing', () => { - apply([ n2, n3, n4]); - apply([n1, n3, n4]); - apply([n1, n2, n4]); - apply([n1, n2, n3 ]); - }); - - test('2 missing', () => { - apply([ n3, n4]); - apply([ n2, n4]); - apply([ n2, n3 ]); - apply([n1, n4]); - apply([n1, n3 ]); - apply([n1, n2, ]); - }); - - test('3 missing', () => { - apply([n1 ]); - apply([ n2 ]); - apply([ n3 ]); - apply([ n4]); - }); - - test('all missing', () => { - apply([ ]); - }); - - test('swaps', () => { - apply([n2, n1, n3, n4]); - apply([n3, n2, n1, n4]); - apply([n4, n2, n3, n1]); - }); - - test('rotations', () => { - apply([n2, n3, n4, n1]); - apply([n3, n4, n1, n2]); - apply([n4, n1, n2, n3]); - }); - - test('reversal', () => { - apply([n4, n3, n2, n1]); - }); - - test('full replace', () => { - apply(['e', 'f', 'g', 'h']); - }); - - test('swap backward edge', () => { - list(["milk", "bread", "chips", "cookie", "honey"]); - list(["chips", "bread", "cookie", "milk", "honey"]); - }); - - test('dispose', () => disposer()); -}); - - -describe('Testing each control flow with fallback', () => { - let div, disposer; - const n1 = 'a', - n2 = 'b', - n3 = 'c', - n4 = 'd'; - const list = S.data([]); - const Component = () => -
<$ each={list()} fallback={'Empty'}>{ item => item}
- - test('Create each control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - expect(div.innerHTML).toBe('Empty'); - list([n1, n2, n3, n4]) - expect(div.innerHTML).toBe('abcd'); - list([]) - expect(div.innerHTML).toBe('Empty'); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing each that maps to undefined', () => { - let div, disposer; - const n1 = 'a', - n2 = 'b', - n3 = 'c', - n4 = 'd'; - const list = S.data([]); - const Component = () => -
<$ each={list()}>{ item => undefined }
- - test('Create each control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - expect(div.innerHTML).toBe(''); - list([n1, n2, n3, n4]) - expect(div.innerHTML).toBe(''); - list([]) - expect(div.innerHTML).toBe(''); - }); - - test('dispose', () => disposer()); -}); \ No newline at end of file diff --git a/test/insert.spec.js b/test/insert.spec.js index 1ace9c00..9ae3ffe8 100644 --- a/test/insert.spec.js +++ b/test/insert.spec.js @@ -188,7 +188,6 @@ describe("r.insert with Markers", () => { //
beforeafter
var container = document.createElement("div"); container.appendChild(document.createTextNode("before")); - container.appendChild(document.createTextNode("")); container.appendChild(document.createTextNode("after")); it("inserts nothing for null", () => { @@ -218,31 +217,31 @@ describe("r.insert with Markers", () => { it("inserts nothing for null in array", () => { const res = insert(["a", null, "b"]); expect(res.innerHTML).toBe("beforeabafter"); - expect(res.childNodes.length).toBe(5); + expect(res.childNodes.length).toBe(4); }); it("inserts nothing for undefined in array", () => { const res = insert(["a", undefined, "b"]); expect(res.innerHTML).toBe("beforeabafter"); - expect(res.childNodes.length).toBe(5); + expect(res.childNodes.length).toBe(4); }); it("inserts nothing for false in array", () => { const res = insert(["a", false, "b"]); expect(res.innerHTML).toBe("beforeabafter"); - expect(res.childNodes.length).toBe(5); + expect(res.childNodes.length).toBe(4); }); it("inserts nothing for true in array", () => { const res = insert(["a", true, "b"]); expect(res.innerHTML).toBe("beforeabafter"); - expect(res.childNodes.length).toBe(5); + expect(res.childNodes.length).toBe(4); }); it("can insert strings", () => { const res = insert("foo"); expect(res.innerHTML).toBe("beforefooafter"); - expect(res.childNodes.length).toBe(4); + expect(res.childNodes.length).toBe(3); }); it("can insert a node", () => { @@ -283,7 +282,7 @@ describe("r.insert with Markers", () => { current; span1.textContent = "1"; div2.textContent = "2"; - span3.textContent = "3" + span3.textContent = "3"; current = r.insert(container, [], marker, current); expect(container.innerHTML).toBe(""); @@ -326,20 +325,20 @@ describe("r.insert with Markers", () => { var parent = document.createElement("div"); parent.innerHTML = ' bar'; var marker = parent.firstChild; - r.insert(parent, 'foo', marker); + let current = r.insert(parent, 'foo', marker); expect(parent.innerHTML).toBe('foo bar'); expect(parent.childNodes.length).toBe(2); - r.insert(parent, '', marker, 'foo'); + r.insert(parent, '', marker, current); expect(parent.innerHTML).toBe(' bar'); }); it("can insert and clear strings with null marker", () => { - var parent = document.createElement("div") - parent.innerHTML = 'hello ' - r.insert(parent, 'foo', null); + var parent = document.createElement("div"); + parent.innerHTML = 'hello '; + let current = r.insert(parent, 'foo', null); expect(parent.innerHTML).toBe('hello foo'); expect(parent.childNodes.length).toBe(2); - r.insert(parent, '', null, 'foo'); + r.insert(parent, '', null, current); expect(parent.innerHTML).toBe('hello '); }); diff --git a/test/portal.spec.jsx b/test/portal.spec.jsx deleted file mode 100644 index 7adab8d8..00000000 --- a/test/portal.spec.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as S from '@ryansolid/s-js'; -import { clearDelegatedEvents } from './runtime'; - -describe('Testing a simple Portal', () => { - let div, disposer; - const testAnchor = document.createElement('div'); - const Component = () => -
<$ portal={testAnchor}>Hi
- - test('Create portal control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - expect(testAnchor.firstChild.innerHTML).toBe('Hi'); - expect(testAnchor.firstChild.host).toBe(div); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing a Portal with Synthetic Events', () => { - let div, disposer, testElem, clicked = false; - const Component = () => -
<$ portal> -
clicked = true } /> -
- - test('Create portal control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - }); - - test('Test portal element clicked', () => { - expect(clicked).toBe(false); - testElem.click(); - expect(clicked).toBe(true); - clicked = false; - clearDelegatedEvents(); - expect(clicked).toBe(false); - testElem.click(); - expect(clicked).toBe(false); - }) - - test('dispose', () => disposer()); -}); \ No newline at end of file diff --git a/test/provide.spec.jsx b/test/provide.spec.jsx deleted file mode 100644 index 8e007433..00000000 --- a/test/provide.spec.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import * as S from '@ryansolid/s-js'; - -describe('Testing providing simple value', () => { - const Context = { - id: Symbol('context') - }, divs = []; - let disposer, i = 0; - const ChildComponent = () => { - const v = S.lookupContext(Context.id); - return
{v}
- }, - Component = () => - <$ provide={Context} value={'hello'}> - <$ provide={Context} value={'hi'}> - - - - - - test('Create provide control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(divs[0].innerHTML).toBe('hi'); - expect(divs[1].innerHTML).toBe('hello'); - }); - - test('dispose', () => disposer()); -}); \ No newline at end of file diff --git a/test/suspend.spec.jsx b/test/suspend.spec.jsx deleted file mode 100644 index d69bf74c..00000000 --- a/test/suspend.spec.jsx +++ /dev/null @@ -1,127 +0,0 @@ -import * as S from '@ryansolid/s-js'; - -describe('Testing an only child suspend control flow', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
<$ suspend={count() < 5}>{() => 'Hi'}
- - test('Create suspend control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - }); - - test('Toggle suspend control flow', () => { - count(7); - expect(div.innerHTML).toBe('Hi'); - count(5); - expect(div.innerHTML).toBe('Hi'); - count(2); - expect(div.innerHTML).toBe(''); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an only child suspend control flow with DOM children', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
<$ suspend={count() < 5}> - {count} - counted -
- - test('Create suspend control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - }); - - test('Toggle suspend control flow', () => { - count(7); - expect(div.firstChild.innerHTML).toBe('7'); - count(5); - expect(div.firstChild.innerHTML).toBe('5'); - count(2); - expect(div.innerHTML).toBe(''); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an only child suspend control flow with DOM children and fallback', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
<$ suspend={count() < 5} - fallback={Too Low} - > - {count} - counted -
- - test('Create suspend control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe('Too Low'); - }); - - test('Toggle suspend control flow', () => { - count(7); - expect(div.firstChild.innerHTML).toBe('7'); - count(5); - expect(div.firstChild.innerHTML).toBe('5'); - count(2); - expect(div.firstChild.innerHTML).toBe('Too Low'); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing a context suspend control flow', () => { - let div, disposer, resolver; - const handlePromise = p => { - const {increment, decrement} = S.lookupContext('suspense'); - const s = S.makeDataNode(); - increment(); - p.then(v => s.next(v)).finally(decrement); - return s.current.bind(s); - }, - LazyComponent = (props) => { - const getComp = handlePromise(new Promise(resolve => resolver = resolve)); - let Comp; - return () => (Comp = getComp()) && (S.sample(() => Comp(props))); - }, - ChildComponent = (props) => props.greeting, - Component = () => -
<$ suspend>
; - - test('Create suspend control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - }); - - test('Toggle suspend control flow', async (done) => { - resolver(ChildComponent); - await Promise.resolve(); - expect(div.innerHTML).toBe('Hi'); - done(); - }); - - test('dispose', () => disposer()); -}); \ No newline at end of file diff --git a/test/switchWhen.spec.jsx b/test/switchWhen.spec.jsx deleted file mode 100644 index b19b2c6e..00000000 --- a/test/switchWhen.spec.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as S from '@ryansolid/s-js'; - -describe('Testing an only child when control flow', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
- <$ switch fallback={'fallback'}> - <$ when={count() && count() < 2}>1 - <$ when={count() && count() < 5}>2 - <$ when={count() && count() < 8}>3 - -
- - test('Create when control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe('fallback'); - }); - - test('Toggle when control flow', () => { - count(1); - expect(div.innerHTML).toBe('1'); - count(4); - expect(div.innerHTML).toBe('2'); - count(7); - expect(div.innerHTML).toBe('3'); - count(9); - expect(div.innerHTML).toBe('fallback'); - }); - - test('dispose', () => disposer()); -}); \ No newline at end of file diff --git a/test/when.spec.jsx b/test/when.spec.jsx deleted file mode 100644 index cdf8fd7b..00000000 --- a/test/when.spec.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import * as S from '@ryansolid/s-js'; - -describe('Testing an only child when control flow', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
<$ when={count() >= 5}>{() => count()}
- - test('Create when control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - }); - - test('Toggle when control flow', () => { - count(7); - expect(div.innerHTML).toBe('7'); - count(5); - expect(div.innerHTML).toBe('7'); - count(2); - expect(div.innerHTML).toBe(''); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an only child when control flow with DOM children', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
<$ when={count() >= 5}> - {count} - counted -
- - test('Create when control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe(''); - }); - - test('Toggle when control flow', () => { - count(7); - expect(div.firstChild.innerHTML).toBe('7'); - count(5); - expect(div.firstChild.innerHTML).toBe('5'); - count(2); - expect(div.innerHTML).toBe(''); - }); - - test('dispose', () => disposer()); -}); - -describe('Testing an only child when control flow with DOM children and fallback', () => { - let div, disposer; - const count = S.data(0); - const Component = () => -
<$ when={count() >= 5} - fallback={Too Low} - > - {count} - counted -
- - test('Create when control flow', () => { - S.root(dispose => { - disposer = dispose; - - }); - - expect(div.innerHTML).toBe('Too Low'); - }); - - test('Toggle when control flow', () => { - count(7); - expect(div.firstChild.innerHTML).toBe('7'); - count(5); - expect(div.firstChild.innerHTML).toBe('5'); - count(2); - expect(div.firstChild.innerHTML).toBe('Too Low'); - }); - - test('dispose', () => disposer()); -}); \ No newline at end of file