Default preview host to localhost (#5753)

* Initial refactor

* Extract as vite plugin

* Cleanup vite plugin

* Reduce option passing

* Use localhost as preview default host

* Simplify base handling

* Fix host handling

* Add changeset

* Remove unused imports

* Remove unused sirv dep

* Try pin playwright to 1.28.1

* Update playwright

* Try this

* Speed up CI

* Try fix page off

* Refactor networkidle

* Ensure open connections are destroyed when the preview server is closed

* Revert debug code

Co-authored-by: Matthew Phillips <matthew@matthewphillips.info>
This commit is contained in:
Bjorn Lu 2023-01-10 01:14:07 +08:00 committed by GitHub
parent f35411487b
commit 302e0ef8f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 183 additions and 270 deletions

View file

@ -0,0 +1,5 @@
---
'astro': major
---
Default preview host to `localhost` instead of `127.0.0.1`. This allows the static server and integration preview servers to serve under ipv6.

View file

@ -155,8 +155,8 @@
"rehype": "^12.0.1", "rehype": "^12.0.1",
"resolve": "^1.22.0", "resolve": "^1.22.0",
"semver": "^7.3.7", "semver": "^7.3.7",
"server-destroy": "^1.0.1",
"shiki": "^0.11.1", "shiki": "^0.11.1",
"sirv": "^2.0.2",
"slash": "^4.0.0", "slash": "^4.0.0",
"string-width": "^5.1.2", "string-width": "^5.1.2",
"strip-ansi": "^7.0.1", "strip-ansi": "^7.0.1",
@ -171,7 +171,7 @@
"zod": "^3.17.3" "zod": "^3.17.3"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.22.2", "@playwright/test": "^1.29.2",
"@types/babel__generator": "^7.6.4", "@types/babel__generator": "^7.6.4",
"@types/babel__traverse": "^7.17.1", "@types/babel__traverse": "^7.17.1",
"@types/chai": "^4.3.1", "@types/chai": "^4.3.1",
@ -191,6 +191,7 @@
"@types/resolve": "^1.20.2", "@types/resolve": "^1.20.2",
"@types/rimraf": "^3.0.2", "@types/rimraf": "^3.0.2",
"@types/send": "^0.17.1", "@types/send": "^0.17.1",
"@types/server-destroy": "^1.0.1",
"@types/unist": "^2.0.6", "@types/unist": "^2.0.6",
"astro-scripts": "workspace:*", "astro-scripts": "workspace:*",
"chai": "^4.3.6", "chai": "^4.3.6",

View file

@ -49,9 +49,6 @@ export default async function dev(
// Start listening to the port // Start listening to the port
const devServerAddressInfo = await startContainer(restart.container); const devServerAddressInfo = await startContainer(restart.container);
const site = settings.config.site
? new URL(settings.config.base, settings.config.site)
: undefined;
info( info(
options.logging, options.logging,
null, null,
@ -59,7 +56,7 @@ export default async function dev(
startupTime: performance.now() - devStart, startupTime: performance.now() - devStart,
resolvedUrls: restart.container.viteServer.resolvedUrls || { local: [], network: [] }, resolvedUrls: restart.container.viteServer.resolvedUrls || { local: [], network: [] },
host: settings.config.server.host, host: settings.config.server.host,
site, base: settings.config.base,
isRestart: options.isRestart, isRestart: options.isRestart,
}) })
); );

View file

