@markdy/astro 0.7.4 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/Markdy.astro +49 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/astro",
3
- "version": "0.7.4",
3
+ "version": "0.7.8",
4
4
  "description": "Astro island component for MarkdyScript animations.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,13 +37,13 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@markdy/renderer-dom": "0.7.4"
40
+ "@markdy/renderer-dom": "0.7.8"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "astro": ">=4.0.0"
44
44
  },
45
45
  "devDependencies": {
46
- "astro": "^6.1.5",
46
+ "astro": "^7.1.1",
47
47
  "typescript": "^5.9.3"
48
48
  }
49
49
  }
package/src/Markdy.astro CHANGED
@@ -121,24 +121,40 @@ const {
121
121
  </noscript>
122
122
  </div>
123
123
 
124
+ <!--
125
+ Rocket Loader rescue. Cloudflare Rocket Loader rewrites module script
126
+ tags in two ways, both of which prevent hydration:
127
+ (A) changes type="module" to a uuid-prefixed value while keeping the tag,
128
+ (B) wraps the whole script tag in an HTML comment, making it invisible to
129
+ querySelectorAll.
130
+
131
+ Two independent triggers run, either of which is sufficient:
132
+
133
+ 1. The inline script with data-cfasync="false" runs as authored when
134
+ Cloudflare honours the official cfasync opt-out (the documented
135
+ escape hatch; works almost always in practice).
136
+
137
+ 2. The img with an onerror handler is a true belt-and-suspenders
138
+ backup. Inline event handlers are never rewritten by Rocket Loader,
139
+ so this still fires when the inline script gets rewritten and never
140
+ executes. The invalid base64 in the data URL guarantees that the
141
+ decode fails synchronously without any network round trip. The
142
+ handler covers case (A) only — it re-injects every rewritten
143
+ module script — which is enough to bootstrap Markdy hydration even
144
+ when the main rescue script never runs.
145
+ -->
124
146
  <script is:inline data-cfasync="false">
125
- // ── Rocket Loader rescue ──────────────────────────────────────────────────
126
- // Cloudflare Rocket Loader rewrites <script type="module" src="..."> in
127
- // one of two ways, both of which prevent the hydration logic from running:
128
- //
129
- // A) Changes type="module" → type="{uuid}-module" (keeps tag in DOM)
130
- // B) Wraps the entire tag in an HTML comment:
131
- // <!--<script type="{uuid}-module" src="..."></script>-->
132
- // making it invisible to querySelectorAll.
133
- //
134
- // `data-cfasync="false"` keeps this rescue script executable even when
135
- // Rocket Loader is active. It rescues module scripts via two strategies:
136
- // 1. Query the DOM for type$="-module" + src (covers case A)
137
- // 2. Regex-parse the raw HTML for commented-out script tags (covers case B)
138
- // Dynamically-created scripts bypass Rocket Loader entirely.
139
147
  (function () {
140
- if (window.__markdyRescue) return;
141
- window.__markdyRescue = true;
148
+ if (window.__markdyRescued) return;
149
+ window.__markdyRescued = true;
150
+
151
+ // Build regexes from string parts so the literal token sequences for
152
+ // opening/closing HTML comments and the closing script tag never appear
153
+ // in this file's source. The HTML parser and Astro's JSX parser would
154
+ // otherwise treat them as special and corrupt the surrounding markup.
155
+ var OPEN = '\x3c' + '!--';
156
+ var CLOSE = '--' + '\x3e';
157
+ var CLOSE_SCRIPT = '\x3c' + '/script' + '\x3e';
142
158
 
143
159
  var rescued = false;
144
160
  function rescueModuleScripts() {
@@ -147,28 +163,24 @@ const {
147
163
 
148
164
  var srcs = [];
149
165
 
150
- // Strategy 1: Rocket Loader kept the tag in DOM but changed the type.
151
- // type="module" → type="{uuid}-module" (still has src attribute)
166
+ // Case (A): Rocket Loader kept the tag in DOM but mangled the type.
152
167
  document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) {
153
168
  srcs.push(s.src);
154
169
  });
155
170
 
156
- // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely:
157
- // <!--<script type="{uuid}-module" src="..."></script>-->
158
- // These are invisible to querySelectorAll, so we parse the raw HTML.
159
- // We handle both attribute orderings (type-first or src-first).
171
+ // Case (B): Rocket Loader commented out the entire script tag.
172
+ // These are invisible to querySelectorAll, so parse the raw HTML.
160
173
  var html = document.documentElement.innerHTML;
161
- var reSrcFirst = /<!--<script\s[^>]*src="([^"]+)"[^>]*type="[^"]*-module"[^>]*>\s*<\/script>-->/g;
162
- var reTypeFirst = /<!--<script\s[^>]*type="[^"]*-module"[^>]*src="([^"]+)"[^>]*>\s*<\/script>-->/g;
174
+ var reSrcFirst = new RegExp(OPEN + '\x3cscript\\s[^>]*src="([^"]+)"[^>]*type="[^"]*-module"[^>]*>\\s*' + CLOSE_SCRIPT + CLOSE, 'g');
175
+ var reTypeFirst = new RegExp(OPEN + '\x3cscript\\s[^>]*type="[^"]*-module"[^>]*src="([^"]+)"[^>]*>\\s*' + CLOSE_SCRIPT + CLOSE, 'g');
163
176
  var m;
164
177
  while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); }
165
178
  while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); }
166
179
 
167
- // Re-inject each found src as a real module script.
168
- // Deduplicate first, then inject. Dynamically-created scripts bypass
169
- // Rocket Loader entirely. Modules with the same URL are only executed
170
- // once by the browser (cached), so re-injecting already-running scripts
171
- // is safe.
180
+ // Re-inject each found src as a real module script. Dynamically-created
181
+ // scripts bypass Rocket Loader entirely; data-cfasync="false" is added
182
+ // as belt-and-suspenders. Modules with the same URL only execute once,
183
+ // so re-injecting an already-running script is safe.
172
184
  var seen = {};
173
185
  srcs.forEach(function (src) {
174
186
  if (seen[src]) return;
@@ -199,6 +211,14 @@ const {
199
211
  }());
200
212
  </script>
201
213
 
214
+ <img
215
+ src="data:image/gif;base64,!"
216
+ alt=""
217
+ aria-hidden="true"
218
+ style="display:none"
219
+ onerror="if(!window.__markdyRescued){window.__markdyRescued=true;document.querySelectorAll('script[type$=&quot;-module&quot;][src]').forEach(function(s){var f=document.createElement('script');f.type='module';f.src=s.src;f.setAttribute('data-cfasync','false');document.head.appendChild(f);});}"
220
+ />
221
+
202
222
  <script>
203
223
  // ── Hydration ────────────────────────────────────────────────────────────
204
224
  // The renderer is dynamically imported so it is code-split from the