diff --git a/.gitignore b/.gitignore index 18d66a1..844fe16 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,9 @@ types/ cjs/* !cjs/package.json dom.js -esm/init.js -init.js +esm/init*.js +init*.js +worker.js keyed.js !esm/keyed.js !esm/dom/keyed.js @@ -24,4 +25,4 @@ signal.js !esm/signal.js !esm/render/signal.js preactive.js -!test/preactive.js +!test/preactive.js \ No newline at end of file diff --git a/README.md b/README.md index fb39673..532b19f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ * **[uhtml/keyed](https://cdn.jsdelivr.net/npm/uhtml/keyed.js)** with extras `{ Hole, render, html, svg, htmlFor, svgFor, attr }`, providing keyed utilities - read [keyed or not ?](https://webreflection.github.io/uhtml/#keyed-or-not-) paragraph to know more * **[uhtml/node](https://cdn.jsdelivr.net/npm/uhtml/node.js)** with *same default* exports but it's for *one-off* nodes creation only so that no cache or updates are available and it's just an easy way to hook *uhtml* into your existing project for DOM creation (not manipulation!) * **[uhtml/init](https://cdn.jsdelivr.net/npm/uhtml/init.js)** which returns a `document => uhtml/keyed` utility that can be bootstrapped with `uhtml/dom`, [LinkeDOM](https://github.com/WebReflection/linkedom), [JSDOM](https://github.com/jsdom/jsdom) for either *SSR* or *Workers* support + * **uhtml/ssr** which exports an utility that both SSR or Workers can use to parse and serve documents. This export provides same keyed utilities except the keyed feature is implicitly disabled as that's usually not desirable at all for SSR or rendering use cases, actually just an overhead. This might change in the future but for now I want to benchmark and see how competitive is `uhtml/ssr` out there. The `uhtml/dom` is also embedded in this export because the `Comment` class needs an override to produce a super clean output (at least until hydro story is up and running). * **[uhtml/dom](https://cdn.jsdelivr.net/npm/uhtml/dom.js)** which returns a specialized *uhtml* compliant DOM environment that can be passed to the `uhtml/init` export to have 100% same-thing running on both client or Web Worker / Server. This entry exports `{ Document, DOMParser }` where the former can be used to create a new *document* while the latter one can parse well formed HTML or SVG content and return the document out of the box. * **[uhtml/reactive](https://cdn.jsdelivr.net/npm/uhtml/reactive.js)** which allows usage of symbols within the optionally *keyed* render function. The only difference with other exports, beside exporting a `reactive` field instead of `render`, so that `const render = reactive(effect)` creates a reactive render per each library, is that the `render(where, () => what)`, with a function as second argument is mandatory when the rendered stuff has signals in it, otherwise these can't side-effect properly. * **[uhtml/signal](https://cdn.jsdelivr.net/npm/uhtml/signal.js)** is an already bundled `uhtml/reactive` with `@webreflection/signal` in it, so that its `render` exported function is already reactive. This is the smallest possible bundle as it's ~3.3Kb but it's not nearly as complete, in terms of features, as *preact* signals are. diff --git a/esm/creator.js b/esm/creator.js index 9f68ce4..2d54f36 100644 --- a/esm/creator.js +++ b/esm/creator.js @@ -1,17 +1,9 @@ import { PersistentFragment } from './persistent-fragment.js'; import { bc, detail } from './literals.js'; import { array, hole } from './handler.js'; -import { empty } from './utils.js'; +import { empty, find } from './utils.js'; import { cache } from './literals.js'; -/** - * @param {DocumentFragment} content - * @param {number[]} path - * @returns {Element} - */ -const find = (content, path) => path.reduceRight(childNodesIndex, content); -const childNodesIndex = (node, i) => node.childNodes[i]; - /** @param {(template: TemplateStringsArray, values: any[]) => import("./parser.js").Resolved} parse */ export default parse => ( /** diff --git a/esm/persistent-fragment.js b/esm/persistent-fragment.js index b2ea1eb..7a90d06 100644 --- a/esm/persistent-fragment.js +++ b/esm/persistent-fragment.js @@ -50,7 +50,7 @@ export class PersistentFragment extends custom(DocumentFragment) { remove(this, true).replaceWith(node); } valueOf() { - let { firstChild, lastChild, parentNode } = this; + const { parentNode } = this; if (parentNode === this) { if (this.#nodes === empty) this.#nodes = [...this.childNodes]; @@ -65,6 +65,7 @@ export class PersistentFragment extends custom(DocumentFragment) { // This is a render-only specific issue but it's tested and // it's worth fixing to me to have more consistent fragments. if (parentNode) { + let { firstChild, lastChild } = this; this.#nodes = [firstChild]; while (firstChild !== lastChild) this.#nodes.push((firstChild = firstChild.nextSibling)); diff --git a/esm/rabbit.js b/esm/rabbit.js index 512e9f7..686afdb 100644 --- a/esm/rabbit.js +++ b/esm/rabbit.js @@ -3,8 +3,8 @@ import { cache } from './literals.js'; import create from './creator.js'; import parser from './parser.js'; -const parseHTML = create(parser(false)); -const parseSVG = create(parser(true)); +const createHTML = create(parser(false)); +const createSVG = create(parser(true)); /** * @param {import("./literals.js").Cache} info @@ -13,7 +13,7 @@ const parseSVG = create(parser(true)); */ const unroll = (info, { s, t, v }) => { if (info.a !== t) { - const { b, c } = (s ? parseSVG : parseHTML)(t, v); + const { b, c } = (s ? createSVG : createHTML)(t, v); info.a = t; info.b = b; info.c = c; diff --git a/esm/ssr.js b/esm/ssr.js new file mode 100644 index 0000000..d8fd893 --- /dev/null +++ b/esm/ssr.js @@ -0,0 +1,6 @@ +import { Hole, render, html, svg, attr } from './index.js'; + +const htmlFor = () => html; +const svgFor = () => svg; + +export { Hole, render, html, svg, htmlFor, svgFor, attr }; diff --git a/esm/utils.js b/esm/utils.js index 1072670..682f7e6 100644 --- a/esm/utils.js +++ b/esm/utils.js @@ -34,3 +34,13 @@ export const gPD = (ref, prop) => { while(!desc && (ref = getPrototypeOf(ref))); return desc; }; + +/* c8 ignore start */ +/** + * @param {DocumentFragment} content + * @param {number[]} path + * @returns {Element} + */ +export const find = (content, path) => path.reduceRight(childNodesIndex, content); +const childNodesIndex = (node, i) => node.childNodes[i]; +/* c8 ignore stop */ diff --git a/package-lock.json b/package-lock.json index e301425..54a2a27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,8 +24,8 @@ "@rollup/plugin-terser": "^0.4.4", "ascjs": "^6.0.3", "c8": "^9.1.0", - "rollup": "^4.13.2", - "typescript": "^5.4.3" + "rollup": "^4.14.2", + "typescript": "^5.4.5" }, "optionalDependencies": { "@preact/signals-core": "^1.6.0", @@ -197,9 +197,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", - "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz", + "integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==", "cpu": [ "arm" ], @@ -210,9 +210,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", - "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz", + "integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==", "cpu": [ "arm64" ], @@ -223,9 +223,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", - "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz", + "integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==", "cpu": [ "arm64" ], @@ -236,9 +236,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", - "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz", + "integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==", "cpu": [ "x64" ], @@ -249,9 +249,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", - "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz", + "integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==", "cpu": [ "arm" ], @@ -262,9 +262,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", - "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz", + "integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==", "cpu": [ "arm64" ], @@ -275,9 +275,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", - "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz", + "integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==", "cpu": [ "arm64" ], @@ -288,11 +288,11 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", - "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz", + "integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==", "cpu": [ - "ppc64le" + "ppc64" ], "dev": true, "optional": true, @@ -301,9 +301,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", - "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz", + "integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==", "cpu": [ "riscv64" ], @@ -314,9 +314,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", - "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz", + "integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==", "cpu": [ "s390x" ], @@ -327,9 +327,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", - "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz", + "integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==", "cpu": [ "x64" ], @@ -340,9 +340,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", - "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz", + "integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==", "cpu": [ "x64" ], @@ -353,9 +353,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", - "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz", + "integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==", "cpu": [ "arm64" ], @@ -366,9 +366,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", - "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz", + "integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==", "cpu": [ "ia32" ], @@ -379,9 +379,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", - "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz", + "integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==", "cpu": [ "x64" ], @@ -1115,9 +1115,9 @@ } }, "node_modules/rollup": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", - "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz", + "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -1130,21 +1130,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.2", - "@rollup/rollup-android-arm64": "4.13.2", - "@rollup/rollup-darwin-arm64": "4.13.2", - "@rollup/rollup-darwin-x64": "4.13.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", - "@rollup/rollup-linux-arm64-gnu": "4.13.2", - "@rollup/rollup-linux-arm64-musl": "4.13.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", - "@rollup/rollup-linux-riscv64-gnu": "4.13.2", - "@rollup/rollup-linux-s390x-gnu": "4.13.2", - "@rollup/rollup-linux-x64-gnu": "4.13.2", - "@rollup/rollup-linux-x64-musl": "4.13.2", - "@rollup/rollup-win32-arm64-msvc": "4.13.2", - "@rollup/rollup-win32-ia32-msvc": "4.13.2", - "@rollup/rollup-win32-x64-msvc": "4.13.2", + "@rollup/rollup-android-arm-eabi": "4.14.2", + "@rollup/rollup-android-arm64": "4.14.2", + "@rollup/rollup-darwin-arm64": "4.14.2", + "@rollup/rollup-darwin-x64": "4.14.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.2", + "@rollup/rollup-linux-arm64-gnu": "4.14.2", + "@rollup/rollup-linux-arm64-musl": "4.14.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.2", + "@rollup/rollup-linux-riscv64-gnu": "4.14.2", + "@rollup/rollup-linux-s390x-gnu": "4.14.2", + "@rollup/rollup-linux-x64-gnu": "4.14.2", + "@rollup/rollup-linux-x64-musl": "4.14.2", + "@rollup/rollup-win32-arm64-msvc": "4.14.2", + "@rollup/rollup-win32-ia32-msvc": "4.14.2", + "@rollup/rollup-win32-x64-msvc": "4.14.2", "fsevents": "~2.3.2" } }, @@ -1333,9 +1333,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 7efa01a..1751b97 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,15 @@ "scripts": { "benchmark:w3c": "node test/benchmark/linkedom.js --w3c; node test/benchmark/linkedom-cached.js --w3c; node test/benchmark/dom.js --w3c", "benchmark:dom": "node test/benchmark/linkedom.js --dom; node test/benchmark/linkedom-cached.js --dom; node test/benchmark/dom.js --dom", - "build": "npm run rollup:es && node rollup/init.cjs && npm run rollup:init && rm -rf cjs/* && npm run cjs && rm -rf types && npm run ts && npm run test && npm run size", + "build": "npm run rollup:es && node rollup/ssr.cjs && node rollup/init.cjs && npm run rollup:init && npm run rollup:ssr && rm -rf cjs/* && npm run cjs && npm run test && npm run size", "cjs": "ascjs --no-default esm cjs", "rollup:es": "rollup --config rollup/es.config.js", "rollup:init": "rollup --config rollup/init.config.js", + "rollup:ssr": "rollup --config rollup/ssr.config.js && rm esm/init-ssr.js_ && terser --module esm/init-ssr.js -mc -o ./worker.js", "server": "npx static-handler .", - "size": "echo \"index $(cat index.js | brotli | wc -c)\";echo \"keyed $(cat keyed.js | brotli | wc -c)\";echo \"reactive $(cat reactive.js | brotli | wc -c)\";echo \"preactive $(cat preactive.js | brotli | wc -c)\";echo \"signal $(cat signal.js | brotli | wc -c)\";echo \"node $(cat node.js | brotli | wc -c)\";", + "size": "echo \"index $(cat index.js | brotli | wc -c)\";echo \"keyed $(cat keyed.js | brotli | wc -c)\";echo \"reactive $(cat reactive.js | brotli | wc -c)\";echo \"preactive $(cat preactive.js | brotli | wc -c)\";echo \"signal $(cat signal.js | brotli | wc -c)\";echo \"node $(cat node.js | brotli | wc -c)\";echo \"worker $(cat worker.js | brotli | wc -c)\";", "test": "c8 node test/coverage.js && node test/modern.mjs", - "coverage": "mkdir -p ./coverage; c8 report --reporter=text-lcov > ./coverage/lcov.info", - "ts": "tsc -p ." + "coverage": "mkdir -p ./coverage; c8 report --reporter=text-lcov > ./coverage/lcov.info" }, "keywords": [ "micro", @@ -28,52 +28,50 @@ "@rollup/plugin-terser": "^0.4.4", "ascjs": "^6.0.3", "c8": "^9.1.0", - "rollup": "^4.13.2", - "typescript": "^5.4.3" + "rollup": "^4.14.2" }, "module": "./esm/index.js", "type": "module", "exports": { ".": { - "types": "./types/index.d.ts", "import": "./esm/index.js", "default": "./cjs/index.js" }, "./dom": { - "types": "./types/dom/index.d.ts", "import": "./esm/dom/index.js", "default": "./cjs/dom/index.js" }, "./init": { - "types": "./types/init.d.ts", "import": "./esm/init.js", "default": "./cjs/init.js" }, "./keyed": { - "types": "./types/keyed.d.ts", "import": "./esm/keyed.js", "default": "./cjs/keyed.js" }, "./node": { - "types": "./types/node.d.ts", "import": "./esm/node.js", "default": "./cjs/node.js" }, "./reactive": { - "types": "./types/reactive.d.ts", "import": "./esm/reactive.js", "default": "./cjs/reactive.js" }, "./preactive": { - "types": "./types/reactive/preact.d.ts", "import": "./esm/reactive/preact.js", "default": "./cjs/reactive/preact.js" }, "./signal": { - "types": "./types/reactive/signal.d.ts", "import": "./esm/reactive/signal.js", "default": "./cjs/reactive/signal.js" }, + "./ssr": { + "import": "./esm/init-ssr.js", + "default": "./cjs/init-ssr.js" + }, + "./worker": { + "import": "./worker.js" + }, "./package.json": "./package.json" }, "unpkg": "./keyed.js", diff --git a/rollup/es.config.js b/rollup/es.config.js index e27339b..fce0e38 100644 --- a/rollup/es.config.js +++ b/rollup/es.config.js @@ -18,6 +18,16 @@ export default [ name: 'uhtml', }, }, + { + plugins: [nodeResolve()], + input: './esm/ssr.js', + output: { + esModule: false, + file: './esm/init-ssr.js', + format: 'iife', + name: 'uhtml', + }, + }, { plugins, input: './esm/index.js', diff --git a/rollup/exports.cjs b/rollup/exports.cjs new file mode 100644 index 0000000..c002a9c --- /dev/null +++ b/rollup/exports.cjs @@ -0,0 +1,14 @@ +module.exports = code => { + const out = []; + code = code.replace( + /^\s+exports\.(\S+)\s*=\s*([^;]+);/gm, + (_, name, exported) => { + out.push(name === exported ? name : `${name}: ${exported}`); + return ''; + } + ); + return code + .replace(/^\s+return exports;/m, `\n return { ${out.join(', ')} };`) + .replace('function (exports) {', 'function () {') + .replace(/\}\)\(\{\}\);(\s*)$/, '})();$1'); +}; diff --git a/rollup/init.cjs b/rollup/init.cjs index 5f94fa9..5fa1b8f 100644 --- a/rollup/init.cjs +++ b/rollup/init.cjs @@ -1,5 +1,6 @@ const { readFileSync, writeFileSync } = require('fs'); const { join } = require('path'); +const fixExports = require('./exports.cjs'); const init = join(__dirname, '..', 'esm', 'init.js'); const uhtml = readFileSync(init).toString(); @@ -13,11 +14,13 @@ writeFileSync(init, ` */ export default document => ${ // tested via integration - uhtml - .replace(`svg || ('ownerSVGElement' in element)`, `/* c8 ignore start */ svg || ('ownerSVGElement' in element) /* c8 ignore stop */`) - .replace(/diffFragment = \(([\S\s]+?)return /, 'diffFragment = /* c8 ignore start */($1/* c8 ignore stop */return ') - .replace(/udomdiff = \(([\S\s]+?)return /, 'udomdiff = /* c8 ignore start */($1/* c8 ignore stop */return ') - .replace(/^(\s+)replaceWith\(([^}]+?)\}/m, '$1/* c8 ignore start */\n$1replaceWith($2}\n$1/* c8 ignore stop */') - .replace(/^(\s+)(["'])use strict\2;/m, '$1$2use strict$2;\n\n$1const { constructor: DocumentFragment } = document.createDocumentFragment();') - .replace(/^[^(]+/, '') + fixExports( + uhtml + .replace(`svg || ('ownerSVGElement' in element)`, `/* c8 ignore start */ svg || ('ownerSVGElement' in element) /* c8 ignore stop */`) + .replace(/diffFragment = \(([\S\s]+?)return /, 'diffFragment = /* c8 ignore start */($1/* c8 ignore stop */return ') + .replace(/udomdiff = \(([\S\s]+?)return /, 'udomdiff = /* c8 ignore start */($1/* c8 ignore stop */return ') + .replace(/^(\s+)replaceWith\(([^}]+?)\}/m, '$1/* c8 ignore start */\n$1replaceWith($2}\n$1/* c8 ignore stop */') + .replace(/^(\s+)(["'])use strict\2;/m, '$1$2use strict$2;\n\n$1const { constructor: DocumentFragment } = document.createDocumentFragment();') + .replace(/^[^(]+/, '') + ) }`); diff --git a/rollup/ssr.cjs b/rollup/ssr.cjs new file mode 100644 index 0000000..5795b7b --- /dev/null +++ b/rollup/ssr.cjs @@ -0,0 +1,49 @@ +const { readFileSync, writeFileSync } = require('fs'); +const { join } = require('path'); +const fixExports = require('./exports.cjs'); + +const init = join(__dirname, '..', 'esm', 'init-ssr.js'); +const uhtml = readFileSync(init).toString(); + +const content = [ + 'const document = content ? new DOMParser().parseFromString(content, ...rest) : new Document;', + 'const { constructor: DocumentFragment } = document.createDocumentFragment();', +]; + +writeFileSync(init + '_', ` +// ⚠️ WARNING - THIS FILE IS AN ARTIFACT - DO NOT EDIT + +import Document from './dom/document.js'; +import DOMParser from './dom/dom-parser.js'; + +import { value } from './dom/symbols.js'; +import Comment from './dom/comment.js'; +Comment.prototype.toString = function toString() { + const content = this[value]; + switch (content) { + case '<>': + case '': + return ''; + default: + return /^\\[\\d+\\]$/.test(content) ? '' : \`\`; + } +}; + +/** + * @param {Document} document + * @returns {import("./keyed.js")} + */ +export default (content, ...rest) => ${ + // tested via integration + fixExports( + uhtml + .replace(/const create(HTML|SVG) = create\(parse\((false|true), false\)\)/g, 'const create$1 = create(parse($2, true))') + .replace(`svg || ('ownerSVGElement' in element)`, `/* c8 ignore start */ svg || ('ownerSVGElement' in element) /* c8 ignore stop */`) + .replace(/diffFragment = \(([\S\s]+?)return /, 'diffFragment = /* c8 ignore start */($1/* c8 ignore stop */return ') + .replace(/udomdiff = \(([\S\s]+?)return /, 'udomdiff = /* c8 ignore start */($1/* c8 ignore stop */return ') + .replace(/^(\s+)replaceWith\(([^}]+?)\}/m, '$1/* c8 ignore start */\n$1replaceWith($2}\n$1/* c8 ignore stop */') + .replace(/^(\s+)(["'])use strict\2;/m, (_, tab, quote) => `${tab}${quote}use strict${quote};\n\n${tab}${content.join(`\n${tab}`)}`) + .replace(/^(\s+)(return exports;)/m, '$1exports.document = document;\n$1$2') + .replace(/^[^(]+/, '') + ) +}`); diff --git a/rollup/ssr.config.js b/rollup/ssr.config.js new file mode 100644 index 0000000..76ac861 --- /dev/null +++ b/rollup/ssr.config.js @@ -0,0 +1,12 @@ +import {nodeResolve} from '@rollup/plugin-node-resolve'; + +export default [ + { + plugins: [nodeResolve()], + input: './esm/init-ssr.js_', + output: { + esModule: true, + file: './esm/init-ssr.js', + }, + }, +]; diff --git a/test/ssr.mjs b/test/ssr.mjs new file mode 100644 index 0000000..c34b453 --- /dev/null +++ b/test/ssr.mjs @@ -0,0 +1,18 @@ +import init from '../esm/init-ssr.js'; + +const { document, render, html } = init(` + + + ${'Hello SSR'} + +
+`.trim() +); + +render(document.getElementById('test'), html` +

+ !!! ${[html``, html``, html`e`]} !!! +

+`); + +console.log(document.toString()); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 264f07d..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "module": "NodeNext", - "target": "esnext", - "moduleResolution": "nodenext", - "allowJs": true, - "declaration": true, - "emitDeclarationOnly": true, - "declarationDir": "types" - }, - "include": [ - "esm/index.js", - "esm/keyed.js", - "esm/node.js", - "esm/reactive.js", - "esm/reactive/preact.js", - "esm/reactive/signal.js", - "esm/dom/index.js", - ] -}