├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── dom-expressions.config.js
├── h
└── package.json
├── html
└── package.json
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── h.ts
├── html.ts
├── index.ts
├── runtime.d.ts
└── runtime.js
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | dist/
4 | types/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | types/tsconfig.tsbuildinfo
3 | *.config.js
4 | tsconfig.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ryan Carniato
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # S JSX
2 |
3 | This library is a demonstration of the raw performance of S.js when used with the DOM Expressions runtime. This is an experimental approach used mostly for benchmarking and I'd recommend you checkout the official renderer for S.js, [Surplus](https://github.com/adamhaile/surplus) or [Solid](https://github.com/ryansolid/solid) which are better tested and provide a more comprehensive set of features.
4 |
5 | It accomplishes this with using [Babel Plugin JSX DOM Expressions](https://github.com/ryansolid/babel-plugin-jsx-dom-expressions). It compiles JSX to DOM statements and wraps expressions in functions that can be called by the library of choice. In this case autorun wrap these expressions ensuring the view stays up to date. Unlike Virtual DOM only the changed nodes are affected and the whole tree is not re-rendered over and over.
6 |
7 | To use simply wrap your code in render:
8 |
9 | ```js
10 | import { render } from 's-jsx';
11 |
12 | render(App, document.body);
13 | ```
14 |
15 | And include 'babel-plugin-jsx-dom-expressions' in your babelrc, webpack babel loader, or rollup babel plugin.
16 |
17 | ```js
18 | "plugins": [["jsx-dom-expressions", {moduleName: 's-jsx'}]]
19 | ```
20 |
21 | # Installation
22 |
23 | ```sh
24 | > npm install s-jsx babel-plugin-jsx-dom-expressions
25 | ```
26 |
27 | Alternatively this library supports Tagged Template Literals or HyperScript for non-precompiled environments by installing the companion library and including variants:
28 | ```js
29 | import { html } from 's-jsx/html'; // or
30 | import { h } from 's-jsx/h';
31 | ```
32 | There is a small performance overhead of using these runtimes but the performance is still very impressive. Further documentation available at: [Lit DOM Expressions](https://github.com/ryansolid/lit-dom-expressions) and [Hyper DOM Expressions](https://github.com/ryansolid/hyper-dom-expressions).
33 |
--------------------------------------------------------------------------------
/dom-expressions.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | output: 'src/runtime.js',
3 | includeTypes: true,
4 | variables: {
5 | imports: [ `import wrap, {value, sample as ignore} from 's-js'` ],
6 | declarations: {
7 | wrapCondition: `(fn) => {
8 | const s = value(ignore(fn));
9 | wrap(() => s(fn()))
10 | return s;
11 | }`
12 | },
13 | includeContext: false,
14 | wrapConditionals: true
15 | }
16 | }
--------------------------------------------------------------------------------
/h/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "s-jsx/h",
3 | "main": "../lib/h.js",
4 | "module": "../dist/h.js",
5 | "types": "../types/h.d.ts",
6 | "sideEffects": false
7 | }
--------------------------------------------------------------------------------
/html/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "s-jsx/html",
3 | "main": "../lib/html.js",
4 | "module": "../dist/html.js",
5 | "types": "../types/html.d.ts",
6 | "sideEffects": false
7 | }
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "s-jsx",
3 | "version": "0.2.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/code-frame": {
8 | "version": "7.8.0",
9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.0.tgz",
10 | "integrity": "sha512-AN2IR/wCUYsM+PdErq6Bp3RFTXl8W0p9Nmymm7zkpsCmh+r/YYcckaCGpU8Q/mEKmST19kkGRaG42A/jxOWwBA==",
11 | "dev": true,
12 | "requires": {
13 | "@babel/highlight": "^7.8.0"
14 | }
15 | },
16 | "@babel/core": {
17 | "version": "7.8.0",
18 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.0.tgz",
19 | "integrity": "sha512-3rqPi/bv/Xfu2YzHvBz4XqMI1fKVwnhntPA1/fjoECrSjrhbOCxlTrbVu5gUtr8zkxW+RpkDOa/HCW93gzS2Dw==",
20 | "dev": true,
21 | "requires": {
22 | "@babel/code-frame": "^7.8.0",
23 | "@babel/generator": "^7.8.0",
24 | "@babel/helpers": "^7.8.0",
25 | "@babel/parser": "^7.8.0",
26 | "@babel/template": "^7.8.0",
27 | "@babel/traverse": "^7.8.0",
28 | "@babel/types": "^7.8.0",
29 | "convert-source-map": "^1.7.0",
30 | "debug": "^4.1.0",
31 | "gensync": "^1.0.0-beta.1",
32 | "json5": "^2.1.0",
33 | "lodash": "^4.17.13",
34 | "resolve": "^1.3.2",
35 | "semver": "^5.4.1",
36 | "source-map": "^0.5.0"
37 | }
38 | },
39 | "@babel/generator": {
40 | "version": "7.8.0",
41 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.0.tgz",
42 | "integrity": "sha512-2Lp2e02CV2C7j/H4n4D9YvsvdhPVVg9GDIamr6Tu4tU35mL3mzOrzl1lZ8ZJtysfZXh+y+AGORc2rPS7yHxBUg==",
43 | "dev": true,
44 | "requires": {
45 | "@babel/types": "^7.8.0",
46 | "jsesc": "^2.5.1",
47 | "lodash": "^4.17.13",
48 | "source-map": "^0.5.0"
49 | }
50 | },
51 | "@babel/helper-create-class-features-plugin": {
52 | "version": "7.8.0",
53 | "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.0.tgz",
54 | "integrity": "sha512-ctCvqYBTlwEl2uF4hCxE0cd/sSw71Zfag0jKa39y4HDLh0BQ4PVBX1384Ye8GqrEZ69xgLp9fwPbv3GgIDDF2Q==",
55 | "dev": true,
56 | "requires": {
57 | "@babel/helper-function-name": "^7.8.0",
58 | "@babel/helper-member-expression-to-functions": "^7.8.0",
59 | "@babel/helper-optimise-call-expression": "^7.8.0",
60 | "@babel/helper-plugin-utils": "^7.8.0",
61 | "@babel/helper-replace-supers": "^7.8.0",
62 | "@babel/helper-split-export-declaration": "^7.8.0"
63 | }
64 | },
65 | "@babel/helper-function-name": {
66 | "version": "7.8.0",
67 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.0.tgz",
68 | "integrity": "sha512-x9psucuU0Xalw+0Vpr2FYJMLB7/KnPSLZhlkUyOGbYAWRDfmtZBrguYpJYiaNCRV7vGkYjO/gF6/J6yMvdWTDw==",
69 | "dev": true,
70 | "requires": {
71 | "@babel/helper-get-function-arity": "^7.8.0",
72 | "@babel/template": "^7.8.0",
73 | "@babel/types": "^7.8.0"
74 | }
75 | },
76 | "@babel/helper-get-function-arity": {
77 | "version": "7.8.0",
78 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.0.tgz",
79 | "integrity": "sha512-eUP5grliToMapQiTaYS2AAO/WwaCG7cuJztR1v/a1aPzUzUeGt+AaI9OvLATc/AfFkF8SLJ10d5ugGt/AQ9d6w==",
80 | "dev": true,
81 | "requires": {
82 | "@babel/types": "^7.8.0"
83 | }
84 | },
85 | "@babel/helper-member-expression-to-functions": {
86 | "version": "7.8.0",
87 | "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.0.tgz",
88 | "integrity": "sha512-0m1QabGrdXuoxX/g+KOAGndoHwskC70WweqHRQyCsaO67KOEELYh4ECcGw6ZGKjDKa5Y7SW4Qbhw6ly4Fah/jQ==",
89 | "dev": true,
90 | "requires": {
91 | "@babel/types": "^7.8.0"
92 | }
93 | },
94 | "@babel/helper-module-imports": {
95 | "version": "7.8.0",
96 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.0.tgz",
97 | "integrity": "sha512-ylY9J6ZxEcjmJaJ4P6aVs/fZdrZVctCGnxxfYXwCnSMapqd544zT8lWK2qI/vBPjE5gS0o2jILnH+AkpsPauEQ==",
98 | "dev": true,
99 | "requires": {
100 | "@babel/types": "^7.8.0"
101 | }
102 | },
103 | "@babel/helper-optimise-call-expression": {
104 | "version": "7.8.0",
105 | "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.0.tgz",
106 | "integrity": "sha512-aiJt1m+K57y0n10fTw+QXcCXzmpkG+o+NoQmAZqlZPstkTE0PZT+Z27QSd/6Gf00nuXJQO4NiJ0/YagSW5kC2A==",
107 | "dev": true,
108 | "requires": {
109 | "@babel/types": "^7.8.0"
110 | }
111 | },
112 | "@babel/helper-plugin-utils": {
113 | "version": "7.8.0",
114 | "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz",
115 | "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==",
116 | "dev": true
117 | },
118 | "@babel/helper-replace-supers": {
119 | "version": "7.8.0",
120 | "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.0.tgz",
121 | "integrity": "sha512-R2CyorW4tcO3YzdkClLpt6MS84G+tPkOi0MmiCn1bvYVnmDpdl9R15XOi3NQW2mhOAEeBnuQ4g1Bh7pT2sX8fg==",
122 | "dev": true,
123 | "requires": {
124 | "@babel/helper-member-expression-to-functions": "^7.8.0",
125 | "@babel/helper-optimise-call-expression": "^7.8.0",
126 | "@babel/traverse": "^7.8.0",
127 | "@babel/types": "^7.8.0"
128 | }
129 | },
130 | "@babel/helper-split-export-declaration": {
131 | "version": "7.8.0",
132 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.0.tgz",
133 | "integrity": "sha512-YhYFhH4T6DlbT6CPtVgLfC1Jp2gbCawU/ml7WJvUpBg01bCrXSzTYMZZXbbIGjq/kHmK8YUATxTppcRGzj31pA==",
134 | "dev": true,
135 | "requires": {
136 | "@babel/types": "^7.8.0"
137 | }
138 | },
139 | "@babel/helpers": {
140 | "version": "7.8.0",
141 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.0.tgz",
142 | "integrity": "sha512-srWKpjAFbiut5JoCReZJ098hLqoZ9HufOnKZPggc7j74XaPuQ+9b3RYPV1M/HfjL63lCNd8uI1O487qIWxAFNA==",
143 | "dev": true,
144 | "requires": {
145 | "@babel/template": "^7.8.0",
146 | "@babel/traverse": "^7.8.0",
147 | "@babel/types": "^7.8.0"
148 | }
149 | },
150 | "@babel/highlight": {
151 | "version": "7.8.0",
152 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.0.tgz",
153 | "integrity": "sha512-OsdTJbHlPtIk2mmtwXItYrdmalJ8T0zpVzNAbKSkHshuywj7zb29Y09McV/jQsQunc/nEyHiPV2oy9llYMLqxw==",
154 | "dev": true,
155 | "requires": {
156 | "chalk": "^2.0.0",
157 | "esutils": "^2.0.2",
158 | "js-tokens": "^4.0.0"
159 | }
160 | },
161 | "@babel/parser": {
162 | "version": "7.8.0",
163 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.0.tgz",
164 | "integrity": "sha512-VVtsnUYbd1+2A2vOVhm4P2qNXQE8L/W859GpUHfUcdhX8d3pEKThZuIr6fztocWx9HbK+00/CR0tXnhAggJ4CA==",
165 | "dev": true
166 | },
167 | "@babel/plugin-syntax-typescript": {
168 | "version": "7.8.0",
169 | "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.0.tgz",
170 | "integrity": "sha512-LrvVrabb993Ve5fzXsyEkfYCuhpXBwsUFjlvgD8UmXXg3r/8/ceooSdRvjdmtPXXz+lHaqZHZooV1jMWer2qkA==",
171 | "dev": true,
172 | "requires": {
173 | "@babel/helper-plugin-utils": "^7.8.0"
174 | }
175 | },
176 | "@babel/plugin-transform-typescript": {
177 | "version": "7.8.0",
178 | "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.0.tgz",
179 | "integrity": "sha512-RhMZnNWcyvX+rM6mk888MaeoVl5pGfmYP3as709n4+0d15SRedz4r+LPRg2a9s4z+t+DM+gy8uz/rmM3Cb8JBw==",
180 | "dev": true,
181 | "requires": {
182 | "@babel/helper-create-class-features-plugin": "^7.8.0",
183 | "@babel/helper-plugin-utils": "^7.8.0",
184 | "@babel/plugin-syntax-typescript": "^7.8.0"
185 | }
186 | },
187 | "@babel/preset-typescript": {
188 | "version": "7.8.0",
189 | "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.8.0.tgz",
190 | "integrity": "sha512-mvu4OmrLK6qRPiXlOkE4yOeOszHzk9itwe6aiMN0RL9Bc5uAwAotVTy4kKl17evLMd1WsvWT1O3mZltynuqxXg==",
191 | "dev": true,
192 | "requires": {
193 | "@babel/helper-plugin-utils": "^7.8.0",
194 | "@babel/plugin-transform-typescript": "^7.8.0"
195 | }
196 | },
197 | "@babel/template": {
198 | "version": "7.8.0",
199 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.0.tgz",
200 | "integrity": "sha512-0NNMDsY2t3ltAVVK1WHNiaePo3tXPUeJpCX4I3xSKFoEl852wJHG8mrgHVADf8Lz1y+8al9cF7cSSfzSnFSYiw==",
201 | "dev": true,
202 | "requires": {
203 | "@babel/code-frame": "^7.8.0",
204 | "@babel/parser": "^7.8.0",
205 | "@babel/types": "^7.8.0"
206 | }
207 | },
208 | "@babel/traverse": {
209 | "version": "7.8.0",
210 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.0.tgz",
211 | "integrity": "sha512-d/6sPXFLGlJHZO/zWDtgFaKyalCOHLedzxpVJn6el1cw+f2TZa7xZEszeXdOw6EUemqRFBAn106BWBvtSck9Qw==",
212 | "dev": true,
213 | "requires": {
214 | "@babel/code-frame": "^7.8.0",
215 | "@babel/generator": "^7.8.0",
216 | "@babel/helper-function-name": "^7.8.0",
217 | "@babel/helper-split-export-declaration": "^7.8.0",
218 | "@babel/parser": "^7.8.0",
219 | "@babel/types": "^7.8.0",
220 | "debug": "^4.1.0",
221 | "globals": "^11.1.0",
222 | "lodash": "^4.17.13"
223 | }
224 | },
225 | "@babel/types": {
226 | "version": "7.8.0",
227 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.0.tgz",
228 | "integrity": "sha512-1RF84ehyx9HH09dMMwGWl3UTWlVoCPtqqJPjGuC4JzMe1ZIVDJ2DT8mv3cPv/A7veLD6sgR7vi95lJqm+ZayIg==",
229 | "dev": true,
230 | "requires": {
231 | "esutils": "^2.0.2",
232 | "lodash": "^4.17.13",
233 | "to-fast-properties": "^2.0.0"
234 | }
235 | },
236 | "@types/estree": {
237 | "version": "0.0.42",
238 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.42.tgz",
239 | "integrity": "sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ==",
240 | "dev": true
241 | },
242 | "@types/node": {
243 | "version": "13.1.6",
244 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.6.tgz",
245 | "integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==",
246 | "dev": true
247 | },
248 | "@types/resolve": {
249 | "version": "0.0.8",
250 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
251 | "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==",
252 | "dev": true,
253 | "requires": {
254 | "@types/node": "*"
255 | }
256 | },
257 | "acorn": {
258 | "version": "7.1.0",
259 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
260 | "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
261 | "dev": true
262 | },
263 | "ansi-styles": {
264 | "version": "3.2.1",
265 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
266 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
267 | "dev": true,
268 | "requires": {
269 | "color-convert": "^1.9.0"
270 | }
271 | },
272 | "builtin-modules": {
273 | "version": "3.1.0",
274 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
275 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
276 | "dev": true
277 | },
278 | "chalk": {
279 | "version": "2.4.2",
280 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
281 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
282 | "dev": true,
283 | "requires": {
284 | "ansi-styles": "^3.2.1",
285 | "escape-string-regexp": "^1.0.5",
286 | "supports-color": "^5.3.0"
287 | }
288 | },
289 | "color-convert": {
290 | "version": "1.9.3",
291 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
292 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
293 | "dev": true,
294 | "requires": {
295 | "color-name": "1.1.3"
296 | }
297 | },
298 | "color-name": {
299 | "version": "1.1.3",
300 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
301 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
302 | "dev": true
303 | },
304 | "convert-source-map": {
305 | "version": "1.7.0",
306 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
307 | "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
308 | "dev": true,
309 | "requires": {
310 | "safe-buffer": "~5.1.1"
311 | }
312 | },
313 | "debug": {
314 | "version": "4.1.1",
315 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
316 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
317 | "dev": true,
318 | "requires": {
319 | "ms": "^2.1.1"
320 | }
321 | },
322 | "dom-expressions": {
323 | "version": "0.15.0",
324 | "resolved": "https://registry.npmjs.org/dom-expressions/-/dom-expressions-0.15.0.tgz",
325 | "integrity": "sha512-cLdwLkjjsWsRB2Qh/OvKWFPKDnUiVP78QzD0DmY3+lvC0zGXhKNe2/cfCANzW3B5a2Xz9wK2r6ix29gXQOla7w==",
326 | "dev": true,
327 | "requires": {
328 | "ejs": "^3.0.1"
329 | }
330 | },
331 | "ejs": {
332 | "version": "3.0.1",
333 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.0.1.tgz",
334 | "integrity": "sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw==",
335 | "dev": true
336 | },
337 | "escape-string-regexp": {
338 | "version": "1.0.5",
339 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
340 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
341 | "dev": true
342 | },
343 | "estree-walker": {
344 | "version": "0.6.1",
345 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
346 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
347 | "dev": true
348 | },
349 | "esutils": {
350 | "version": "2.0.3",
351 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
352 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
353 | "dev": true
354 | },
355 | "gensync": {
356 | "version": "1.0.0-beta.1",
357 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
358 | "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==",
359 | "dev": true
360 | },
361 | "globals": {
362 | "version": "11.12.0",
363 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
364 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
365 | "dev": true
366 | },
367 | "has-flag": {
368 | "version": "3.0.0",
369 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
370 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
371 | "dev": true
372 | },
373 | "hyper-dom-expressions": {
374 | "version": "0.15.0",
375 | "resolved": "https://registry.npmjs.org/hyper-dom-expressions/-/hyper-dom-expressions-0.15.0.tgz",
376 | "integrity": "sha512-Ju5uNfMgx6yQLMaXcJl8qIcOaxbCFC9VZ7Y9E0D99/52QZPe+cPjthKmus9hocUp/2FYSzngE+xcw7hWgCgF/g==",
377 | "dev": true
378 | },
379 | "is-module": {
380 | "version": "1.0.0",
381 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
382 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
383 | "dev": true
384 | },
385 | "js-tokens": {
386 | "version": "4.0.0",
387 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
388 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
389 | "dev": true
390 | },
391 | "jsesc": {
392 | "version": "2.5.2",
393 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
394 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
395 | "dev": true
396 | },
397 | "json5": {
398 | "version": "2.1.1",
399 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz",
400 | "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==",
401 | "dev": true,
402 | "requires": {
403 | "minimist": "^1.2.0"
404 | }
405 | },
406 | "lit-dom-expressions": {
407 | "version": "0.15.0",
408 | "resolved": "https://registry.npmjs.org/lit-dom-expressions/-/lit-dom-expressions-0.15.0.tgz",
409 | "integrity": "sha512-vbu8bfr0VIkDcSUzx5I0OTsuXRDeeIpWpVGQzN2zZsbh+w+hmgyeQfLzABLICl5+1u1+mtigwPOOEMsG+EcOBQ==",
410 | "dev": true
411 | },
412 | "lodash": {
413 | "version": "4.17.15",
414 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
415 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
416 | "dev": true
417 | },
418 | "minimist": {
419 | "version": "1.2.0",
420 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
421 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
422 | "dev": true
423 | },
424 | "ms": {
425 | "version": "2.1.2",
426 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
427 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
428 | "dev": true
429 | },
430 | "ncp": {
431 | "version": "2.0.0",
432 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
433 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
434 | "dev": true
435 | },
436 | "path-parse": {
437 | "version": "1.0.6",
438 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
439 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
440 | "dev": true
441 | },
442 | "resolve": {
443 | "version": "1.14.2",
444 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz",
445 | "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==",
446 | "dev": true,
447 | "requires": {
448 | "path-parse": "^1.0.6"
449 | }
450 | },
451 | "rollup": {
452 | "version": "1.29.0",
453 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.29.0.tgz",
454 | "integrity": "sha512-V63Iz0dSdI5qPPN5HmCN6OBRzBFhMqNWcvwgq863JtSCTU6Vdvqq6S2fYle/dSCyoPrBkIP3EIr1RVs3HTRqqg==",
455 | "dev": true,
456 | "requires": {
457 | "@types/estree": "*",
458 | "@types/node": "*",
459 | "acorn": "^7.1.0"
460 | }
461 | },
462 | "rollup-plugin-babel": {
463 | "version": "4.3.3",
464 | "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-4.3.3.tgz",
465 | "integrity": "sha512-tKzWOCmIJD/6aKNz0H1GMM+lW1q9KyFubbWzGiOG540zxPPifnEAHTZwjo0g991Y+DyOZcLqBgqOdqazYE5fkw==",
466 | "dev": true,
467 | "requires": {
468 | "@babel/helper-module-imports": "^7.0.0",
469 | "rollup-pluginutils": "^2.8.1"
470 | }
471 | },
472 | "rollup-plugin-node-resolve": {
473 | "version": "5.2.0",
474 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz",
475 | "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==",
476 | "dev": true,
477 | "requires": {
478 | "@types/resolve": "0.0.8",
479 | "builtin-modules": "^3.1.0",
480 | "is-module": "^1.0.0",
481 | "resolve": "^1.11.1",
482 | "rollup-pluginutils": "^2.8.1"
483 | }
484 | },
485 | "rollup-pluginutils": {
486 | "version": "2.8.2",
487 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
488 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
489 | "dev": true,
490 | "requires": {
491 | "estree-walker": "^0.6.1"
492 | }
493 | },
494 | "s-js": {
495 | "version": "0.4.9",
496 | "resolved": "https://registry.npmjs.org/s-js/-/s-js-0.4.9.tgz",
497 | "integrity": "sha512-RtpOm+cM6O0sHg6IA70wH+UC3FZcND+rccBZpBAHzlUgNO2Bm5BN+FnM8+OBxzXdwpKWFwX11JGF0MFRkhSoIQ=="
498 | },
499 | "safe-buffer": {
500 | "version": "5.1.2",
501 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
502 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
503 | "dev": true
504 | },
505 | "semver": {
506 | "version": "5.7.1",
507 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
508 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
509 | "dev": true
510 | },
511 | "source-map": {
512 | "version": "0.5.7",
513 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
514 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
515 | "dev": true
516 | },
517 | "supports-color": {
518 | "version": "5.5.0",
519 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
520 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
521 | "dev": true,
522 | "requires": {
523 | "has-flag": "^3.0.0"
524 | }
525 | },
526 | "to-fast-properties": {
527 | "version": "2.0.0",
528 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
529 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
530 | "dev": true
531 | },
532 | "typescript": {
533 | "version": "3.7.4",
534 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
535 | "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
536 | "dev": true
537 | }
538 | }
539 | }
540 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "s-jsx",
3 | "description": "An alternative JSX renderer for S.js",
4 | "version": "0.2.0",
5 | "author": "Ryan Carniato",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/ryansolid/s-jsx"
10 | },
11 | "module": "dist/index.js",
12 | "main": "lib/index.js",
13 | "types": "types/index.d.ts",
14 | "scripts": {
15 | "build": "dom-expressions && rollup -c && tsc",
16 | "prepublishOnly": "npm run build"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "7.8.0",
20 | "@babel/preset-typescript": "7.8.0",
21 | "dom-expressions": "0.15.0",
22 | "hyper-dom-expressions": "~0.15.0",
23 | "lit-dom-expressions": "~0.15.0",
24 | "ncp": "2.0.0",
25 | "rollup": "^1.29.0",
26 | "rollup-plugin-babel": "4.3.3",
27 | "rollup-plugin-node-resolve": "5.2.0",
28 | "typescript": "3.7.4"
29 | },
30 | "dependencies": {
31 | "s-js": "~0.4.9"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import nodeResolve from 'rollup-plugin-node-resolve';
3 |
4 | const plugins = [
5 | nodeResolve({
6 | extensions: ['.js', '.ts']
7 | }),
8 | babel({
9 | extensions: ['.js', '.ts'],
10 | presets: ["@babel/preset-typescript"],
11 | exclude: 'node_modules/**',
12 | retainLines: true
13 | })
14 | ];
15 |
16 | export default [{
17 | input: 'src/index.ts',
18 | output: [{
19 | format: 'cjs',
20 | file: 'lib/index.js'
21 | }, {
22 | format: 'es',
23 | file: 'dist/index.js'
24 | }],
25 | external: ['s-js'],
26 | plugins
27 | }, {
28 | input: 'src/html.ts',
29 | output: [{
30 | format: 'cjs',
31 | file: 'lib/html.js'
32 | }, {
33 | format: 'es',
34 | file: 'dist/html.js'
35 | }],
36 | external: ['./index', 'lit-dom-expressions'],
37 | plugins
38 | }, {
39 | input: 'src/h.ts',
40 | output: [{
41 | format: 'cjs',
42 | file: 'lib/h.js'
43 | }, {
44 | format: 'es',
45 | file: 'dist/h.js'
46 | }],
47 | external: ['./index', 'hyper-dom-expressions'],
48 | plugins
49 | }];
--------------------------------------------------------------------------------
/src/h.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { createHyperScript } from 'hyper-dom-expressions';
3 | import * as r from './index';
4 |
5 | export const h = createHyperScript(r);
--------------------------------------------------------------------------------
/src/html.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { createHTML } from 'lit-dom-expressions';
3 | import * as r from './index';
4 |
5 | export const html = createHTML(r);
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import S from 's-js';
2 | export * from './runtime'
3 |
4 | import { insert, hydrate as hydr, renderToString as rTS } from "./runtime";
5 |
6 | type MountableElement = Element | Document | ShadowRoot | DocumentFragment;
7 |
8 | export function render(code: () => any, mount: MountableElement): () => void {
9 | let dispose: () => void;
10 | S.root(disposer => {
11 | dispose = disposer;
12 | insert(mount, code());
13 | });
14 | return dispose!;
15 | }
16 |
17 | export function renderToString(code: () => any): Promise {
18 | return S.root(dispose => {
19 | const p = rTS(code);
20 | dispose();
21 | return p;
22 | });
23 | }
24 |
25 | export function hydrate(
26 | code: () => any,
27 | element: MountableElement
28 | ): () => void {
29 | let disposer: () => void;
30 | hydr(() => {
31 | disposer = render(code, element);
32 | }, element);
33 | return disposer!;
34 | }
35 |
--------------------------------------------------------------------------------
/src/runtime.d.ts:
--------------------------------------------------------------------------------
1 | export function template(html: string, isSVG?: boolean): Element;
2 | export function wrap(fn: (prev?: T) => T): any;
3 | export function wrapCondition(fn: () => any): () => any;
4 | export function insert(
5 | parent: Element | Document | ShadowRoot | DocumentFragment,
6 | accessor: any,
7 | init?: any,
8 | marker?: Node
9 | ): any;
10 | export function createComponent(
11 | Comp: (props: any) => any,
12 | props: any,
13 | dynamicKeys?: string[]
14 | ): any;
15 | export function delegateEvents(eventNames: string[]): void;
16 | export function clearDelegatedEvents(): void;
17 | export function spread(
18 | node: Element,
19 | accessor: any,
20 | isSVG?: Boolean,
21 | skipChildren?: Boolean
22 | ): void;
23 | export function classList(
24 | node: Element,
25 | value: { [k: string]: boolean },
26 | prev?: { [k: string]: boolean }
27 | ): void;
28 | export function currentContext(): any;
29 | export function renderToString(
30 | fn: (done?: () => void) => any,
31 | options?: {
32 | timeoutMs?: number;
33 | }
34 | ): Promise;
35 | export function hydrate(
36 | fn: () => unknown,
37 | node: Element | Document | ShadowRoot | DocumentFragment
38 | ): void;
39 | export function getNextElement(
40 | template: HTMLTemplateElement,
41 | isSSR: boolean
42 | ): Node;
43 | export function getNextMarker(start: Node): [Node, Array];
44 |
--------------------------------------------------------------------------------
/src/runtime.js:
--------------------------------------------------------------------------------
1 | import { Attributes, SVGAttributes, NonComposedEvents } from 'dom-expressions';
2 | import wrap, {value, sample as ignore} from 's-js';
3 |
4 | const wrapCondition = (fn) => {
5 | const s = value(ignore(fn));
6 | wrap(() => s(fn()))
7 | return s;
8 | };
9 |
10 | const eventRegistry = new Set();
11 | const config = {};
12 |
13 | export { wrap, wrapCondition };
14 |
15 | export function template(html, isSVG) {
16 | const t = document.createElement('template');
17 | t.innerHTML = html;
18 | if (t.innerHTML !== html) throw new Error(`Template html does not match input:\n${t.innerHTML}\n${html}`);
19 | let node = t.content.firstChild;
20 | if (isSVG) node = node.firstChild;
21 | return node;
22 | }
23 |
24 | export function createComponent(Comp, props, dynamicKeys) {
25 | if (dynamicKeys) {
26 | for (let i = 0; i < dynamicKeys.length; i++) dynamicProp(props, dynamicKeys[i]);
27 | }
28 |
29 | return ignore(() => Comp(props));
30 | }
31 |
32 | export function delegateEvents(eventNames) {
33 | for (let i = 0, l = eventNames.length; i < l; i++) {
34 | const name = eventNames[i];
35 | if (!eventRegistry.has(name)) {
36 | eventRegistry.add(name);
37 | document.addEventListener(name, eventHandler);
38 | }
39 | }
40 | }
41 |
42 | export function clearDelegatedEvents() {
43 | for (let name of eventRegistry.keys()) document.removeEventListener(name, eventHandler);
44 | eventRegistry.clear();
45 | }
46 |
47 | export function classList(node, value, prev) {
48 | const classKeys = Object.keys(value);
49 | for (let i = 0, len = classKeys.length; i < len; i++) {
50 | const key = classKeys[i],
51 | classValue = value[key],
52 | classNames = key.split(/\s+/);
53 | if (prev && prev[key] === classValue) continue;
54 | for (let j = 0, nameLen = classNames.length; j < nameLen; j++)
55 | node.classList.toggle(classNames[j], classValue);
56 | }
57 | }
58 |
59 | export function spread(node, accessor, isSVG, skipChildren) {
60 | if (typeof accessor === 'function') {
61 | wrap(current => spreadExpression(node, accessor(), current, isSVG, skipChildren));
62 | } else spreadExpression(node, accessor, undefined, isSVG, skipChildren);
63 | }
64 |
65 | export function insert(parent, accessor, marker, initial) {
66 | if (marker !== undefined && !initial) initial = [];
67 | if (typeof accessor === 'function')
68 | wrap((current = initial) => insertExpression(parent, accessor(), current, marker));
69 | else if (Array.isArray(accessor) && checkDynamicArray(accessor)) {
70 | wrap((current = initial) => insertExpression(parent, accessor, current, marker));
71 | } else {
72 | return insertExpression(parent, accessor, initial, marker);
73 | }
74 | }
75 |
76 | // SSR
77 | export function renderToString(code, options = {}) {
78 | options = { timeoutMs: 10000, ...options }
79 | config.hydrate = { id: '', count: 0 };
80 | const container = document.createElement("div");
81 | return new Promise(resolve => {
82 | setTimeout(() => resolve(container.innerHTML), options.timeoutMs);
83 | if (!code.length) {
84 | insert(container, code());
85 | resolve(container.innerHTML);
86 | } else insert(container, code(() => resolve(container.innerHTML)));
87 | });
88 | }
89 |
90 | export function hydrate(code, root) {
91 | config.hydrate = { id: '', count: 0, registry: new Map() };
92 | const templates = root.querySelectorAll(`*[_hk]`);
93 | for (let i = 0; i < templates.length; i++) {
94 | const node = templates[i];
95 | config.hydrate.registry.set(node.getAttribute('_hk'), node);
96 | }
97 | code();
98 | delete config.hydrate;
99 | }
100 |
101 | export function getNextElement(template, isSSR) {
102 | const hydrate = config.hydrate;
103 | let node, key;
104 | if (!hydrate || !hydrate.registry || !(node = hydrate.registry.get(key = `${hydrate.id}:${hydrate.count++}`))) {
105 | const el = template.cloneNode(true);
106 | if (isSSR && hydrate)
107 | el.setAttribute('_hk', `${hydrate.id}:${hydrate.count++}`);
108 | return el;
109 | }
110 | if (window && window._$HYDRATION) window._$HYDRATION.completed.add(key);
111 | return node;
112 | }
113 |
114 | export function getNextMarker(start) {
115 | let end = start,
116 | count = 0,
117 | current = [];
118 | if (config.hydrate && config.hydrate.registry) {
119 | while (end) {
120 | if (end.nodeType === 8) {
121 | const v = end.nodeValue;
122 | if (v === "#") count++;
123 | else if (v === "/") {
124 | if (count === 0) return [end, current];
125 | count--;
126 | }
127 | }
128 | current.push(end);
129 | end = end.nextSibling;
130 | }
131 | }
132 | return [end, current];
133 | }
134 |
135 | export function runHydrationEvents(id) {
136 | if (window && window._$HYDRATION) {
137 | const { completed, events } = window._$HYDRATION;
138 | while (events.length) {
139 | const [id, e] = events[0];
140 | if (!completed.has(id)) return;
141 | eventHandler(e);
142 | events.shift();
143 | }
144 | }
145 | }
146 |
147 | // Internal Functions
148 | function dynamicProp(props, key) {
149 | const src = props[key];
150 | Object.defineProperty(props, key, {
151 | get() { return src(); },
152 | enumerable: true
153 | });
154 | }
155 |
156 | function lookup(el) {
157 | return el && (el.model || lookup(el.host || el.parentNode));
158 | }
159 |
160 | function eventHandler(e) {
161 | const key = `__${e.type}`;
162 | let node = (e.composedPath && e.composedPath()[0]) || e.target;
163 | // reverse Shadow DOM retargetting
164 | if (e.target !== node) {
165 | Object.defineProperty(e, 'target', {
166 | configurable: true,
167 | value: node
168 | })
169 | }
170 |
171 | // simulate currentTarget
172 | Object.defineProperty(e, 'currentTarget', {
173 | configurable: true,
174 | get() { return node; }
175 | })
176 |
177 | while (node !== null) {
178 | const handler = node[key];
179 | if (handler) {
180 | const model = handler.length > 1 ? lookup(node): undefined;
181 | handler(e, model);
182 | if (e.cancelBubble) return;
183 | }
184 | node = (node.host && node.host instanceof Node) ? node.host : node.parentNode;
185 | }
186 | }
187 |
188 | function spreadExpression(node, props, prevProps = {}, isSVG, skipChildren) {
189 | let info;
190 | if (!skipChildren && "children" in props) {
191 | wrap(() =>
192 | (prevProps.children = insertExpression(
193 | node,
194 | props.children,
195 | prevProps.children
196 | ))
197 | );
198 | }
199 | wrap(() => {
200 | for (const prop in props) {
201 | if (prop === "children") continue;
202 | const value = props[prop];
203 | if (value === prevProps[prop]) continue;
204 | if (prop === "style") {
205 | Object.assign(node.style, value);
206 | } else if (prop === "classList") {
207 | classList(node, value, prevProps[prop]);
208 | // really only for forwarding from Components, can't forward normal ref
209 | } else if (prop === "ref" || prop === "forwardRef") {
210 | value(node);
211 | } else if (prop.slice(0, 2) === "on") {
212 | const lc = prop.toLowerCase();
213 | if (lc !== prop && !NonComposedEvents.has(lc.slice(2))) {
214 | const name = lc.slice(2);
215 | node[`__${name}`] = value;
216 | delegateEvents([name]);
217 | } else node[lc] = value;
218 | } else if (prop === "events") {
219 | for (const eventName in value)
220 | node.addEventListener(eventName, value[eventName]);
221 | } else if ((info = Attributes[prop])) {
222 | if (info.type === "attribute") {
223 | node.setAttribute(prop, value);
224 | } else node[info.alias] = value;
225 | } else if (isSVG) {
226 | if ((info = SVGAttributes[prop])) {
227 | if (info.alias) node.setAttribute(info.alias, value);
228 | else node.setAttribute(prop, value);
229 | } else
230 | node.setAttribute(
231 | prop.replace(/([A-Z])/g, g => `-${g[0].toLowerCase()}`),
232 | value
233 | );
234 | } else node[prop] = value;
235 | prevProps[prop] = value;
236 | }
237 | });
238 | return prevProps;
239 | }
240 |
241 | function normalizeIncomingArray(normalized, array) {
242 | for (let i = 0, len = array.length; i < len; i++) {
243 | let item = array[i], t;
244 | if (item instanceof Node) {
245 | normalized.push(item);
246 | } else if (item == null || item === true || item === false) { // matches null, undefined, true or false
247 | // skip
248 | } else if (Array.isArray(item)) {
249 | normalizeIncomingArray(normalized, item);
250 | } else if ((t = typeof item) === 'string') {
251 | normalized.push(document.createTextNode(item));
252 | } else if (t === 'function') {
253 | const idx = item();
254 | normalizeIncomingArray(normalized, Array.isArray(idx) ? idx : [idx]);
255 | } else normalized.push(document.createTextNode(item.toString()));
256 | }
257 | return normalized;
258 | }
259 |
260 | function appendNodes(parent, array, marker) {
261 | for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker);
262 | }
263 |
264 | function cleanChildren(parent, current, marker, replacement) {
265 | if (marker === undefined) return parent.textContent = '';
266 | const node = (replacement || document.createTextNode(''));
267 | if (current.length) {
268 | node !== current[0] && parent.replaceChild(node, current[0]);
269 | for (let i = current.length - 1; i > 0; i--) parent.removeChild(current[i]);
270 | } else parent.insertBefore(node, marker);
271 | return [node];
272 | }
273 |
274 | function checkDynamicArray(array) {
275 | for (let i = 0, len = array.length; i < len; i++) {
276 | const item = array[i];
277 | if (Array.isArray(item) && checkDynamicArray(item) || typeof item === 'function') return true;
278 | }
279 | return false;
280 | }
281 |
282 | function insertExpression(parent, value, current, marker) {
283 |
284 | if (value === current) return current;
285 | const t = typeof value,
286 | multi = marker !== undefined;
287 | parent = (multi && current[0] && current[0].parentNode) || parent;
288 |
289 | if (t === 'string' || t === 'number') {
290 | if (t === 'number') value = value.toString();
291 | if (multi) {
292 | let node = current[0];
293 | if (node && node.nodeType === 3) {
294 | node.data = value;
295 | } else node = document.createTextNode(value);
296 | current = cleanChildren(parent, current, marker, node)
297 | } else {
298 | if (current !== '' && typeof current === 'string') {
299 | current = parent.firstChild.data = value;
300 | } else current = parent.textContent = value;
301 | }
302 | } else if (value == null || t === 'boolean') {
303 | if (config.hydrate && config.hydrate.registry) return current;
304 | current = cleanChildren(parent, current, marker);
305 | } else if (t === 'function') {
306 | wrap(() => current = insertExpression(parent, value(), current, marker));
307 |
308 | } else if (Array.isArray(value)) {
309 | const array = normalizeIncomingArray([], value);
310 | if (config.hydrate && config.hydrate.registry) return current;
311 | if (array.length === 0) {
312 | current = cleanChildren(parent, current, marker);
313 | if (multi) return current;
314 | } else {
315 | if (Array.isArray(current)) {
316 | if (current.length === 0) {
317 | appendNodes(parent, array, marker);
318 | } else reconcileArrays(parent, current, array);
319 | } else if (current == null || current === '') {
320 | appendNodes(parent, array);
321 | } else {
322 | reconcileArrays(parent, (multi && current) || [parent.firstChild], array);
323 | }
324 | }
325 | current = array;
326 | } else if (value instanceof Node) {
327 | if (Array.isArray(current)) {
328 | if (multi) return current = cleanChildren(parent, current, marker, value);
329 | cleanChildren(parent, current, null, value);
330 | } else if (current == null || current === '') {
331 | parent.appendChild(value);
332 | } else parent.replaceChild(value, parent.firstChild);
333 | current = value;
334 | }
335 |
336 | return current;
337 | }
338 |
339 | // Picked from
340 | // https://github.com/adamhaile/surplus/blob/master/src/runtime/content.ts#L368
341 | var NOMATCH = -1
342 |
343 | // reconcile the content of parent from ns to us
344 | // see ivi's excellent writeup of diffing arrays in a vdom library:
345 | // https://github.com/ivijs/ivi/blob/2c81ead934b9128e092cc2a5ef2d3cabc73cb5dd/packages/ivi/src/vdom/implementation.ts#L1187
346 | // this code isn't identical, since we're diffing real dom nodes to nodes-or-strings,
347 | // but the core methodology of trimming ends and reversals, matching nodes, then using
348 | // the longest increasing subsequence to minimize DOM ops is inspired by ivi.
349 | function reconcileArrays(parent, ns, us) {
350 | var ulen = us.length,
351 | // n = nodes, u = updates
352 | // ranges defined by min and max indices
353 | nmin = 0,
354 | nmax = ns.length - 1,
355 | umin = 0,
356 | umax = ulen - 1,
357 | // start nodes of ranges
358 | n = ns[nmin],
359 | u = us[umin],
360 | // end nodes of ranges
361 | nx = ns[nmax],
362 | ux = us[umax],
363 | // node, if any, just after ux, used for doing .insertBefore() to put nodes at end
364 | ul = nx.nextSibling,
365 | i, j, k,
366 | loop = true;
367 |
368 | // scan over common prefixes, suffixes, and simple reversals
369 | fixes: while (loop) {
370 | loop = false;
371 |
372 | // common prefix, u === n
373 | while (u === n) {
374 | umin++;
375 | nmin++;
376 | if (umin > umax || nmin > nmax) break fixes;
377 | u = us[umin];
378 | n = ns[nmin];
379 | }
380 |
381 | // common suffix, ux === nx
382 | while (ux === nx) {
383 | ul = nx;
384 | umax--;
385 | nmax--;
386 | if (umin > umax || nmin > nmax) break fixes;
387 | ux = us[umax];
388 | nx = ns[nmax];
389 | }
390 |
391 | // reversal u === nx, have to swap node forward
392 | while (u === nx) {
393 | loop = true;
394 | parent.insertBefore(nx, n);
395 | umin++;
396 | nmax--;
397 | if (umin > umax || nmin > nmax) break fixes;
398 | u = us[umin];
399 | nx = ns[nmax];
400 | }
401 |
402 | // reversal ux === n, have to swap node back
403 | while (ux === n) {
404 | loop = true;
405 | if (ul === null) parent.appendChild(n);
406 | else parent.insertBefore(n, ul);
407 | ul = n;
408 | umax--;
409 | nmin++;
410 | if (umin > umax || nmin > nmax) break fixes;
411 | ux = us[umax];
412 | n = ns[nmin];
413 | }
414 | }
415 |
416 | // if that covered all updates, just need to remove any remaining nodes and we're done
417 | if (umin > umax) {
418 | // remove any remaining nodes
419 | while (nmin <= nmax) {
420 | parent.removeChild(ns[nmax]);
421 | nmax--;
422 | }
423 | return;
424 | }
425 |
426 | // if that covered all current nodes, just need to insert any remaining updates and we're done
427 | if (nmin > nmax) {
428 | // insert any remaining nodes
429 | while (umin <= umax) {
430 | parent.insertBefore(us[umin], ul);
431 | umin++;
432 | }
433 | return;
434 | }
435 |
436 | // Positions for reusing nodes from current DOM state
437 | const P = new Array(umax - umin + 1),
438 | I = new Map();
439 | for(let i = umin; i <= umax; i++) {
440 | P[i] = NOMATCH;
441 | I.set(us[i], i);
442 | }
443 |
444 | let reusingNodes = umin + us.length - 1 - umax,
445 | toRemove = []
446 |
447 | for(let i = nmin; i <= nmax; i++) {
448 | if (I.has(ns[i])) {
449 | P[I.get(ns[i])] = i
450 | reusingNodes++
451 | } else toRemove.push(i)
452 | }
453 |
454 | // Fast path for full replace
455 | if (reusingNodes === 0) {
456 | if (n !== parent.firstChild || nx !== parent.lastChild) {
457 | for (i = nmin; i <= nmax; i++) parent.removeChild(ns[i]);
458 | while (umin <= umax) {
459 | parent.insertBefore(us[umin], ul);
460 | umin++
461 | }
462 | return;
463 | }
464 | // no nodes preserved, use fast clear and append
465 | parent.textContent = '';
466 | while (umin <= umax) {
467 | parent.appendChild(us[umin]);
468 | umin++;
469 | }
470 | return;
471 | }
472 |
473 | // find longest common sequence between ns and us, represented as the indices
474 | // of the longest increasing subsequence in src
475 | var lcs = longestPositiveIncreasingSubsequence(P, umin),
476 | nodes = [],
477 | tmp = ns[nmin], lisIdx = lcs.length - 1, tmpB;;
478 |
479 | // Collect nodes to work with them
480 | for(let i = nmin; i <= nmax; i++) {
481 | nodes[i] = tmp;
482 | tmp = tmp.nextSibling;
483 | }
484 |
485 | for(let i = 0; i < toRemove.length; i++) parent.removeChild(nodes[toRemove[i]])
486 |
487 | for(let i = umax; i >= umin; i--) {
488 | if(lcs[lisIdx] === i) {
489 | ul = nodes[P[lcs[lisIdx]]]
490 | lisIdx--
491 | } else {
492 | tmpB = (P[i] === NOMATCH) ? us[i] : nodes[P[i]];
493 | parent.insertBefore(tmpB, ul)
494 | ul = tmpB
495 | }
496 | }
497 | }
498 |
499 | // return an array of the indices of ns that comprise the longest increasing subsequence within ns
500 | function longestPositiveIncreasingSubsequence(ns, newStart) {
501 | let seq = [],
502 | is = [],
503 | l = -1,
504 | pre = new Array(ns.length);
505 |
506 | for (let i = newStart, len = ns.length; i < len; i++) {
507 | let n = ns[i];
508 | if (n < 0) continue;
509 | let j = findGreatestIndexLEQ(seq, n);
510 | if (j !== -1) pre[i] = is[j];
511 | if (j === l) {
512 | l++;
513 | seq[l] = n;
514 | is[l] = i;
515 | } else if (n < seq[j + 1]) {
516 | seq[j + 1] = n;
517 | is[j + 1] = i;
518 | }
519 | }
520 |
521 | for (let i = is[l]; l >= 0; i = pre[i], l--) {
522 | seq[l] = i;
523 | }
524 |
525 | return seq;
526 | }
527 |
528 | function findGreatestIndexLEQ(seq, n) {
529 | // invariant: lo is guaranteed to be index of a value <= n, hi to be >
530 | // therefore, they actually start out of range: (-1, last + 1)
531 | var lo = -1,
532 | hi = seq.length;
533 |
534 | // fast path for simple increasing sequences
535 | if (hi > 0 && seq[hi - 1] <= n) return hi - 1;
536 |
537 | while (hi - lo > 1) {
538 | var mid = Math.floor((lo + hi) / 2);
539 | if (seq[mid] > n) {
540 | hi = mid;
541 | } else {
542 | lo = mid;
543 | }
544 | }
545 |
546 | return lo;
547 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "types",
4 | "emitDeclarationOnly": true,
5 | "declaration": true,
6 | "incremental": true,
7 | "target": "esnext",
8 | "moduleResolution": "node",
9 | "strict": true
10 | }
11 | }
--------------------------------------------------------------------------------