From dda70c357081d5482d4144280c58375188ab7e6e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 18:16:49 +0900 Subject: [PATCH 01/12] docs: consistenly use Context type --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 23c7532..14318be 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ HTML classes, not random combination of query selectors. ``` ```js -function MyComponent({ on }) { +function MyComponent({ on }: Context) { alert(`Hello, I'm ${el.textContext}!`); } @@ -315,7 +315,7 @@ function PubComponent({ on, pub }: Context) { }; } -function SubComponent({ on, sub }) { +function SubComponent({ on, sub }: Context) { sub(EVENT); // This adds sub:my-event class to the mounted element, which means it subscribes to that event. on[EVENT] = () => { From d0d1c3d1af93df090be2d3eb32a86f9f9613708e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 18:31:10 +0900 Subject: [PATCH 02/12] chore: move dist files to docs/ --- deno.json | 6 +++--- dist.js => docs/dist.js | 0 dist.min.js => docs/dist.min.js | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename dist.js => docs/dist.js (100%) rename dist.min.js => docs/dist.min.js (100%) diff --git a/deno.json b/deno.json index bdc263f..93dad19 100644 --- a/deno.json +++ b/deno.json @@ -13,9 +13,9 @@ "cov": "deno test --coverage -A", "lcov": "deno coverage --lcov cov > lcov.info", "html_cov": "deno coverage --html", - "dist": "deno run -A jsr:@kt3k/pack mod.ts > dist.js", - "min": "deno run -A npm:terser --compress --mangle --toplevel -o dist.min.js -- dist.js", - "size": "deno run --allow-read https://deno.land/x/gzip_size@v0.3.0/cli.ts --include-original dist.min.js", + "dist": "deno run -A jsr:@kt3k/pack mod.ts > docs/dist.js", + "min": "deno run -A npm:terser --compress --mangle --toplevel -o docs/dist.min.js -- docs/dist.js", + "size": "deno run --allow-read https://deno.land/x/gzip_size@v0.3.0/cli.ts --include-original docs/dist.min.js", "twd": "deno run -A --allow-read=. --allow-write=style.css --allow-net=deno.land,esm.sh,cdn.esm.sh https://deno.land/x/twd@v0.4.8/cli.ts -o style.css index.html", "twd-w": "deno task twd -- -w", "start": "deno run --allow-read=. --allow-net=0.0.0.0:8000 deploy.ts" diff --git a/dist.js b/docs/dist.js similarity index 100% rename from dist.js rename to docs/dist.js diff --git a/dist.min.js b/docs/dist.min.js similarity index 100% rename from dist.min.js rename to docs/dist.min.js From dd716b0577551bc8487167dd4ac1669f25f8a39e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 18:33:16 +0900 Subject: [PATCH 03/12] chore: exclude dist from formatting --- deno.json | 2 +- docs/dist.js | 55 ++++++++++---------- docs/dist.min.js | 133 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 31 deletions(-) diff --git a/deno.json b/deno.json index 93dad19..0e91ce4 100644 --- a/deno.json +++ b/deno.json @@ -7,7 +7,7 @@ "compilerOptions": { "lib": ["deno.ns", "deno.unstable", "dom", "esnext"] }, - "exclude": ["node", "dist.js", "dist.min.js"], + "exclude": ["docs/dist.js", "docs/dist.min.js"], "tasks": { "test": "deno test -A", "cov": "deno test --coverage -A", diff --git a/docs/dist.js b/docs/dist.js index 40d34cd..06c4445 100644 --- a/docs/dist.js +++ b/docs/dist.js @@ -20,16 +20,17 @@ function logEvent({ component, e, module, - color + color, }) { - if (typeof __DEV__ === "boolean" && !__DEV__) + if (typeof __DEV__ === "boolean" && !__DEV__) { return; + } const event = e.type; console.groupCollapsed( `${module}> %c${event}%c on %c${component}`, boldColor(color || defaultEventColor), "", - boldColor("#1a80cc") + boldColor("#1a80cc"), ); console.log(e); if (e.target) { @@ -49,17 +50,17 @@ function assertComponentNameIsValid(name) { assert(typeof name === "string", "The name should be a string"); assert( !!registry[name], - `The component of the given name is not registered: ${name}` + `The component of the given name is not registered: ${name}`, ); } function register(component, name) { assert( typeof name === "string" && !!name, - "Component name must be a non-empty string" + "Component name must be a non-empty string", ); assert( !registry[name], - `The component of the given name is already registered: ${name}` + `The component of the given name is already registered: ${name}`, ); const initClass = `${name}-\u{1F48A}`; const initializer = (el) => { @@ -82,7 +83,7 @@ function register(component, name) { set(_2, type, value) { assert( typeof value === "function", - `Event handler must be a function, ${typeof value} (${value}) is given` + `Event handler must be a function, ${typeof value} (${value}) is given`, ); const listener = (e) => { if (el !== e.target && !el.contains(e.target)) { @@ -90,7 +91,7 @@ function register(component, name) { module: "outside", color: "#39cccc", e, - component: name + component: name, }); value(e); } @@ -100,7 +101,7 @@ function register(component, name) { document.removeEventListener(type, listener); }, { once: true }); return true; - } + }, }); } return null; @@ -110,7 +111,7 @@ function register(component, name) { const selector = args[0]; assert( typeof selector === "string", - "Delegation selector must be a string. ${typeof selector} is given." + "Delegation selector must be a string. ${typeof selector} is given.", ); return new Proxy({}, { set(_, type, value) { @@ -120,17 +121,17 @@ function register(component, name) { type, // deno-lint-ignore no-explicit-any value, - selector + selector, ); return true; - } + }, }); - } + }, }); const pub = (type, data) => { document.querySelectorAll(`.sub\\:${type}`).forEach((el2) => { el2.dispatchEvent( - new CustomEvent(type, { bubbles: false, detail: data }) + new CustomEvent(type, { bubbles: false, detail: data }), ); }); }; @@ -141,7 +142,7 @@ function register(component, name) { pub, sub, query: (s) => el.querySelector(s), - queryAll: (s) => el.querySelectorAll(s) + queryAll: (s) => el.querySelectorAll(s), }; const html = component(context); if (typeof html === "string") { @@ -158,18 +159,20 @@ function register(component, name) { function addEventListener(name, el, type, handler, selector) { assert( typeof handler === "function", - `Event handler must be a function, ${typeof handler} (${handler}) is given` + `Event handler must be a function, ${typeof handler} (${handler}) is given`, ); const listener = (e) => { - if (!selector || [].some.call( - el.querySelectorAll(selector), - (node) => node === e.target || node.contains(e.target) - )) { + if ( + !selector || [].some.call( + el.querySelectorAll(selector), + (node) => node === e.target || node.contains(e.target), + ) + ) { logEvent({ module: "\u{1F48A}", color: "#e0407b", e, - component: name + component: name, }); handler(e); } @@ -190,20 +193,16 @@ function mount(name, el) { classNames.map((className) => { [].map.call( (el || document).querySelectorAll(registry[className].sel), - registry[className] + registry[className], ); }); } function unmount(name, el) { assert( !!registry[name], - `The component of the given name is not registered: ${name}` + `The component of the given name is not registered: ${name}`, ); el.dispatchEvent(new CustomEvent(`__unmount__:${name}`)); } -export { - mount, - register, - unmount -}; +export { mount, register, unmount }; /*! Cell v0.1.0 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ diff --git a/docs/dist.min.js b/docs/dist.min.js index 28c37ea..b0e0735 100644 --- a/docs/dist.min.js +++ b/docs/dist.min.js @@ -1,2 +1,131 @@ -var e,t="readystatechange";var n=e=>`color: ${e}; font-weight: bold;`,o="#f012be";function s({component:e,e:t,module:s,color:r}){if("boolean"==typeof __DEV__&&!__DEV__)return;const c=t.type;console.groupCollapsed(`${s}> %c${c}%c on %c${e}`,n(r||o),"",n("#1a80cc")),console.log(t),t.target&&console.log(t.target),console.groupEnd()}var r={};function c(e,t){if(!e)throw new Error(t)}function i(n,o){c("string"==typeof o&&!!o,"Component name must be a non-empty string"),c(!r[o],`The component of the given name is already registered: ${o}`);const i=`${o}-💊`,u=e=>{if(!e.classList.contains(i)){e.classList.add(o),e.classList.add(i),e.addEventListener(`__ummount__:${o}`,(()=>{e.classList.remove(i)}),{once:!0});const t=new Proxy((()=>{}),{set:(t,n,s)=>(a(o,e,n,s),!0),get:(t,n)=>"outside"===n?new Proxy({},{set(t,n,r){c("function"==typeof r,`Event handler must be a function, ${typeof r} (${r}) is given`);const i=t=>{e===t.target||e.contains(t.target)||(s({module:"outside",color:"#39cccc",e:t,component:o}),r(t))};return document.addEventListener(n,i),e.addEventListener(`__unmount__:${o}`,(()=>{document.removeEventListener(n,i)}),{once:!0}),!0}}):null,apply(t,n,s){const r=s[0];return c("string"==typeof r,"Delegation selector must be a string. ${typeof selector} is given."),new Proxy({},{set:(t,n,s)=>(a(o,e,n,s,r),!0)})}}),r=t=>e.classList.add(`sub:${t}`),l=n({el:e,on:t,pub:(e,t)=>{document.querySelectorAll(`.sub\\:${e}`).forEach((n=>{n.dispatchEvent(new CustomEvent(e,{bubbles:!1,detail:t}))}))},sub:r,query:t=>e.querySelector(t),queryAll:t=>e.querySelectorAll(t)});"string"==typeof l&&(e.innerHTML=l)}};u.sel=`.${o}:not(.${i})`,r[o]=u,(e=e||new Promise((e=>{const n=document,o=()=>{"complete"===n.readyState&&(e(),n.removeEventListener(t,o))};n.addEventListener(t,o),o()}))).then((()=>{l(o)}))}function a(e,t,n,o,r){c("function"==typeof o,`Event handler must be a function, ${typeof o} (${o}) is given`);const i=n=>{r&&![].some.call(t.querySelectorAll(r),(e=>e===n.target||e.contains(n.target)))||(s({module:"💊",color:"#e0407b",e:n,component:e}),o(n))};t.addEventListener(`__unmount__:${e}`,(()=>{t.removeEventListener(n,i)}),{once:!0}),t.addEventListener(n,i)}function l(e,t){let n;e?(!function(e){c("string"==typeof e,"The name should be a string"),c(!!r[e],`The component of the given name is not registered: ${e}`)}(e),n=[e]):n=Object.keys(r),n.map((e=>{[].map.call((t||document).querySelectorAll(r[e].sel),r[e])}))}function u(e,t){c(!!r[e],`The component of the given name is not registered: ${e}`),t.dispatchEvent(new CustomEvent(`__unmount__:${e}`))}export{l as mount,i as register,u as unmount}; -/*! Cell v0.1.0 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ \ No newline at end of file +var e, t = "readystatechange"; +var n = (e) => `color: ${e}; font-weight: bold;`, o = "#f012be"; +function s({ component: e, e: t, module: s, color: r }) { + if ("boolean" == typeof __DEV__ && !__DEV__) return; + const c = t.type; + console.groupCollapsed( + `${s}> %c${c}%c on %c${e}`, + n(r || o), + "", + n("#1a80cc"), + ), + console.log(t), + t.target && console.log(t.target), + console.groupEnd(); +} +var r = {}; +function c(e, t) { + if (!e) throw new Error(t); +} +function i(n, o) { + c("string" == typeof o && !!o, "Component name must be a non-empty string"), + c(!r[o], `The component of the given name is already registered: ${o}`); + const i = `${o}-💊`, + u = (e) => { + if (!e.classList.contains(i)) { + e.classList.add(o), + e.classList.add(i), + e.addEventListener(`__ummount__:${o}`, () => { + e.classList.remove(i); + }, { once: !0 }); + const t = new Proxy(() => {}, { + set: (t, n, s) => (a(o, e, n, s), !0), + get: (t, n) => + "outside" === n + ? new Proxy({}, { + set(t, n, r) { + c( + "function" == typeof r, + `Event handler must be a function, ${typeof r} (${r}) is given`, + ); + const i = (t) => { + e === t.target || e.contains(t.target) || + (s({ + module: "outside", + color: "#39cccc", + e: t, + component: o, + }), + r(t)); + }; + return document.addEventListener(n, i), + e.addEventListener(`__unmount__:${o}`, () => { + document.removeEventListener(n, i); + }, { once: !0 }), + !0; + }, + }) + : null, + apply(t, n, s) { + const r = s[0]; + return c( + "string" == typeof r, + "Delegation selector must be a string. ${typeof selector} is given.", + ), + new Proxy({}, { set: (t, n, s) => (a(o, e, n, s, r), !0) }); + }, + }), + r = (t) => e.classList.add(`sub:${t}`), + l = n({ + el: e, + on: t, + pub: (e, t) => { + document.querySelectorAll(`.sub\\:${e}`).forEach((n) => { + n.dispatchEvent(new CustomEvent(e, { bubbles: !1, detail: t })); + }); + }, + sub: r, + query: (t) => e.querySelector(t), + queryAll: (t) => e.querySelectorAll(t), + }); + "string" == typeof l && (e.innerHTML = l); + } + }; + u.sel = `.${o}:not(.${i})`, + r[o] = u, + (e = e || new Promise((e) => { + const n = document, + o = () => { + "complete" === n.readyState && (e(), n.removeEventListener(t, o)); + }; + n.addEventListener(t, o), o(); + })).then(() => { + l(o); + }); +} +function a(e, t, n, o, r) { + c( + "function" == typeof o, + `Event handler must be a function, ${typeof o} (${o}) is given`, + ); + const i = (n) => { + r && + ![].some.call( + t.querySelectorAll(r), + (e) => e === n.target || e.contains(n.target), + ) || + (s({ module: "💊", color: "#e0407b", e: n, component: e }), o(n)); + }; + t.addEventListener(`__unmount__:${e}`, () => { + t.removeEventListener(n, i); + }, { once: !0 }), t.addEventListener(n, i); +} +function l(e, t) { + let n; + e + ? (!function (e) { + c("string" == typeof e, "The name should be a string"), + c(!!r[e], `The component of the given name is not registered: ${e}`); + }(e), + n = [e]) + : n = Object.keys(r), + n.map((e) => { + [].map.call((t || document).querySelectorAll(r[e].sel), r[e]); + }); +} +function u(e, t) { + c(!!r[e], `The component of the given name is not registered: ${e}`), + t.dispatchEvent(new CustomEvent(`__unmount__:${e}`)); +} +export { i as register, l as mount, u as unmount }; +/*! Cell v0.1.0 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ From efede4025e1186621e54ce4eb3e7b6bde8bdae3f Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 18:34:37 +0900 Subject: [PATCH 04/12] chore: update dist --- deno.lock | 71 ++++++++++++++++++++++++- docs/dist.js | 57 ++++++++++---------- docs/dist.min.js | 133 +---------------------------------------------- 3 files changed, 101 insertions(+), 160 deletions(-) diff --git a/deno.lock b/deno.lock index 3ca9e29..7a3e7cf 100644 --- a/deno.lock +++ b/deno.lock @@ -3,7 +3,8 @@ "packages": { "specifiers": { "jsr:@std/assert@^0.226.0": "jsr:@std/assert@0.226.0", - "jsr:@std/internal@^1.0.0": "jsr:@std/internal@1.0.0" + "jsr:@std/internal@^1.0.0": "jsr:@std/internal@1.0.0", + "npm:terser": "npm:terser@5.31.0" }, "jsr": { "@std/assert@0.226.0": { @@ -15,6 +16,74 @@ "@std/internal@1.0.0": { "integrity": "ac6a6dfebf838582c4b4f61a6907374e27e05bedb6ce276e0f1608fe84e7cd9a" } + }, + "npm": { + "@jridgewell/gen-mapping@0.3.5": { + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dependencies": { + "@jridgewell/set-array": "@jridgewell/set-array@1.2.1", + "@jridgewell/sourcemap-codec": "@jridgewell/sourcemap-codec@1.4.15", + "@jridgewell/trace-mapping": "@jridgewell/trace-mapping@0.3.25" + } + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dependencies": {} + }, + "@jridgewell/set-array@1.2.1": { + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dependencies": {} + }, + "@jridgewell/source-map@0.3.6": { + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dependencies": { + "@jridgewell/gen-mapping": "@jridgewell/gen-mapping@0.3.5", + "@jridgewell/trace-mapping": "@jridgewell/trace-mapping@0.3.25" + } + }, + "@jridgewell/sourcemap-codec@1.4.15": { + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dependencies": {} + }, + "@jridgewell/trace-mapping@0.3.25": { + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "@jridgewell/resolve-uri@3.1.2", + "@jridgewell/sourcemap-codec": "@jridgewell/sourcemap-codec@1.4.15" + } + }, + "acorn@8.11.3": { + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dependencies": {} + }, + "buffer-from@1.1.2": { + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dependencies": {} + }, + "commander@2.20.3": { + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dependencies": {} + }, + "source-map-support@0.5.21": { + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "buffer-from@1.1.2", + "source-map": "source-map@0.6.1" + } + }, + "source-map@0.6.1": { + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dependencies": {} + }, + "terser@5.31.0": { + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "dependencies": { + "@jridgewell/source-map": "@jridgewell/source-map@0.3.6", + "acorn": "acorn@8.11.3", + "commander": "commander@2.20.3", + "source-map-support": "source-map-support@0.5.21" + } + } } }, "remote": { diff --git a/docs/dist.js b/docs/dist.js index 06c4445..afb592d 100644 --- a/docs/dist.js +++ b/docs/dist.js @@ -20,17 +20,16 @@ function logEvent({ component, e, module, - color, + color }) { - if (typeof __DEV__ === "boolean" && !__DEV__) { + if (typeof __DEV__ === "boolean" && !__DEV__) return; - } const event = e.type; console.groupCollapsed( `${module}> %c${event}%c on %c${component}`, boldColor(color || defaultEventColor), "", - boldColor("#1a80cc"), + boldColor("#1a80cc") ); console.log(e); if (e.target) { @@ -50,17 +49,17 @@ function assertComponentNameIsValid(name) { assert(typeof name === "string", "The name should be a string"); assert( !!registry[name], - `The component of the given name is not registered: ${name}`, + `The component of the given name is not registered: ${name}` ); } function register(component, name) { assert( typeof name === "string" && !!name, - "Component name must be a non-empty string", + "Component name must be a non-empty string" ); assert( !registry[name], - `The component of the given name is already registered: ${name}`, + `The component of the given name is already registered: ${name}` ); const initClass = `${name}-\u{1F48A}`; const initializer = (el) => { @@ -83,7 +82,7 @@ function register(component, name) { set(_2, type, value) { assert( typeof value === "function", - `Event handler must be a function, ${typeof value} (${value}) is given`, + `Event handler must be a function, ${typeof value} (${value}) is given` ); const listener = (e) => { if (el !== e.target && !el.contains(e.target)) { @@ -91,7 +90,7 @@ function register(component, name) { module: "outside", color: "#39cccc", e, - component: name, + component: name }); value(e); } @@ -101,7 +100,7 @@ function register(component, name) { document.removeEventListener(type, listener); }, { once: true }); return true; - }, + } }); } return null; @@ -111,7 +110,7 @@ function register(component, name) { const selector = args[0]; assert( typeof selector === "string", - "Delegation selector must be a string. ${typeof selector} is given.", + "Delegation selector must be a string. ${typeof selector} is given." ); return new Proxy({}, { set(_, type, value) { @@ -121,17 +120,17 @@ function register(component, name) { type, // deno-lint-ignore no-explicit-any value, - selector, + selector ); return true; - }, + } }); - }, + } }); const pub = (type, data) => { document.querySelectorAll(`.sub\\:${type}`).forEach((el2) => { el2.dispatchEvent( - new CustomEvent(type, { bubbles: false, detail: data }), + new CustomEvent(type, { bubbles: false, detail: data }) ); }); }; @@ -142,7 +141,7 @@ function register(component, name) { pub, sub, query: (s) => el.querySelector(s), - queryAll: (s) => el.querySelectorAll(s), + queryAll: (s) => el.querySelectorAll(s) }; const html = component(context); if (typeof html === "string") { @@ -159,20 +158,18 @@ function register(component, name) { function addEventListener(name, el, type, handler, selector) { assert( typeof handler === "function", - `Event handler must be a function, ${typeof handler} (${handler}) is given`, + `Event handler must be a function, ${typeof handler} (${handler}) is given` ); const listener = (e) => { - if ( - !selector || [].some.call( - el.querySelectorAll(selector), - (node) => node === e.target || node.contains(e.target), - ) - ) { + if (!selector || [].some.call( + el.querySelectorAll(selector), + (node) => node === e.target || node.contains(e.target) + )) { logEvent({ module: "\u{1F48A}", color: "#e0407b", e, - component: name, + component: name }); handler(e); } @@ -193,16 +190,20 @@ function mount(name, el) { classNames.map((className) => { [].map.call( (el || document).querySelectorAll(registry[className].sel), - registry[className], + registry[className] ); }); } function unmount(name, el) { assert( !!registry[name], - `The component of the given name is not registered: ${name}`, + `The component of the given name is not registered: ${name}` ); el.dispatchEvent(new CustomEvent(`__unmount__:${name}`)); } -export { mount, register, unmount }; -/*! Cell v0.1.0 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ +export { + mount, + register, + unmount +}; +/*! Cell v0.1.4 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ diff --git a/docs/dist.min.js b/docs/dist.min.js index b0e0735..39ea702 100644 --- a/docs/dist.min.js +++ b/docs/dist.min.js @@ -1,131 +1,2 @@ -var e, t = "readystatechange"; -var n = (e) => `color: ${e}; font-weight: bold;`, o = "#f012be"; -function s({ component: e, e: t, module: s, color: r }) { - if ("boolean" == typeof __DEV__ && !__DEV__) return; - const c = t.type; - console.groupCollapsed( - `${s}> %c${c}%c on %c${e}`, - n(r || o), - "", - n("#1a80cc"), - ), - console.log(t), - t.target && console.log(t.target), - console.groupEnd(); -} -var r = {}; -function c(e, t) { - if (!e) throw new Error(t); -} -function i(n, o) { - c("string" == typeof o && !!o, "Component name must be a non-empty string"), - c(!r[o], `The component of the given name is already registered: ${o}`); - const i = `${o}-💊`, - u = (e) => { - if (!e.classList.contains(i)) { - e.classList.add(o), - e.classList.add(i), - e.addEventListener(`__ummount__:${o}`, () => { - e.classList.remove(i); - }, { once: !0 }); - const t = new Proxy(() => {}, { - set: (t, n, s) => (a(o, e, n, s), !0), - get: (t, n) => - "outside" === n - ? new Proxy({}, { - set(t, n, r) { - c( - "function" == typeof r, - `Event handler must be a function, ${typeof r} (${r}) is given`, - ); - const i = (t) => { - e === t.target || e.contains(t.target) || - (s({ - module: "outside", - color: "#39cccc", - e: t, - component: o, - }), - r(t)); - }; - return document.addEventListener(n, i), - e.addEventListener(`__unmount__:${o}`, () => { - document.removeEventListener(n, i); - }, { once: !0 }), - !0; - }, - }) - : null, - apply(t, n, s) { - const r = s[0]; - return c( - "string" == typeof r, - "Delegation selector must be a string. ${typeof selector} is given.", - ), - new Proxy({}, { set: (t, n, s) => (a(o, e, n, s, r), !0) }); - }, - }), - r = (t) => e.classList.add(`sub:${t}`), - l = n({ - el: e, - on: t, - pub: (e, t) => { - document.querySelectorAll(`.sub\\:${e}`).forEach((n) => { - n.dispatchEvent(new CustomEvent(e, { bubbles: !1, detail: t })); - }); - }, - sub: r, - query: (t) => e.querySelector(t), - queryAll: (t) => e.querySelectorAll(t), - }); - "string" == typeof l && (e.innerHTML = l); - } - }; - u.sel = `.${o}:not(.${i})`, - r[o] = u, - (e = e || new Promise((e) => { - const n = document, - o = () => { - "complete" === n.readyState && (e(), n.removeEventListener(t, o)); - }; - n.addEventListener(t, o), o(); - })).then(() => { - l(o); - }); -} -function a(e, t, n, o, r) { - c( - "function" == typeof o, - `Event handler must be a function, ${typeof o} (${o}) is given`, - ); - const i = (n) => { - r && - ![].some.call( - t.querySelectorAll(r), - (e) => e === n.target || e.contains(n.target), - ) || - (s({ module: "💊", color: "#e0407b", e: n, component: e }), o(n)); - }; - t.addEventListener(`__unmount__:${e}`, () => { - t.removeEventListener(n, i); - }, { once: !0 }), t.addEventListener(n, i); -} -function l(e, t) { - let n; - e - ? (!function (e) { - c("string" == typeof e, "The name should be a string"), - c(!!r[e], `The component of the given name is not registered: ${e}`); - }(e), - n = [e]) - : n = Object.keys(r), - n.map((e) => { - [].map.call((t || document).querySelectorAll(r[e].sel), r[e]); - }); -} -function u(e, t) { - c(!!r[e], `The component of the given name is not registered: ${e}`), - t.dispatchEvent(new CustomEvent(`__unmount__:${e}`)); -} -export { i as register, l as mount, u as unmount }; -/*! Cell v0.1.0 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ +var e,t="readystatechange";var n=e=>`color: ${e}; font-weight: bold;`,o="#f012be";function s({component:e,e:t,module:s,color:r}){if("boolean"==typeof __DEV__&&!__DEV__)return;const c=t.type;console.groupCollapsed(`${s}> %c${c}%c on %c${e}`,n(r||o),"",n("#1a80cc")),console.log(t),t.target&&console.log(t.target),console.groupEnd()}var r={};function c(e,t){if(!e)throw new Error(t)}function i(n,o){c("string"==typeof o&&!!o,"Component name must be a non-empty string"),c(!r[o],`The component of the given name is already registered: ${o}`);const i=`${o}-💊`,u=e=>{if(!e.classList.contains(i)){e.classList.add(o),e.classList.add(i),e.addEventListener(`__ummount__:${o}`,(()=>{e.classList.remove(i)}),{once:!0});const t=new Proxy((()=>{}),{set:(t,n,s)=>(a(o,e,n,s),!0),get:(t,n)=>"outside"===n?new Proxy({},{set(t,n,r){c("function"==typeof r,`Event handler must be a function, ${typeof r} (${r}) is given`);const i=t=>{e===t.target||e.contains(t.target)||(s({module:"outside",color:"#39cccc",e:t,component:o}),r(t))};return document.addEventListener(n,i),e.addEventListener(`__unmount__:${o}`,(()=>{document.removeEventListener(n,i)}),{once:!0}),!0}}):null,apply(t,n,s){const r=s[0];return c("string"==typeof r,"Delegation selector must be a string. ${typeof selector} is given."),new Proxy({},{set:(t,n,s)=>(a(o,e,n,s,r),!0)})}}),r=t=>e.classList.add(`sub:${t}`),l=n({el:e,on:t,pub:(e,t)=>{document.querySelectorAll(`.sub\\:${e}`).forEach((n=>{n.dispatchEvent(new CustomEvent(e,{bubbles:!1,detail:t}))}))},sub:r,query:t=>e.querySelector(t),queryAll:t=>e.querySelectorAll(t)});"string"==typeof l&&(e.innerHTML=l)}};u.sel=`.${o}:not(.${i})`,r[o]=u,(e=e||new Promise((e=>{const n=document,o=()=>{"complete"===n.readyState&&(e(),n.removeEventListener(t,o))};n.addEventListener(t,o),o()}))).then((()=>{l(o)}))}function a(e,t,n,o,r){c("function"==typeof o,`Event handler must be a function, ${typeof o} (${o}) is given`);const i=n=>{r&&![].some.call(t.querySelectorAll(r),(e=>e===n.target||e.contains(n.target)))||(s({module:"💊",color:"#e0407b",e:n,component:e}),o(n))};t.addEventListener(`__unmount__:${e}`,(()=>{t.removeEventListener(n,i)}),{once:!0}),t.addEventListener(n,i)}function l(e,t){let n;e?(!function(e){c("string"==typeof e,"The name should be a string"),c(!!r[e],`The component of the given name is not registered: ${e}`)}(e),n=[e]):n=Object.keys(r),n.map((e=>{[].map.call((t||document).querySelectorAll(r[e].sel),r[e])}))}function u(e,t){c(!!r[e],`The component of the given name is not registered: ${e}`),t.dispatchEvent(new CustomEvent(`__unmount__:${e}`))}export{l as mount,i as register,u as unmount}; +/*! Cell v0.1.4 | Copyright 2024 Yoshiya Hinosawa and Capsule contributors | MIT license */ \ No newline at end of file From ddf9784482bbf881111a0fb69751b79a876bd323 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 20:27:47 +0900 Subject: [PATCH 05/12] docs: update live demos --- README.md | 6 +- deno.json | 2 +- cell-logo.svg => docs/cell-logo.svg | 0 docs/dist.js | 2 + docs/dist.min.js | 2 +- index.html => docs/index.html | 124 +++++++++++++++------------- style.css => docs/style.css | 0 7 files changed, 74 insertions(+), 62 deletions(-) rename cell-logo.svg => docs/cell-logo.svg (100%) rename index.html => docs/index.html (65%) rename style.css => docs/style.css (100%) diff --git a/README.md b/README.md index 14318be..459a276 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -capsule +cell # Cell v0.1.4 @@ -59,11 +59,11 @@ Vanilla js (ES Module): ```html diff --git a/style.css b/docs/style.css similarity index 100% rename from style.css rename to docs/style.css From e7d8e9f8a13b530f63b10de08869cdceec200356 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 20:29:11 +0900 Subject: [PATCH 06/12] chore: update twd task --- deno.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deno.json b/deno.json index a2b0b3f..7627e4d 100644 --- a/deno.json +++ b/deno.json @@ -16,8 +16,7 @@ "dist": "deno run -A jsr:@kt3k/pack mod.ts > docs/dist.js", "min": "deno run -A npm:terser --compress --mangle --toplevel -o docs/dist.min.js -- docs/dist.js", "size": "deno run --allow-read https://deno.land/x/gzip_size@v0.3.0/cli.ts --include-original docs/dist.min.js", - "twd": "deno run -A --allow-read=. --allow-write=style.css --allow-net=deno.land,esm.sh,cdn.esm.sh https://deno.land/x/twd@v0.4.8/cli.ts -o style.css index.html", - "twd-w": "deno task twd -- -w", + "twd": "deno run -A --allow-read=. --allow-write=style.css --allow-net=deno.land,esm.sh,cdn.esm.sh https://deno.land/x/twd@v0.4.8/cli.ts -o docs/style.css docs/index.html", "start": "deno run --allow-sys --allow-read=. --allow-net=0.0.0.0:8000 jsr:@std/http/file-server docs --port 8000" }, "imports": { From 2d5555b08839b4bc33e359b451963d09eedc722e Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 20:29:50 +0900 Subject: [PATCH 07/12] chore: remove deploy.ts --- deploy.ts | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 deploy.ts diff --git a/deploy.ts b/deploy.ts deleted file mode 100644 index 9b8ee05..0000000 --- a/deploy.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { lookup } from "https://deno.land/x/media_types/mod.ts"; - -Deno.serve(async ({ url }) => { - let path = "." + new URL(url).pathname; - if (path.endsWith("/")) { - path += "index.html"; - } - return new Response(await Deno.readFile(path), { - headers: { "content-type": lookup(path) || "application/octet-stream" }, - }); -}); From 6522de2d13fe44deeba258e96fbea66dc7d2ff0d Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 18 Jun 2024 20:30:17 +0900 Subject: [PATCH 08/12] chore: remove loader.js --- loader.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 loader.js diff --git a/loader.js b/loader.js deleted file mode 100644 index 4899dd5..0000000 --- a/loader.js +++ /dev/null @@ -1 +0,0 @@ -globalThis.capsuleLoader = import("./dist.min.js"); From 871318ffa17a50fd80f2ce419f01d5db71eadbe8 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 19 Jun 2024 13:59:35 +0900 Subject: [PATCH 09/12] test: enable some tests --- deno.json | 4 +- mod.ts | 12 +- test.ts | 341 ++++++++++++++++++++++++++++-------------------------- util.ts | 6 +- 4 files changed, 188 insertions(+), 175 deletions(-) diff --git a/deno.json b/deno.json index 7627e4d..ebcaf0c 100644 --- a/deno.json +++ b/deno.json @@ -9,8 +9,8 @@ }, "exclude": ["docs/dist.js", "docs/dist.min.js"], "tasks": { - "test": "deno test -A", - "cov": "deno test --coverage -A", + "test": "deno test", + "cov": "deno test --coverage", "lcov": "deno coverage --lcov cov > lcov.info", "html_cov": "deno coverage --html", "dist": "deno run -A jsr:@kt3k/pack mod.ts > docs/dist.js", diff --git a/mod.ts b/mod.ts index 15ff480..87e717e 100644 --- a/mod.ts +++ b/mod.ts @@ -104,7 +104,7 @@ export function register(component: Component, name: string) { // when deno_dom fixes add class. el.classList.add(name); el.classList.add(initClass); - el.addEventListener(`__ummount__:${name}`, () => { + el.addEventListener(`__unmount__:${name}`, () => { el.classList.remove(initClass); }, { once: true }); @@ -200,9 +200,13 @@ export function register(component: Component, name: string) { registry[name] = initializer; - documentReady().then(() => { - mount(name); - }); + if (document.readyState === "complete") { + mount(); + } else { + documentReady().then(() => { + mount(name); + }); + } } function addEventListener( diff --git a/test.ts b/test.ts index 4e46cb2..c86e016 100644 --- a/test.ts +++ b/test.ts @@ -1,8 +1,8 @@ // Copyright 2022 Yoshiya Hinosawa. All rights reserved. MIT license. -import { assert, assertExists, assertFalse } from "@std/assert"; +import { assert, assertEquals, assertExists, assertThrows } from "@std/assert"; import "./dom_polyfill.ts"; -import { type Context, mount, register } from "./mod.ts"; +import { type Context, mount, register, unmount } from "./mod.ts"; // disable debug logs because it's too verbose for unit testing // deno-lint-ignore no-explicit-any @@ -20,11 +20,6 @@ Deno.test("Component body is called when the component is mounted", () => { } register(Component, name); - - assertFalse(called); - - mount(); - assert(called); }); @@ -32,6 +27,7 @@ Deno.test("sub() add sub:type class to the dom", () => { const name = randomName(); document.body.innerHTML = `
`; + const div = queryByClass(name); function Component({ el, sub }: Context) { el.classList.add("foo"); @@ -41,11 +37,6 @@ Deno.test("sub() add sub:type class to the dom", () => { register(Component, name); - mount(); - - const div = document.body.querySelector(`.${name}`); - - assertExists(div); assert(div.classList.contains("foo")); assert(div.classList.contains("sub:bar")); assert(div.innerHTML === "

hello

"); @@ -69,50 +60,55 @@ Deno.test("on.__unmount__ is called when the componet is unmounted", () => { unmount(name, query(`.${name}`)!); assert(called); }); +*/ Deno.test("unmount removes the event listeners", () => { const name = randomName(); - const { on } = component(name); document.body.innerHTML = `
`; - const el = queryByClass(name); + const div = queryByClass(name); let count = 0; - on["my-event"] = () => { - count++; - }; - mount(); + function Component({ on }: Context) { + on["my-event"] = () => { + count++; + }; + } + register(Component, name); + assertEquals(count, 0); - el?.dispatchEvent(new CustomEvent("my-event")); + div.dispatchEvent(new CustomEvent("my-event")); assertEquals(count, 1); - el?.dispatchEvent(new CustomEvent("my-event")); + div.dispatchEvent(new CustomEvent("my-event")); assertEquals(count, 2); - unmount(name, el!); - el?.dispatchEvent(new CustomEvent("my-event")); + unmount(name, div!); + div.dispatchEvent(new CustomEvent("my-event")); assertEquals(count, 2); }); Deno.test("on[event] is called when the event is dispatched", () => { const name = randomName(); - const { on } = component(name); document.body.innerHTML = `
`; + const div = queryByClass(name); let called = false; + function Component({ on }: Context) { + on.click = () => { + called = true; + }; + on.click = () => { + called = true; + }; + } + register(Component, name); - on.click = () => { - called = true; - }; - - mount(); - - query("div")?.dispatchEvent(new Event("click")); + div.dispatchEvent(new Event("click")); assert(called); }); Deno.test("on(selector)[event] is called when the event is dispatched only under the selector", async () => { const name = randomName(); - const { on } = component(name); document.body.innerHTML = `
`; @@ -120,21 +116,22 @@ Deno.test("on(selector)[event] is called when the event is dispatched only under let onBtn1ClickCalled = false; let onBtn2ClickCalled = false; - on(".btn1").click = () => { - onBtn1ClickCalled = true; - }; - - on(".btn2").click = () => { - onBtn2ClickCalled = true; - }; + function Component({ on }: Context) { + on(".btn1").click = () => { + onBtn1ClickCalled = true; + }; - mount(); + on(".btn2").click = () => { + onBtn2ClickCalled = true; + }; + } + register(Component, name); const btn = queryByClass("btn1"); // FIXME(kt3k): workaround for deno_dom & deno issue // deno_dom doesn't bubble event when the direct target dom doesn't have event handler - btn?.addEventListener("click", () => {}); - btn?.dispatchEvent(new Event("click", { bubbles: true })); + btn.addEventListener("click", () => {}); + btn.dispatchEvent(new Event("click", { bubbles: true })); await new Promise((r) => setTimeout(r, 100)); assert(onBtn1ClickCalled); @@ -143,18 +140,18 @@ Deno.test("on(selector)[event] is called when the event is dispatched only under Deno.test("on.outside.event works", () => { const name = randomName(); - const { on } = component(name); document.body.innerHTML = `
`; let calledCount = 0; + function Component({ on }: Context) { + on.outside.click = () => { + calledCount++; + }; + } + register(Component, name); - on.outside.click = () => { - calledCount++; - }; - - mount(); assertEquals(calledCount, 0); const sibling = queryByClass("sibling")!; @@ -169,24 +166,14 @@ Deno.test("on.outside.event works", () => { root.addEventListener("click", () => {}); root.dispatchEvent(new Event("click", { bubbles: true })); assertEquals(calledCount, 2); -}); -Deno.test("`is` works", () => { - const name = randomName(); - const { is } = component(name); - document.body.innerHTML = `
`; - is("foo"); - mount(); - assert(queryByClass(name)?.classList.contains("foo")); -}); -Deno.test("innerHTML works", () => { - const name = randomName(); - const { innerHTML } = component(name); - document.body.innerHTML = `
`; - innerHTML("

hello

"); - mount(); - assertEquals(queryByClass(name)?.innerHTML, "

hello

"); + // checks if the event listener is removed after unmount + unmount(name, queryByClass(name)); + sibling.dispatchEvent(new Event("click", { bubbles: true })); + root.dispatchEvent(new Event("click", { bubbles: true })); + assertEquals(calledCount, 2); }); + Deno.test("pub, sub works", () => { const EVENT = "my-event"; const name1 = randomName(); @@ -196,21 +183,18 @@ Deno.test("pub, sub works", () => {
`; - { - const { on, sub } = component(name1); + function SubComponent({ on, sub }: Context) { sub(EVENT); on[EVENT] = () => { subCalled = true; }; } - { - const { on } = component(name2); - on.__mount__ = ({ pub }) => { - pub(EVENT); - }; + function PubComponent({ pub }: Context) { + pub(EVENT); } + register(SubComponent, name1); assert(!subCalled); - mount(); + register(PubComponent, name2); assert(subCalled); }); @@ -223,132 +207,142 @@ Deno.test("query, queryAll works", () => {

baz

`; - const { on } = component(name); - on.__mount__ = ({ query, queryAll }) => { + function Component({ query, queryAll }: Context) { assert(query("p") !== null); assertEquals(query("p")?.textContent, "foo"); assertEquals(queryAll("p")[0].textContent, "foo"); assertEquals(queryAll("p")[1].textContent, "bar"); assertEquals(queryAll("p")[2].textContent, "baz"); - }; + } + register(Component, name); }); + Deno.test("assign wrong type to on.event, on.outside.event, on(selector).event", () => { - const { on } = component(randomName()); - assertThrows(() => { - on.click = ""; - }); - assertThrows(() => { - on.click = 1; - }); - assertThrows(() => { - on.click = Symbol(); - }); - assertThrows(() => { - on.click = {}; - }); - assertThrows(() => { - on.click = []; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(".btn").click = "" as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(".btn").click = 1 as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(".btn").click = Symbol() as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(".btn").click = {} as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(".btn").click = [] as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on.outside.click = "" as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on.outside.click = 1 as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on.outside.click = Symbol() as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on.outside.click = {} as any; - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on.outside.click = [] as any; - }); + function Component({ on }: Context) { + assertThrows(() => { + on.click = ""; + }); + assertThrows(() => { + on.click = 1; + }); + assertThrows(() => { + on.click = Symbol(); + }); + assertThrows(() => { + on.click = {}; + }); + assertThrows(() => { + on.click = []; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(".btn").click = "" as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(".btn").click = 1 as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(".btn").click = Symbol() as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(".btn").click = {} as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(".btn").click = [] as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on.outside.click = "" as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on.outside.click = 1 as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on.outside.click = Symbol() as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on.outside.click = {} as any; + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on.outside.click = [] as any; + }); + } + register(Component, randomName()); }); + Deno.test("wrong type selector throws with on(selector).event", () => { - const { on } = component(randomName()); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(1 as any); - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on(1n as any); - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on({} as any); - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on([] as any); - }); - assertThrows(() => { - // deno-lint-ignore no-explicit-any - on((() => {}) as any); - }); + function Component({ on }: Context) { + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(1 as any); + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on(1n as any); + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on({} as any); + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on([] as any); + }); + assertThrows(() => { + // deno-lint-ignore no-explicit-any + on((() => {}) as any); + }); + } + register(Component, randomName()); }); -Deno.test("component throws with non string input", () => { + +Deno.test("register throws with non string input", () => { + function Component() {} assertThrows(() => { // deno-lint-ignore no-explicit-any - component(1 as any); + register(Component, 1 as any); }); assertThrows(() => { // deno-lint-ignore no-explicit-any - component(1n as any); + register(Component, 1n as any); }); assertThrows(() => { // deno-lint-ignore no-explicit-any - component(Symbol() as any); + register(Component, Symbol() as any); }); assertThrows(() => { // empty name throws - component(""); + register(Component, ""); }); assertThrows(() => { // deno-lint-ignore no-explicit-any - component((() => {}) as any); + register(Component, (() => {}) as any); }); assertThrows(() => { // deno-lint-ignore no-explicit-any - component({} as any); + register(Component, {} as any); }); assertThrows(() => { // deno-lint-ignore no-explicit-any - component([] as any); + register(Component, [] as any); }); }); -Deno.test("component throws with already registered name", () => { + +Deno.test("register throws with already registered name", () => { const name = randomName(); - component(name); + function Component() {} + register(Component, name); assertThrows(() => { - component(name); + register(Component, name); }); }); @@ -358,11 +352,26 @@ Deno.test("unmount with non registered name throws", () => { }); }); +Deno.test("mount() throws with unregistered name", () => { + assertThrows(() => { + mount(randomName()); + }); +}); + +Deno.test("on.foo returns null", () => { + const name = randomName(); + document.body.innerHTML = `
`; -const query = (s: string) => document.querySelector(s); -const queryByClass = (name: string) => - document.querySelector(`.${name}`); + function Component({ on }: Context) { + assertEquals(on.foo, null); + } + register(Component, name); +}); -*/ // test utils const randomName = () => "c-" + Math.random().toString(36).slice(2); +const queryByClass = (name: string) => { + const el = document.querySelector(`.${name}`); + assertExists(el); + return el; +}; diff --git a/util.ts b/util.ts index aac4a92..ebed71c 100644 --- a/util.ts +++ b/util.ts @@ -3,9 +3,8 @@ const READY_STATE_CHANGE = "readystatechange"; let p: Promise; -export function documentReady() { - return p = p || new Promise((resolve) => { - const doc = document; +export function documentReady(doc = document) { + p ??= new Promise((resolve) => { const checkReady = () => { if (doc.readyState === "complete") { resolve(); @@ -17,6 +16,7 @@ export function documentReady() { checkReady(); }); + return p; } interface LogEventMessage { From ef002034157aa379001e36d00a3a38f8e62ad6fc Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 20 Jun 2024 00:32:26 +0900 Subject: [PATCH 10/12] docs: update live demo url --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 459a276..5c39281 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,11 @@ ## TodoMVC TodoMVC implementation is also available -[here](https://github.com/kt3k/cell-todomvc). +[here](https://github.com/kt3k/cell-todomvc) (WIP). ## Live examples -See [the live demos](https://celljs.deno.dev/). +See [the live demos](https://kt3k.github.io/cell). # Install From d518952dfce2301a9198ca674dd5df5eb3d2b2e2 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 20 Jun 2024 00:34:30 +0900 Subject: [PATCH 11/12] docs: fix vanilla example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c39281..0662b2a 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Vanilla js (ES Module):