astro 3.1.4 → 3.2.0
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.
- package/client.d.ts +7 -0
- package/components/ViewTransitions.astro +9 -431
- package/dist/assets/build/generate.js +2 -1
- package/dist/content/types-generator.js +2 -1
- package/dist/core/build/static-build.js +15 -4
- package/dist/core/constants.js +1 -1
- package/dist/core/create-vite.js +0 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/errors-data.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/render/params-and-props.js +4 -0
- package/dist/integrations/index.js +2 -1
- package/dist/transitions/router.d.ts +8 -0
- package/dist/transitions/router.js +324 -0
- package/dist/transitions/vite-plugin-transitions.js +10 -0
- package/dist/vite-plugin-astro-server/route.js +15 -14
- package/dist/vite-plugin-markdown/index.js +3 -2
- package/package.json +3 -2
package/client.d.ts
CHANGED
|
@@ -120,6 +120,13 @@ declare module 'astro:transitions' {
|
|
|
120
120
|
export const ViewTransitions: ViewTransitionsModule['default'];
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
declare module 'astro:transitions/client' {
|
|
124
|
+
type TransitionRouterModule = typeof import('./dist/transitions/router.js');
|
|
125
|
+
export const supportsViewTransitions: TransitionRouterModule['supportsViewTransitions'];
|
|
126
|
+
export const transitionEnabledOnThisPage: TransitionRouterModule['transitionEnabledOnThisPage'];
|
|
127
|
+
export const navigate: TransitionRouterModule['navigate'];
|
|
128
|
+
}
|
|
129
|
+
|
|
123
130
|
declare module 'astro:middleware' {
|
|
124
131
|
export * from 'astro/middleware/namespace';
|
|
125
132
|
}
|
|
@@ -11,91 +11,12 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
11
11
|
<meta name="astro-view-transitions-enabled" content="true" />
|
|
12
12
|
<meta name="astro-view-transitions-fallback" content={fallback} />
|
|
13
13
|
<script>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
intraPage?: boolean;
|
|
21
|
-
};
|
|
22
|
-
type Events = 'astro:page-load' | 'astro:after-swap';
|
|
23
|
-
|
|
24
|
-
// only update history entries that are managed by us
|
|
25
|
-
// leave other entries alone and do not accidently add state.
|
|
26
|
-
const persistState = (state: State) => history.state && history.replaceState(state, '');
|
|
27
|
-
// @ts-expect-error: startViewTransition might exist
|
|
28
|
-
const supportsViewTransitions = !!document.startViewTransition;
|
|
29
|
-
const transitionEnabledOnThisPage = () =>
|
|
30
|
-
!!document.querySelector('[name="astro-view-transitions-enabled"]');
|
|
31
|
-
const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name));
|
|
32
|
-
const onPageLoad = () => triggerEvent('astro:page-load');
|
|
33
|
-
const PERSIST_ATTR = 'data-astro-transition-persist';
|
|
34
|
-
const parser = new DOMParser();
|
|
35
|
-
// explained at its usage
|
|
36
|
-
let noopEl: HTMLDivElement;
|
|
37
|
-
if (import.meta.env.DEV) {
|
|
38
|
-
noopEl = document.createElement('div');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// The History API does not tell you if navigation is forward or back, so
|
|
42
|
-
// you can figure it using an index. On pushState the index is incremented so you
|
|
43
|
-
// can use that to determine popstate if going forward or back.
|
|
44
|
-
let currentHistoryIndex = 0;
|
|
45
|
-
if (history.state) {
|
|
46
|
-
// we reloaded a page with history state
|
|
47
|
-
// (e.g. history navigation from non-transition page or browser reload)
|
|
48
|
-
currentHistoryIndex = history.state.index;
|
|
49
|
-
scrollTo({ left: history.state.scrollX, top: history.state.scrollY });
|
|
50
|
-
} else if (transitionEnabledOnThisPage()) {
|
|
51
|
-
history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, '');
|
|
52
|
-
}
|
|
53
|
-
const throttle = (cb: (...args: any[]) => any, delay: number) => {
|
|
54
|
-
let wait = false;
|
|
55
|
-
// During the waiting time additional events are lost.
|
|
56
|
-
// So repeat the callback at the end if we have swallowed events.
|
|
57
|
-
let onceMore = false;
|
|
58
|
-
return (...args: any[]) => {
|
|
59
|
-
if (wait) {
|
|
60
|
-
onceMore = true;
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
cb(...args);
|
|
64
|
-
wait = true;
|
|
65
|
-
setTimeout(() => {
|
|
66
|
-
if (onceMore) {
|
|
67
|
-
onceMore = false;
|
|
68
|
-
cb(...args);
|
|
69
|
-
}
|
|
70
|
-
wait = false;
|
|
71
|
-
}, delay);
|
|
72
|
-
};
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// returns the contents of the page or null if the router can't deal with it.
|
|
76
|
-
async function fetchHTML(
|
|
77
|
-
href: string
|
|
78
|
-
): Promise<null | { html: string; redirected?: string; mediaType: DOMParserSupportedType }> {
|
|
79
|
-
try {
|
|
80
|
-
const res = await fetch(href);
|
|
81
|
-
// drop potential charset (+ other name/value pairs) as parser needs the mediaType
|
|
82
|
-
const mediaType = res.headers.get('content-type')?.replace(/;.*$/, '');
|
|
83
|
-
// the DOMParser can handle two types of HTML
|
|
84
|
-
if (mediaType !== 'text/html' && mediaType !== 'application/xhtml+xml') {
|
|
85
|
-
// everything else (e.g. audio/mp3) will be handled by the browser but not by us
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
const html = await res.text();
|
|
89
|
-
return {
|
|
90
|
-
html,
|
|
91
|
-
redirected: res.redirected ? res.url : undefined,
|
|
92
|
-
mediaType,
|
|
93
|
-
};
|
|
94
|
-
} catch (err) {
|
|
95
|
-
// can't fetch, let someone else deal with it.
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
14
|
+
import {
|
|
15
|
+
supportsViewTransitions,
|
|
16
|
+
transitionEnabledOnThisPage,
|
|
17
|
+
navigate,
|
|
18
|
+
} from 'astro:transitions/client';
|
|
19
|
+
export type Fallback = 'none' | 'animate' | 'swap';
|
|
99
20
|
|
|
100
21
|
function getFallback(): Fallback {
|
|
101
22
|
const el = document.querySelector('[name="astro-view-transitions-fallback"]');
|
|
@@ -105,263 +26,6 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
105
26
|
return 'animate';
|
|
106
27
|
}
|
|
107
28
|
|
|
108
|
-
function markScriptsExec() {
|
|
109
|
-
for (const script of document.scripts) {
|
|
110
|
-
script.dataset.astroExec = '';
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function runScripts() {
|
|
115
|
-
let wait = Promise.resolve();
|
|
116
|
-
for (const script of Array.from(document.scripts)) {
|
|
117
|
-
if (script.dataset.astroExec === '') continue;
|
|
118
|
-
const newScript = document.createElement('script');
|
|
119
|
-
newScript.innerHTML = script.innerHTML;
|
|
120
|
-
for (const attr of script.attributes) {
|
|
121
|
-
if (attr.name === 'src') {
|
|
122
|
-
const p = new Promise((r) => {
|
|
123
|
-
newScript.onload = r;
|
|
124
|
-
});
|
|
125
|
-
wait = wait.then(() => p as any);
|
|
126
|
-
}
|
|
127
|
-
newScript.setAttribute(attr.name, attr.value);
|
|
128
|
-
}
|
|
129
|
-
newScript.dataset.astroExec = '';
|
|
130
|
-
script.replaceWith(newScript);
|
|
131
|
-
}
|
|
132
|
-
return wait;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function isInfinite(animation: Animation) {
|
|
136
|
-
const effect = animation.effect;
|
|
137
|
-
if (!effect || !(effect instanceof KeyframeEffect) || !effect.target) return false;
|
|
138
|
-
const style = window.getComputedStyle(effect.target, effect.pseudoElement);
|
|
139
|
-
return style.animationIterationCount === 'infinite';
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const updateHistoryAndScrollPosition = (toLocation) => {
|
|
143
|
-
if (toLocation.href !== location.href) {
|
|
144
|
-
history.pushState(
|
|
145
|
-
{ index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 },
|
|
146
|
-
'',
|
|
147
|
-
toLocation.href
|
|
148
|
-
);
|
|
149
|
-
// now we are on the new page for non-history navigations!
|
|
150
|
-
// (with history navigation page change happens before popstate is fired)
|
|
151
|
-
}
|
|
152
|
-
// freshly loaded pages start from the top
|
|
153
|
-
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
|
154
|
-
|
|
155
|
-
if (toLocation.hash) {
|
|
156
|
-
// because we are already on the target page ...
|
|
157
|
-
// ... what comes next is a intra-page navigation
|
|
158
|
-
// that won't reload the page but instead scroll to the fragment
|
|
159
|
-
location.href = toLocation.href;
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// replace head and body of the windows document with contents from newDocument
|
|
164
|
-
// if !popstate, update the history entry and scroll position according to toLocation
|
|
165
|
-
// if popState is given, this holds the scroll position for history navigation
|
|
166
|
-
// if fallback === "animate" then simulate view transitions
|
|
167
|
-
async function updateDOM(
|
|
168
|
-
newDocument: Document,
|
|
169
|
-
toLocation: URL,
|
|
170
|
-
popState?: State,
|
|
171
|
-
fallback?: Fallback
|
|
172
|
-
) {
|
|
173
|
-
// Check for a head element that should persist and returns it,
|
|
174
|
-
// either because it has the data attribute or is a link el.
|
|
175
|
-
const persistedHeadElement = (el: HTMLElement): Element | null => {
|
|
176
|
-
const id = el.getAttribute(PERSIST_ATTR);
|
|
177
|
-
const newEl = id && newDocument.head.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
|
178
|
-
if (newEl) {
|
|
179
|
-
return newEl;
|
|
180
|
-
}
|
|
181
|
-
if (el.matches('link[rel=stylesheet]')) {
|
|
182
|
-
const href = el.getAttribute('href');
|
|
183
|
-
return newDocument.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
|
|
184
|
-
}
|
|
185
|
-
// What follows is a fix for an issue (#8472) with missing client:only styles after transition.
|
|
186
|
-
// That problem exists only in dev mode where styles are injected into the page by Vite.
|
|
187
|
-
// Returning a noop element ensures that the styles are not removed from the old document.
|
|
188
|
-
// Guarding the code below with the dev mode check
|
|
189
|
-
// allows tree shaking to remove this code in production.
|
|
190
|
-
if (import.meta.env.DEV) {
|
|
191
|
-
if (el.tagName === 'STYLE' && el.dataset.viteDevId) {
|
|
192
|
-
const devId = el.dataset.viteDevId;
|
|
193
|
-
// If this same style tag exists, remove it from the new page
|
|
194
|
-
return (
|
|
195
|
-
newDocument.querySelector(`style[data-astro-dev-id="${devId}"]`) ||
|
|
196
|
-
// Otherwise, keep it anyways. This is client:only styles.
|
|
197
|
-
noopEl
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return null;
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const swap = () => {
|
|
205
|
-
// swap attributes of the html element
|
|
206
|
-
// - delete all attributes from the current document
|
|
207
|
-
// - insert all attributes from doc
|
|
208
|
-
// - reinsert all original attributes that are named 'data-astro-*'
|
|
209
|
-
const html = document.documentElement;
|
|
210
|
-
const astro = [...html.attributes].filter(
|
|
211
|
-
({ name }) => (html.removeAttribute(name), name.startsWith('data-astro-'))
|
|
212
|
-
);
|
|
213
|
-
[...newDocument.documentElement.attributes, ...astro].forEach(({ name, value }) =>
|
|
214
|
-
html.setAttribute(name, value)
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// Replace scripts in both the head and body.
|
|
218
|
-
for (const s1 of document.scripts) {
|
|
219
|
-
for (const s2 of newDocument.scripts) {
|
|
220
|
-
if (
|
|
221
|
-
// Inline
|
|
222
|
-
(!s1.src && s1.textContent === s2.textContent) ||
|
|
223
|
-
// External
|
|
224
|
-
(s1.src && s1.type === s2.type && s1.src === s2.src)
|
|
225
|
-
) {
|
|
226
|
-
// the old script is in the new document: we mark it as executed to prevent re-execution
|
|
227
|
-
s2.dataset.astroExec = '';
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Swap head
|
|
234
|
-
for (const el of Array.from(document.head.children)) {
|
|
235
|
-
const newEl = persistedHeadElement(el as HTMLElement);
|
|
236
|
-
// If the element exists in the document already, remove it
|
|
237
|
-
// from the new document and leave the current node alone
|
|
238
|
-
if (newEl) {
|
|
239
|
-
newEl.remove();
|
|
240
|
-
} else {
|
|
241
|
-
// Otherwise remove the element in the head. It doesn't exist in the new page.
|
|
242
|
-
el.remove();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Everything left in the new head is new, append it all.
|
|
247
|
-
document.head.append(...newDocument.head.children);
|
|
248
|
-
|
|
249
|
-
// Persist elements in the existing body
|
|
250
|
-
const oldBody = document.body;
|
|
251
|
-
|
|
252
|
-
// this will reset scroll Position
|
|
253
|
-
document.body.replaceWith(newDocument.body);
|
|
254
|
-
for (const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) {
|
|
255
|
-
const id = el.getAttribute(PERSIST_ATTR);
|
|
256
|
-
const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
|
257
|
-
if (newEl) {
|
|
258
|
-
// The element exists in the new page, replace it with the element
|
|
259
|
-
// from the old page so that state is preserved.
|
|
260
|
-
newEl.replaceWith(el);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (popState) {
|
|
265
|
-
scrollTo(popState.scrollX, popState.scrollY); // usings 'auto' scrollBehavior
|
|
266
|
-
} else {
|
|
267
|
-
updateHistoryAndScrollPosition(toLocation);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
triggerEvent('astro:after-swap');
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
// Wait on links to finish, to prevent FOUC
|
|
274
|
-
const links: Promise<any>[] = [];
|
|
275
|
-
for (const el of newDocument.querySelectorAll('head link[rel=stylesheet]')) {
|
|
276
|
-
// Do not preload links that are already on the page.
|
|
277
|
-
if (
|
|
278
|
-
!document.querySelector(
|
|
279
|
-
`[${PERSIST_ATTR}="${el.getAttribute(PERSIST_ATTR)}"], link[rel=stylesheet]`
|
|
280
|
-
)
|
|
281
|
-
) {
|
|
282
|
-
const c = document.createElement('link');
|
|
283
|
-
c.setAttribute('rel', 'preload');
|
|
284
|
-
c.setAttribute('as', 'style');
|
|
285
|
-
c.setAttribute('href', el.getAttribute('href')!);
|
|
286
|
-
links.push(
|
|
287
|
-
new Promise<any>((resolve) => {
|
|
288
|
-
['load', 'error'].forEach((evName) => c.addEventListener(evName, resolve));
|
|
289
|
-
document.head.append(c);
|
|
290
|
-
})
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
links.length && (await Promise.all(links));
|
|
295
|
-
|
|
296
|
-
if (fallback === 'animate') {
|
|
297
|
-
// Trigger the animations
|
|
298
|
-
const currentAnimations = document.getAnimations();
|
|
299
|
-
document.documentElement.dataset.astroTransitionFallback = 'old';
|
|
300
|
-
const newAnimations = document
|
|
301
|
-
.getAnimations()
|
|
302
|
-
.filter((a) => !currentAnimations.includes(a) && !isInfinite(a));
|
|
303
|
-
const finished = Promise.all(newAnimations.map((a) => a.finished));
|
|
304
|
-
const fallbackSwap = () => {
|
|
305
|
-
swap();
|
|
306
|
-
document.documentElement.dataset.astroTransitionFallback = 'new';
|
|
307
|
-
};
|
|
308
|
-
await finished;
|
|
309
|
-
fallbackSwap();
|
|
310
|
-
} else {
|
|
311
|
-
swap();
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
async function transition(direction: Direction, toLocation: URL, popState?: State) {
|
|
316
|
-
let finished: Promise<void>;
|
|
317
|
-
const href = toLocation.href;
|
|
318
|
-
const response = await fetchHTML(href);
|
|
319
|
-
// If there is a problem fetching the new page, just do an MPA navigation to it.
|
|
320
|
-
if (response === null) {
|
|
321
|
-
location.href = href;
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
// if there was a redirection, show the final URL in the browser's address bar
|
|
325
|
-
if (response.redirected) {
|
|
326
|
-
toLocation = new URL(response.redirected);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const newDocument = parser.parseFromString(response.html, response.mediaType);
|
|
330
|
-
// The next line might look like a hack,
|
|
331
|
-
// but it is actually necessary as noscript elements
|
|
332
|
-
// and their contents are returned as markup by the parser,
|
|
333
|
-
// see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString
|
|
334
|
-
newDocument.querySelectorAll('noscript').forEach((el) => el.remove());
|
|
335
|
-
|
|
336
|
-
if (!newDocument.querySelector('[name="astro-view-transitions-enabled"]')) {
|
|
337
|
-
location.href = href;
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (!popState) {
|
|
342
|
-
// save the current scroll position before we change the DOM and transition to the new page
|
|
343
|
-
history.replaceState({ ...history.state, scrollX, scrollY }, '');
|
|
344
|
-
}
|
|
345
|
-
document.documentElement.dataset.astroTransition = direction;
|
|
346
|
-
if (supportsViewTransitions) {
|
|
347
|
-
// @ts-expect-error: startViewTransition exist
|
|
348
|
-
finished = document.startViewTransition(() =>
|
|
349
|
-
updateDOM(newDocument, toLocation, popState)
|
|
350
|
-
).finished;
|
|
351
|
-
} else {
|
|
352
|
-
finished = updateDOM(newDocument, toLocation, popState, getFallback());
|
|
353
|
-
}
|
|
354
|
-
try {
|
|
355
|
-
await finished;
|
|
356
|
-
} finally {
|
|
357
|
-
// skip this for the moment as it tends to stop fallback animations
|
|
358
|
-
// document.documentElement.removeAttribute('data-astro-transition');
|
|
359
|
-
await runScripts();
|
|
360
|
-
markScriptsExec();
|
|
361
|
-
onPageLoad();
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
29
|
// Prefetching
|
|
366
30
|
function maybePrefetch(pathname: string) {
|
|
367
31
|
if (document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return;
|
|
@@ -406,83 +70,9 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
406
70
|
return;
|
|
407
71
|
}
|
|
408
72
|
ev.preventDefault();
|
|
409
|
-
navigate(link.href
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
function navigate(href) {
|
|
413
|
-
// not ours
|
|
414
|
-
if (!transitionEnabledOnThisPage()) {
|
|
415
|
-
location.href = href;
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
const toLocation = new URL(href, location.href);
|
|
419
|
-
// We do not have page transitions on navigations to the same page (intra-page navigation)
|
|
420
|
-
// but we want to handle prevent reload on navigation to the same page
|
|
421
|
-
// Same page means same origin, path and query params (but maybe different hash)
|
|
422
|
-
if (
|
|
423
|
-
location.origin === toLocation.origin &&
|
|
424
|
-
location.pathname === toLocation.pathname &&
|
|
425
|
-
location.search === toLocation.search
|
|
426
|
-
) {
|
|
427
|
-
// mark current position as non transition intra-page scrolling
|
|
428
|
-
if (location.href !== toLocation.href) {
|
|
429
|
-
history.replaceState({ ...history.state, intraPage: true }, '');
|
|
430
|
-
history.pushState(
|
|
431
|
-
{ index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 },
|
|
432
|
-
'',
|
|
433
|
-
toLocation.href
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
if (toLocation.hash) {
|
|
437
|
-
location.href = toLocation.href;
|
|
438
|
-
} else {
|
|
439
|
-
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
|
440
|
-
}
|
|
441
|
-
} else {
|
|
442
|
-
transition('forward', toLocation);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
addEventListener('popstate', (ev) => {
|
|
447
|
-
if (!transitionEnabledOnThisPage() && ev.state) {
|
|
448
|
-
// The current page doesn't have View Transitions enabled
|
|
449
|
-
// but the page we navigate to does (because it set the state).
|
|
450
|
-
// Do a full page refresh to reload the client-side router from the new page.
|
|
451
|
-
// Scroll restauration will then happen during the reload when the router's code is re-executed
|
|
452
|
-
if (history.scrollRestoration) {
|
|
453
|
-
history.scrollRestoration = 'manual';
|
|
454
|
-
}
|
|
455
|
-
location.reload();
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// History entries without state are created by the browser (e.g. for hash links)
|
|
460
|
-
// Our view transition entries always have state.
|
|
461
|
-
// Just ignore stateless entries.
|
|
462
|
-
// The browser will handle navigation fine without our help
|
|
463
|
-
if (ev.state === null) {
|
|
464
|
-
if (history.scrollRestoration) {
|
|
465
|
-
history.scrollRestoration = 'auto';
|
|
466
|
-
}
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// With the default "auto", the browser will jump to the old scroll position
|
|
471
|
-
// before the ViewTransition is complete.
|
|
472
|
-
if (history.scrollRestoration) {
|
|
473
|
-
history.scrollRestoration = 'manual';
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const state: State = history.state;
|
|
477
|
-
if (state.intraPage) {
|
|
478
|
-
// this is non transition intra-page scrolling
|
|
479
|
-
scrollTo(state.scrollX, state.scrollY);
|
|
480
|
-
} else {
|
|
481
|
-
const nextIndex = state.index;
|
|
482
|
-
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
|
|
483
|
-
currentHistoryIndex = nextIndex;
|
|
484
|
-
transition(direction, new URL(location.href), state);
|
|
485
|
-
}
|
|
73
|
+
navigate(link.href, {
|
|
74
|
+
history: link.dataset.astroHistory === 'replace' ? 'replace' : 'auto',
|
|
75
|
+
});
|
|
486
76
|
});
|
|
487
77
|
|
|
488
78
|
['mouseenter', 'touchstart', 'focus'].forEach((evName) => {
|
|
@@ -503,17 +93,5 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
503
93
|
{ passive: true, capture: true }
|
|
504
94
|
);
|
|
505
95
|
});
|
|
506
|
-
|
|
507
|
-
addEventListener('load', onPageLoad);
|
|
508
|
-
// There's not a good way to record scroll position before a back button.
|
|
509
|
-
// So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position.
|
|
510
|
-
const updateState = () => {
|
|
511
|
-
persistState({ ...history.state, scrollX, scrollY });
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
if ('onscrollend' in window) addEventListener('scrollend', updateState);
|
|
515
|
-
else addEventListener('scroll', throttle(updateState, 300));
|
|
516
|
-
|
|
517
|
-
markScriptsExec();
|
|
518
96
|
}
|
|
519
97
|
</script>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs, { readFileSync } from "node:fs";
|
|
2
2
|
import { basename, join } from "node:path/posix";
|
|
3
|
+
import { getOutDirWithinCwd } from "../../core/build/common.js";
|
|
3
4
|
import { prependForwardSlash } from "../../core/path.js";
|
|
4
5
|
import { isServerLikeOutput } from "../../prerender/utils.js";
|
|
5
6
|
import { getConfiguredImageService, isESMImportedImage } from "../internal.js";
|
|
@@ -23,7 +24,7 @@ async function generateImage(pipeline, options, filepath) {
|
|
|
23
24
|
serverRoot = config.build.server;
|
|
24
25
|
clientRoot = config.build.client;
|
|
25
26
|
} else {
|
|
26
|
-
serverRoot = config.outDir;
|
|
27
|
+
serverRoot = getOutDirWithinCwd(config.outDir);
|
|
27
28
|
clientRoot = config.outDir;
|
|
28
29
|
}
|
|
29
30
|
const isLocalImage = isESMImportedImage(options.src);
|
|
@@ -53,7 +53,8 @@ async function createContentTypesGenerator({
|
|
|
53
53
|
objectMode: true
|
|
54
54
|
});
|
|
55
55
|
for (const entry of globResult) {
|
|
56
|
-
const
|
|
56
|
+
const fullPath = path.join(fileURLToPath(contentPaths.contentDir), entry.path);
|
|
57
|
+
const entryURL = pathToFileURL(fullPath);
|
|
57
58
|
if (entryURL.href.startsWith(contentPaths.config.url.href))
|
|
58
59
|
continue;
|
|
59
60
|
if (entry.dirent.isFile()) {
|
|
@@ -4,7 +4,7 @@ import glob from "fast-glob";
|
|
|
4
4
|
import { bgGreen, bgMagenta, black, dim } from "kleur/colors";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path, { extname } from "node:path";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
8
|
import * as vite from "vite";
|
|
9
9
|
import {
|
|
10
10
|
createBuildInternals,
|
|
@@ -109,7 +109,8 @@ async function ssrBuild(opts, internals, input, container) {
|
|
|
109
109
|
const viteBuildConfig = {
|
|
110
110
|
...viteConfig,
|
|
111
111
|
mode: viteConfig.mode || "production",
|
|
112
|
-
|
|
112
|
+
// Check using `settings...` as `viteConfig` always defaults to `warn` by Astro
|
|
113
|
+
logLevel: settings.config.vite.logLevel ?? "error",
|
|
113
114
|
build: {
|
|
114
115
|
target: "esnext",
|
|
115
116
|
// Vite defaults cssMinify to false in SSR by default, but we want to minify it
|
|
@@ -137,6 +138,14 @@ async function ssrBuild(opts, internals, input, container) {
|
|
|
137
138
|
const sanitizedName = name.split(".")[0];
|
|
138
139
|
return `chunks/${sanitizedName}_[hash].mjs`;
|
|
139
140
|
}
|
|
141
|
+
for (let i = 0; i < name.length; i++) {
|
|
142
|
+
if (name[i] === "%") {
|
|
143
|
+
const third = name.codePointAt(i + 2) | 32;
|
|
144
|
+
if (name[i + 1] !== "2" || third !== 102) {
|
|
145
|
+
return `chunks/${name.replace(/%/g, "_percent_")}_[hash].mjs`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
140
149
|
return `chunks/[name]_[hash].mjs`;
|
|
141
150
|
},
|
|
142
151
|
assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
|
|
@@ -199,7 +208,8 @@ ${bgGreen(black(" building client "))}`);
|
|
|
199
208
|
const viteBuildConfig = {
|
|
200
209
|
...viteConfig,
|
|
201
210
|
mode: viteConfig.mode || "production",
|
|
202
|
-
|
|
211
|
+
// Check using `settings...` as `viteConfig` always defaults to `warn` by Astro
|
|
212
|
+
logLevel: settings.config.vite.logLevel ?? "info",
|
|
203
213
|
build: {
|
|
204
214
|
target: "esnext",
|
|
205
215
|
...viteConfig.build,
|
|
@@ -240,7 +250,8 @@ async function runPostBuildHooks(container, ssrReturn, clientReturn) {
|
|
|
240
250
|
const build = container.options.settings.config.build;
|
|
241
251
|
for (const [fileName, mutation] of mutations) {
|
|
242
252
|
const root = isServerLikeOutput(config) ? mutation.build === "server" ? build.server : build.client : config.outDir;
|
|
243
|
-
const
|
|
253
|
+
const fullPath = path.join(fileURLToPath(root), fileName);
|
|
254
|
+
const fileURL = pathToFileURL(fullPath);
|
|
244
255
|
await fs.promises.mkdir(new URL("./", fileURL), { recursive: true });
|
|
245
256
|
await fs.promises.writeFile(fileURL, mutation.code, "utf-8");
|
|
246
257
|
}
|
package/dist/core/constants.js
CHANGED
package/dist/core/create-vite.js
CHANGED
|
@@ -188,7 +188,6 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
|
|
|
188
188
|
result = vite.mergeConfig(result, settings.config.vite || {});
|
|
189
189
|
}
|
|
190
190
|
result = vite.mergeConfig(result, commandConfig);
|
|
191
|
-
result.customLogger = vite.createLogger(result.logLevel ?? "warn");
|
|
192
191
|
return result;
|
|
193
192
|
}
|
|
194
193
|
const COMMON_DEPENDENCIES_NOT_ASTRO = [
|
package/dist/core/dev/dev.js
CHANGED
|
@@ -20,7 +20,7 @@ async function dev(inlineConfig) {
|
|
|
20
20
|
base: restart.container.settings.config.base
|
|
21
21
|
})
|
|
22
22
|
);
|
|
23
|
-
const currentVersion = "3.
|
|
23
|
+
const currentVersion = "3.2.0";
|
|
24
24
|
if (currentVersion.includes("-")) {
|
|
25
25
|
logger.warn(null, msg.prerelease({ currentVersion }));
|
|
26
26
|
}
|
|
@@ -203,7 +203,7 @@ const ImageNotFound = {
|
|
|
203
203
|
const NoImageMetadata = {
|
|
204
204
|
name: "NoImageMetadata",
|
|
205
205
|
title: "Could not process image metadata.",
|
|
206
|
-
message: (imagePath) => `Could not process image metadata${imagePath ?
|
|
206
|
+
message: (imagePath) => `Could not process image metadata${imagePath ? ` for \`${imagePath}\`` : ""}.`,
|
|
207
207
|
hint: "This is often caused by a corrupted or malformed image. Re-exporting the image from your image editor may fix this issue."
|
|
208
208
|
};
|
|
209
209
|
const MarkdownImageNotFound = {
|
package/dist/core/messages.js
CHANGED
|
@@ -50,7 +50,7 @@ function serverStart({
|
|
|
50
50
|
base,
|
|
51
51
|
isRestart = false
|
|
52
52
|
}) {
|
|
53
|
-
const version = "3.
|
|
53
|
+
const version = "3.2.0";
|
|
54
54
|
const localPrefix = `${dim("\u2503")} Local `;
|
|
55
55
|
const networkPrefix = `${dim("\u2503")} Network `;
|
|
56
56
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -235,7 +235,7 @@ function printHelp({
|
|
|
235
235
|
message.push(
|
|
236
236
|
linebreak(),
|
|
237
237
|
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
238
|
-
`v${"3.
|
|
238
|
+
`v${"3.2.0"}`
|
|
239
239
|
)} ${headline}`
|
|
240
240
|
);
|
|
241
241
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AstroError, AstroErrorData } from "../errors/index.js";
|
|
2
|
+
import { routeIsRedirect } from "../redirects/index.js";
|
|
2
3
|
import { getParams } from "../routing/params.js";
|
|
3
4
|
import { RouteCache, callGetStaticPaths, findPathItemByKey } from "./route-cache.js";
|
|
4
5
|
async function getParamsAndProps(opts) {
|
|
@@ -7,6 +8,9 @@ async function getParamsAndProps(opts) {
|
|
|
7
8
|
return [{}, {}];
|
|
8
9
|
}
|
|
9
10
|
const params = getRouteParams(route, pathname) ?? {};
|
|
11
|
+
if (routeIsRedirect(route)) {
|
|
12
|
+
return [params, {}];
|
|
13
|
+
}
|
|
10
14
|
validatePrerenderEndpointCollision(route, mod, params);
|
|
11
15
|
const staticPaths = await callGetStaticPaths({
|
|
12
16
|
mod,
|
|
@@ -41,7 +41,8 @@ async function runHookConfigSetup({
|
|
|
41
41
|
let updatedSettings = { ...settings, config: updatedConfig };
|
|
42
42
|
let addedClientDirectives = /* @__PURE__ */ new Map();
|
|
43
43
|
let astroJSXRenderer = null;
|
|
44
|
-
for (
|
|
44
|
+
for (let i = 0; i < updatedConfig.integrations.length; i++) {
|
|
45
|
+
const integration = updatedConfig.integrations[i];
|
|
45
46
|
if (integration.hooks?.["astro:config:setup"]) {
|
|
46
47
|
let addPageExtension2 = function(...input) {
|
|
47
48
|
const exts = input.flat(Infinity).map((ext) => `.${ext.replace(/^\./, "")}`);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Fallback = 'none' | 'animate' | 'swap';
|
|
2
|
+
export type Direction = 'forward' | 'back';
|
|
3
|
+
export type Options = {
|
|
4
|
+
history?: 'auto' | 'push' | 'replace';
|
|
5
|
+
};
|
|
6
|
+
export declare const supportsViewTransitions = true;
|
|
7
|
+
export declare const transitionEnabledOnThisPage: () => boolean;
|
|
8
|
+
export declare function navigate(href: string, options?: Options): void;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
const persistState = (state) => history.state && history.replaceState(state, "");
|
|
2
|
+
const supportsViewTransitions = !!document.startViewTransition;
|
|
3
|
+
const transitionEnabledOnThisPage = () => !!document.querySelector('[name="astro-view-transitions-enabled"]');
|
|
4
|
+
const samePage = (otherLocation) => location.pathname === otherLocation.pathname && location.search === otherLocation.search;
|
|
5
|
+
const triggerEvent = (name) => document.dispatchEvent(new Event(name));
|
|
6
|
+
const onPageLoad = () => triggerEvent("astro:page-load");
|
|
7
|
+
const announce = () => {
|
|
8
|
+
let div = document.createElement("div");
|
|
9
|
+
div.setAttribute("aria-live", "assertive");
|
|
10
|
+
div.setAttribute("aria-atomic", "true");
|
|
11
|
+
div.setAttribute(
|
|
12
|
+
"style",
|
|
13
|
+
"position:absolute;left:0;top:0;clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden;white-space:nowrap;width:1px;height:1px"
|
|
14
|
+
);
|
|
15
|
+
document.body.append(div);
|
|
16
|
+
setTimeout(
|
|
17
|
+
() => {
|
|
18
|
+
let title = document.title || document.querySelector("h1")?.textContent || location.pathname;
|
|
19
|
+
div.textContent = title;
|
|
20
|
+
},
|
|
21
|
+
// Much thought went into this magic number; the gist is that screen readers
|
|
22
|
+
// need to see that the element changed and might not do so if it happens
|
|
23
|
+
// too quickly.
|
|
24
|
+
60
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
const PERSIST_ATTR = "data-astro-transition-persist";
|
|
28
|
+
const parser = new DOMParser();
|
|
29
|
+
let noopEl;
|
|
30
|
+
if (import.meta.env.DEV) {
|
|
31
|
+
noopEl = document.createElement("div");
|
|
32
|
+
}
|
|
33
|
+
let currentHistoryIndex = 0;
|
|
34
|
+
if (history.state) {
|
|
35
|
+
currentHistoryIndex = history.state.index;
|
|
36
|
+
scrollTo({ left: history.state.scrollX, top: history.state.scrollY });
|
|
37
|
+
} else if (transitionEnabledOnThisPage()) {
|
|
38
|
+
history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, "");
|
|
39
|
+
}
|
|
40
|
+
const throttle = (cb, delay) => {
|
|
41
|
+
let wait = false;
|
|
42
|
+
let onceMore = false;
|
|
43
|
+
return (...args) => {
|
|
44
|
+
if (wait) {
|
|
45
|
+
onceMore = true;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
cb(...args);
|
|
49
|
+
wait = true;
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
if (onceMore) {
|
|
52
|
+
onceMore = false;
|
|
53
|
+
cb(...args);
|
|
54
|
+
}
|
|
55
|
+
wait = false;
|
|
56
|
+
}, delay);
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
async function fetchHTML(href) {
|
|
60
|
+
try {
|
|
61
|
+
const res = await fetch(href);
|
|
62
|
+
const mediaType = res.headers.get("content-type")?.replace(/;.*$/, "");
|
|
63
|
+
if (mediaType !== "text/html" && mediaType !== "application/xhtml+xml") {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const html = await res.text();
|
|
67
|
+
return {
|
|
68
|
+
html,
|
|
69
|
+
redirected: res.redirected ? res.url : void 0,
|
|
70
|
+
mediaType
|
|
71
|
+
};
|
|
72
|
+
} catch (err) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function getFallback() {
|
|
77
|
+
const el = document.querySelector('[name="astro-view-transitions-fallback"]');
|
|
78
|
+
if (el) {
|
|
79
|
+
return el.getAttribute("content");
|
|
80
|
+
}
|
|
81
|
+
return "animate";
|
|
82
|
+
}
|
|
83
|
+
function markScriptsExec() {
|
|
84
|
+
for (const script of document.scripts) {
|
|
85
|
+
script.dataset.astroExec = "";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function runScripts() {
|
|
89
|
+
let wait = Promise.resolve();
|
|
90
|
+
for (const script of Array.from(document.scripts)) {
|
|
91
|
+
if (script.dataset.astroExec === "")
|
|
92
|
+
continue;
|
|
93
|
+
const newScript = document.createElement("script");
|
|
94
|
+
newScript.innerHTML = script.innerHTML;
|
|
95
|
+
for (const attr of script.attributes) {
|
|
96
|
+
if (attr.name === "src") {
|
|
97
|
+
const p = new Promise((r) => {
|
|
98
|
+
newScript.onload = r;
|
|
99
|
+
});
|
|
100
|
+
wait = wait.then(() => p);
|
|
101
|
+
}
|
|
102
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
103
|
+
}
|
|
104
|
+
newScript.dataset.astroExec = "";
|
|
105
|
+
script.replaceWith(newScript);
|
|
106
|
+
}
|
|
107
|
+
return wait;
|
|
108
|
+
}
|
|
109
|
+
function isInfinite(animation) {
|
|
110
|
+
const effect = animation.effect;
|
|
111
|
+
if (!effect || !(effect instanceof KeyframeEffect) || !effect.target)
|
|
112
|
+
return false;
|
|
113
|
+
const style = window.getComputedStyle(effect.target, effect.pseudoElement);
|
|
114
|
+
return style.animationIterationCount === "infinite";
|
|
115
|
+
}
|
|
116
|
+
const updateHistoryAndScrollPosition = (toLocation, replace, intraPage) => {
|
|
117
|
+
const fresh = !samePage(toLocation);
|
|
118
|
+
if (toLocation.href !== location.href) {
|
|
119
|
+
if (replace) {
|
|
120
|
+
history.replaceState({ ...history.state }, "", toLocation.href);
|
|
121
|
+
} else {
|
|
122
|
+
history.replaceState({ ...history.state, intraPage }, "");
|
|
123
|
+
history.pushState({ index: ++currentHistoryIndex, scrollX, scrollY }, "", toLocation.href);
|
|
124
|
+
}
|
|
125
|
+
if (fresh) {
|
|
126
|
+
scrollTo({ left: 0, top: 0, behavior: "instant" });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (toLocation.hash) {
|
|
130
|
+
location.href = toLocation.href;
|
|
131
|
+
} else {
|
|
132
|
+
scrollTo({ left: 0, top: 0, behavior: "instant" });
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
async function updateDOM(newDocument, toLocation, options, popState, fallback) {
|
|
136
|
+
const persistedHeadElement = (el) => {
|
|
137
|
+
const id = el.getAttribute(PERSIST_ATTR);
|
|
138
|
+
const newEl = id && newDocument.head.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
|
139
|
+
if (newEl) {
|
|
140
|
+
return newEl;
|
|
141
|
+
}
|
|
142
|
+
if (el.matches("link[rel=stylesheet]")) {
|
|
143
|
+
const href = el.getAttribute("href");
|
|
144
|
+
return newDocument.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
|
|
145
|
+
}
|
|
146
|
+
if (import.meta.env.DEV) {
|
|
147
|
+
if (el.tagName === "STYLE" && el.dataset.viteDevId) {
|
|
148
|
+
const devId = el.dataset.viteDevId;
|
|
149
|
+
return newDocument.querySelector(`style[data-astro-dev-id="${devId}"]`) || // Otherwise, keep it anyways. This is client:only styles.
|
|
150
|
+
noopEl;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
};
|
|
155
|
+
const swap = () => {
|
|
156
|
+
const html = document.documentElement;
|
|
157
|
+
const astro = [...html.attributes].filter(
|
|
158
|
+
({ name }) => (html.removeAttribute(name), name.startsWith("data-astro-"))
|
|
159
|
+
);
|
|
160
|
+
[...newDocument.documentElement.attributes, ...astro].forEach(
|
|
161
|
+
({ name, value }) => html.setAttribute(name, value)
|
|
162
|
+
);
|
|
163
|
+
for (const s1 of document.scripts) {
|
|
164
|
+
for (const s2 of newDocument.scripts) {
|
|
165
|
+
if (
|
|
166
|
+
// Inline
|
|
167
|
+
!s1.src && s1.textContent === s2.textContent || // External
|
|
168
|
+
s1.src && s1.type === s2.type && s1.src === s2.src
|
|
169
|
+
) {
|
|
170
|
+
s2.dataset.astroExec = "";
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const el of Array.from(document.head.children)) {
|
|
176
|
+
const newEl = persistedHeadElement(el);
|
|
177
|
+
if (newEl) {
|
|
178
|
+
newEl.remove();
|
|
179
|
+
} else {
|
|
180
|
+
el.remove();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
document.head.append(...newDocument.head.children);
|
|
184
|
+
const oldBody = document.body;
|
|
185
|
+
document.body.replaceWith(newDocument.body);
|
|
186
|
+
for (const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) {
|
|
187
|
+
const id = el.getAttribute(PERSIST_ATTR);
|
|
188
|
+
const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
|
189
|
+
if (newEl) {
|
|
190
|
+
newEl.replaceWith(el);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (popState) {
|
|
194
|
+
scrollTo(popState.scrollX, popState.scrollY);
|
|
195
|
+
} else {
|
|
196
|
+
updateHistoryAndScrollPosition(toLocation, options.history === "replace", false);
|
|
197
|
+
}
|
|
198
|
+
triggerEvent("astro:after-swap");
|
|
199
|
+
};
|
|
200
|
+
const links = [];
|
|
201
|
+
for (const el of newDocument.querySelectorAll("head link[rel=stylesheet]")) {
|
|
202
|
+
if (!document.querySelector(
|
|
203
|
+
`[${PERSIST_ATTR}="${el.getAttribute(PERSIST_ATTR)}"], link[rel=stylesheet]`
|
|
204
|
+
)) {
|
|
205
|
+
const c = document.createElement("link");
|
|
206
|
+
c.setAttribute("rel", "preload");
|
|
207
|
+
c.setAttribute("as", "style");
|
|
208
|
+
c.setAttribute("href", el.getAttribute("href"));
|
|
209
|
+
links.push(
|
|
210
|
+
new Promise((resolve) => {
|
|
211
|
+
["load", "error"].forEach((evName) => c.addEventListener(evName, resolve));
|
|
212
|
+
document.head.append(c);
|
|
213
|
+
})
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
links.length && await Promise.all(links);
|
|
218
|
+
if (fallback === "animate") {
|
|
219
|
+
const currentAnimations = document.getAnimations();
|
|
220
|
+
document.documentElement.dataset.astroTransitionFallback = "old";
|
|
221
|
+
const newAnimations = document.getAnimations().filter((a) => !currentAnimations.includes(a) && !isInfinite(a));
|
|
222
|
+
const finished = Promise.all(newAnimations.map((a) => a.finished));
|
|
223
|
+
const fallbackSwap = () => {
|
|
224
|
+
swap();
|
|
225
|
+
document.documentElement.dataset.astroTransitionFallback = "new";
|
|
226
|
+
};
|
|
227
|
+
await finished;
|
|
228
|
+
fallbackSwap();
|
|
229
|
+
} else {
|
|
230
|
+
swap();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function transition(direction, toLocation, options, popState) {
|
|
234
|
+
let finished;
|
|
235
|
+
const href = toLocation.href;
|
|
236
|
+
const response = await fetchHTML(href);
|
|
237
|
+
if (response === null) {
|
|
238
|
+
location.href = href;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (response.redirected) {
|
|
242
|
+
toLocation = new URL(response.redirected);
|
|
243
|
+
}
|
|
244
|
+
const newDocument = parser.parseFromString(response.html, response.mediaType);
|
|
245
|
+
newDocument.querySelectorAll("noscript").forEach((el) => el.remove());
|
|
246
|
+
if (!newDocument.querySelector('[name="astro-view-transitions-enabled"]')) {
|
|
247
|
+
location.href = href;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (!popState) {
|
|
251
|
+
history.replaceState({ ...history.state, scrollX, scrollY }, "");
|
|
252
|
+
}
|
|
253
|
+
document.documentElement.dataset.astroTransition = direction;
|
|
254
|
+
if (supportsViewTransitions) {
|
|
255
|
+
finished = document.startViewTransition(
|
|
256
|
+
() => updateDOM(newDocument, toLocation, options, popState)
|
|
257
|
+
).finished;
|
|
258
|
+
} else {
|
|
259
|
+
finished = updateDOM(newDocument, toLocation, options, popState, getFallback());
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
await finished;
|
|
263
|
+
} finally {
|
|
264
|
+
await runScripts();
|
|
265
|
+
markScriptsExec();
|
|
266
|
+
onPageLoad();
|
|
267
|
+
announce();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function navigate(href, options) {
|
|
271
|
+
if (!transitionEnabledOnThisPage()) {
|
|
272
|
+
location.href = href;
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const toLocation = new URL(href, location.href);
|
|
276
|
+
if (location.origin === toLocation.origin && samePage(toLocation)) {
|
|
277
|
+
updateHistoryAndScrollPosition(toLocation, options?.history === "replace", true);
|
|
278
|
+
} else {
|
|
279
|
+
transition("forward", toLocation, options ?? {});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (supportsViewTransitions || getFallback() !== "none") {
|
|
283
|
+
addEventListener("popstate", (ev) => {
|
|
284
|
+
if (!transitionEnabledOnThisPage() && ev.state) {
|
|
285
|
+
if (history.scrollRestoration) {
|
|
286
|
+
history.scrollRestoration = "manual";
|
|
287
|
+
}
|
|
288
|
+
location.reload();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (ev.state === null) {
|
|
292
|
+
if (history.scrollRestoration) {
|
|
293
|
+
history.scrollRestoration = "auto";
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (history.scrollRestoration) {
|
|
298
|
+
history.scrollRestoration = "manual";
|
|
299
|
+
}
|
|
300
|
+
const state = history.state;
|
|
301
|
+
if (state.intraPage) {
|
|
302
|
+
scrollTo(state.scrollX, state.scrollY);
|
|
303
|
+
} else {
|
|
304
|
+
const nextIndex = state.index;
|
|
305
|
+
const direction = nextIndex > currentHistoryIndex ? "forward" : "back";
|
|
306
|
+
currentHistoryIndex = nextIndex;
|
|
307
|
+
transition(direction, new URL(location.href), {}, state);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
addEventListener("load", onPageLoad);
|
|
311
|
+
const updateState = () => {
|
|
312
|
+
persistState({ ...history.state, scrollX, scrollY });
|
|
313
|
+
};
|
|
314
|
+
if ("onscrollend" in window)
|
|
315
|
+
addEventListener("scrollend", updateState);
|
|
316
|
+
else
|
|
317
|
+
addEventListener("scroll", throttle(updateState, 300));
|
|
318
|
+
markScriptsExec();
|
|
319
|
+
}
|
|
320
|
+
export {
|
|
321
|
+
navigate,
|
|
322
|
+
supportsViewTransitions,
|
|
323
|
+
transitionEnabledOnThisPage
|
|
324
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as vite from "vite";
|
|
2
2
|
const virtualModuleId = "astro:transitions";
|
|
3
3
|
const resolvedVirtualModuleId = "\0" + virtualModuleId;
|
|
4
|
+
const virtualClientModuleId = "astro:transitions/client";
|
|
5
|
+
const resolvedVirtualClientModuleId = "\0" + virtualClientModuleId;
|
|
4
6
|
function astroTransitions() {
|
|
5
7
|
return {
|
|
6
8
|
name: "astro:transitions",
|
|
@@ -8,6 +10,9 @@ function astroTransitions() {
|
|
|
8
10
|
if (id === virtualModuleId) {
|
|
9
11
|
return resolvedVirtualModuleId;
|
|
10
12
|
}
|
|
13
|
+
if (id === virtualClientModuleId) {
|
|
14
|
+
return resolvedVirtualClientModuleId;
|
|
15
|
+
}
|
|
11
16
|
},
|
|
12
17
|
load(id) {
|
|
13
18
|
if (id === resolvedVirtualModuleId) {
|
|
@@ -16,6 +21,11 @@ function astroTransitions() {
|
|
|
16
21
|
export { default as ViewTransitions } from "astro/components/ViewTransitions.astro";
|
|
17
22
|
`;
|
|
18
23
|
}
|
|
24
|
+
if (id === resolvedVirtualClientModuleId) {
|
|
25
|
+
return `
|
|
26
|
+
export * from "astro/transitions/router";
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
19
29
|
}
|
|
20
30
|
};
|
|
21
31
|
}
|
|
@@ -155,19 +155,20 @@ async function handleRoute({
|
|
|
155
155
|
let response = await pipeline.renderRoute(renderContext, mod);
|
|
156
156
|
if (response.status === 404 && has404Route(manifestData)) {
|
|
157
157
|
const fourOhFourRoute = await matchRoute("/404", manifestData, pipeline);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
158
|
+
if (fourOhFourRoute?.route !== options.route)
|
|
159
|
+
return handleRoute({
|
|
160
|
+
...options,
|
|
161
|
+
matchedRoute: fourOhFourRoute,
|
|
162
|
+
url: new URL(pathname, url),
|
|
163
|
+
status: 404,
|
|
164
|
+
body,
|
|
165
|
+
origin,
|
|
166
|
+
pipeline,
|
|
167
|
+
manifestData,
|
|
168
|
+
incomingRequest,
|
|
169
|
+
incomingResponse,
|
|
170
|
+
manifest
|
|
171
|
+
});
|
|
171
172
|
}
|
|
172
173
|
if (route.type === "endpoint") {
|
|
173
174
|
await writeWebResponse(incomingResponse, response);
|
|
@@ -259,7 +260,7 @@ function getStatus(matchedRoute) {
|
|
|
259
260
|
return 500;
|
|
260
261
|
}
|
|
261
262
|
function has404Route(manifest) {
|
|
262
|
-
return manifest.routes.
|
|
263
|
+
return manifest.routes.some((route) => route.route === "/404");
|
|
263
264
|
}
|
|
264
265
|
export {
|
|
265
266
|
handleRoute,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import matter from "gray-matter";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
9
|
import { normalizePath } from "vite";
|
|
10
10
|
import { AstroError, AstroErrorData, MarkdownError } from "../core/errors/index.js";
|
|
11
11
|
import { isMarkdownFile } from "../core/util.js";
|
|
@@ -57,8 +57,9 @@ function markdown({ settings, logger }) {
|
|
|
57
57
|
const { fileId, fileUrl } = getFileInfo(id, settings.config);
|
|
58
58
|
const rawFile = await fs.promises.readFile(fileId, "utf-8");
|
|
59
59
|
const raw = safeMatter(rawFile, id);
|
|
60
|
+
const fileURL = pathToFileURL(fileId);
|
|
60
61
|
const renderResult = await processor.render(raw.content, {
|
|
61
|
-
fileURL
|
|
62
|
+
fileURL,
|
|
62
63
|
frontmatter: raw.data
|
|
63
64
|
}).catch((err) => {
|
|
64
65
|
if (err instanceof InvalidAstroDataError) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "withastro",
|
|
@@ -77,7 +77,8 @@
|
|
|
77
77
|
"types": "./dist/core/middleware/namespace.d.ts",
|
|
78
78
|
"default": "./dist/core/middleware/namespace.js"
|
|
79
79
|
},
|
|
80
|
-
"./transitions": "./dist/transitions/index.js"
|
|
80
|
+
"./transitions": "./dist/transitions/index.js",
|
|
81
|
+
"./transitions/router": "./dist/transitions/router.js"
|
|
81
82
|
},
|
|
82
83
|
"imports": {
|
|
83
84
|
"#astro/*": "./dist/*.js"
|