Compare commits
17 commits
main
...
mt/client-
Author | SHA1 | Date | |
---|---|---|---|
|
068e618cb9 | ||
|
85cc8daff8 | ||
|
9ed7da1e3b | ||
|
d50aaea36d | ||
|
05833ab44e | ||
|
6bba4efb3f | ||
|
a3ebad84a8 | ||
|
4e2c65c27c | ||
|
9c7cd7c9f6 | ||
|
49cbbf8d23 | ||
|
8eed6cc7ac | ||
|
cb62f02ea1 | ||
|
d125095d09 | ||
|
365adee6f6 | ||
|
20c1b3b0d0 | ||
|
84d1f05964 | ||
|
363f80011b |
50 changed files with 350 additions and 171 deletions
5
.changeset/cuddly-vans-reply.md
Normal file
5
.changeset/cuddly-vans-reply.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/markdown-remark': patch
|
||||
---
|
||||
|
||||
Remove `is:raw` from remark Shiki plugin
|
25
.changeset/eleven-buttons-wash.md
Normal file
25
.changeset/eleven-buttons-wash.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
'@astrojs/cloudflare': patch
|
||||
'@astrojs/partytown': patch
|
||||
'@astrojs/alpinejs': patch
|
||||
'@astrojs/prefetch': patch
|
||||
'@astrojs/tailwind': patch
|
||||
'@astrojs/markdoc': patch
|
||||
'@astrojs/sitemap': patch
|
||||
'@astrojs/underscore-redirects': patch
|
||||
'@astrojs/preact': patch
|
||||
'@astrojs/svelte': patch
|
||||
'@astrojs/vercel': patch
|
||||
'@astrojs/react': patch
|
||||
'@astrojs/solid-js': patch
|
||||
'@astrojs/node': patch
|
||||
'@astrojs/lit': patch
|
||||
'@astrojs/mdx': patch
|
||||
'@astrojs/vue': patch
|
||||
'@astrojs/internal-helpers': patch
|
||||
'@astrojs/markdown-remark': patch
|
||||
'@astrojs/telemetry': patch
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Add provenance statement when publishing the library from CI
|
5
.changeset/gentle-hats-dress.md
Normal file
5
.changeset/gentle-hats-dress.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix view transitions with client:only components
|
5
.changeset/green-crabs-breathe.md
Normal file
5
.changeset/green-crabs-breathe.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/telemetry': patch
|
||||
---
|
||||
|
||||
Removed an unnecessary dependency.
|
5
.changeset/red-masks-drop.md
Normal file
5
.changeset/red-masks-drop.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix `tsconfig.json` update causing the server to crash
|
5
.changeset/tasty-meals-buy.md
Normal file
5
.changeset/tasty-meals-buy.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Remove unused CSS output files when inlined
|
5
.changeset/thirty-ravens-fly.md
Normal file
5
.changeset/thirty-ravens-fly.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Node-based adapters now create less server-side javascript
|
5
.changeset/tricky-otters-cross.md
Normal file
5
.changeset/tricky-otters-cross.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/partytown': patch
|
||||
---
|
||||
|
||||
Expose types for TypeScript users
|
5
.changeset/witty-fishes-heal.md
Normal file
5
.changeset/witty-fishes-heal.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Improve `astro info` copy to clipboard compatability
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
// ISOMORPHIC FILE: NO TOP-LEVEL IMPORT/REQUIRE() ALLOWED
|
||||
// This file has to run as both ESM and CJS on older Node.js versions
|
||||
// Needed for Stackblitz: https://github.com/stackblitz/webcontainer-core/issues/281
|
||||
|
||||
const CI_INSTRUCTIONS = {
|
||||
NETLIFY: 'https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript',
|
||||
|
@ -16,15 +15,11 @@ const CI_INSTRUCTIONS = {
|
|||
const engines = '>=18.14.1';
|
||||
const skipSemverCheckIfAbove = 19;
|
||||
|
||||
// HACK (2023-08-18) Stackblitz does not support Node 18 yet, so we'll fake Node 16 support for some time until it's supported
|
||||
// TODO: Remove when Node 18 is supported on Stackblitz
|
||||
const isStackblitz = process.env.SHELL === '/bin/jsh' && process.versions.webcontainer != null;
|
||||
|
||||
/** `astro *` */
|
||||
async function main() {
|
||||
const version = process.versions.node;
|
||||
// Fast-path for higher Node.js versions
|
||||
if (!isStackblitz && (parseInt(version) || 0) <= skipSemverCheckIfAbove) {
|
||||
if ((parseInt(version) || 0) <= skipSemverCheckIfAbove) {
|
||||
try {
|
||||
const semver = await import('semver');
|
||||
if (!semver.satisfies(version, engines)) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import Layout from '../components/Layout.astro';
|
|||
import Island from '../components/Island';
|
||||
---
|
||||
<Layout>
|
||||
<p id="page-one">Page 1</p>
|
||||
<a id="click-two" href="/client-only-two">go to page 2</a>
|
||||
<div transition:persist="island">
|
||||
<Island client:only count={5}>message here</Island>
|
||||
|
|
|
@ -4,6 +4,7 @@ import Island from '../components/Island';
|
|||
---
|
||||
<Layout>
|
||||
<p id="page-two">Page 2</p>
|
||||
<a id="click-one" href="/client-only-one">go to page 1</a>
|
||||
<div transition:persist="island">
|
||||
<Island client:only count={5}>message here</Island>
|
||||
</div>
|
||||
|
|
|
@ -649,9 +649,14 @@ test.describe('View Transitions', () => {
|
|||
});
|
||||
|
||||
test('client:only styles are retained on transition', async ({ page, astro }) => {
|
||||
const loads = [];
|
||||
page.addListener('load', async (p) => {
|
||||
loads.push(p);
|
||||
});
|
||||
|
||||
const totalExpectedStyles = 7;
|
||||
|
||||
// Go to page 1
|
||||
// Go to page 1 (normal load)
|
||||
await page.goto(astro.resolveUrl('/client-only-one'));
|
||||
let msg = page.locator('.counter-message');
|
||||
await expect(msg).toHaveText('message here');
|
||||
|
@ -659,13 +664,24 @@ test.describe('View Transitions', () => {
|
|||
let styles = await page.locator('style').all();
|
||||
expect(styles.length).toEqual(totalExpectedStyles);
|
||||
|
||||
// Transition to page 2 (will do a full load)
|
||||
await page.click('#click-two');
|
||||
|
||||
let pageTwo = page.locator('#page-two');
|
||||
await expect(pageTwo, 'should have content').toHaveText('Page 2');
|
||||
|
||||
// Transition to page 1 (will do a full load)
|
||||
await page.click('#click-one');
|
||||
|
||||
let pageOne = page.locator('#page-one');
|
||||
await expect(pageOne, 'should have content').toHaveText('Page 1');
|
||||
|
||||
// Transition to page 1 (real transition, no full load)
|
||||
await page.click('#click-two');
|
||||
|
||||
styles = await page.locator('style').all();
|
||||
expect(styles.length).toEqual(totalExpectedStyles, 'style count has not changed');
|
||||
expect(loads.length, 'There should only be 1 page load').toEqual(3);
|
||||
});
|
||||
|
||||
test('Horizontal scroll position restored on back button', async ({ page, astro }) => {
|
||||
|
|
|
@ -168,7 +168,6 @@
|
|||
"string-width": "^6.1.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"tsconfig-resolver": "^3.0.1",
|
||||
"undici": "^5.23.0",
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"vfile": "^5.3.7",
|
||||
"vite": "^4.4.9",
|
||||
|
@ -226,5 +225,8 @@
|
|||
"engines": {
|
||||
"node": ">=18.14.1",
|
||||
"npm": ">=6.14.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1168,10 +1168,10 @@ export interface AstroUserConfig {
|
|||
* Pass [rehype plugins](https://github.com/remarkjs/remark-rehype) to customize how your Markdown's output HTML is processed. You can import and apply the plugin function (recommended), or pass the plugin name as a string.
|
||||
*
|
||||
* ```js
|
||||
* import rehypeMinifyHtml from 'rehype-minify';
|
||||
* import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';
|
||||
* {
|
||||
* markdown: {
|
||||
* rehypePlugins: [rehypeMinifyHtml]
|
||||
* rehypePlugins: [rehypeAccessibleEmojis]
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
|
|
|
@ -41,10 +41,22 @@ export async function printInfo({ flags }: InfoOptions) {
|
|||
await copyToClipboard(output.trim());
|
||||
}
|
||||
|
||||
const SUPPORTED_SYSTEM = new Set(['darwin', 'win32']);
|
||||
async function copyToClipboard(text: string) {
|
||||
const system = platform();
|
||||
if (!SUPPORTED_SYSTEM.has(system)) return;
|
||||
let command = '';
|
||||
if (system === 'darwin') {
|
||||
command = 'pbcopy';
|
||||
} else if (system === 'win32') {
|
||||
command = 'clip';
|
||||
} else {
|
||||
// Unix: check if `xclip` is installed
|
||||
const output = execSync('which xclip', { encoding: 'utf8' });
|
||||
if (output[0] !== '/') {
|
||||
// Did not find a path for xclip, bail out!
|
||||
return;
|
||||
}
|
||||
command = 'xclip -sel clipboard -l 1';
|
||||
}
|
||||
|
||||
console.log();
|
||||
const { shouldCopy } = await prompts({
|
||||
|
@ -54,11 +66,11 @@ async function copyToClipboard(text: string) {
|
|||
initial: true,
|
||||
});
|
||||
if (!shouldCopy) return;
|
||||
const command = system === 'darwin' ? 'pbcopy' : 'clip';
|
||||
|
||||
try {
|
||||
execSync(`echo ${JSON.stringify(text.trim())} | ${command}`, {
|
||||
execSync(command, {
|
||||
input: text.trim(),
|
||||
encoding: 'utf8',
|
||||
stdio: 'ignore',
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(
|
||||
|
|
|
@ -148,9 +148,15 @@ export const _internal = {
|
|||
hasContentFlag(modUrl, DATA_FLAG) ||
|
||||
Boolean(getContentRendererByViteId(modUrl, settings))
|
||||
) {
|
||||
const mod = await viteServer.moduleGraph.getModuleByUrl(modUrl);
|
||||
if (mod) {
|
||||
viteServer.moduleGraph.invalidateModule(mod);
|
||||
try {
|
||||
const mod = await viteServer.moduleGraph.getModuleByUrl(modUrl);
|
||||
if (mod) {
|
||||
viteServer.moduleGraph.invalidateModule(mod);
|
||||
}
|
||||
} catch (e: any) {
|
||||
// The server may be closed due to a restart caused by this file change
|
||||
if (e.code === 'ERR_CLOSED_SERVER') break;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
|
|||
const inlineConfig = settings.config.build.inlineStylesheets;
|
||||
const { assetsInlineLimit = 4096 } = settings.config.vite?.build ?? {};
|
||||
|
||||
Object.entries(bundle).forEach(([_, stylesheet]) => {
|
||||
Object.entries(bundle).forEach(([id, stylesheet]) => {
|
||||
if (
|
||||
stylesheet.type !== 'asset' ||
|
||||
stylesheet.name?.endsWith('.css') !== true ||
|
||||
|
@ -224,10 +224,15 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
|
|||
: { type: 'external', src: stylesheet.fileName };
|
||||
|
||||
const pages = Array.from(eachPageData(internals));
|
||||
let sheetAddedToPage = false;
|
||||
|
||||
pages.forEach((pageData) => {
|
||||
const orderingInfo = pagesToCss[pageData.moduleSpecifier]?.[stylesheet.fileName];
|
||||
if (orderingInfo !== undefined) return pageData.styles.push({ ...orderingInfo, sheet });
|
||||
if (orderingInfo !== undefined) {
|
||||
pageData.styles.push({ ...orderingInfo, sheet });
|
||||
sheetAddedToPage = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const propagatedPaths = pagesToPropagatedCss[pageData.moduleSpecifier];
|
||||
if (propagatedPaths === undefined) return;
|
||||
|
@ -243,8 +248,21 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
|
|||
pageData.propagatedStyles.set(pageInfoId, new Set()).get(pageInfoId)!;
|
||||
|
||||
propagatedStyles.add(sheet);
|
||||
sheetAddedToPage = true;
|
||||
});
|
||||
});
|
||||
|
||||
if (toBeInlined && sheetAddedToPage) {
|
||||
// CSS is already added to all used pages, we can delete it from the bundle
|
||||
// and make sure no chunks reference it via `importedCss` (for Vite preloading)
|
||||
// to avoid duplicate CSS.
|
||||
delete bundle[id];
|
||||
for (const chunk of Object.values(bundle)) {
|
||||
if (chunk.type === 'chunk') {
|
||||
chunk.viteMetadata?.importedCss?.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -39,7 +39,6 @@ export function createAPIContext({
|
|||
props,
|
||||
adapterName,
|
||||
}: CreateAPIContext): APIContext {
|
||||
initResponseWithEncoding();
|
||||
const context = {
|
||||
cookies: new AstroCookies(request),
|
||||
request,
|
||||
|
@ -92,44 +91,28 @@ export function createAPIContext({
|
|||
|
||||
type ResponseParameters = ConstructorParameters<typeof Response>;
|
||||
|
||||
export let ResponseWithEncoding: ReturnType<typeof initResponseWithEncoding>;
|
||||
// TODO Remove this after StackBlitz supports Node 18.
|
||||
let initResponseWithEncoding = () => {
|
||||
class LocalResponseWithEncoding extends Response {
|
||||
constructor(
|
||||
body: ResponseParameters[0],
|
||||
init: ResponseParameters[1],
|
||||
encoding?: BufferEncoding
|
||||
) {
|
||||
// If a body string is given, try to encode it to preserve the behaviour as simple objects.
|
||||
// We don't do the full handling as simple objects so users can control how headers are set instead.
|
||||
if (typeof body === 'string') {
|
||||
// In NodeJS, we can use Buffer.from which supports all BufferEncoding
|
||||
if (typeof Buffer !== 'undefined' && Buffer.from) {
|
||||
body = Buffer.from(body, encoding);
|
||||
}
|
||||
// In non-NodeJS, use the web-standard TextEncoder for utf-8 strings
|
||||
else if (encoding == null || encoding === 'utf8' || encoding === 'utf-8') {
|
||||
body = encoder.encode(body);
|
||||
}
|
||||
export class ResponseWithEncoding extends Response {
|
||||
constructor(body: ResponseParameters[0], init: ResponseParameters[1], encoding?: BufferEncoding) {
|
||||
// If a body string is given, try to encode it to preserve the behaviour as simple objects.
|
||||
// We don't do the full handling as simple objects so users can control how headers are set instead.
|
||||
if (typeof body === 'string') {
|
||||
// In NodeJS, we can use Buffer.from which supports all BufferEncoding
|
||||
if (typeof Buffer !== 'undefined' && Buffer.from) {
|
||||
body = Buffer.from(body, encoding);
|
||||
}
|
||||
|
||||
super(body, init);
|
||||
|
||||
if (encoding) {
|
||||
this.headers.set('X-Astro-Encoding', encoding);
|
||||
// In non-NodeJS, use the web-standard TextEncoder for utf-8 strings
|
||||
else if (encoding == null || encoding === 'utf8' || encoding === 'utf-8') {
|
||||
body = encoder.encode(body);
|
||||
}
|
||||
}
|
||||
|
||||
super(body, init);
|
||||
|
||||
if (encoding) {
|
||||
this.headers.set('X-Astro-Encoding', encoding);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the module scoped variable.
|
||||
ResponseWithEncoding = LocalResponseWithEncoding;
|
||||
|
||||
// Turn this into a noop.
|
||||
initResponseWithEncoding = (() => {}) as any;
|
||||
|
||||
return LocalResponseWithEncoding;
|
||||
};
|
||||
}
|
||||
|
||||
export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>(
|
||||
mod: EndpointHandler,
|
||||
|
|
|
@ -1158,7 +1158,7 @@ export const ContentSchemaContainsSlugError = {
|
|||
/**
|
||||
* @docs
|
||||
* @message A collection queried via `getCollection()` does not exist.
|
||||
* @deprecated Collections that do not exist no longer result in an error. A warning is omitted instead.
|
||||
* @deprecated Collections that do not exist no longer result in an error. A warning is given instead.
|
||||
* @description
|
||||
* When querying a collection, ensure a collection directory with the requested name exists under `src/content/`.
|
||||
*/
|
||||
|
|
|
@ -1,19 +1,52 @@
|
|||
import { EventEmitter } from 'node:events';
|
||||
import path from 'node:path';
|
||||
import type * as vite from 'vite';
|
||||
import type { ModuleLoader, ModuleLoaderEventEmitter } from './loader.js';
|
||||
|
||||
export function createViteLoader(viteServer: vite.ViteDevServer): ModuleLoader {
|
||||
const events = new EventEmitter() as ModuleLoaderEventEmitter;
|
||||
|
||||
viteServer.watcher.on('add', (...args) => events.emit('file-add', args));
|
||||
viteServer.watcher.on('unlink', (...args) => events.emit('file-unlink', args));
|
||||
viteServer.watcher.on('change', (...args) => events.emit('file-change', args));
|
||||
let isTsconfigUpdated = false;
|
||||
function isTsconfigUpdate(filePath: string) {
|
||||
const result = path.basename(filePath) === 'tsconfig.json';
|
||||
if (result) isTsconfigUpdated = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
wrapMethod(viteServer.ws, 'send', (msg) => {
|
||||
// Skip event emit on tsconfig change as Vite restarts the server, and we don't
|
||||
// want to trigger unnecessary work that will be invalidated shortly.
|
||||
viteServer.watcher.on('add', (...args) => {
|
||||
if (!isTsconfigUpdate(args[0])) {
|
||||
events.emit('file-add', args);
|
||||
}
|
||||
});
|
||||
viteServer.watcher.on('unlink', (...args) => {
|
||||
if (!isTsconfigUpdate(args[0])) {
|
||||
events.emit('file-unlink', args);
|
||||
}
|
||||
});
|
||||
viteServer.watcher.on('change', (...args) => {
|
||||
if (!isTsconfigUpdate(args[0])) {
|
||||
events.emit('file-change', args);
|
||||
}
|
||||
});
|
||||
|
||||
const _wsSend = viteServer.ws.send;
|
||||
viteServer.ws.send = function (...args: any) {
|
||||
// If the tsconfig changed, Vite will trigger a reload as it invalidates the module.
|
||||
// However in Astro, the whole server is restarted when the tsconfig changes. If we
|
||||
// do a restart and reload at the same time, the browser will refetch and the server
|
||||
// is not ready yet, causing a blank page. Here we block that reload from happening.
|
||||
if (isTsconfigUpdated) {
|
||||
isTsconfigUpdated = false;
|
||||
return;
|
||||
}
|
||||
const msg = args[0] as vite.HMRPayload;
|
||||
if (msg?.type === 'error') {
|
||||
events.emit('hmr-error', msg);
|
||||
}
|
||||
});
|
||||
_wsSend.apply(this, args);
|
||||
};
|
||||
|
||||
return {
|
||||
import(src) {
|
||||
|
@ -56,11 +89,3 @@ export function createViteLoader(viteServer: vite.ViteDevServer): ModuleLoader {
|
|||
events,
|
||||
};
|
||||
}
|
||||
|
||||
function wrapMethod(object: any, method: string, newFn: (...args: any[]) => void) {
|
||||
const orig = object[method];
|
||||
object[method] = function (...args: any[]) {
|
||||
newFn.apply(this, args);
|
||||
return orig.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,63 +1,7 @@
|
|||
import buffer from 'node:buffer';
|
||||
import crypto from 'node:crypto';
|
||||
import {
|
||||
ByteLengthQueuingStrategy,
|
||||
CountQueuingStrategy,
|
||||
ReadableByteStreamController,
|
||||
ReadableStream,
|
||||
ReadableStreamBYOBReader,
|
||||
ReadableStreamBYOBRequest,
|
||||
ReadableStreamDefaultController,
|
||||
ReadableStreamDefaultReader,
|
||||
TransformStream,
|
||||
WritableStream,
|
||||
WritableStreamDefaultController,
|
||||
WritableStreamDefaultWriter,
|
||||
} from 'node:stream/web';
|
||||
import { File, FormData, Headers, Request, Response, fetch } from 'undici';
|
||||
|
||||
// NOTE: This file does not intend to polyfill everything that exists, its main goal is to make life easier
|
||||
// for users deploying to runtime that do support these features. In the future, we hope for this file to disappear.
|
||||
|
||||
// HACK (2023-08-18) Stackblitz does not support Node 18 yet, so we'll fake Node 16 support for some time until it's supported
|
||||
// TODO: Remove when Node 18 is supported on Stackblitz. File should get imported from `node:buffer` instead of `undici` once this is removed
|
||||
const isStackblitz = process.env.SHELL === '/bin/jsh' && process.versions.webcontainer != null;
|
||||
|
||||
export function apply() {
|
||||
if (isStackblitz) {
|
||||
const neededPolyfills = {
|
||||
ByteLengthQueuingStrategy,
|
||||
CountQueuingStrategy,
|
||||
ReadableByteStreamController,
|
||||
ReadableStream,
|
||||
ReadableStreamBYOBReader,
|
||||
ReadableStreamBYOBRequest,
|
||||
ReadableStreamDefaultController,
|
||||
ReadableStreamDefaultReader,
|
||||
TransformStream,
|
||||
WritableStream,
|
||||
WritableStreamDefaultController,
|
||||
WritableStreamDefaultWriter,
|
||||
File,
|
||||
FormData,
|
||||
Headers,
|
||||
Request,
|
||||
Response,
|
||||
fetch,
|
||||
};
|
||||
|
||||
for (let polyfillName of Object.keys(neededPolyfills)) {
|
||||
if (Object.hasOwnProperty.call(globalThis, polyfillName)) continue;
|
||||
|
||||
// Add polyfill to globalThis
|
||||
Object.defineProperty(globalThis, polyfillName, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: neededPolyfills[polyfillName as keyof typeof neededPolyfills],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove when Node 18 is dropped for Node 20
|
||||
if (!globalThis.crypto) {
|
||||
Object.defineProperty(globalThis, 'crypto', {
|
||||
|
@ -68,7 +12,7 @@ export function apply() {
|
|||
// Remove when Node 18 is dropped for Node 20
|
||||
if (!globalThis.File) {
|
||||
Object.defineProperty(globalThis, 'File', {
|
||||
value: File,
|
||||
value: buffer.File,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,15 @@ type State = {
|
|||
};
|
||||
type Events = 'astro:page-load' | 'astro:after-swap';
|
||||
|
||||
let viteDevIds: { static: Record<string, string[]>; dynamic: Record<string, string[]> };
|
||||
if (import.meta.env.DEV) {
|
||||
// viteDevIds on a page
|
||||
viteDevIds = JSON.parse(
|
||||
sessionStorage.getItem('astro:viteDevIds') || '{"static":{},"dynamic":{}}'
|
||||
);
|
||||
}
|
||||
const page = (url: { origin: string; pathname: string }) => url.origin + url.pathname;
|
||||
|
||||
// only update history entries that are managed by us
|
||||
// leave other entries alone and do not accidently add state.
|
||||
const persistState = (state: State) => history.state && history.replaceState(state, '');
|
||||
|
@ -44,8 +53,10 @@ const PERSIST_ATTR = 'data-astro-transition-persist';
|
|||
const parser = new DOMParser();
|
||||
// explained at its usage
|
||||
let noopEl: HTMLDivElement;
|
||||
let reloadEl: HTMLDivElement;
|
||||
if (import.meta.env.DEV) {
|
||||
noopEl = document.createElement('div');
|
||||
reloadEl = document.createElement('div');
|
||||
}
|
||||
|
||||
// The History API does not tell you if navigation is forward or back, so
|
||||
|
@ -198,20 +209,40 @@ async function updateDOM(
|
|||
const href = el.getAttribute('href');
|
||||
return newDocument.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
|
||||
}
|
||||
// What follows is a fix for an issue (#8472) with missing client:only styles after transition.
|
||||
// That problem exists only in dev mode where styles are injected into the page by Vite.
|
||||
// Returning a noop element ensures that the styles are not removed from the old document.
|
||||
// Guarding the code below with the dev mode check
|
||||
// allows tree shaking to remove this code in production.
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
if (el.tagName === 'STYLE' && el.dataset.viteDevId) {
|
||||
const devId = el.dataset.viteDevId;
|
||||
// If this same style tag exists, remove it from the new page
|
||||
return (
|
||||
newDocument.querySelector(`style[data-vite-dev-id="${devId}"]`) ||
|
||||
// Otherwise, keep it anyways. This is client:only styles.
|
||||
noopEl
|
||||
);
|
||||
const viteDevId = el.getAttribute('data-vite-dev-id');
|
||||
if (!viteDevId) {
|
||||
return null;
|
||||
}
|
||||
const newDevEl = newDocument.head.querySelector(`[data-vite-dev-id="${viteDevId}"]`);
|
||||
if (newDevEl) {
|
||||
return newDevEl;
|
||||
}
|
||||
// What follows is a fix for an issue (#8472) with missing client:only styles after transition.
|
||||
// That problem exists only in dev mode where styles are injected into the page by Vite.
|
||||
// Returning a noop element ensures that the styles are not removed from the old document.
|
||||
// Guarding the code below with the dev mode check
|
||||
// allows tree shaking to remove this code in production.
|
||||
if (
|
||||
document.querySelector(
|
||||
`[${PERSIST_ATTR}] astro-island[client="only"], astro-island[client="only"][${PERSIST_ATTR}]`
|
||||
)
|
||||
) {
|
||||
const here = page(toLocation);
|
||||
const dynamicViteDevIds = viteDevIds.dynamic[here];
|
||||
if (!dynamicViteDevIds) {
|
||||
console.info(`
|
||||
${toLocation.pathname}
|
||||
Development mode only: This page uses view transitions with persisted client:only Astro islands.
|
||||
On the first transition to this page, Astro did a full page reload to capture the dynamic effects of the client only code.
|
||||
`);
|
||||
location.href = toLocation.href;
|
||||
return reloadEl;
|
||||
}
|
||||
if (dynamicViteDevIds?.includes(viteDevId)) {
|
||||
return noopEl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -249,12 +280,16 @@ async function updateDOM(
|
|||
// Swap head
|
||||
for (const el of Array.from(document.head.children)) {
|
||||
const newEl = persistedHeadElement(el as HTMLElement);
|
||||
if (newEl === reloadEl) {
|
||||
return;
|
||||
}
|
||||
// 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.
|
||||
// Otherwise remove the element from the head.
|
||||
// It doesn't exist in the new page or will be re-inserted after this loop
|
||||
el.remove();
|
||||
}
|
||||
}
|
||||
|
@ -336,6 +371,20 @@ async function transition(
|
|||
options: Options,
|
||||
popState?: State
|
||||
) {
|
||||
if (import.meta.env.DEV) {
|
||||
const thisPageStaticViteDevIds = viteDevIds.static[page(location)];
|
||||
if (thisPageStaticViteDevIds) {
|
||||
const allViteDevIds = new Set<string>();
|
||||
document.head
|
||||
.querySelectorAll('[data-vite-dev-id]')
|
||||
.forEach((el) => allViteDevIds.add(el.getAttribute('data-vite-dev-id')!));
|
||||
viteDevIds.dynamic[page(location)] = [...allViteDevIds].filter(
|
||||
(x) => !thisPageStaticViteDevIds.includes(x)
|
||||
);
|
||||
sessionStorage.setItem('astro:viteDevIds', JSON.stringify(viteDevIds, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
let finished: Promise<void>;
|
||||
const href = toLocation.href;
|
||||
const response = await fetchHTML(href);
|
||||
|
@ -360,6 +409,14 @@ async function transition(
|
|||
location.href = href;
|
||||
return;
|
||||
}
|
||||
if (import.meta.env.DEV) {
|
||||
const staticViteDevIds = new Set<string>();
|
||||
newDocument.querySelectorAll('head > [data-vite-dev-id]').forEach((el) => {
|
||||
staticViteDevIds.add(el.getAttribute('data-vite-dev-id')!);
|
||||
});
|
||||
viteDevIds.static[page(toLocation)] = [...staticViteDevIds];
|
||||
sessionStorage.setItem('astro:viteDevIds', JSON.stringify(viteDevIds, null, 2));
|
||||
}
|
||||
|
||||
if (!popState) {
|
||||
// save the current scroll position before we change the DOM and transition to the new page
|
||||
|
|
|
@ -170,7 +170,7 @@ export async function loadFixture(inlineConfig) {
|
|||
try {
|
||||
return await fetch(resolvedUrl, init);
|
||||
} catch (err) {
|
||||
// undici throws a vague error when it fails, so we log the url here to easily debug it
|
||||
// node fetch throws a vague error when it fails, so we log the url here to easily debug it
|
||||
if (err.message?.includes('fetch failed')) {
|
||||
console.error(`[astro test] failed to fetch ${resolvedUrl}`);
|
||||
console.error(err);
|
||||
|
|
|
@ -39,5 +39,8 @@
|
|||
"devDependencies": {
|
||||
"astro": "workspace:*",
|
||||
"astro-scripts": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,5 +58,8 @@
|
|||
"cheerio": "1.0.0-rc.12",
|
||||
"mocha": "^10.2.0",
|
||||
"wrangler": "^3.5.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,5 +59,8 @@
|
|||
"peerDependencies": {
|
||||
"@webcomponents/template-shadowroot": "^0.2.1",
|
||||
"lit": "^2.7.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,5 +94,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,5 +79,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,9 @@
|
|||
"cheerio": "1.0.0-rc.12",
|
||||
"express": "^4.18.2",
|
||||
"mocha": "^10.2.0",
|
||||
"node-mocks-http": "^1.13.0",
|
||||
"undici": "^5.23.0"
|
||||
"node-mocks-http": "^1.13.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,15 +8,12 @@ import type { OutgoingHttpHeaders } from 'node:http';
|
|||
* @returns NodeJS OutgoingHttpHeaders object with multiple set-cookie handled as an array of values
|
||||
*/
|
||||
export const createOutgoingHttpHeaders = (
|
||||
webHeaders: Headers | undefined | null
|
||||
headers: Headers | undefined | null
|
||||
): OutgoingHttpHeaders | undefined => {
|
||||
if (!webHeaders) {
|
||||
if (!headers) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// re-type to access Header.getSetCookie()
|
||||
const headers = webHeaders as HeadersWithGetSetCookie;
|
||||
|
||||
// at this point, a multi-value'd set-cookie header is invalid (it was concatenated as a single CSV, which is not valid for set-cookie)
|
||||
const nodeHeaders: OutgoingHttpHeaders = Object.fromEntries(headers.entries());
|
||||
|
||||
|
@ -26,7 +23,8 @@ export const createOutgoingHttpHeaders = (
|
|||
|
||||
// if there is > 1 set-cookie header, we have to fix it to be an array of values
|
||||
if (headers.has('set-cookie')) {
|
||||
const cookieHeaders = headers.getSetCookie();
|
||||
// @ts-expect-error
|
||||
const cookieHeaders = headers.getSetCookie() as string[];
|
||||
if (cookieHeaders.length > 1) {
|
||||
// the Headers.entries() API already normalized all header names to lower case so we can safely index this as 'set-cookie'
|
||||
nodeHeaders['set-cookie'] = cookieHeaders;
|
||||
|
@ -35,8 +33,3 @@ export const createOutgoingHttpHeaders = (
|
|||
|
||||
return nodeHeaders;
|
||||
};
|
||||
|
||||
interface HeadersWithGetSetCookie extends Headers {
|
||||
// the @astrojs/webapi polyfill makes this available (as of undici@5.19.0), but tsc doesn't pick it up on the built-in Headers type from DOM lib
|
||||
getSetCookie(): string[];
|
||||
}
|
||||
|
|
|
@ -38,5 +38,8 @@
|
|||
"devDependencies": {
|
||||
"astro": "workspace:*",
|
||||
"astro-scripts": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|||
import sirv from './sirv.js';
|
||||
const resolve = createRequire(import.meta.url).resolve;
|
||||
|
||||
type PartytownOptions = {
|
||||
export type PartytownOptions = {
|
||||
config?: PartytownConfig;
|
||||
};
|
||||
|
||||
|
|
|
@ -52,5 +52,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,5 +40,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"throttles": "^1.0.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,5 +67,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,5 +43,8 @@
|
|||
"chai": "^4.3.7",
|
||||
"mocha": "^10.2.0",
|
||||
"xml2js": "0.6.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,5 +47,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,5 +53,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,5 +45,8 @@
|
|||
"peerDependencies": {
|
||||
"astro": "workspace:^3.2.2",
|
||||
"tailwindcss": "^3.0.24"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,5 +72,8 @@
|
|||
"chai-jest-snapshot": "^2.0.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"mocha": "^10.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,5 +61,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,5 +37,8 @@
|
|||
"keywords": [
|
||||
"astro",
|
||||
"astro-component"
|
||||
]
|
||||
],
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,5 +57,8 @@
|
|||
"chai": "^4.3.7",
|
||||
"mdast-util-mdx-expression": "^1.3.2",
|
||||
"mocha": "^10.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,8 +76,8 @@ export function remarkShiki({
|
|||
// It would become this before hitting our regexes:
|
||||
// <span class="line"
|
||||
|
||||
// Replace "shiki" class naming with "astro" and add "is:raw".
|
||||
html = html.replace(/<pre class="(.*?)shiki(.*?)"/, `<pre is:raw class="$1astro-code$2"`);
|
||||
// Replace "shiki" class naming with "astro".
|
||||
html = html.replace(/<pre class="(.*?)shiki(.*?)"/, `<pre class="$1astro-code$2"`);
|
||||
// Add "user-select: none;" for "+"/"-" diff symbols
|
||||
if (node.lang === 'diff') {
|
||||
html = html.replace(
|
||||
|
|
12
packages/markdown/remark/test/shiki.js
Normal file
12
packages/markdown/remark/test/shiki.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { createMarkdownProcessor } from '../dist/index.js';
|
||||
import chai from 'chai';
|
||||
|
||||
describe('shiki syntax highlighting', async () => {
|
||||
const processor = await createMarkdownProcessor();
|
||||
|
||||
it('does not add is:raw to the output', async () => {
|
||||
const { code } = await processor.render('```\ntest\n```');
|
||||
|
||||
chai.expect(code).not.to.contain('is:raw');
|
||||
});
|
||||
});
|
|
@ -35,7 +35,6 @@
|
|||
"dset": "^3.1.2",
|
||||
"is-docker": "^3.0.0",
|
||||
"is-wsl": "^3.0.0",
|
||||
"undici": "^5.23.0",
|
||||
"which-pm-runs": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -49,5 +48,8 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const ASTRO_TELEMETRY_ENDPOINT = `https://telemetry.astro.build/api/v1/record`;
|
||||
import { fetch } from 'undici';
|
||||
|
||||
export function post(body: Record<string, any>): Promise<any> {
|
||||
return fetch(ASTRO_TELEMETRY_ENDPOINT, {
|
||||
|
|
|
@ -38,5 +38,8 @@
|
|||
"keywords": [
|
||||
"astro",
|
||||
"astro-component"
|
||||
]
|
||||
],
|
||||
"publishConfig": {
|
||||
"provenance": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -628,9 +628,6 @@ importers:
|
|||
tsconfig-resolver:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
undici:
|
||||
specifier: ^5.23.0
|
||||
version: 5.23.0
|
||||
unist-util-visit:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2
|
||||
|
@ -4307,9 +4304,6 @@ importers:
|
|||
node-mocks-http:
|
||||
specifier: ^1.13.0
|
||||
version: 1.13.0
|
||||
undici:
|
||||
specifier: ^5.23.0
|
||||
version: 5.23.0
|
||||
|
||||
packages/integrations/node/test/fixtures/api-route:
|
||||
dependencies:
|
||||
|
@ -5005,9 +4999,6 @@ importers:
|
|||
is-wsl:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
undici:
|
||||
specifier: ^5.23.0
|
||||
version: 5.23.0
|
||||
which-pm-runs:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
|
@ -17276,6 +17267,7 @@ packages:
|
|||
engines: {node: '>=14.0'}
|
||||
dependencies:
|
||||
busboy: 1.6.0
|
||||
dev: true
|
||||
|
||||
/unherit@3.0.1:
|
||||
resolution: {integrity: sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==}
|
||||
|
|
Loading…
Reference in a new issue