Feat: expose server on local network with new --host flag (#2760)
* feat: update config to support bool --hostname * fix: show localhost for --hostname=true * feat: address logging feature parity w/ Vite * chore: update type docs * refactor: extract local, network prefs to variable * feat: add --host to --help output * feat: deprecate --hostname, add --host * feat: add --host tests * feat: update preview to support new flags * fix: show --host in dev server log * feat: update config tests for --host flag * chore: test lint * chore: update lock with new fixture * chore: add changeset * refactor: add more details to JSdocs * fix: update path tests * feat: only expose when --host is not local * fix: make flag --help less verbose * fix: address @types comments * fix: lint * chore: remove unused import * fix: use host flag for config test * fix: ensure local logs come before network * refactor: switch up that network logging one last time! * feat: update unit tests * chore: remove debugging block * fix: only parse network logs if network is present
This commit is contained in:
parent
2bb2c2f7d1
commit
77b9c95352
16 changed files with 239 additions and 86 deletions
5
.changeset/breezy-walls-guess.md
Normal file
5
.changeset/breezy-walls-guess.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
Introduce a new --host flag + host devOption to expose your server on a network IP
|
|
@ -24,6 +24,7 @@ export interface CLIFlags {
|
|||
projectRoot?: string;
|
||||
site?: string;
|
||||
sitemap?: boolean;
|
||||
host?: string | boolean;
|
||||
hostname?: string;
|
||||
port?: number;
|
||||
config?: string;
|
||||
|
@ -314,12 +315,29 @@ export interface AstroUserConfig {
|
|||
* @name Dev Options
|
||||
*/
|
||||
devOptions?: {
|
||||
/**
|
||||
* @docs
|
||||
* @name devOptions.host
|
||||
* @type {string | boolean}
|
||||
* @default `false`
|
||||
* @version 0.24.0
|
||||
* @description
|
||||
* Set which network IP addresses the dev server should listen on (i.e. non-localhost IPs).
|
||||
* - `false` - do not expose on a network IP address
|
||||
* - `true` - listen on all addresses, including LAN and public addresses
|
||||
* - `[custom-address]` - expose on a network IP address at `[custom-address]`
|
||||
*/
|
||||
host?: string | boolean;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name devOptions.hostname
|
||||
* @type {string}
|
||||
* @default `'localhost'`
|
||||
* @deprecated Use `host` instead
|
||||
* @description
|
||||
* > **This option is deprecated.** Consider using `host` instead.
|
||||
*
|
||||
* Set which IP addresses the dev server should listen on. Set this to 0.0.0.0 to listen on all addresses, including LAN and public addresses.
|
||||
*/
|
||||
hostname?: string;
|
||||
|
|
|
@ -38,6 +38,7 @@ function printHelp() {
|
|||
title('Flags');
|
||||
table(
|
||||
[
|
||||
['--host [optional IP]', 'Expose server on network'],
|
||||
['--config <path>', 'Specify the path to the Astro config file.'],
|
||||
['--project-root <path>', 'Specify the path to the project root folder.'],
|
||||
['--no-sitemap', 'Disable sitemap generation (build only).'],
|
||||
|
|
|
@ -65,6 +65,7 @@ export const AstroConfigSchema = z.object({
|
|||
.default({}),
|
||||
devOptions: z
|
||||
.object({
|
||||
host: z.union([z.string(), z.boolean()]).optional().default(false),
|
||||
hostname: z.string().optional().default('localhost'),
|
||||
port: z.number().optional().default(3000),
|
||||
trailingSlash: z
|
||||
|
@ -125,6 +126,7 @@ function resolveFlags(flags: Partial<Flags>): CLIFlags {
|
|||
port: typeof flags.port === 'number' ? flags.port : undefined,
|
||||
config: typeof flags.config === 'string' ? flags.config : undefined,
|
||||
hostname: typeof flags.hostname === 'string' ? flags.hostname : undefined,
|
||||
host: typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
|
||||
legacyBuild: typeof flags.legacyBuild === 'boolean' ? flags.legacyBuild : false,
|
||||
experimentalSsr: typeof flags.experimentalSsr === 'boolean' ? flags.experimentalSsr : false,
|
||||
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : false,
|
||||
|
@ -138,6 +140,7 @@ function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) {
|
|||
if (typeof flags.sitemap === 'boolean') astroConfig.buildOptions.sitemap = flags.sitemap;
|
||||
if (typeof flags.site === 'string') astroConfig.buildOptions.site = flags.site;
|
||||
if (typeof flags.port === 'number') astroConfig.devOptions.port = flags.port;
|
||||
if (typeof flags.host === 'string' || typeof flags.host === 'boolean') astroConfig.devOptions.host = flags.host;
|
||||
if (typeof flags.hostname === 'string') astroConfig.devOptions.hostname = flags.hostname;
|
||||
if (typeof flags.legacyBuild === 'boolean') astroConfig.buildOptions.legacyBuild = flags.legacyBuild;
|
||||
if (typeof flags.experimentalSsr === 'boolean') {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { createVite } from '../create-vite.js';
|
|||
import { defaultLogOptions, info, warn, LogOptions } from '../logger.js';
|
||||
import * as vite from 'vite';
|
||||
import * as msg from '../messages.js';
|
||||
import { getLocalAddress } from './util.js';
|
||||
import { getResolvedHostForVite } from './util.js';
|
||||
|
||||
export interface DevOptions {
|
||||
logging: LogOptions;
|
||||
|
@ -24,13 +24,13 @@ export default async function dev(config: AstroConfig, options: DevOptions = { l
|
|||
polyfill(globalThis, {
|
||||
exclude: 'window document',
|
||||
});
|
||||
// start the server
|
||||
|
||||
// TODO: remove call once --hostname is baselined
|
||||
const host = getResolvedHostForVite(config);
|
||||
const viteUserConfig = vite.mergeConfig(
|
||||
{
|
||||
mode: 'development',
|
||||
server: {
|
||||
host: config.devOptions.hostname,
|
||||
},
|
||||
server: { host },
|
||||
},
|
||||
config.vite || {}
|
||||
);
|
||||
|
@ -38,15 +38,9 @@ export default async function dev(config: AstroConfig, options: DevOptions = { l
|
|||
const viteServer = await vite.createServer(viteConfig);
|
||||
await viteServer.listen(config.devOptions.port);
|
||||
|
||||
const address = viteServer.httpServer!.address() as AddressInfo;
|
||||
const localAddress = getLocalAddress(address.address, config.devOptions.hostname);
|
||||
// Log to console
|
||||
const devServerAddressInfo = viteServer.httpServer!.address() as AddressInfo;
|
||||
const site = config.buildOptions.site ? new URL(config.buildOptions.site) : undefined;
|
||||
info(
|
||||
options.logging,
|
||||
null,
|
||||
msg.devStart({ startupTime: performance.now() - devStart, port: address.port, localAddress, networkAddress: address.address, site, https: !!viteUserConfig.server?.https })
|
||||
);
|
||||
info(options.logging, null, msg.devStart({ startupTime: performance.now() - devStart, config, devServerAddressInfo, site, https: !!viteUserConfig.server?.https }));
|
||||
|
||||
const currentVersion = process.env.PACKAGE_VERSION ?? '0.0.0';
|
||||
if (currentVersion.includes('-')) {
|
||||
|
@ -54,7 +48,7 @@ export default async function dev(config: AstroConfig, options: DevOptions = { l
|
|||
}
|
||||
|
||||
return {
|
||||
address,
|
||||
address: devServerAddressInfo,
|
||||
stop: () => viteServer.close(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import type { AstroConfig } from '../../@types/astro';
|
||||
|
||||
export const localIps = new Set(['localhost', '127.0.0.1']);
|
||||
|
||||
/** Pad string () */
|
||||
export function pad(input: string, minLength: number, dir?: 'left' | 'right'): string {
|
||||
let output = input;
|
||||
|
@ -11,10 +15,36 @@ export function emoji(char: string, fallback: string) {
|
|||
return process.platform !== 'win32' ? char : fallback;
|
||||
}
|
||||
|
||||
export function getLocalAddress(serverAddress: string, configHostname: string): string {
|
||||
if (configHostname === 'localhost' || serverAddress === '127.0.0.1' || serverAddress === '0.0.0.0') {
|
||||
// TODO: remove once --hostname is baselined
|
||||
export function getResolvedHostForVite(config: AstroConfig) {
|
||||
if (config.devOptions.host === false && config.devOptions.hostname !== 'localhost') {
|
||||
return config.devOptions.hostname;
|
||||
} else {
|
||||
return config.devOptions.host;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalAddress(serverAddress: string, config: AstroConfig): string {
|
||||
// TODO: remove once --hostname is baselined
|
||||
const host = getResolvedHostForVite(config);
|
||||
if (typeof host === 'boolean' || host === 'localhost') {
|
||||
return 'localhost';
|
||||
} else {
|
||||
return serverAddress;
|
||||
}
|
||||
}
|
||||
|
||||
export type NetworkLogging = 'none' | 'host-to-expose' | 'visible';
|
||||
|
||||
export function getNetworkLogging(config: AstroConfig): NetworkLogging {
|
||||
// TODO: remove once --hostname is baselined
|
||||
const host = getResolvedHostForVite(config);
|
||||
|
||||
if (host === false) {
|
||||
return 'host-to-expose';
|
||||
} else if (typeof host === 'string' && localIps.has(host)) {
|
||||
return 'none';
|
||||
} else {
|
||||
return 'visible';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
* Dev server messages (organized here to prevent clutter)
|
||||
*/
|
||||
|
||||
import type { AddressInfo } from 'net';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { bold, dim, red, green, underline, yellow, bgYellow, cyan, bgGreen, black } from 'kleur/colors';
|
||||
import { pad, emoji } from './dev/util.js';
|
||||
import { pad, emoji, getLocalAddress, getNetworkLogging } from './dev/util.js';
|
||||
import os from 'os';
|
||||
import type { AddressInfo } from 'net';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
|
||||
const PREFIX_PADDING = 6;
|
||||
|
||||
|
@ -30,31 +32,50 @@ export function hmr({ file }: { file: string }): string {
|
|||
/** Display dev server host and startup time */
|
||||
export function devStart({
|
||||
startupTime,
|
||||
port,
|
||||
localAddress,
|
||||
networkAddress,
|
||||
devServerAddressInfo,
|
||||
config,
|
||||
https,
|
||||
site,
|
||||
}: {
|
||||
startupTime: number;
|
||||
port: number;
|
||||
localAddress: string;
|
||||
networkAddress: string;
|
||||
devServerAddressInfo: AddressInfo;
|
||||
config: AstroConfig;
|
||||
https: boolean;
|
||||
site: URL | undefined;
|
||||
}): string {
|
||||
// PACAKGE_VERSION is injected at build-time
|
||||
// PACKAGE_VERSION is injected at build-time
|
||||
const version = process.env.PACKAGE_VERSION ?? '0.0.0';
|
||||
const rootPath = site ? site.pathname : '/';
|
||||
const toDisplayUrl = (hostname: string) => `${https ? 'https' : 'http'}://${hostname}:${port}${rootPath}`;
|
||||
const localPrefix = `${dim('┃')} Local `;
|
||||
const networkPrefix = `${dim('┃')} Network `;
|
||||
|
||||
const messages = [
|
||||
`${emoji('🚀 ', '')}${bgGreen(black(` astro `))} ${green(`v${version}`)} ${dim(`started in ${Math.round(startupTime)}ms`)}`,
|
||||
'',
|
||||
`${dim('┃')} Local ${bold(cyan(toDisplayUrl(localAddress)))}`,
|
||||
`${dim('┃')} Network ${bold(cyan(toDisplayUrl(networkAddress)))}`,
|
||||
'',
|
||||
];
|
||||
const { address: networkAddress, port } = devServerAddressInfo;
|
||||
const localAddress = getLocalAddress(networkAddress, config);
|
||||
const networkLogging = getNetworkLogging(config);
|
||||
const toDisplayUrl = (hostname: string) => `${https ? 'https' : 'http'}://${hostname}:${port}${rootPath}`;
|
||||
let addresses = [];
|
||||
|
||||
if (networkLogging === 'none') {
|
||||
addresses = [`${localPrefix}${bold(cyan(toDisplayUrl(localAddress)))}`];
|
||||
} else if (networkLogging === 'host-to-expose') {
|
||||
addresses = [`${localPrefix}${bold(cyan(toDisplayUrl(localAddress)))}`, `${networkPrefix}${dim('use --host to expose')}`];
|
||||
} else {
|
||||
addresses = Object.values(os.networkInterfaces())
|
||||
.flatMap((networkInterface) => networkInterface ?? [])
|
||||
.filter((networkInterface) => networkInterface?.address && networkInterface?.family === 'IPv4')
|
||||
.map(({ address }) => {
|
||||
if (address.includes('127.0.0.1')) {
|
||||
const displayAddress = address.replace('127.0.0.1', localAddress);
|
||||
return `${localPrefix}${bold(cyan(toDisplayUrl(displayAddress)))}`;
|
||||
} else {
|
||||
return `${networkPrefix}${bold(cyan(toDisplayUrl(address)))}`;
|
||||
}
|
||||
})
|
||||
// ensure Local logs come before Network
|
||||
.sort((msg) => (msg.startsWith(localPrefix) ? -1 : 1));
|
||||
}
|
||||
|
||||
const messages = [`${emoji('🚀 ', '')}${bgGreen(black(` astro `))} ${green(`v${version}`)} ${dim(`started in ${Math.round(startupTime)}ms`)}`, '', ...addresses, ''];
|
||||
return messages.map((msg) => ` ${msg}`).join('\n');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { AstroConfig } from '../../@types/astro';
|
||||
import type { LogOptions } from '../logger';
|
||||
import type { Stats } from 'fs';
|
||||
import type { AddressInfo } from 'net';
|
||||
import http from 'http';
|
||||
import sirv from 'sirv';
|
||||
|
@ -8,16 +7,15 @@ import { performance } from 'perf_hooks';
|
|||
import { fileURLToPath } from 'url';
|
||||
import * as msg from '../messages.js';
|
||||
import { error, info } from '../logger.js';
|
||||
import { appendForwardSlash, trimSlashes } from '../path.js';
|
||||
import { getLocalAddress } from '../dev/util.js';
|
||||
import { subpathNotUsedTemplate, notFoundTemplate } from '../../template/4xx.js';
|
||||
import { getResolvedHostForHttpServer } from './util.js';
|
||||
|
||||
interface PreviewOptions {
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
export interface PreviewServer {
|
||||
hostname: string;
|
||||
host?: string;
|
||||
port: number;
|
||||
server: http.Server;
|
||||
stop(): Promise<void>;
|
||||
|
@ -75,7 +73,8 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
|
|||
}
|
||||
});
|
||||
|
||||
let { hostname, port } = config.devOptions;
|
||||
let { port } = config.devOptions;
|
||||
const host = getResolvedHostForHttpServer(config);
|
||||
|
||||
let httpServer: http.Server;
|
||||
|
||||
|
@ -85,12 +84,10 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
|
|||
let showedListenMsg = false;
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const listen = () => {
|
||||
httpServer = server.listen(port, hostname, async () => {
|
||||
httpServer = server.listen(port, host, async () => {
|
||||
if (!showedListenMsg) {
|
||||
const { address: networkAddress } = server.address() as AddressInfo;
|
||||
const localAddress = getLocalAddress(networkAddress, hostname);
|
||||
|
||||
info(logging, null, msg.devStart({ startupTime: performance.now() - timerStart, port, localAddress, networkAddress, https: false, site: baseURL }));
|
||||
const devServerAddressInfo = server.address() as AddressInfo;
|
||||
info(logging, null, msg.devStart({ startupTime: performance.now() - timerStart, config, devServerAddressInfo, https: false, site: baseURL }));
|
||||
}
|
||||
showedListenMsg = true;
|
||||
resolve();
|
||||
|
@ -121,7 +118,7 @@ export default async function preview(config: AstroConfig, { logging }: PreviewO
|
|||
await startServer(startServerTime);
|
||||
|
||||
return {
|
||||
hostname,
|
||||
host,
|
||||
port,
|
||||
server: httpServer!,
|
||||
stop: async () => {
|
||||
|
|
17
packages/astro/src/core/preview/util.ts
Normal file
17
packages/astro/src/core/preview/util.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { AstroConfig } from '../../@types/astro';
|
||||
|
||||
export function getResolvedHostForHttpServer(config: AstroConfig) {
|
||||
const { host, hostname } = config.devOptions;
|
||||
|
||||
if (host === false && hostname === 'localhost') {
|
||||
// Use a secure default
|
||||
return '127.0.0.1';
|
||||
} else if (host === true) {
|
||||
// If passed --host in the CLI without arguments
|
||||
return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)
|
||||
} else if (typeof host === 'string') {
|
||||
return host;
|
||||
} else {
|
||||
return hostname;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,15 @@
|
|||
import { expect } from 'chai';
|
||||
import { cli, parseCliDevStart } from './test-utils.js';
|
||||
import { cli, parseCliDevStart, cliServerLogSetup } from './test-utils.js';
|
||||
import { promises as fs } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { isIPv4 } from 'net';
|
||||
|
||||
describe('astro cli', () => {
|
||||
const cliServerLogSetupWithFixture = (flags, cmd) => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
return cliServerLogSetup(['--project-root', fileURLToPath(projectRootURL), ...flags], cmd);
|
||||
};
|
||||
|
||||
it('astro', async () => {
|
||||
const proc = await cli();
|
||||
|
||||
|
@ -32,28 +38,52 @@ describe('astro cli', () => {
|
|||
expect(messages[0]).to.contain('started in');
|
||||
});
|
||||
|
||||
const hostnames = [undefined, '0.0.0.0', '127.0.0.1'];
|
||||
['dev', 'preview'].forEach((cmd) => {
|
||||
const networkLogFlags = [['--host'], ['--host', '0.0.0.0'], ['--hostname', '0.0.0.0']];
|
||||
networkLogFlags.forEach(([flag, flagValue]) => {
|
||||
it(`astro ${cmd} ${flag} ${flagValue ?? ''} - network log`, async () => {
|
||||
const { local, network } = await cliServerLogSetupWithFixture(flagValue ? [flag, flagValue] : [flag], cmd);
|
||||
|
||||
hostnames.forEach((hostname) => {
|
||||
const hostnameArgs = hostname ? ['--hostname', hostname] : [];
|
||||
it(`astro dev ${hostnameArgs.join(' ') || '(no --hostname)'}`, async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), ...hostnameArgs);
|
||||
expect(local).to.not.be.undefined;
|
||||
expect(network).to.not.be.undefined;
|
||||
|
||||
const { messages } = await parseCliDevStart(proc);
|
||||
const localURL = new URL(local);
|
||||
const networkURL = new URL(network);
|
||||
|
||||
const local = messages[1].replace(/Local\s*/g, '');
|
||||
const network = messages[2].replace(/Network\s*/g, '');
|
||||
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+!
|
||||
expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual(3000, `Expected Port to be >= 3000`);
|
||||
expect(networkURL.port).to.be.equal(localURL.port, `Expected local and network ports to be equal`);
|
||||
expect(isIPv4(networkURL.hostname)).to.be.equal(true, `Expected network URL to respect --host flag`);
|
||||
});
|
||||
});
|
||||
|
||||
expect(local).to.not.be.undefined;
|
||||
expect(network).to.not.be.undefined;
|
||||
const localURL = new URL(local);
|
||||
const networkURL = new URL(network);
|
||||
|
||||
expect(localURL.hostname).to.be.equal('localhost', `Expected local URL to be on localhost`);
|
||||
// Note: our tests run in parallel so this could be 3000+!
|
||||
expect(Number.parseInt(localURL.port)).to.be.greaterThanOrEqual(3000, `Expected Port to be >= 3000`);
|
||||
expect(networkURL.hostname).to.be.equal(hostname ?? '127.0.0.1', `Expected Network URL to use passed hostname`);
|
||||
const hostToExposeFlags = [['', ''], ['--hostname', 'localhost']];
|
||||
hostToExposeFlags.forEach(([flag, flagValue]) => {
|
||||
it(`astro ${cmd} ${flag} ${flagValue} - host to expose`, async () => {
|
||||
const { local, network } = await cliServerLogSetupWithFixture([flag, flagValue], cmd);
|
||||
|
||||
expect(local).to.not.be.undefined;
|
||||
expect(network).to.not.be.undefined;
|
||||
const localURL = new URL(local);
|
||||
|
||||
expect(localURL.hostname).to.be.equal('localhost', `Expected local URL to be on localhost`);
|
||||
expect(() => new URL(networkURL)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
const noNetworkLogFlags = [['--host', 'localhost'], ['--host', '127.0.0.1'], ['--hostname', '127.0.0.1']];
|
||||
noNetworkLogFlags.forEach(([flag, flagValue]) => {
|
||||
it(`astro ${cmd} ${flag} ${flagValue} - no network log`, async () => {
|
||||
const { local, network } = await cliServerLogSetupWithFixture([flag, flagValue], cmd);
|
||||
|
||||
expect(local).to.not.be.undefined;
|
||||
expect(network).to.be.undefined;
|
||||
|
||||
const localURL = new URL(local);
|
||||
expect(localURL.hostname).to.be.equal(flagValue, `Expected local URL to be on localhost`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
import { expect } from 'chai';
|
||||
import { cli, loadFixture } from './test-utils.js';
|
||||
import { cli, loadFixture, cliServerLogSetup } from './test-utils.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { isIPv4 } from 'net';
|
||||
|
||||
describe('config', () => {
|
||||
let hostnameFixture;
|
||||
let hostFixture;
|
||||
let portFixture;
|
||||
|
||||
before(async () => {
|
||||
[hostnameFixture, portFixture] = await Promise.all([loadFixture({ projectRoot: './fixtures/config-hostname/' }), loadFixture({ projectRoot: './fixtures/config-port/' })]);
|
||||
[hostnameFixture, hostFixture, portFixture] = await Promise.all([
|
||||
loadFixture({ projectRoot: './fixtures/config-hostname/' }),
|
||||
loadFixture({ projectRoot: './fixtures/config-host/' }),
|
||||
loadFixture({ projectRoot: './fixtures/config-port/' }),
|
||||
]);
|
||||
});
|
||||
|
||||
// TODO: remove test once --hostname is baselined
|
||||
describe('hostname', () => {
|
||||
it('can be specified in astro.config.mjs', async () => {
|
||||
expect(hostnameFixture.config.devOptions.hostname).to.equal('0.0.0.0');
|
||||
|
@ -17,19 +24,24 @@ describe('config', () => {
|
|||
|
||||
it('can be specified via --hostname flag', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--hostname', '127.0.0.1');
|
||||
const { network } = await cliServerLogSetup(['--project-root', fileURLToPath(projectRootURL), '--hostname', '0.0.0.0']);
|
||||
|
||||
let stdout = '';
|
||||
const networkURL = new URL(network);
|
||||
expect(isIPv4(networkURL.hostname)).to.be.equal(true, `Expected network URL to respect --hostname flag`);
|
||||
});
|
||||
});
|
||||
|
||||
for await (const chunk of proc.stdout) {
|
||||
stdout += chunk;
|
||||
describe('host', () => {
|
||||
it('can be specified in astro.config.mjs', async () => {
|
||||
expect(hostFixture.config.devOptions.host).to.equal(true);
|
||||
});
|
||||
|
||||
if (chunk.includes('Local')) break;
|
||||
}
|
||||
it('can be specified via --host flag', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const { network } = await cliServerLogSetup(['--project-root', fileURLToPath(projectRootURL), '--host']);
|
||||
|
||||
proc.kill();
|
||||
|
||||
expect(stdout).to.include('127.0.0.1');
|
||||
const networkURL = new URL(network);
|
||||
expect(isIPv4(networkURL.hostname)).to.be.equal(true, `Expected network URL to respect --hostname flag`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -37,19 +49,10 @@ describe('config', () => {
|
|||
it('can be passed via --config', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = new URL('./fixtures/config-path/config/my-config.mjs', import.meta.url);
|
||||
const proc = cli('dev', '--project-root', fileURLToPath(projectRootURL), '--config', configFileURL.pathname);
|
||||
const { network } = await cliServerLogSetup(['--project-root', fileURLToPath(projectRootURL), '--config', configFileURL.pathname]);
|
||||
|
||||
let stdout = '';
|
||||
|
||||
for await (const chunk of proc.stdout) {
|
||||
stdout += chunk;
|
||||
|
||||
if (chunk.includes('Local')) break;
|
||||
}
|
||||
|
||||
proc.kill();
|
||||
|
||||
expect(stdout).to.include('127.0.0.1');
|
||||
const networkURL = new URL(network);
|
||||
expect(isIPv4(networkURL.hostname)).to.be.equal(true, `Expected network URL to respect --hostname flag`);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
6
packages/astro/test/fixtures/config-host/astro.config.mjs
vendored
Normal file
6
packages/astro/test/fixtures/config-host/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
export default {
|
||||
devOptions: {
|
||||
host: true
|
||||
}
|
||||
}
|
8
packages/astro/test/fixtures/config-host/package.json
vendored
Normal file
8
packages/astro/test/fixtures/config-host/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "@astrojs/test-config-host",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
export default {
|
||||
devOptions: {
|
||||
hostname: '127.0.0.1',
|
||||
host: true,
|
||||
port: 8080,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -143,4 +143,18 @@ export async function parseCliDevStart(proc) {
|
|||
return { messages };
|
||||
}
|
||||
|
||||
export async function cliServerLogSetup(flags = [], cmd = 'dev') {
|
||||
const proc = cli(cmd, ...flags);
|
||||
|
||||
const { messages } = await parseCliDevStart(proc);
|
||||
|
||||
const localRaw = (messages[1] ?? '').includes('Local') ? messages[1] : undefined;
|
||||
const networkRaw = (messages[2] ?? '').includes('Network') ? messages[2] : undefined;
|
||||
|
||||
const local = localRaw?.replace(/Local\s*/g, '');
|
||||
const network = networkRaw?.replace(/Network\s*/g, '');
|
||||
|
||||
return { local, network };
|
||||
}
|
||||
|
||||
export const isWindows = os.platform() === 'win32';
|
||||
|
|
|
@ -754,6 +754,12 @@ importers:
|
|||
dependencies:
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/config-host:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/config-hostname:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
|
|
Loading…
Reference in a new issue