@ -14,14 +14,11 @@ import {
underline, underline,
yellow, yellow,
} from 'kleur/colors'; } from 'kleur/colors';
import type { AddressInfo } from 'net';
import os from 'os';
import { ResolvedServerUrls } from 'vite'; import { ResolvedServerUrls } from 'vite';
import { ZodError } from 'zod'; import { ZodError } from 'zod';
import { renderErrorMarkdown } from './errors/dev/utils.js'; import { renderErrorMarkdown } from './errors/dev/utils.js';
import { AstroError, CompilerError, ErrorWithMetadata } from './errors/index.js'; import { AstroError, CompilerError, ErrorWithMetadata } from './errors/index.js';
import { removeTrailingForwardSlash } from './path.js'; import { emoji, padMultilineString } from './util.js';
import { emoji, getLocalAddress, padMultilineString } from './util.js';
const PREFIX_PADDING = 6; const PREFIX_PADDING = 6;
@ -58,31 +55,26 @@ export function serverStart({
startupTime, startupTime,
resolvedUrls, resolvedUrls,
host, host,
site, base,
isRestart = false, isRestart = false,
}: { }: {
startupTime: number; startupTime: number;
resolvedUrls: ResolvedServerUrls; resolvedUrls: ResolvedServerUrls;
host: string | boolean; host: string | boolean;
site: URL | undefined; base: string;
isRestart?: boolean; isRestart?: boolean;
}): string { }): string {
// PACKAGE_VERSION is injected at build-time // PACKAGE_VERSION is injected at build-time
const version = process.env.PACKAGE_VERSION ?? '0.0.0'; const version = process.env.PACKAGE_VERSION ?? '0.0.0';
const rootPath = site ? site.pathname : '/';
const localPrefix = `${dim('┃')} Local `; const localPrefix = `${dim('┃')} Local `;
const networkPrefix = `${dim('┃')} Network `; const networkPrefix = `${dim('┃')} Network `;
const emptyPrefix = ' '.repeat(11); const emptyPrefix = ' '.repeat(11);
const localUrlMessages = resolvedUrls.local.map((url, i) => { const localUrlMessages = resolvedUrls.local.map((url, i) => {
return `${i === 0 ? localPrefix : emptyPrefix}${bold( return `${i === 0 ? localPrefix : emptyPrefix}${bold(cyan(new URL(url).origin + base))}`;
cyan(removeTrailingForwardSlash(url) + rootPath)
)}`;
}); });
const networkUrlMessages = resolvedUrls.network.map((url, i) => { const networkUrlMessages = resolvedUrls.network.map((url, i) => {
return `${i === 0 ? networkPrefix : emptyPrefix}${bold( return `${i === 0 ? networkPrefix : emptyPrefix}${bold(cyan(new URL(url).origin + base))}`;
cyan(removeTrailingForwardSlash(url) + rootPath)
)}`;
}); });
if (networkUrlMessages.length === 0) { if (networkUrlMessages.length === 0) {
@ -109,50 +101,6 @@ export function serverStart({
.join('\n'); .join('\n');
} }
export function resolveServerUrls({
address,
host,
https,
}: {
address: AddressInfo;
host: string | boolean;
https: boolean;
}): ResolvedServerUrls {
const { address: networkAddress, port } = address;
const localAddress = getLocalAddress(networkAddress, host);
const networkLogging = getNetworkLogging(host);
const toDisplayUrl = (hostname: string) => `${https ? 'https' : 'http'}://${hostname}:${port}`;
let local = toDisplayUrl(localAddress);
let network: string | null = null;
if (networkLogging === 'visible') {
const ipv4Networks = Object.values(os.networkInterfaces())
.flatMap((networkInterface) => networkInterface ?? [])
.filter(
(networkInterface) =>
networkInterface?.address &&
// Node < v18
((typeof networkInterface.family === 'string' && networkInterface.family === 'IPv4') ||
// Node >= v18
(typeof networkInterface.family === 'number' && (networkInterface as any).family === 4))
);
for (let { address: ipv4Address } of ipv4Networks) {
if (ipv4Address.includes('127.0.0.1')) {
const displayAddress = ipv4Address.replace('127.0.0.1', localAddress);
local = toDisplayUrl(displayAddress);
} else {
network = toDisplayUrl(ipv4Address);
}
}
}
return {
local: [local],
network: network ? [network] : [],
};
}
export function telemetryNotice() { export function telemetryNotice() {
const headline = yellow(`Astro now collects ${bold('anonymous')} usage data.`); const headline = yellow(`Astro now collects ${bold('anonymous')} usage data.`);
const why = `This ${bold('optional program')} will help shape our roadmap.`; const why = `This ${bold('optional program')} will help shape our roadmap.`;
@ -228,11 +176,6 @@ export function cancelled(message: string, tip?: string) {
.join('\n'); .join('\n');
} }
/** Display port in use */
export function portInUse({ port }: { port: number }): string {
return `Port ${port} in use. Trying a new one…`;
}
const LOCAL_IP_HOSTS = new Set(['localhost', '127.0.0.1']); const LOCAL_IP_HOSTS = new Set(['localhost', '127.0.0.1']);
export function getNetworkLogging(host: string | boolean): 'none' | 'host-to-expose' | 'visible' { export function getNetworkLogging(host: string | boolean): 'none' | 'host-to-expose' | 'visible' {

View file

@ -23,11 +23,9 @@ export default async function preview(
logging: logging, logging: logging,
}); });
await runHookConfigDone({ settings: settings, logging: logging }); await runHookConfigDone({ settings: settings, logging: logging });
const host = getResolvedHostForHttpServer(settings.config.server.host);
const { port, headers } = settings.config.server;
if (settings.config.output === 'static') { if (settings.config.output === 'static') {
const server = await createStaticPreviewServer(settings, { logging, host, port, headers }); const server = await createStaticPreviewServer(settings, logging);
return server; return server;
} }
if (!settings.adapter) { if (!settings.adapter) {
@ -55,8 +53,8 @@ export default async function preview(
outDir: settings.config.outDir, outDir: settings.config.outDir,
client: settings.config.build.client, client: settings.config.build.client,
serverEntrypoint: new URL(settings.config.build.serverEntry, settings.config.build.server), serverEntrypoint: new URL(settings.config.build.serverEntry, settings.config.build.server),
host, host: getResolvedHostForHttpServer(settings.config.server.host),
port, port: settings.config.server.port,
base: settings.config.base, base: settings.config.base,
}); });

View file

@ -1,15 +1,14 @@
import type { AddressInfo } from 'net'; import http from 'http';
import { performance } from 'perf_hooks';
import { fileURLToPath } from 'url';
import { preview, type PreviewServer as VitePreviewServer } from 'vite';
import type { AstroSettings } from '../../@types/astro'; import type { AstroSettings } from '../../@types/astro';
import type { LogOptions } from '../logger/core'; import type { LogOptions } from '../logger/core';
import fs from 'fs';
import http, { OutgoingHttpHeaders } from 'http';
import { performance } from 'perf_hooks';
import sirv from 'sirv';
import { fileURLToPath } from 'url';
import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js';
import { error, info } from '../logger/core.js'; import { error, info } from '../logger/core.js';
import * as msg from '../messages.js'; import * as msg from '../messages.js';
import { getResolvedHostForHttpServer } from './util.js';
import { vitePluginAstroPreview } from './vite-plugin-astro-preview.js';
import enableDestroy from 'server-destroy';
export interface PreviewServer { export interface PreviewServer {
host?: string; host?: string;
@ -19,160 +18,65 @@ export interface PreviewServer {
stop(): Promise<void>; stop(): Promise<void>;
} }
const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/;
/** The primary dev action */
export default async function createStaticPreviewServer( export default async function createStaticPreviewServer(
settings: AstroSettings, settings: AstroSettings,
{ logging: LogOptions
logging,
host,
port,
headers,
}: {
logging: LogOptions;
host: string | undefined;
port: number;
headers: OutgoingHttpHeaders | undefined;
}
): Promise<PreviewServer> { ): Promise<PreviewServer> {
const startServerTime = performance.now(); const startServerTime = performance.now();
const defaultOrigin = 'http://localhost';
const trailingSlash = settings.config.trailingSlash; let previewServer: VitePreviewServer;
/** Base request URL. */ try {
let baseURL = new URL(settings.config.base, new URL(settings.config.site || '/', defaultOrigin)); previewServer = await preview({
const staticFileServer = sirv(fileURLToPath(settings.config.outDir), { configFile: false,
dev: true, base: settings.config.base,
etag: true, appType: 'mpa',
maxAge: 0, build: {
setHeaders: (res, pathname, stats) => { outDir: fileURLToPath(settings.config.outDir),
for (const [name, value] of Object.entries(headers ?? {})) {
if (value) res.setHeader(name, value);
}
}, },
}); preview: {
// Create the preview server, send static files out of the `dist/` directory.
const server = http.createServer((req, res) => {
const requestURL = new URL(req.url as string, defaultOrigin);
// respond 404 to requests outside the base request directory
if (!requestURL.pathname.startsWith(baseURL.pathname)) {
res.statusCode = 404;
res.end(subpathNotUsedTemplate(baseURL.pathname, requestURL.pathname));
return;
}
/** Relative request path. */
const pathname = requestURL.pathname.slice(baseURL.pathname.length - 1);
const isRoot = pathname === '/';
const hasTrailingSlash = isRoot || pathname.endsWith('/');
function sendError(message: string) {
res.statusCode = 404;
res.end(notFoundTemplate(pathname, message));
}
switch (true) {
case hasTrailingSlash && trailingSlash == 'never' && !isRoot:
sendError('Not Found (trailingSlash is set to "never")');
return;
case !hasTrailingSlash &&
trailingSlash == 'always' &&
!isRoot &&
!HAS_FILE_EXTENSION_REGEXP.test(pathname):
sendError('Not Found (trailingSlash is set to "always")');
return;
default: {
// HACK: rewrite req.url so that sirv finds the file
req.url = '/' + req.url?.replace(baseURL.pathname, '');
staticFileServer(req, res, () => {
const errorPagePath = fileURLToPath(settings.config.outDir + '/404.html');
if (fs.existsSync(errorPagePath)) {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html;charset=utf-8');
res.end(fs.readFileSync(errorPagePath));
} else {
staticFileServer(req, res, () => {
sendError('Not Found');
});
}
});
return;
}
}
});
let httpServer: http.Server;
/** Expose dev server to `port` */
function startServer(timerStart: number): Promise<void> {
let showedPortTakenMsg = false;
let showedListenMsg = false;
return new Promise<void>((resolve, reject) => {
const listen = () => {
httpServer = server.listen(port, host, async () => {
if (!showedListenMsg) {
const resolvedUrls = msg.resolveServerUrls({
address: server.address() as AddressInfo,
host: settings.config.server.host, host: settings.config.server.host,
https: false, port: settings.config.server.port,
headers: settings.config.server.headers,
},
plugins: [vitePluginAstroPreview(settings)],
}); });
} catch (err) {
if (err instanceof Error) {
error(logging, 'astro', err.stack || err.message);
}
throw err;
}
enableDestroy(previewServer.httpServer);
// Log server start URLs
info( info(
logging, logging,
null, null,
msg.serverStart({ msg.serverStart({
startupTime: performance.now() - timerStart, startupTime: performance.now() - startServerTime,
resolvedUrls, resolvedUrls: previewServer.resolvedUrls,
host: settings.config.server.host, host: settings.config.server.host,
site: baseURL, base: settings.config.base,
}) })
); );
}
showedListenMsg = true;
resolve();
});
httpServer?.on('error', onError);
};
const onError = (err: NodeJS.ErrnoException) => {
if (err.code && err.code === 'EADDRINUSE') {
if (!showedPortTakenMsg) {
info(logging, 'astro', msg.portInUse({ port }));
showedPortTakenMsg = true; // only print this once
}
port++;
return listen(); // retry
} else {
error(logging, 'astro', err.stack || err.message);
httpServer?.removeListener('error', onError);
reject(err); // reject
}
};
listen();
});
}
// Start listening on `hostname:port`.
await startServer(startServerTime);
// Resolves once the server is closed // Resolves once the server is closed
function closed() { function closed() {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
httpServer!.addListener('close', resolve); previewServer.httpServer.addListener('close', resolve);
httpServer!.addListener('error', reject); previewServer.httpServer.addListener('error', reject);
}); });
} }
return { return {
host, host: getResolvedHostForHttpServer(settings.config.server.host),
port, port: settings.config.server.port,
closed, closed,
server: httpServer!, server: previewServer.httpServer,
stop: async () => { stop: async () => {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
httpServer.close((err) => (err ? reject(err) : resolve(undefined))); previewServer.httpServer.destroy((err) => (err ? reject(err) : resolve(undefined)));
}); });
}, },
}; };

