Persistent DOM in ViewTransitions (#7861)
* First pass at transition:persist * Persistent islands * Changeset * Updated compiler * Use official release * Upgrade again * Refactor to allow head content to persist untouched * >= * Specify the types for "astro:persist" * Automatically persist links * Use reference for array * Upgrade to latest compiler version * Explain the feature * Update .changeset/empty-experts-unite.md Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> * Update .changeset/empty-experts-unite.md Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> * Update .changeset/empty-experts-unite.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/empty-experts-unite.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> --------- Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
4e651af16f
commit
41afb84057
18 changed files with 243 additions and 21 deletions
27
.changeset/empty-experts-unite.md
Normal file
27
.changeset/empty-experts-unite.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
'astro': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Persistent DOM and Islands in Experimental View Transitions
|
||||||
|
|
||||||
|
With `viewTransitions: true` enabled in your Astro config's experimental section, pages using the `<ViewTransition />` routing component can now access a new `transition:persist` directive.
|
||||||
|
|
||||||
|
With this directive, you can keep the state of DOM elements and islands on the old page when transitioning to the new page.
|
||||||
|
|
||||||
|
For example, to keep a video playing across page navigation, add `transition:persist` to the element:
|
||||||
|
|
||||||
|
```astro
|
||||||
|
<video controls="" autoplay="" transition:persist>
|
||||||
|
<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
|
||||||
|
</video>
|
||||||
|
```
|
||||||
|
|
||||||
|
This `<video>` element, with its current state, will be moved over to the next page (if the video also exists on that page).
|
||||||
|
|
||||||
|
Likewise, this feature works with any client-side framework component island. In this example, a counter's state is preserved and moved to the new page:
|
||||||
|
|
||||||
|
```astro
|
||||||
|
<Counter count={5} client:load transition:persist />
|
||||||
|
```
|
||||||
|
|
||||||
|
See our [View Transitions Guide](https://docs.astro.build/en/guides/view-transitions/#maintaining-state) to learn more on usage.
|
|
@ -34,6 +34,7 @@ const { fallback = 'animate' } = Astro.props as Props;
|
||||||
!!document.querySelector('[name="astro-view-transitions-enabled"]');
|
!!document.querySelector('[name="astro-view-transitions-enabled"]');
|
||||||
const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name));
|
const triggerEvent = (name: Events) => document.dispatchEvent(new Event(name));
|
||||||
const onload = () => triggerEvent('astro:load');
|
const onload = () => triggerEvent('astro:load');
|
||||||
|
const PERSIST_ATTR = 'data-astro-transition-persist';
|
||||||
|
|
||||||
const throttle = (cb: (...args: any[]) => any, delay: number) => {
|
const throttle = (cb: (...args: any[]) => any, delay: number) => {
|
||||||
let wait = false;
|
let wait = false;
|
||||||
|
@ -86,8 +87,50 @@ const { fallback = 'animate' } = Astro.props as Props;
|
||||||
async function updateDOM(dir: Direction, html: string, state?: State, fallback?: Fallback) {
|
async function updateDOM(dir: Direction, html: string, state?: State, fallback?: Fallback) {
|
||||||
const doc = parser.parseFromString(html, 'text/html');
|
const doc = parser.parseFromString(html, 'text/html');
|
||||||
doc.documentElement.dataset.astroTransition = dir;
|
doc.documentElement.dataset.astroTransition = dir;
|
||||||
|
|
||||||
|
// Check for a head element that should persist, either because it has the data
|
||||||
|
// attribute or is a link el.
|
||||||
|
const persistedHeadElement = (el: Element): Element | null => {
|
||||||
|
const id = el.getAttribute(PERSIST_ATTR);
|
||||||
|
const newEl = id && doc.head.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
||||||
|
if(newEl) {
|
||||||
|
return newEl;
|
||||||
|
}
|
||||||
|
if(el.matches('link[rel=stylesheet]')) {
|
||||||
|
const href = el.getAttribute('href');
|
||||||
|
return doc.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const swap = () => {
|
const swap = () => {
|
||||||
document.documentElement.replaceWith(doc.documentElement);
|
// Swap head
|
||||||
|
for(const el of Array.from(document.head.children)) {
|
||||||
|
const newEl = persistedHeadElement(el);
|
||||||
|
// If the element exists in the document already, remove it
|
||||||
|
// from the new document and leave the current node alone
|
||||||
|
if(newEl) {
|
||||||
|
newEl.remove();
|
||||||
|
} else {
|
||||||
|
// Otherwise remove the element in the head. It doesn't exist in the new page.
|
||||||
|
el.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Everything left in the new head is new, append it all.
|
||||||
|
document.head.append(...doc.head.children);
|
||||||
|
|
||||||
|
// Move over persist stuff in the body
|
||||||
|
const oldBody = document.body;
|
||||||
|
document.body.replaceWith(doc.body);
|
||||||
|
for(const el of oldBody.querySelectorAll(`[${PERSIST_ATTR}]`)) {
|
||||||
|
const id = el.getAttribute(PERSIST_ATTR);
|
||||||
|
const newEl = document.querySelector(`[${PERSIST_ATTR}="${id}"]`);
|
||||||
|
if(newEl) {
|
||||||
|
// The element exists in the new page, replace it with the element
|
||||||
|
// from the old page so that state is preserved.
|
||||||
|
newEl.replaceWith(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state?.scrollY != null) {
|
if (state?.scrollY != null) {
|
||||||
scrollTo(0, state.scrollY);
|
scrollTo(0, state.scrollY);
|
||||||
|
@ -97,17 +140,21 @@ const { fallback = 'animate' } = Astro.props as Props;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wait on links to finish, to prevent FOUC
|
// Wait on links to finish, to prevent FOUC
|
||||||
const links = Array.from(doc.querySelectorAll('head link[rel=stylesheet]')).map(
|
const links: Promise<any>[] = [];
|
||||||
(link) =>
|
for(const el of doc.querySelectorAll('head link[rel=stylesheet]')) {
|
||||||
new Promise((resolve) => {
|
// Do not preload links that are already on the page.
|
||||||
const c = link.cloneNode();
|
if(!document.querySelector(`[${PERSIST_ATTR}="${el.getAttribute(PERSIST_ATTR)}"], link[rel=stylesheet]`)) {
|
||||||
|
const c = document.createElement('link');
|
||||||
|
c.setAttribute('rel', 'preload');
|
||||||
|
c.setAttribute('as', 'style');
|
||||||
|
c.setAttribute('href', el.getAttribute('href')!);
|
||||||
|
links.push(new Promise<any>(resolve => {
|
||||||
['load', 'error'].forEach((evName) => c.addEventListener(evName, resolve));
|
['load', 'error'].forEach((evName) => c.addEventListener(evName, resolve));
|
||||||
document.head.append(c);
|
document.head.append(c);
|
||||||
})
|
}));
|
||||||
);
|
}
|
||||||
if (links.length) {
|
|
||||||
await Promise.all(links);
|
|
||||||
}
|
}
|
||||||
|
links.length && await Promise.all(links);
|
||||||
|
|
||||||
if (fallback === 'animate') {
|
if (fallback === 'animate') {
|
||||||
let isAnimating = false;
|
let isAnimating = false;
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
integrations: [react()],
|
||||||
experimental: {
|
experimental: {
|
||||||
viewTransitions: true,
|
viewTransitions: true,
|
||||||
assets: true,
|
assets: true,
|
||||||
},
|
},
|
||||||
|
vite: {
|
||||||
|
build: {
|
||||||
|
assetsInlineLimit: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "workspace:*"
|
"astro": "workspace:*",
|
||||||
|
"@astrojs/react": "workspace:*",
|
||||||
|
"react": "^18.1.0",
|
||||||
|
"react-dom": "^18.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
.counter {
|
||||||
|
display: grid;
|
||||||
|
font-size: 2em;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
margin-top: 2em;
|
||||||
|
place-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter-message {
|
||||||
|
text-align: center;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import './Island.css';
|
||||||
|
|
||||||
|
export default function Counter({ children, count: initialCount, id }) {
|
||||||
|
const [count, setCount] = useState(initialCount);
|
||||||
|
const add = () => setCount((i) => i + 1);
|
||||||
|
const subtract = () => setCount((i) => i - 1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div id={id} className="counter">
|
||||||
|
<button className="decrement" onClick={subtract}>-</button>
|
||||||
|
<pre>{count}</pre>
|
||||||
|
<button className="increment" onClick={add}>+</button>
|
||||||
|
</div>
|
||||||
|
<div className="counter-message">{children}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
<video controls="" autoplay="" name="media" transition:persist transition:name="video">
|
||||||
|
<source src="https://ia804502.us.archive.org/33/items/GoldenGa1939_3/GoldenGa1939_3_512kb.mp4" type="video/mp4">
|
||||||
|
</video>
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
import Layout from '../components/Layout.astro';
|
||||||
|
import Island from '../components/Island.jsx';
|
||||||
|
---
|
||||||
|
<Layout>
|
||||||
|
<p id="island-one">Page 1</p>
|
||||||
|
<a id="click-two" href="/island-two">go to 2</a>
|
||||||
|
<Island count={5} client:load transition:persist transition:name="counter" />
|
||||||
|
</Layout>
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
import Layout from '../components/Layout.astro';
|
||||||
|
import Island from '../components/Island.jsx';
|
||||||
|
---
|
||||||
|
<Layout>
|
||||||
|
<p id="island-two">Page 2</p>
|
||||||
|
<a id="click-one" href="/island-one">go to 1</a>
|
||||||
|
<Island count={2} client:load transition:persist transition:name="counter" />
|
||||||
|
</Layout>
|
|
@ -0,0 +1,17 @@
|
||||||
|
---
|
||||||
|
import Layout from '../components/Layout.astro';
|
||||||
|
import Video from '../components/Video.astro';
|
||||||
|
---
|
||||||
|
<Layout>
|
||||||
|
<p id="video-one">Page 1</p>
|
||||||
|
<a id="click-two" href="/video-two">go to 2</a>
|
||||||
|
<Video />
|
||||||
|
<script>
|
||||||
|
const vid = document.querySelector('video');
|
||||||
|
vid.addEventListener('canplay', () => {
|
||||||
|
// Jump to the 1 minute mark
|
||||||
|
vid.currentTime = 60;
|
||||||
|
vid.dataset.ready = '';
|
||||||
|
}, { once: true });
|
||||||
|
</script>
|
||||||
|
</Layout>
|
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
import Layout from '../components/Layout.astro';
|
||||||
|
import Video from '../components/Video.astro';
|
||||||
|
---
|
||||||
|
<style>
|
||||||
|
#video-two {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<Layout>
|
||||||
|
<p id="video-two">Page 2</p>
|
||||||
|
<a id="click-one" href="/video-one">go to 1</a>
|
||||||
|
<Video />
|
||||||
|
</Layout>
|
|
@ -243,4 +243,40 @@ test.describe('View Transitions', () => {
|
||||||
const img = page.locator('img[data-astro-transition-scope]');
|
const img = page.locator('img[data-astro-transition-scope]');
|
||||||
await expect(img).toBeVisible('The image tag should have the transition scope attribute.');
|
await expect(img).toBeVisible('The image tag should have the transition scope attribute.');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('<video> can persist using transition:persist', async ({ page, astro }) => {
|
||||||
|
const getTime = () => document.querySelector('video').currentTime;
|
||||||
|
|
||||||
|
// Go to page 1
|
||||||
|
await page.goto(astro.resolveUrl('/video-one'));
|
||||||
|
const vid = page.locator('video[data-ready]');
|
||||||
|
await expect(vid).toBeVisible();
|
||||||
|
const firstTime = await page.evaluate(getTime);
|
||||||
|
|
||||||
|
// Navigate to page 2
|
||||||
|
await page.click('#click-two');
|
||||||
|
const p = page.locator('#video-two');
|
||||||
|
await expect(p).toBeVisible();
|
||||||
|
const secondTime = await page.evaluate(getTime);
|
||||||
|
|
||||||
|
expect(secondTime).toBeGreaterThanOrEqual(firstTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Islands can persist using transition:persist', async ({ page, astro }) => {
|
||||||
|
// Go to page 1
|
||||||
|
await page.goto(astro.resolveUrl('/island-one'));
|
||||||
|
let cnt = page.locator('.counter pre');
|
||||||
|
await expect(cnt).toHaveText('5');
|
||||||
|
|
||||||
|
await page.click('.increment');
|
||||||
|
await expect(cnt).toHaveText('6');
|
||||||
|
|
||||||
|
// Navigate to page 2
|
||||||
|
await page.click('#click-two');
|
||||||
|
const p = page.locator('#island-two');
|
||||||
|
await expect(p).toBeVisible();
|
||||||
|
cnt = page.locator('.counter pre');
|
||||||
|
// Count should remain
|
||||||
|
await expect(cnt).toHaveText('6');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
"test:e2e:match": "playwright test -g"
|
"test:e2e:match": "playwright test -g"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/compiler": "^1.6.3",
|
"@astrojs/compiler": "^1.8.0",
|
||||||
"@astrojs/internal-helpers": "^0.1.1",
|
"@astrojs/internal-helpers": "^0.1.1",
|
||||||
"@astrojs/language-server": "^1.0.0",
|
"@astrojs/language-server": "^1.0.0",
|
||||||
"@astrojs/markdown-remark": "^2.2.1",
|
"@astrojs/markdown-remark": "^2.2.1",
|
||||||
|
|
|
@ -92,6 +92,7 @@ export interface AstroBuiltinAttributes {
|
||||||
'is:raw'?: boolean;
|
'is:raw'?: boolean;
|
||||||
'transition:animate'?: 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;
|
'transition:animate'?: 'morph' | 'slide' | 'fade' | TransitionDirectionalAnimations;
|
||||||
'transition:name'?: string;
|
'transition:name'?: string;
|
||||||
|
'transition:persist'?: boolean | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AstroDefineVarsAttribute {
|
export interface AstroDefineVarsAttribute {
|
||||||
|
|
|
@ -46,6 +46,7 @@ export async function compile({
|
||||||
scopedStyleStrategy: astroConfig.scopedStyleStrategy,
|
scopedStyleStrategy: astroConfig.scopedStyleStrategy,
|
||||||
resultScopedSlot: true,
|
resultScopedSlot: true,
|
||||||
experimentalTransitions: astroConfig.experimental.viewTransitions,
|
experimentalTransitions: astroConfig.experimental.viewTransitions,
|
||||||
|
experimentalPersistence: astroConfig.experimental.viewTransitions,
|
||||||
transitionsAnimationURL: 'astro/components/viewtransitions.css',
|
transitionsAnimationURL: 'astro/components/viewtransitions.css',
|
||||||
preprocessStyle: createStylePreprocessor({
|
preprocessStyle: createStylePreprocessor({
|
||||||
filename,
|
filename,
|
||||||
|
|
|
@ -22,6 +22,8 @@ interface ExtractedProps {
|
||||||
props: Record<string | number | symbol, any>;
|
props: Record<string | number | symbol, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const transitionDirectivesToCopyOnIsland = Object.freeze(['data-astro-transition-scope', 'data-astro-transition-persist']);
|
||||||
|
|
||||||
// Used to extract the directives, aka `client:load` information about a component.
|
// Used to extract the directives, aka `client:load` information about a component.
|
||||||
// Finds these special props and removes them from what gets passed into the component.
|
// Finds these special props and removes them from what gets passed into the component.
|
||||||
export function extractDirectives(
|
export function extractDirectives(
|
||||||
|
@ -166,5 +168,11 @@ export async function generateHydrateScript(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
transitionDirectivesToCopyOnIsland.forEach(name => {
|
||||||
|
if(props[name]) {
|
||||||
|
island.props[name] = props[name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return island;
|
return island;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,11 @@ function incrementTransitionNumber(result: SSRResult) {
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTransitionScope(result: SSRResult, hash: string) {
|
export function createTransitionScope(result: SSRResult, hash: string) {
|
||||||
const num = incrementTransitionNumber(result);
|
const num = incrementTransitionNumber(result);
|
||||||
return `astro-${hash}-${num}`;
|
return `astro-${hash}-${num}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderTransition(
|
export function renderTransition(
|
||||||
result: SSRResult,
|
result: SSRResult,
|
||||||
hash: string,
|
hash: string,
|
||||||
|
|
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
|
@ -486,8 +486,8 @@ importers:
|
||||||
packages/astro:
|
packages/astro:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler':
|
'@astrojs/compiler':
|
||||||
specifier: ^1.6.3
|
specifier: ^1.8.0
|
||||||
version: 1.6.3
|
version: 1.8.0
|
||||||
'@astrojs/internal-helpers':
|
'@astrojs/internal-helpers':
|
||||||
specifier: ^0.1.1
|
specifier: ^0.1.1
|
||||||
version: link:../internal-helpers
|
version: link:../internal-helpers
|
||||||
|
@ -1487,9 +1487,18 @@ importers:
|
||||||
|
|
||||||
packages/astro/e2e/fixtures/view-transitions:
|
packages/astro/e2e/fixtures/view-transitions:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@astrojs/react':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../../../integrations/react
|
||||||
astro:
|
astro:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../..
|
version: link:../../..
|
||||||
|
react:
|
||||||
|
specifier: ^18.1.0
|
||||||
|
version: 18.2.0
|
||||||
|
react-dom:
|
||||||
|
specifier: ^18.1.0
|
||||||
|
version: 18.2.0(react@18.2.0)
|
||||||
|
|
||||||
packages/astro/e2e/fixtures/vue-component:
|
packages/astro/e2e/fixtures/vue-component:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5602,8 +5611,8 @@ packages:
|
||||||
sisteransi: 1.0.5
|
sisteransi: 1.0.5
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@astrojs/compiler@1.6.3:
|
/@astrojs/compiler@1.8.0:
|
||||||
resolution: {integrity: sha512-n0xTuBznKspc0plk6RHBOlSv/EwQGyMNSxEOPj7HMeiRNnXX4woeSopN9hQsLkqraDds1eRvB4u99buWgVNJig==}
|
resolution: {integrity: sha512-E0TI/uyO8n+IPSZ4Fvl9Lne8JKEasR6ZMGvE2G096oTWOXSsPAhRs2LomV3z+/VRepo2h+t/SdVo54wox4eJwA==}
|
||||||
|
|
||||||
/@astrojs/internal-helpers@0.1.1:
|
/@astrojs/internal-helpers@0.1.1:
|
||||||
resolution: {integrity: sha512-+LySbvFbjv2nO2m/e78suleQOGEru4Cnx73VsZbrQgB2u7A4ddsQg3P2T0zC0e10jgcT+c6nNlKeLpa6nRhQIg==}
|
resolution: {integrity: sha512-+LySbvFbjv2nO2m/e78suleQOGEru4Cnx73VsZbrQgB2u7A4ddsQg3P2T0zC0e10jgcT+c6nNlKeLpa6nRhQIg==}
|
||||||
|
@ -5613,7 +5622,7 @@ packages:
|
||||||
resolution: {integrity: sha512-oEw7AwJmzjgy6HC9f5IdrphZ1GVgfV/+7xQuyf52cpTiRWd/tJISK3MsKP0cDkVlfodmNABNFnAaAWuLZEiiiA==}
|
resolution: {integrity: sha512-oEw7AwJmzjgy6HC9f5IdrphZ1GVgfV/+7xQuyf52cpTiRWd/tJISK3MsKP0cDkVlfodmNABNFnAaAWuLZEiiiA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler': 1.6.3
|
'@astrojs/compiler': 1.8.0
|
||||||
'@jridgewell/trace-mapping': 0.3.18
|
'@jridgewell/trace-mapping': 0.3.18
|
||||||
'@vscode/emmet-helper': 2.8.8
|
'@vscode/emmet-helper': 2.8.8
|
||||||
events: 3.3.0
|
events: 3.3.0
|
||||||
|
@ -15692,7 +15701,7 @@ packages:
|
||||||
resolution: {integrity: sha512-dPzop0gKZyVGpTDQmfy+e7FKXC9JT3mlpfYA2diOVz+Ui+QR1U4G/s+OesKl2Hib2JJOtAYJs/l+ovgT0ljlFA==}
|
resolution: {integrity: sha512-dPzop0gKZyVGpTDQmfy+e7FKXC9JT3mlpfYA2diOVz+Ui+QR1U4G/s+OesKl2Hib2JJOtAYJs/l+ovgT0ljlFA==}
|
||||||
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
|
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler': 1.6.3
|
'@astrojs/compiler': 1.8.0
|
||||||
prettier: 2.8.8
|
prettier: 2.8.8
|
||||||
sass-formatter: 0.7.6
|
sass-formatter: 0.7.6
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -15701,7 +15710,7 @@ packages:
|
||||||
resolution: {integrity: sha512-lJ/mG/Lz/ccSwNtwqpFS126mtMVzFVyYv0ddTF9wqwrEG4seECjKDAyw/oGv915rAcJi8jr89990nqfpmG+qdg==}
|
resolution: {integrity: sha512-lJ/mG/Lz/ccSwNtwqpFS126mtMVzFVyYv0ddTF9wqwrEG4seECjKDAyw/oGv915rAcJi8jr89990nqfpmG+qdg==}
|
||||||
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
|
engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler': 1.6.3
|
'@astrojs/compiler': 1.8.0
|
||||||
prettier: 2.8.8
|
prettier: 2.8.8
|
||||||
sass-formatter: 0.7.6
|
sass-formatter: 0.7.6
|
||||||
synckit: 0.8.5
|
synckit: 0.8.5
|
||||||
|
@ -18709,7 +18718,7 @@ packages:
|
||||||
sharp:
|
sharp:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/compiler': 1.6.3
|
'@astrojs/compiler': 1.8.0
|
||||||
'@astrojs/internal-helpers': 0.1.1
|
'@astrojs/internal-helpers': 0.1.1
|
||||||
'@astrojs/language-server': 1.0.0
|
'@astrojs/language-server': 1.0.0
|
||||||
'@astrojs/markdown-remark': 2.2.1(astro@2.9.7)
|
'@astrojs/markdown-remark': 2.2.1(astro@2.9.7)
|
||||||
|
|
Loading…
Add table
Reference in a new issue