astro 3.1.1 → 3.1.3
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/components/ViewTransitions.astro +172 -123
- package/content-module.template.mjs +1 -1
- package/dist/assets/services/squoosh.js +3 -3
- package/dist/assets/utils/emitAsset.js +1 -4
- package/dist/assets/utils/metadata.d.ts +1 -1
- package/dist/assets/utils/metadata.js +6 -5
- package/dist/assets/vite-plugin-assets.js +17 -0
- package/dist/cli/add/index.js +10 -3
- package/dist/content/runtime.d.ts +3 -3
- package/dist/content/runtime.js +6 -11
- package/dist/core/build/generate.js +4 -2
- package/dist/core/build/plugins/plugin-middleware.js +1 -1
- package/dist/core/config/schema.d.ts +36 -60
- package/dist/core/config/schema.js +8 -23
- package/dist/core/constants.js +1 -1
- package/dist/core/cookies/index.d.ts +1 -1
- package/dist/core/cookies/index.js +7 -2
- package/dist/core/cookies/response.d.ts +1 -0
- package/dist/core/cookies/response.js +5 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/errors-data.d.ts +31 -0
- package/dist/core/errors/errors-data.js +14 -0
- package/dist/core/messages.js +2 -2
- package/dist/core/middleware/callMiddleware.js +9 -2
- package/dist/core/middleware/loadMiddleware.js +1 -1
- package/dist/events/session.d.ts +14 -14
- package/dist/events/session.js +61 -53
- package/dist/integrations/astroFeaturesValidation.js +2 -2
- package/dist/integrations/index.js +3 -3
- package/dist/runtime/server/scripts.js +3 -3
- package/dist/vite-plugin-astro/hmr.js +1 -9
- package/dist/vite-plugin-config-alias/index.js +7 -4
- package/dist/vite-plugin-markdown/index.js +18 -26
- package/package.json +2 -2
|
@@ -17,18 +17,26 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
17
17
|
index: number;
|
|
18
18
|
scrollX: number;
|
|
19
19
|
scrollY: number;
|
|
20
|
+
intraPage?: boolean;
|
|
20
21
|
};
|
|
21
22
|
type Events = 'astro:page-load' | 'astro:after-swap';
|
|
22
23
|
|
|
23
24
|
// only update history entries that are managed by us
|
|
24
25
|
// leave other entries alone and do not accidently add state.
|
|
25
26
|
const persistState = (state: State) => history.state && history.replaceState(state, '');
|
|
27
|
+
// @ts-expect-error: startViewTransition might exist
|
|
26
28
|
const supportsViewTransitions = !!document.startViewTransition;
|
|
27
29
|
const transitionEnabledOnThisPage = () =>
|
|
28
30
|
!!document.querySelector('[name="astro-view-transitions-enabled"]');
|
|
29
31
|
const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name));
|
|
30
32
|
const onPageLoad = () => triggerEvent('astro:page-load');
|
|
31
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
|
+
}
|
|
32
40
|
|
|
33
41
|
// The History API does not tell you if navigation is forward or back, so
|
|
34
42
|
// you can figure it using an index. On pushState the index is incremented so you
|
|
@@ -40,7 +48,7 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
40
48
|
currentHistoryIndex = history.state.index;
|
|
41
49
|
scrollTo({ left: history.state.scrollX, top: history.state.scrollY });
|
|
42
50
|
} else if (transitionEnabledOnThisPage()) {
|
|
43
|
-
history.replaceState({ index: currentHistoryIndex, scrollX, scrollY }, '');
|
|
51
|
+
history.replaceState({ index: currentHistoryIndex, scrollX, scrollY, intraPage: false }, '');
|
|
44
52
|
}
|
|
45
53
|
const throttle = (cb: (...args: any[]) => any, delay: number) => {
|
|
46
54
|
let wait = false;
|
|
@@ -64,19 +72,28 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
64
72
|
};
|
|
65
73
|
};
|
|
66
74
|
|
|
67
|
-
|
|
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 }> {
|
|
68
79
|
try {
|
|
69
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
|
+
}
|
|
70
88
|
const html = await res.text();
|
|
71
89
|
return {
|
|
72
|
-
ok: res.ok,
|
|
73
90
|
html,
|
|
74
91
|
redirected: res.redirected ? res.url : undefined,
|
|
75
|
-
|
|
76
|
-
mediaType: res.headers.get('content-type')?.replace(/;.*$/, ''),
|
|
92
|
+
mediaType,
|
|
77
93
|
};
|
|
78
94
|
} catch (err) {
|
|
79
|
-
|
|
95
|
+
// can't fetch, let someone else deal with it.
|
|
96
|
+
return null;
|
|
80
97
|
}
|
|
81
98
|
}
|
|
82
99
|
|
|
@@ -98,19 +115,19 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
98
115
|
let wait = Promise.resolve();
|
|
99
116
|
for (const script of Array.from(document.scripts)) {
|
|
100
117
|
if (script.dataset.astroExec === '') continue;
|
|
101
|
-
const
|
|
102
|
-
|
|
118
|
+
const newScript = document.createElement('script');
|
|
119
|
+
newScript.innerHTML = script.innerHTML;
|
|
103
120
|
for (const attr of script.attributes) {
|
|
104
121
|
if (attr.name === 'src') {
|
|
105
122
|
const p = new Promise((r) => {
|
|
106
|
-
|
|
123
|
+
newScript.onload = r;
|
|
107
124
|
});
|
|
108
125
|
wait = wait.then(() => p as any);
|
|
109
126
|
}
|
|
110
|
-
|
|
127
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
111
128
|
}
|
|
112
|
-
|
|
113
|
-
script.replaceWith(
|
|
129
|
+
newScript.dataset.astroExec = '';
|
|
130
|
+
script.replaceWith(newScript);
|
|
114
131
|
}
|
|
115
132
|
return wait;
|
|
116
133
|
}
|
|
@@ -122,46 +139,60 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
122
139
|
return style.animationIterationCount === 'infinite';
|
|
123
140
|
}
|
|
124
141
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
+
};
|
|
131
162
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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.
|
|
135
175
|
const persistedHeadElement = (el: HTMLElement): Element | null => {
|
|
136
176
|
const id = el.getAttribute(PERSIST_ATTR);
|
|
137
|
-
const newEl = id &&
|
|
177
|
+
const newEl = id && newDocument.head.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
|
138
178
|
if (newEl) {
|
|
139
179
|
return newEl;
|
|
140
180
|
}
|
|
141
181
|
if (el.matches('link[rel=stylesheet]')) {
|
|
142
182
|
const href = el.getAttribute('href');
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
if (el.tagName === 'SCRIPT') {
|
|
146
|
-
let s1 = el as HTMLScriptElement;
|
|
147
|
-
for (const s2 of doc.scripts) {
|
|
148
|
-
if (
|
|
149
|
-
// Inline
|
|
150
|
-
(s1.textContent && s1.textContent === s2.textContent) ||
|
|
151
|
-
// External
|
|
152
|
-
(s1.type === s2.type && s1.src === s2.src)
|
|
153
|
-
) {
|
|
154
|
-
return s2;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
183
|
+
return newDocument.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
|
|
157
184
|
}
|
|
158
|
-
//
|
|
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.
|
|
159
190
|
if (import.meta.env.DEV) {
|
|
160
191
|
if (el.tagName === 'STYLE' && el.dataset.viteDevId) {
|
|
161
192
|
const devId = el.dataset.viteDevId;
|
|
162
193
|
// If this same style tag exists, remove it from the new page
|
|
163
194
|
return (
|
|
164
|
-
|
|
195
|
+
newDocument.querySelector(`style[data-astro-dev-id="${devId}"]`) ||
|
|
165
196
|
// Otherwise, keep it anyways. This is client:only styles.
|
|
166
197
|
noopEl
|
|
167
198
|
);
|
|
@@ -171,10 +202,6 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
171
202
|
};
|
|
172
203
|
|
|
173
204
|
const swap = () => {
|
|
174
|
-
// noscript tags inside head element are not honored on swap (#7969).
|
|
175
|
-
// Remove them before swapping.
|
|
176
|
-
doc.querySelectorAll('head noscript').forEach((el) => el.remove());
|
|
177
|
-
|
|
178
205
|
// swap attributes of the html element
|
|
179
206
|
// - delete all attributes from the current document
|
|
180
207
|
// - insert all attributes from doc
|
|
@@ -183,10 +210,26 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
183
210
|
const astro = [...html.attributes].filter(
|
|
184
211
|
({ name }) => (html.removeAttribute(name), name.startsWith('data-astro-'))
|
|
185
212
|
);
|
|
186
|
-
[...
|
|
213
|
+
[...newDocument.documentElement.attributes, ...astro].forEach(({ name, value }) =>
|
|
187
214
|
html.setAttribute(name, value)
|
|
188
215
|
);
|
|
189
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
|
+
|
|
190
233
|
// Swap head
|
|
191
234
|
for (const el of Array.from(document.head.children)) {
|
|
192
235
|
const newEl = persistedHeadElement(el as HTMLElement);
|
|
@@ -199,12 +242,15 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
199
242
|
el.remove();
|
|
200
243
|
}
|
|
201
244
|
}
|
|
245
|
+
|
|
202
246
|
// Everything left in the new head is new, append it all.
|
|
203
|
-
document.head.append(...
|
|
247
|
+
document.head.append(...newDocument.head.children);
|
|
204
248
|
|
|
205
249
|
// Persist elements in the existing body
|
|
206
250
|
const oldBody = document.body;
|
|
207
|
-
|
|
251
|
+
|
|
252
|
+
// this will reset scroll Position
|
|
253
|
+
document.body.replaceWith(newDocument.body);
|
|
208
254
|
for (const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) {
|
|
209
255
|
const id = el.getAttribute(PERSIST_ATTR);
|
|
210
256
|
const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
|
@@ -215,39 +261,18 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
215
261
|
}
|
|
216
262
|
}
|
|
217
263
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
let initialScrollX = 0;
|
|
223
|
-
let initialScrollY = 0;
|
|
224
|
-
if (!state && loc.hash) {
|
|
225
|
-
const id = decodeURIComponent(loc.hash.slice(1));
|
|
226
|
-
const elem = document.getElementById(id);
|
|
227
|
-
// prefer scrollIntoView() over scrollTo() because it takes scroll-padding into account
|
|
228
|
-
if (elem) {
|
|
229
|
-
elem.scrollIntoView();
|
|
230
|
-
initialScrollX = Math.max(
|
|
231
|
-
0,
|
|
232
|
-
elem.offsetLeft + elem.offsetWidth - document.documentElement.clientWidth
|
|
233
|
-
);
|
|
234
|
-
initialScrollY = elem.offsetTop;
|
|
235
|
-
}
|
|
236
|
-
} else if (state) {
|
|
237
|
-
scrollTo(state.scrollX, state.scrollY); // usings default scrollBehavior
|
|
264
|
+
if (popState) {
|
|
265
|
+
scrollTo(popState.scrollX, popState.scrollY); // usings 'auto' scrollBehavior
|
|
266
|
+
} else {
|
|
267
|
+
updateHistoryAndScrollPosition(toLocation);
|
|
238
268
|
}
|
|
239
|
-
|
|
240
|
-
history.pushState(
|
|
241
|
-
{ index: ++currentHistoryIndex, scrollX: initialScrollX, scrollY: initialScrollY },
|
|
242
|
-
'',
|
|
243
|
-
loc.href
|
|
244
|
-
);
|
|
269
|
+
|
|
245
270
|
triggerEvent('astro:after-swap');
|
|
246
271
|
};
|
|
247
272
|
|
|
248
273
|
// Wait on links to finish, to prevent FOUC
|
|
249
274
|
const links: Promise<any>[] = [];
|
|
250
|
-
for (const el of
|
|
275
|
+
for (const el of newDocument.querySelectorAll('head link[rel=stylesheet]')) {
|
|
251
276
|
// Do not preload links that are already on the page.
|
|
252
277
|
if (
|
|
253
278
|
!document.querySelector(
|
|
@@ -287,32 +312,44 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
287
312
|
}
|
|
288
313
|
}
|
|
289
314
|
|
|
290
|
-
async function
|
|
315
|
+
async function transition(direction: Direction, toLocation: URL, popState?: State) {
|
|
291
316
|
let finished: Promise<void>;
|
|
292
|
-
const href =
|
|
293
|
-
const
|
|
294
|
-
// if there was a redirection, show the final URL in the browser's address bar
|
|
295
|
-
redirected && (loc = new URL(redirected));
|
|
317
|
+
const href = toLocation.href;
|
|
318
|
+
const response = await fetchHTML(href);
|
|
296
319
|
// If there is a problem fetching the new page, just do an MPA navigation to it.
|
|
297
|
-
if (
|
|
320
|
+
if (response === null) {
|
|
298
321
|
location.href = href;
|
|
299
322
|
return;
|
|
300
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());
|
|
301
335
|
|
|
302
|
-
|
|
303
|
-
if (!doc.querySelector('[name="astro-view-transitions-enabled"]')) {
|
|
336
|
+
if (!newDocument.querySelector('[name="astro-view-transitions-enabled"]')) {
|
|
304
337
|
location.href = href;
|
|
305
338
|
return;
|
|
306
339
|
}
|
|
307
340
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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;
|
|
312
346
|
if (supportsViewTransitions) {
|
|
313
|
-
|
|
347
|
+
// @ts-expect-error: startViewTransition exist
|
|
348
|
+
finished = document.startViewTransition(() =>
|
|
349
|
+
updateDOM(newDocument, toLocation, popState)
|
|
350
|
+
).finished;
|
|
314
351
|
} else {
|
|
315
|
-
finished = updateDOM(
|
|
352
|
+
finished = updateDOM(newDocument, toLocation, popState, getFallback());
|
|
316
353
|
}
|
|
317
354
|
try {
|
|
318
355
|
await finished;
|
|
@@ -328,7 +365,9 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
328
365
|
// Prefetching
|
|
329
366
|
function maybePrefetch(pathname: string) {
|
|
330
367
|
if (document.querySelector(`link[rel=prefetch][href="${pathname}"]`)) return;
|
|
368
|
+
// @ts-expect-error: connection might exist
|
|
331
369
|
if (navigator.connection) {
|
|
370
|
+
// @ts-expect-error: connection does exist
|
|
332
371
|
let conn = navigator.connection;
|
|
333
372
|
if (conn.saveData || /(2|3)g/.test(conn.effectiveType || '')) return;
|
|
334
373
|
}
|
|
@@ -339,8 +378,6 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
339
378
|
}
|
|
340
379
|
|
|
341
380
|
if (supportsViewTransitions || getFallback() !== 'none') {
|
|
342
|
-
markScriptsExec();
|
|
343
|
-
|
|
344
381
|
document.addEventListener('click', (ev) => {
|
|
345
382
|
let link = ev.target;
|
|
346
383
|
if (link instanceof Element && link.tagName !== 'A') {
|
|
@@ -362,43 +399,49 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
362
399
|
ev.ctrlKey || // new tab (windows)
|
|
363
400
|
ev.altKey || // download
|
|
364
401
|
ev.shiftKey || // new window
|
|
365
|
-
ev.defaultPrevented
|
|
366
|
-
!transitionEnabledOnThisPage()
|
|
402
|
+
ev.defaultPrevented
|
|
367
403
|
) {
|
|
368
404
|
// No page transitions in these cases,
|
|
369
405
|
// Let the browser standard action handle this
|
|
370
406
|
return;
|
|
371
407
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
408
|
+
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;
|
|
378
438
|
} else {
|
|
379
|
-
// Special case: self link without hash
|
|
380
|
-
// If handed to the browser it will reload the page
|
|
381
|
-
// But we want to handle it like any other same page navigation
|
|
382
|
-
// So we scroll to the top of the page but do not start page transitions
|
|
383
|
-
ev.preventDefault();
|
|
384
|
-
// push state on the first navigation but not if we were here already
|
|
385
|
-
if (location.hash) {
|
|
386
|
-
history.replaceState(
|
|
387
|
-
{ index: currentHistoryIndex, scrollX, scrollY: -(scrollY + 1) },
|
|
388
|
-
''
|
|
389
|
-
);
|
|
390
|
-
const newState: State = { index: ++currentHistoryIndex, scrollX: 0, scrollY: 0 };
|
|
391
|
-
history.pushState(newState, '', link.href);
|
|
392
|
-
}
|
|
393
439
|
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
|
394
|
-
return;
|
|
395
440
|
}
|
|
441
|
+
} else {
|
|
442
|
+
transition('forward', toLocation);
|
|
396
443
|
}
|
|
397
|
-
|
|
398
|
-
// these are the cases we will handle: same origin, different page
|
|
399
|
-
ev.preventDefault();
|
|
400
|
-
navigate('forward', new URL(link.href));
|
|
401
|
-
});
|
|
444
|
+
}
|
|
402
445
|
|
|
403
446
|
addEventListener('popstate', (ev) => {
|
|
404
447
|
if (!transitionEnabledOnThisPage() && ev.state) {
|
|
@@ -406,7 +449,9 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
406
449
|
// but the page we navigate to does (because it set the state).
|
|
407
450
|
// Do a full page refresh to reload the client-side router from the new page.
|
|
408
451
|
// Scroll restauration will then happen during the reload when the router's code is re-executed
|
|
409
|
-
|
|
452
|
+
if (history.scrollRestoration) {
|
|
453
|
+
history.scrollRestoration = 'manual';
|
|
454
|
+
}
|
|
410
455
|
location.reload();
|
|
411
456
|
return;
|
|
412
457
|
}
|
|
@@ -429,13 +474,14 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
429
474
|
}
|
|
430
475
|
|
|
431
476
|
const state: State = history.state;
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (state.scrollY < 0) {
|
|
436
|
-
scrollTo(state.scrollX, -(state.scrollY + 1));
|
|
477
|
+
if (state.intraPage) {
|
|
478
|
+
// this is non transition intra-page scrolling
|
|
479
|
+
scrollTo(state.scrollX, state.scrollY);
|
|
437
480
|
} else {
|
|
438
|
-
|
|
481
|
+
const nextIndex = state.index;
|
|
482
|
+
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
|
|
483
|
+
currentHistoryIndex = nextIndex;
|
|
484
|
+
transition(direction, new URL(location.href), state);
|
|
439
485
|
}
|
|
440
486
|
});
|
|
441
487
|
|
|
@@ -457,6 +503,7 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
457
503
|
{ passive: true, capture: true }
|
|
458
504
|
);
|
|
459
505
|
});
|
|
506
|
+
|
|
460
507
|
addEventListener('load', onPageLoad);
|
|
461
508
|
// There's not a good way to record scroll position before a back button.
|
|
462
509
|
// So the way we do it is by listening to scrollend if supported, and if not continuously record the scroll position.
|
|
@@ -466,5 +513,7 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|
|
466
513
|
|
|
467
514
|
if ('onscrollend' in window) addEventListener('scrollend', updateState);
|
|
468
515
|
else addEventListener('scroll', throttle(updateState, 300));
|
|
516
|
+
|
|
517
|
+
markScriptsExec();
|
|
469
518
|
}
|
|
470
519
|
</script>
|
|
@@ -70,7 +70,7 @@ export const getEntryBySlug = createGetEntryBySlug({
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
export const getDataEntryById = createGetDataEntryById({
|
|
73
|
-
dataCollectionToEntryMap,
|
|
73
|
+
getEntryImport: createGlobLookup(dataCollectionToEntryMap),
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
export const getEntry = createGetEntry({
|
|
@@ -18,8 +18,8 @@ const qualityTable = {
|
|
|
18
18
|
webp: baseQuality
|
|
19
19
|
// Squoosh's PNG encoder does not support a quality setting, so we can skip that here
|
|
20
20
|
};
|
|
21
|
-
async function getRotationForEXIF(inputBuffer) {
|
|
22
|
-
const meta = await imageMetadata(inputBuffer);
|
|
21
|
+
async function getRotationForEXIF(inputBuffer, src) {
|
|
22
|
+
const meta = await imageMetadata(inputBuffer, src);
|
|
23
23
|
if (!meta)
|
|
24
24
|
return void 0;
|
|
25
25
|
switch (meta.orientation) {
|
|
@@ -45,7 +45,7 @@ const service = {
|
|
|
45
45
|
if (format === "svg")
|
|
46
46
|
return { data: inputBuffer, format: "svg" };
|
|
47
47
|
const operations = [];
|
|
48
|
-
const rotation = await getRotationForEXIF(inputBuffer);
|
|
48
|
+
const rotation = await getRotationForEXIF(inputBuffer, transform.src);
|
|
49
49
|
if (rotation) {
|
|
50
50
|
operations.push(rotation);
|
|
51
51
|
}
|
|
@@ -14,10 +14,7 @@ async function emitESMImage(id, watchMode, fileEmitter) {
|
|
|
14
14
|
} catch (err) {
|
|
15
15
|
return void 0;
|
|
16
16
|
}
|
|
17
|
-
const fileMetadata = await imageMetadata(fileData);
|
|
18
|
-
if (!fileMetadata) {
|
|
19
|
-
return void 0;
|
|
20
|
-
}
|
|
17
|
+
const fileMetadata = await imageMetadata(fileData, id);
|
|
21
18
|
const emittedImage = {
|
|
22
19
|
src: "",
|
|
23
20
|
...fileMetadata
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
import type { ImageMetadata } from '../types.js';
|
|
3
|
-
export declare function imageMetadata(data: Buffer): Promise<Omit<ImageMetadata, 'src'
|
|
3
|
+
export declare function imageMetadata(data: Buffer, src?: string): Promise<Omit<ImageMetadata, 'src'>>;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import probe from "probe-image-size";
|
|
2
|
-
|
|
2
|
+
import { AstroError, AstroErrorData } from "../../core/errors/index.js";
|
|
3
|
+
async function imageMetadata(data, src) {
|
|
3
4
|
const result = probe.sync(data);
|
|
4
5
|
if (result === null) {
|
|
5
|
-
throw new
|
|
6
|
+
throw new AstroError({
|
|
7
|
+
...AstroErrorData.NoImageMetadata,
|
|
8
|
+
message: AstroErrorData.NoImageMetadata.message(src)
|
|
9
|
+
});
|
|
6
10
|
}
|
|
7
11
|
const { width, height, type, orientation } = result;
|
|
8
12
|
const isPortrait = (orientation || 0) >= 5;
|
|
9
|
-
if (!width || !height || !type) {
|
|
10
|
-
return void 0;
|
|
11
|
-
}
|
|
12
13
|
return {
|
|
13
14
|
width: isPortrait ? height : width,
|
|
14
15
|
height: isPortrait ? width : height,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
2
|
import { normalizePath } from "vite";
|
|
3
|
+
import { extendManualChunks } from "../core/build/plugins/util.js";
|
|
4
|
+
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
3
5
|
import {
|
|
4
6
|
appendForwardSlash,
|
|
5
7
|
joinPaths,
|
|
@@ -21,6 +23,15 @@ function assets({
|
|
|
21
23
|
// Expose the components and different utilities from `astro:assets` and handle serving images from `/_image` in dev
|
|
22
24
|
{
|
|
23
25
|
name: "astro:assets",
|
|
26
|
+
outputOptions(outputOptions) {
|
|
27
|
+
extendManualChunks(outputOptions, {
|
|
28
|
+
after(id) {
|
|
29
|
+
if (id.includes("astro/dist/assets/services/")) {
|
|
30
|
+
return `astro-assets-services`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
},
|
|
24
35
|
async resolveId(id) {
|
|
25
36
|
if (id === VIRTUAL_SERVICE_ID) {
|
|
26
37
|
return await this.resolve(settings.config.image.service.entrypoint);
|
|
@@ -102,6 +113,12 @@ function assets({
|
|
|
102
113
|
}
|
|
103
114
|
if (assetRegex.test(id)) {
|
|
104
115
|
const meta = await emitESMImage(id, this.meta.watchMode, this.emitFile);
|
|
116
|
+
if (!meta) {
|
|
117
|
+
throw new AstroError({
|
|
118
|
+
...AstroErrorData.ImageNotFound,
|
|
119
|
+
message: AstroErrorData.ImageNotFound.message(id)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
105
122
|
return `export default ${JSON.stringify(meta)}`;
|
|
106
123
|
}
|
|
107
124
|
}
|
package/dist/cli/add/index.js
CHANGED
|
@@ -8,7 +8,12 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
8
8
|
import ora from "ora";
|
|
9
9
|
import preferredPM from "preferred-pm";
|
|
10
10
|
import prompts from "prompts";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
loadTSConfig,
|
|
13
|
+
resolveConfig,
|
|
14
|
+
resolveConfigPath,
|
|
15
|
+
resolveRoot
|
|
16
|
+
} from "../../core/config/index.js";
|
|
12
17
|
import {
|
|
13
18
|
defaultTSConfig,
|
|
14
19
|
presets,
|
|
@@ -20,7 +25,7 @@ import { appendForwardSlash } from "../../core/path.js";
|
|
|
20
25
|
import { apply as applyPolyfill } from "../../core/polyfill.js";
|
|
21
26
|
import { parseNpmName } from "../../core/util.js";
|
|
22
27
|
import { eventCliSession, telemetry } from "../../events/index.js";
|
|
23
|
-
import { createLoggerFromFlags } from "../flags.js";
|
|
28
|
+
import { createLoggerFromFlags, flagsToAstroInlineConfig } from "../flags.js";
|
|
24
29
|
import { generate, parse, t, visit } from "./babel.js";
|
|
25
30
|
import { ensureImport } from "./imports.js";
|
|
26
31
|
import { wrapDefaultExport } from "./wrapper.js";
|
|
@@ -66,7 +71,9 @@ async function getRegistry() {
|
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
async function add(names, { flags }) {
|
|
69
|
-
|
|
74
|
+
const inlineConfig = flagsToAstroInlineConfig(flags);
|
|
75
|
+
const { userConfig } = await resolveConfig(inlineConfig, "add");
|
|
76
|
+
telemetry.record(eventCliSession("add", userConfig));
|
|
70
77
|
applyPolyfill();
|
|
71
78
|
if (flags.help || names.length === 0) {
|
|
72
79
|
printHelp({
|
|
@@ -13,7 +13,7 @@ export declare function createGetCollection({ contentCollectionToEntryMap, dataC
|
|
|
13
13
|
contentCollectionToEntryMap: CollectionToEntryMap;
|
|
14
14
|
dataCollectionToEntryMap: CollectionToEntryMap;
|
|
15
15
|
getRenderEntryImport: GetEntryImport;
|
|
16
|
-
}): (collection: string, filter?: ((entry: any) => unknown) | undefined) => Promise<any[]>;
|
|
16
|
+
}): (collection: string, filter?: ((entry: any) => unknown) | undefined) => Promise<any[] | undefined>;
|
|
17
17
|
export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImport, }: {
|
|
18
18
|
getEntryImport: GetEntryImport;
|
|
19
19
|
getRenderEntryImport: GetEntryImport;
|
|
@@ -25,8 +25,8 @@ export declare function createGetEntryBySlug({ getEntryImport, getRenderEntryImp
|
|
|
25
25
|
data: any;
|
|
26
26
|
render(): Promise<RenderResult>;
|
|
27
27
|
} | undefined>;
|
|
28
|
-
export declare function createGetDataEntryById({
|
|
29
|
-
|
|
28
|
+
export declare function createGetDataEntryById({ getEntryImport }: {
|
|
29
|
+
getEntryImport: GetEntryImport;
|
|
30
30
|
}): (collection: string, id: string) => Promise<{
|
|
31
31
|
id: any;
|
|
32
32
|
collection: any;
|