View file

@ -1,7 +1,7 @@
export function getResolvedHostForHttpServer(host: string | boolean) { export function getResolvedHostForHttpServer(host: string | boolean) {
if (host === false) { if (host === false) {
// Use a secure default // Use a secure default
return '127.0.0.1'; return 'localhost';
} else if (host === true) { } else if (host === true) {
// If passed --host in the CLI without arguments // If passed --host in the CLI without arguments
return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs) return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)
@ -9,3 +9,11 @@ export function getResolvedHostForHttpServer(host: string | boolean) {
return host; return host;
} }
} }
export function stripBase(path: string, base: string): string {
if (path === base) {
return '/';
}
const baseWithSlash = base.endsWith('/') ? base : base + '/';
return path.replace(RegExp('^' + baseWithSlash), '/');
}

View file

@ -0,0 +1,68 @@
import fs from 'fs';
import { fileURLToPath } from 'url';
import { Plugin } from 'vite';
import { AstroSettings } from '../../@types/astro.js';
import { notFoundTemplate, subpathNotUsedTemplate } from '../../template/4xx.js';
import { stripBase } from './util.js';
const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/;
export function vitePluginAstroPreview(settings: AstroSettings): Plugin {
const { base, outDir, trailingSlash } = settings.config;
return {
name: 'astro:preview',
apply: 'serve',
configurePreviewServer(server) {
server.middlewares.use((req, res, next) => {
// respond 404 to requests outside the base request directory
if (!req.url!.startsWith(base)) {
res.statusCode = 404;
res.end(subpathNotUsedTemplate(base, req.url!));
return;
}
const pathname = stripBase(req.url!, base);
const isRoot = pathname === '/';
// Validate trailingSlash
if (!isRoot) {
const hasTrailingSlash = pathname.endsWith('/');
if (hasTrailingSlash && trailingSlash == 'never') {
res.statusCode = 404;
res.end(notFoundTemplate(pathname, 'Not Found (trailingSlash is set to "never")'));
return;
}
if (
!hasTrailingSlash &&
trailingSlash == 'always' &&
!HAS_FILE_EXTENSION_REGEXP.test(pathname)
) {
res.statusCode = 404;
res.end(notFoundTemplate(pathname, 'Not Found (trailingSlash is set to "always")'));
return;
}
}
next();
});
return () => {
server.middlewares.use((req, res) => {
const errorPagePath = fileURLToPath(outDir + '/404.html');
if (fs.existsSync(errorPagePath)) {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html;charset=utf-8');
res.end(fs.readFileSync(errorPagePath));
} else {
const pathname = stripBase(req.url!, base);
res.statusCode = 404;
res.end(notFoundTemplate(pathname, 'Not Found'));
}
});
};
},
};
}

