From 8dadf133da5c4133fa4f84741c24ef19f6da1810 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 18:51:55 +0500 Subject: [PATCH 01/20] chore: ignore cdn --- biome.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biome.json b/biome.json index 5aa645b..a90ae3f 100644 --- a/biome.json +++ b/biome.json @@ -7,7 +7,7 @@ }, "files": { "ignoreUnknown": false, - "ignore": ["dist"] + "ignore": ["dist", "cdn"] }, "formatter": { "enabled": true, From a39209b2f6c17adbb525a35344004e4342637c29 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 18:54:12 +0500 Subject: [PATCH 02/20] feat: add benchie.js sources restored by deobfuscation --- cdn/benchie.js | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 cdn/benchie.js diff --git a/cdn/benchie.js b/cdn/benchie.js new file mode 100644 index 0000000..0f41269 --- /dev/null +++ b/cdn/benchie.js @@ -0,0 +1,113 @@ +const $__CDN_LIST = [ + window.location.origin, + // ... + // ... +]; + +/**! + * PathScale CONFIDENTIAL + * __ + * [2025] PathScale PTE Ltd + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains the property of PathScale PTE Ltd. + * The intellectual and technical concepts contained herein are proprietary to PathScale PTE Ltd + * and may be covered by Singapore and Foreign Patents, patents in process, and are protected + * by trade secret or copyright law. Dissemination of this information or reproduction of this + * material is strictly forbidden unless prior written permission is obtained from PathScale PTE Ltd. + */ + +let $__CDN; + +/** + * Fetches a resource by URL and caches it using the Cache API. + * Returns a blob URL. + */ +async function fetchAndCache(url, base) { + const absoluteUrl = new URL(url, base).href; + + if (!window.caches) return absoluteUrl; + + const cache = await window.caches.open("v1"); + let response = await cache.match(url); + + if (!response) { + response = await fetch(absoluteUrl, { method: "GET", cache: "no-store" }); + await cache.put(url, response.clone()); + } + + const blob = await response.blob(); + return URL.createObjectURL(blob); +} + +/** + * Replaces `data-href` and `data-src` with actual working `href`/`src` + * using fetchAndCache. + */ +async function resolveElementResource(el, cdnBase) { + if (el.dataset.href) { + if (el.href) URL.revokeObjectURL(el.href); + el.setAttribute("href", await fetchAndCache(el.dataset.href, cdnBase)); + } + + if (el.dataset.src) { + if (el.src) URL.revokeObjectURL(el.src); + el.setAttribute("src", await fetchAndCache(el.dataset.src, cdnBase)); + } +} + +window.addEventListener("DOMContentLoaded", async function () { + const abortController = new AbortController(); + + // Ping all CDNs in parallel — the first to respond wins + const cdnRace = $__CDN_LIST.map(url => + (async function tryHead(u, signal) { + await fetch(u, { + signal, + cache: "no-store", + mode: "no-cors", + method: "HEAD", + body: null + }); + return u; + })(url, abortController.signal) + ); + + $__CDN = await Promise.race(cdnRace); + abortController.abort(); + + // Find elements with data-src and data-href and update them + const dataSrcElements = Array.from(document.querySelectorAll("[data-src]")); + const dataHrefElements = Array.from(document.querySelectorAll("[data-href]")); + + const srcRewrites = dataSrcElements.map(el => + fetchAndCache(el.dataset.src, $__CDN).then(blobUrl => el.setAttribute("src", blobUrl)) + ); + + const hrefRewrites = dataHrefElements.map(el => + fetchAndCache(el.dataset.href, $__CDN).then(blobUrl => el.setAttribute("href", blobUrl)) + ); + + await Promise.all(srcRewrites); + await Promise.all(hrefRewrites); + + // Watch for DOM changes and apply blob URLs if needed + new MutationObserver(async (mutations) => { + for await (const mutation of mutations) { + if (mutation.type === "attributes") { + await resolveElementResource(mutation.target, $__CDN); + } else if (mutation.type === "childList") { + for await (const node of mutation.addedNodes) { + if ("dataset" in node) { + await resolveElementResource(node, $__CDN); + } + } + } + } + }).observe(document, { + attributes: true, + attributeFilter: ["data-src", "data-href"], + childList: true, + subtree: true + }); +}); From a6e86922875667fdbe4a86ddd575f72e6b32ce9b Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 19:14:17 +0500 Subject: [PATCH 03/20] chore: add .gitignore --- cdn/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 cdn/.gitignore diff --git a/cdn/.gitignore b/cdn/.gitignore new file mode 100644 index 0000000..72bf6dd --- /dev/null +++ b/cdn/.gitignore @@ -0,0 +1 @@ +benchie.min.js From 869b9f168c19e6604aecf93ead59f67b2bbc20c6 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 19:17:45 +0500 Subject: [PATCH 04/20] add build benchie script --- cdn/benchie.js | 6 ++++++ cdn/build-benchie.ts | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 cdn/build-benchie.ts diff --git a/cdn/benchie.js b/cdn/benchie.js index 0f41269..c5b4ea6 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -1,9 +1,15 @@ +/* + +// Define before: + const $__CDN_LIST = [ window.location.origin, // ... // ... ]; +*/ + /**! * PathScale CONFIDENTIAL * __ diff --git a/cdn/build-benchie.ts b/cdn/build-benchie.ts new file mode 100644 index 0000000..8de2e81 --- /dev/null +++ b/cdn/build-benchie.ts @@ -0,0 +1,9 @@ +await Bun.build({ + entrypoints: ['benchie.js'], + outdir: '.', + minify: true, + target: 'browser', + naming: "[dir]/[name].min.[ext]", +}); + +console.log('✅ benchie.min.js built using Bun'); From 1dda010b0193d990885a5d1ad99e9e9140e58d7b Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 19:22:48 +0500 Subject: [PATCH 05/20] feat: bun uses another format for keep comments --- cdn/benchie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdn/benchie.js b/cdn/benchie.js index c5b4ea6..51869ce 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -10,7 +10,7 @@ const $__CDN_LIST = [ */ -/**! +/*!* * PathScale CONFIDENTIAL * __ * [2025] PathScale PTE Ltd From 0e408a85690e322d8d46bdaf7d58495c2a924bfe Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 19:38:39 +0500 Subject: [PATCH 06/20] docs: add README.md --- cdn/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 cdn/README.md diff --git a/cdn/README.md b/cdn/README.md new file mode 100644 index 0000000..4bdfa84 --- /dev/null +++ b/cdn/README.md @@ -0,0 +1,3 @@ +```shell +bun run build-benchie.ts +``` From ba8867eb81d70cda4243c340195ab4e587371399 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 19:40:22 +0500 Subject: [PATCH 07/20] feat: inject new minified benchie code --- rollup.config.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 3e756bb..ecd5f83 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,3 +1,4 @@ +import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import zlib from "node:zlib"; @@ -112,15 +113,7 @@ const template = ({ attributes, files, meta, publicPath, title }) => { const $__CDN_LIST = [ window.location.origin ]; - /**! - * PathScale CONFIDENTIAL - * __ - * [2020] PathScale PTE Ltd - * All Rights Reserved. - * - * NOTICE: All information contained herein is, and remains the property of PathScale PTE Ltd. The intellectual and technical concepts contained herein are proprietary to PathScale PTE Ltd and may be covered by Singapore and Foreign Patents, patents in process, and are protected by trade secret or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from PathScale PTE Ltd. - */ - let $__CDN;async function t(t,e){const a=new URL(t,e).href;if(!window.caches)return a;const r=await window.caches.open("v1");let s=await r.match(t);s||(s=await fetch(a,{method:"GET",cache:"no-store"}),await r.put(t,s.clone()));const c=await s.blob();return URL.createObjectURL(c)}async function e(e,a){e.dataset.href&&(e.href&&URL.revokeObjectURL(e.href),e.setAttribute("href",await t(e.dataset.href,a))),e.dataset.src&&(e.src&&URL.revokeObjectURL(e.src),e.setAttribute("src",await t(e.dataset.src,a)))}window.addEventListener("DOMContentLoaded",(async function(){const a=new AbortController,r=$__CDN_LIST.map(t=>async function(t,e){return await fetch(t,{signal:e,cache:"no-store",mode:"no-cors",method:"HEAD",body:null}),t}(t,a.signal));$__CDN=await Promise.race(r),a.abort();const s=Array.from(document.querySelectorAll("[data-src]")),c=Array.from(document.querySelectorAll("[data-href]")),o=s.map(async e=>{const a=e.dataset.src;e.setAttribute("src",await t(a,$__CDN))}),n=c.map(async e=>{const a=e.dataset.href;e.setAttribute("href",await t(a,$__CDN))});await Promise.all(o),await Promise.all(n),new MutationObserver(async t=>{for await(const a of t)if("attributes"===a.type)await e(a.target,$__CDN);else if("childList"===a.type)for await(const t of a.addedNodes)"dataset"in t&&await e(t,$__CDN)}).observe(document,{attributes:!0,attributeFilter:["data-src","data-href"],childList:!0,subtree:!0})})); + ${fs.readFileSync("cdn/benchie.min.js", "utf8").trim()} ${links} From 90f3e9f3dba3d96e5c61844504e46b92d7af5a36 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 20:25:06 +0500 Subject: [PATCH 08/20] define benchie globals --- cdn/benchie.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cdn/benchie.js b/cdn/benchie.js index 51869ce..e451db8 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -46,6 +46,12 @@ async function fetchAndCache(url, base) { return URL.createObjectURL(blob); } +/** + * Make `fetchAndCache` globally available via `window.t` + * Useful for debugging or using manually from external scripts + */ +window.t = fetchAndCache + /** * Replaces `data-href` and `data-src` with actual working `href`/`src` * using fetchAndCache. @@ -82,6 +88,9 @@ window.addEventListener("DOMContentLoaded", async function () { $__CDN = await Promise.race(cdnRace); abortController.abort(); + // Expose the value to global scope so it won't be renamed + window.$__CDN = $__CDN; + // Find elements with data-src and data-href and update them const dataSrcElements = Array.from(document.querySelectorAll("[data-src]")); const dataHrefElements = Array.from(document.querySelectorAll("[data-href]")); From 5e254db903e361eca88e90778f75fd2e6497819e Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 21:08:51 +0500 Subject: [PATCH 09/20] feat: cdn meta replacement --- cdn/benchie.js | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/cdn/benchie.js b/cdn/benchie.js index e451db8..adede68 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -68,6 +68,33 @@ async function resolveElementResource(el, cdnBase) { } } +/** + * Helper to convert into a real + */ +async function resolveMetaCdnElement(metaEl, cdnBase) { + const content = metaEl.getAttribute('content'); + if (!content) return; + + const params = {}; + content.split(',').forEach((part) => { + const [key, value] = part.split('=').map((s) => s.trim()); + if (key && value) params[key] = value; + }); + + if (params.tag !== 'link') return; + + const rel = params.rel || 'stylesheet'; + const href = params.href; + if (!href) return; + + const blobUrl = await fetchAndCache(href, cdnBase); + const linkEl = document.createElement('link'); + linkEl.setAttribute('rel', rel); + linkEl.setAttribute('href', blobUrl); + + metaEl.replaceWith(linkEl); +} + window.addEventListener("DOMContentLoaded", async function () { const abortController = new AbortController(); @@ -91,10 +118,15 @@ window.addEventListener("DOMContentLoaded", async function () { // Expose the value to global scope so it won't be renamed window.$__CDN = $__CDN; - // Find elements with data-src and data-href and update them + // Collect elements for CDN resource resolution: + // - + // - elements with data-src or data-href + const cdnMetaElements = Array.from(document.querySelectorAll('meta[name="pathscale-cdn"]')); const dataSrcElements = Array.from(document.querySelectorAll("[data-src]")); const dataHrefElements = Array.from(document.querySelectorAll("[data-href]")); + const metaRewrites = cdnMetaElements.map(el => resolveMetaCdnElement(el)); + const srcRewrites = dataSrcElements.map(el => fetchAndCache(el.dataset.src, $__CDN).then(blobUrl => el.setAttribute("src", blobUrl)) ); @@ -103,6 +135,7 @@ window.addEventListener("DOMContentLoaded", async function () { fetchAndCache(el.dataset.href, $__CDN).then(blobUrl => el.setAttribute("href", blobUrl)) ); + await Promise.all(metaRewrites); await Promise.all(srcRewrites); await Promise.all(hrefRewrites); From d9f3a6629573caaff908e7403a1ee4b07071156f Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 21:15:53 +0500 Subject: [PATCH 10/20] feat: validate meta element --- cdn/benchie.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cdn/benchie.js b/cdn/benchie.js index adede68..89af314 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -72,6 +72,15 @@ async function resolveElementResource(el, cdnBase) { * Helper to convert into a real */ async function resolveMetaCdnElement(metaEl, cdnBase) { + // Ensure the element is the expected + if ( + !(metaEl instanceof Element) || + metaEl.tagName !== "META" || + metaEl.getAttribute("name") !== "pathscale-cdn" + ) { + return; + } + const content = metaEl.getAttribute('content'); if (!content) return; From cf4d5a32df03802ef2aadd47d84c66d6e4e81472 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 21:18:47 +0500 Subject: [PATCH 11/20] feat: meta cdn --- rollup.config.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index ecd5f83..5957f64 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -61,8 +61,9 @@ const template = ({ attributes, files, meta, publicPath, title }) => { const links = (files.css || []) .map(({ fileName }) => { const file = addVersion(fileName); - const attrs = makeHtmlAttributes(attributes.link); - return ``; + const href = `${publicPath}${file}`; + // It will be replaces by by Benchie in browser runtime + return ``; }) .join("\n"); From 0393d22e0ef0ef8599005bfb2c4c1f99bac1e57f Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 21:35:39 +0500 Subject: [PATCH 12/20] feat: meta cdn using json format --- cdn/benchie.js | 13 ++++++++----- rollup.config.js | 7 ++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/cdn/benchie.js b/cdn/benchie.js index 89af314..32b98a5 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -70,6 +70,7 @@ async function resolveElementResource(el, cdnBase) { /** * Helper to convert into a real + * Expects JSON in the `content` attribute */ async function resolveMetaCdnElement(metaEl, cdnBase) { // Ensure the element is the expected @@ -84,11 +85,13 @@ async function resolveMetaCdnElement(metaEl, cdnBase) { const content = metaEl.getAttribute('content'); if (!content) return; - const params = {}; - content.split(',').forEach((part) => { - const [key, value] = part.split('=').map((s) => s.trim()); - if (key && value) params[key] = value; - }); + let params; + try { + params = JSON.parse(content); + } catch { + console.warn('Invalid JSON in pathscale-cdn meta tag:', content); + return; + } if (params.tag !== 'link') return; diff --git a/rollup.config.js b/rollup.config.js index 5957f64..05b78bd 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -63,7 +63,12 @@ const template = ({ attributes, files, meta, publicPath, title }) => { const file = addVersion(fileName); const href = `${publicPath}${file}`; // It will be replaces by by Benchie in browser runtime - return ``; + const contentJson = JSON.stringify({ + tag: "link", + rel: "stylesheet", + href: href, + }); + return ``; }) .join("\n"); From 774ca563583dbf71b5c656b10fdf945a9259b363 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 21:41:15 +0500 Subject: [PATCH 13/20] fix: pass cdn base --- cdn/benchie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdn/benchie.js b/cdn/benchie.js index 32b98a5..bf25297 100644 --- a/cdn/benchie.js +++ b/cdn/benchie.js @@ -137,7 +137,7 @@ window.addEventListener("DOMContentLoaded", async function () { const dataSrcElements = Array.from(document.querySelectorAll("[data-src]")); const dataHrefElements = Array.from(document.querySelectorAll("[data-href]")); - const metaRewrites = cdnMetaElements.map(el => resolveMetaCdnElement(el)); + const metaRewrites = cdnMetaElements.map(el => resolveMetaCdnElement(el, $__CDN)); const srcRewrites = dataSrcElements.map(el => fetchAndCache(el.dataset.src, $__CDN).then(blobUrl => el.setAttribute("src", blobUrl)) From 337c8a8bda9e31807c7c15ca0d3296dfe55f8790 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 22:11:23 +0500 Subject: [PATCH 14/20] docs: add README.md --- cdn/README.md | 114 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/cdn/README.md b/cdn/README.md index 4bdfa84..19b34be 100644 --- a/cdn/README.md +++ b/cdn/README.md @@ -1,3 +1,115 @@ -```shell +# Benchie CDN Helper + +Benchie is a small helper script that dynamically selects the fastest CDN at runtime, based on response time. It fetches and caches resources like CSS or JS, and injects them into the DOM using blob URLs. This improves performance and flexibility without requiring HTML changes for different CDNs. + +--- + +## Setup + +To enable Benchie, include the following script in your ``: + +```html + +``` + +--- + +## Usage + +To declare resources that should be rewritten to blob URLs and loaded via the best available CDN, use the following HTML attributes: + +### 1. **Declarative `` tag** + +You can inject a `` element by declaring a special `` tag with the `name="pathscale-cdn"` and a JSON string as its `content`: + +```html + +``` + +This will be replaced at runtime with: + +```html + +``` + +**Important:** + +* The `content` value must be a **valid JSON string**. +* Currently only `tag: link` is supported. + +### 2. **Using `data-src` and `data-href`** + +For existing tags, you can use: + +```html + + +``` + +These will be automatically rewritten into: + +```html + + +``` + +--- + +## Global Helpers + +When Benchie loads, it exposes the following global values: + +### `window.t` + +A globally available async function for resolving blob URLs manually: + +```js +const blobUrl = await window.t('/some-resource.js', window.$__CDN); +``` + +### `window.$__CDN` + +The base CDN URL that was selected at runtime (e.g. `https://cdn.example.com`). +This is determined by racing all URLs from the `$__CDN_LIST`. + +--- + +## Build Instructions + +To build the minified version of Benchie: + +1. Navigate to the `cdn` directory: + +```bash +cd cdn +``` + +2. Run the build script (requires [Bun](https://bun.sh)): + +```bash bun run build-benchie.ts ``` + +This will generate `cdn/benchie.min.js` from the source file `cdn/benchie.js`, using Bun’s built-in minifier. + +--- + +## License + +This code is proprietary and confidential: + +``` +PathScale CONFIDENTIAL +© 2025 PathScale PTE Ltd. All rights reserved. +Do not distribute without prior written permission. +``` From 2afe87833d2d573aaae6eb25a191974008315a2b Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 22:13:13 +0500 Subject: [PATCH 15/20] docs: add README.md --- cdn/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdn/README.md b/cdn/README.md index 19b34be..8658ac2 100644 --- a/cdn/README.md +++ b/cdn/README.md @@ -1,6 +1,6 @@ # Benchie CDN Helper -Benchie is a small helper script that dynamically selects the fastest CDN at runtime, based on response time. It fetches and caches resources like CSS or JS, and injects them into the DOM using blob URLs. This improves performance and flexibility without requiring HTML changes for different CDNs. +Benchie is a small helper script that dynamically selects the fastest CDN at runtime, based on response time. It fetches and caches resources like images, css or js, and injects them into the DOM using blob URLs. This improves performance and flexibility without requiring HTML changes for different CDNs. --- From 0d4183aba567cf5d0cb8981aaafd71a7a826a66b Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 22:14:43 +0500 Subject: [PATCH 16/20] docs: add README.md --- cdn/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cdn/README.md b/cdn/README.md index 8658ac2..b8e4949 100644 --- a/cdn/README.md +++ b/cdn/README.md @@ -10,8 +10,8 @@ To enable Benchie, include the following script in your ``: ```html ``` + +> Using `` instead of `` ensures HTML5 validity, since the `href` attribute is required on `` elements. +> +> However, you can use `` if HTML5 validation is not a concern in your case. + --- ## Global Helpers From 013a1c005bd8c721b5c7540b37b5c901c31905c1 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 22:31:20 +0500 Subject: [PATCH 18/20] docs: add note about meta --- cdn/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cdn/README.md b/cdn/README.md index a9c3b55..bca5cb4 100644 --- a/cdn/README.md +++ b/cdn/README.md @@ -64,7 +64,9 @@ These will be automatically rewritten into: ``` -> Using `` instead of `` ensures HTML5 validity, since the `href` attribute is required on `` elements. +> ### 👉 Why meta?
+> Using `` instead of `` ensures HTML5 validity. +> Since the `href` attribute is required on `` elements. > > However, you can use `` if HTML5 validation is not a concern in your case. From 5f7fa1108cfb05610e325d6c0cdc402bf1f53211 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 22:31:57 +0500 Subject: [PATCH 19/20] fix: doctype at the very first line --- rollup.config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index 05b78bd..3f5a39b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -86,8 +86,7 @@ const template = ({ attributes, files, meta, publicPath, title }) => { const url = "https://vue3.dev"; const imageUrl = `${url}/vue3-ui.png`; - return ` - + return ` From 0d4e733646093e695f9935439b1773b5b79f13c7 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 3 May 2025 22:32:43 +0500 Subject: [PATCH 20/20] docs: br --- cdn/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdn/README.md b/cdn/README.md index bca5cb4..6c7eac7 100644 --- a/cdn/README.md +++ b/cdn/README.md @@ -65,7 +65,7 @@ These will be automatically rewritten into: > ### 👉 Why meta?
-> Using `` instead of `` ensures HTML5 validity. +> Using `` instead of `` ensures HTML5 validity.
> Since the `href` attribute is required on `` elements. > > However, you can use `` if HTML5 validation is not a concern in your case.