View file

@ -164,14 +164,6 @@ export function emoji(char: string, fallback: string) {
return process.platform !== 'win32' ? char : fallback; return process.platform !== 'win32' ? char : fallback;
} }
export function getLocalAddress(serverAddress: string, host: string | boolean): string {
if (typeof host === 'boolean' || host === 'localhost') {
return 'localhost';
} else {
return serverAddress;
}
}
/** /**
* Simulate Vite's resolve and import analysis so we can import the id as an URL * Simulate Vite's resolve and import analysis so we can import the id as an URL
* through a script tag or a dynamic import as-is. * through a script tag or a dynamic import as-is.

View file

@ -77,17 +77,11 @@ describe('astro cli', () => {
const localURL = new URL(local); const localURL = new URL(local);
const networkURL = new URL(network); const networkURL = new URL(network);
if (cmd === 'dev') {
expect(localURL.hostname).to.be.oneOf( expect(localURL.hostname).to.be.oneOf(
['localhost', '127.0.0.1'], ['localhost', '127.0.0.1'],
`Expected local URL to be on localhost` `Expected local URL to be on localhost`
); );
} else {
expect(localURL.hostname).to.be.equal(
flagValue ?? 'localhost',
`Expected local URL to be on localhost`
);
}
// Note: our tests run in parallel so this could be 3000+! // Note: our tests run in parallel so this could be 3000+!
expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual( expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual(
3000, 3000,
@ -113,17 +107,11 @@ describe('astro cli', () => {
expect(network).to.not.be.undefined; expect(network).to.not.be.undefined;
const localURL = new URL(local); const localURL = new URL(local);
if (cmd === 'dev') {
expect(localURL.hostname).to.be.oneOf( expect(localURL.hostname).to.be.oneOf(
['localhost', '127.0.0.1'], ['localhost', '127.0.0.1'],
`Expected local URL to be on localhost` `Expected local URL to be on localhost`
); );
} else {
expect(localURL.hostname).to.be.equal(
'localhost',
`Expected local URL to be on localhost`
);
}
expect(() => new URL(networkURL)).to.throw(); expect(() => new URL(networkURL)).to.throw();
}); });
}); });
@ -140,14 +128,10 @@ describe('astro cli', () => {
expect(network).to.be.undefined; expect(network).to.be.undefined;
const localURL = new URL(local); const localURL = new URL(local);
if (cmd === 'dev') {
expect(localURL.hostname).to.be.oneOf( expect(localURL.hostname).to.be.oneOf(
['localhost', '127.0.0.1'], ['localhost', '127.0.0.1'],
`Expected local URL to be on localhost` `Expected local URL to be on localhost`
); );
} else {
expect(localURL.hostname).to.be.equal(flagValue, `Expected local URL to be on localhost`);
}
}); });
}); });
}); });

View file

@ -31,13 +31,13 @@
"test:match": "playwright test -g" "test:match": "playwright test -g"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.26.0", "@playwright/test": "^1.29.2",
"@types/chai": "^4.3.1", "@types/chai": "^4.3.1",
"@types/chai-as-promised": "^7.1.5", "@types/chai-as-promised": "^7.1.5",
"@types/mocha": "^9.1.1", "@types/mocha": "^9.1.1",
"astro": "workspace:*", "astro": "workspace:*",
"astro-scripts": "workspace:*", "astro-scripts": "workspace:*",
"playwright": "^1.22.2" "playwright": "^1.29.2"
}, },
"dependencies": { "dependencies": {
"throttles": "^1.0.1" "throttles": "^1.0.1"

View file

@ -19,7 +19,7 @@ test.describe('Basic prefetch', () => {
test('skips /admin', async ({ page, astro }) => { test('skips /admin', async ({ page, astro }) => {
const requests = []; const requests = [];
page.on('request', async (request) => requests.push(request.url())); page.on('request', (request) => requests.push(request.url()));
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));
@ -56,7 +56,7 @@ test.describe('Basic prefetch', () => {
test('skips /admin', async ({ page, astro }) => { test('skips /admin', async ({ page, astro }) => {
const requests = []; const requests = [];
page.on('request', async (request) => requests.push(request.url())); page.on('request', (request) => requests.push(request.url()));
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));

View file

@ -27,7 +27,7 @@ test.describe('Custom prefetch selectors', () => {
test('only prefetches /contact', async ({ page, astro }) => { test('only prefetches /contact', async ({ page, astro }) => {
const requests = []; const requests = [];
page.on('request', async (request) => requests.push(request.url())); page.on('request', (request) => requests.push(request.url()));
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));
@ -64,7 +64,7 @@ test.describe('Custom prefetch selectors', () => {
test('only prefetches /contact', async ({ page, astro }) => { test('only prefetches /contact', async ({ page, astro }) => {
const requests = []; const requests = [];
page.on('request', async (request) => requests.push(request.url())); page.on('request', (request) => requests.push(request.url()));
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));

View file

@ -39,7 +39,7 @@ test.describe('Style prefetch', () => {
test('style fetching', async ({ page, astro }) => { test('style fetching', async ({ page, astro }) => {
const requests = []; const requests = [];
page.on('request', async (request) => requests.push(request.url())); page.on('request', (request) => requests.push(request.url()));
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));

View file

@ -396,7 +396,7 @@ importers:
'@babel/plugin-transform-react-jsx': ^7.17.12 '@babel/plugin-transform-react-jsx': ^7.17.12
'@babel/traverse': ^7.18.2 '@babel/traverse': ^7.18.2
'@babel/types': ^7.18.4 '@babel/types': ^7.18.4
'@playwright/test': ^1.22.2 '@playwright/test': ^1.29.2
'@types/babel__core': ^7.1.19 '@types/babel__core': ^7.1.19
'@types/babel__generator': ^7.6.4 '@types/babel__generator': ^7.6.4
'@types/babel__traverse': ^7.17.1 '@types/babel__traverse': ^7.17.1
@ -418,6 +418,7 @@ importers:
'@types/resolve': ^1.20.2 '@types/resolve': ^1.20.2
'@types/rimraf': ^3.0.2 '@types/rimraf': ^3.0.2
'@types/send': ^0.17.1 '@types/send': ^0.17.1
'@types/server-destroy': ^1.0.1
'@types/unist': ^2.0.6 '@types/unist': ^2.0.6
'@types/yargs-parser': ^21.0.0 '@types/yargs-parser': ^21.0.0
acorn: ^8.8.1 acorn: ^8.8.1
@ -466,8 +467,8 @@ importers:
rollup: ^3.9.0 rollup: ^3.9.0
sass: ^1.52.2 sass: ^1.52.2
semver: ^7.3.7 semver: ^7.3.7
server-destroy: ^1.0.1
shiki: ^0.11.1 shiki: ^0.11.1
sirv: ^2.0.2
slash: ^4.0.0 slash: ^4.0.0
srcset-parse: ^1.1.0 srcset-parse: ^1.1.0
string-width: ^5.1.2 string-width: ^5.1.2
@ -529,8 +530,8 @@ importers:
rehype: 12.0.1 rehype: 12.0.1
resolve: 1.22.1 resolve: 1.22.1
semver: 7.3.8 semver: 7.3.8
server-destroy: 1.0.1
shiki: 0.11.1 shiki: 0.11.1
sirv: 2.0.2
slash: 4.0.0 slash: 4.0.0
string-width: 5.1.2 string-width: 5.1.2
strip-ansi: 7.0.1 strip-ansi: 7.0.1
@ -564,6 +565,7 @@ importers:
'@types/resolve': 1.20.2 '@types/resolve': 1.20.2
'@types/rimraf': 3.0.2 '@types/rimraf': 3.0.2
'@types/send': 0.17.1 '@types/send': 0.17.1
'@types/server-destroy': 1.0.1
'@types/unist': 2.0.6 '@types/unist': 2.0.6
astro-scripts: link:../../scripts astro-scripts: link:../../scripts
chai: 4.3.7 chai: 4.3.7
@ -3139,13 +3141,13 @@ importers:
packages/integrations/prefetch: packages/integrations/prefetch:
specifiers: specifiers:
'@playwright/test': ^1.26.0 '@playwright/test': ^1.29.2
'@types/chai': ^4.3.1 '@types/chai': ^4.3.1
'@types/chai-as-promised': ^7.1.5 '@types/chai-as-promised': ^7.1.5
'@types/mocha': ^9.1.1 '@types/mocha': ^9.1.1
astro: workspace:* astro: workspace:*
astro-scripts: workspace:* astro-scripts: workspace:*
playwright: ^1.22.2 playwright: ^1.29.2
throttles: ^1.0.1 throttles: ^1.0.1
dependencies: dependencies:
throttles: 1.0.1 throttles: 1.0.1
@ -7055,6 +7057,7 @@ packages:
/@types/node/14.18.36: /@types/node/14.18.36:
resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==} resolution: {integrity: sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==}
dev: true
/@types/node/16.18.11: /@types/node/16.18.11:
resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==} resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==}
@ -7126,7 +7129,7 @@ packages:
/@types/resolve/1.17.1: /@types/resolve/1.17.1:
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
dependencies: dependencies:
'@types/node': 14.18.36 '@types/node': 18.11.18
/@types/resolve/1.20.2: /@types/resolve/1.20.2:
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
@ -7141,7 +7144,7 @@ packages:
/@types/sax/1.2.4: /@types/sax/1.2.4:
resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==} resolution: {integrity: sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==}
dependencies: dependencies:
'@types/node': 17.0.45 '@types/node': 18.11.18
dev: false dev: false
/@types/scheduler/0.16.2: /@types/scheduler/0.16.2:
@ -7162,6 +7165,12 @@ packages:
'@types/node': 18.11.18 '@types/node': 18.11.18
dev: true dev: true
/@types/server-destroy/1.0.1:
resolution: {integrity: sha512-77QGr7waZbE0Y0uF+G+uH3H3SmhyA78Jf2r5r7QSrpg0U3kSXduWpGjzP9PvPLR/KCy+kHjjpnugRHsYTnHopg==}
dependencies:
'@types/node': 18.11.18
dev: true
/@types/set-cookie-parser/2.4.2: /@types/set-cookie-parser/2.4.2:
resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==} resolution: {integrity: sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==}
dependencies: dependencies:
@ -11129,7 +11138,7 @@ packages:
resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
engines: {node: '>= 10.13.0'} engines: {node: '>= 10.13.0'}
dependencies: dependencies:
'@types/node': 14.18.36 '@types/node': 18.11.18
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 7.2.0 supports-color: 7.2.0
@ -13994,6 +14003,10 @@ packages:
randombytes: 2.1.0 randombytes: 2.1.0
dev: true dev: true
/server-destroy/1.0.1:
resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==}
dev: false
/set-blocking/2.0.0: /set-blocking/2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}