Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/astro/src/cli/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { StdioOptions } from 'node:child_process';
import type { AnyCommand } from './domain/command.js';
import type { HelpPayload } from './domain/help-payload.js';

Expand All @@ -22,3 +23,22 @@ export interface AstroVersionProvider {
export interface CommandRunner {
run: <T extends AnyCommand>(command: T, ...args: Parameters<T['run']>) => ReturnType<T['run']>;
}

export interface CommandExecutor {
execute: (
command: string,
args?: Array<string>,
options?: {
cwd?: string;
env?: Record<string, string | undefined>;
shell?: boolean;
input?: string;
stdio?: StdioOptions;
},
) => Promise<{ stdout: string }>;
}

export interface OperatingSystemProvider {
getName: () => NodeJS.Platform;
getDisplayName: () => string;
}
22 changes: 7 additions & 15 deletions packages/astro/src/cli/docs/core/open-docs.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import type { Logger } from '../../../core/logger/core.js';
import type { CommandExecutor, OperatingSystemProvider } from '../../definitions.js';
import { defineCommand } from '../../domain/command.js';
import type { CommandExecutor, PlatformProvider } from '../definitions.js';
import type { Platform } from '../domains/platform.js';
import type { CloudIdeProvider } from '../definitions.js';

interface Options {
url: string;
platformProvider: PlatformProvider;
operatingSystemProvider: OperatingSystemProvider;
logger: Logger;
commandExecutor: CommandExecutor;
cloudIdeProvider: CloudIdeProvider;
}

function getExecInputForPlatform(
platform: Platform,
): [command: string, args?: Array<string>] | null {
function getExecInputForPlatform(platform: string): [command: string, args?: Array<string>] | null {
switch (platform) {
case 'android':
case 'linux':
Expand All @@ -23,13 +22,6 @@ function getExecInputForPlatform(
return ['cmd', ['/c', 'start']];
case 'gitpod':
return ['/ide/bin/remote-cli/gitpod-code', ['--openExternal']];
case 'aix':
case 'freebsd':
case 'haiku':
case 'openbsd':
case 'sunos':
case 'cygwin':
case 'netbsd':
default:
return null;
}
Expand All @@ -43,8 +35,8 @@ export const openDocsCommand = defineCommand({
},
description: `Launches the Astro Docs website directly from the terminal.`,
},
async run({ url, platformProvider, logger, commandExecutor }: Options) {
const platform = platformProvider.get();
async run({ url, operatingSystemProvider, logger, commandExecutor, cloudIdeProvider }: Options) {
const platform = cloudIdeProvider.getName() ?? operatingSystemProvider.getName();
const input = getExecInputForPlatform(platform);
if (!input) {
logger.error(
Expand Down
17 changes: 3 additions & 14 deletions packages/astro/src/cli/docs/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import type { Platform } from './domains/platform.js';
import type { CloudIde } from './domain/cloud-ide.js';

export interface PlatformProvider {
get: () => Platform;
}

export interface CommandExecutor {
execute: (
command: string,
args?: Array<string>,
options?: {
cwd?: string;
env: Record<string, string | undefined>;
},
) => Promise<{ stdout: string }>;
export interface CloudIdeProvider {
getName: () => CloudIde | null;
}
1 change: 1 addition & 0 deletions packages/astro/src/cli/docs/domain/cloud-ide.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type CloudIde = 'gitpod';
1 change: 0 additions & 1 deletion packages/astro/src/cli/docs/domains/platform.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { CloudIdeProvider } from '../definitions.js';

export function createProcessCloudIdeProvider(): CloudIdeProvider {
return {
getName() {
return Boolean(process.env.GITPOD_REPO_ROOT) ? 'gitpod' : null;
},
};
}
10 changes: 0 additions & 10 deletions packages/astro/src/cli/docs/infra/process-platform-provider.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/astro/src/cli/flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { AstroInlineConfig } from '../types/public/config.js';
// Alias for now, but allows easier migration to node's `parseArgs` in the future.
export type Flags = Arguments;

/** @deprecated Use AstroConfigResolver instead */
export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig {
return {
// Inline-only configs
Expand Down
70 changes: 62 additions & 8 deletions packages/astro/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function resolveCommand(flags: yargs.Arguments): CLICommand {
* to present user-friendly error output where the fn is called.
**/
async function runCommand(cmd: string, flags: yargs.Arguments) {
await import('../core/polyfill.js').then((m) => m.apply());
const [
{ createLoggerFromFlags },
{ createPicocolorsTextStyler },
Expand Down Expand Up @@ -93,9 +94,58 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
return;
}
case 'info': {
const { printInfo } = await import('./info/index.js');
await printInfo({ flags });
return;
const [
{ createProcessOperatingSystemProvider },
{ createCliAstroConfigResolver },
{ createCliDebugInfoProvider },
{ createTinyexecCommandExecutor },
{ getPackageManager },
{ createStyledDebugInfoFormatter },
{ createPromptsPrompt },
{ createCliClipboard },
{ createPassthroughTextStyler },
{ infoCommand },
] = await Promise.all([
import('./infra/process-operating-system-provider.js'),
import('./info/infra/cli-astro-config-resolver.js'),
import('./info/infra/cli-debug-info-provider.js'),
import('./infra/tinyexec-command-executor.js'),
import('./info/core/get-package-manager.js'),
import('./info/infra/styled-debug-info-formatter.js'),
import('./info/infra/prompts-prompt.js'),
import('./info/infra/cli-clipboard.js'),
import('./infra/passthrough-text-styler.js'),
import('./info/core/info.js'),
]);
const operatingSystemProvider = createProcessOperatingSystemProvider();
const astroConfigResolver = createCliAstroConfigResolver({ flags });
const commandExecutor = createTinyexecCommandExecutor();
const debugInfoProvider = createCliDebugInfoProvider({
config: await astroConfigResolver.resolve(),
astroVersionProvider,
operatingSystemProvider,
packageManager: await getPackageManager({
configUserAgent: process.env.npm_config_user_agent,
commandExecutor,
}),
});
const prompt = createPromptsPrompt({ force: flags.copy });
const clipboard = createCliClipboard({
commandExecutor,
logger,
operatingSystemProvider,
prompt,
});

return await runner.run(infoCommand, {
logger,
debugInfoProvider,
getDebugInfoFormatter: ({ pretty }) =>
createStyledDebugInfoFormatter({
textStyler: pretty ? textStyler : createPassthroughTextStyler(),
}),
clipboard,
});
}
case 'create-key': {
const [{ createCryptoKeyGenerator }, { createKeyCommand }] = await Promise.all([
Expand All @@ -109,21 +159,25 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
case 'docs': {
const [
{ createTinyexecCommandExecutor },
{ createProcessPlatformProvider },
{ createProcessOperatingSystemProvider },
{ createProcessCloudIdeProvider },
{ openDocsCommand },
] = await Promise.all([
import('./docs/infra/tinyexec-command-executor.js'),
import('./docs/infra/process-platform-provider.js'),
import('./infra/tinyexec-command-executor.js'),
import('./infra/process-operating-system-provider.js'),
import('./docs/infra/process-cloud-ide-provider.js'),
import('./docs/core/open-docs.js'),
]);
const commandExecutor = createTinyexecCommandExecutor();
const platformProvider = createProcessPlatformProvider();
const operatingSystemProvider = createProcessOperatingSystemProvider();
const cloudIdeProvider = createProcessCloudIdeProvider();

return await runner.run(openDocsCommand, {
url: 'https://docs.astro.build/',
logger,
commandExecutor,
platformProvider,
operatingSystemProvider,
cloudIdeProvider,
});
}
case 'telemetry': {
Expand Down
43 changes: 43 additions & 0 deletions packages/astro/src/cli/info/core/get-package-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { CommandExecutor } from '../../definitions.js';
import type { PackageManager } from '../definitions.js';

interface Options {
configUserAgent: string | undefined;
commandExecutor: CommandExecutor;
}

export async function getPackageManager({
configUserAgent,
commandExecutor,
}: Options): Promise<PackageManager> {
if (!configUserAgent) {
const { createNoopPackageManager } = await import('../infra/noop-package-manager.js');
return createNoopPackageManager();
}
const specifier = configUserAgent.split(' ')[0];
const _name = specifier.substring(0, specifier.lastIndexOf('/'));
const name = _name === 'npminstall' ? 'cnpm' : _name;

switch (name) {
case 'pnpm': {
const { createPnpmPackageManager } = await import('../infra/pnpm-package-manager.js');
return createPnpmPackageManager({ commandExecutor });
}
case 'npm': {
const { createNpmPackageManager } = await import('../infra/npm-package-manager.js');
return createNpmPackageManager({ commandExecutor });
}
case 'yarn': {
const { createYarnPackageManager } = await import('../infra/yarn-package-manager.js');
return createYarnPackageManager({ commandExecutor });
}
case 'bun': {
const { createBunPackageManager } = await import('../infra/bun-package-manager.js');
return createBunPackageManager();
}
default: {
const { createNoopPackageManager } = await import('../infra/noop-package-manager.js');
return createNoopPackageManager();
}
}
}
29 changes: 29 additions & 0 deletions packages/astro/src/cli/info/core/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Logger } from '../../../core/logger/core.js';
import { defineCommand } from '../../domain/command.js';
import type { Clipboard, DebugInfoFormatter, DebugInfoProvider } from '../definitions.js';

interface Options {
debugInfoProvider: DebugInfoProvider;
getDebugInfoFormatter: (options: { pretty: boolean }) => DebugInfoFormatter;
logger: Logger;
clipboard: Clipboard;
}

export const infoCommand = defineCommand({
help: {
commandName: 'astro info',
tables: {
Flags: [
['--help (-h)', 'See all available flags.'],
['--copy', 'Force copy of the output.'],
],
},
description:
'Reports useful information about your current Astro environment. Useful for providing information when opening an issue.',
},
async run({ debugInfoProvider, getDebugInfoFormatter, logger, clipboard }: Options) {
const debugInfo = await debugInfoProvider.get();
logger.info('SKIP_FORMAT', getDebugInfoFormatter({ pretty: true }).format(debugInfo));
await clipboard.copy(getDebugInfoFormatter({ pretty: false }).format(debugInfo));
},
});
27 changes: 27 additions & 0 deletions packages/astro/src/cli/info/definitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { AstroConfig } from '../../types/public/index.js';
import type { DebugInfo } from './domain/debug-info.js';

export interface DebugInfoProvider {
get: () => Promise<DebugInfo>;
}

export interface DebugInfoFormatter {
format: (info: DebugInfo) => string;
}

export interface Clipboard {
copy: (text: string) => Promise<void>;
}

export interface PackageManager {
getName: () => string;
getPackageVersion: (name: string) => Promise<string | undefined>;
}

export interface AstroConfigResolver {
resolve: () => Promise<AstroConfig>;
}

export interface Prompt {
confirm: (input: { message: string; defaultValue?: boolean }) => Promise<boolean>;
}
1 change: 1 addition & 0 deletions packages/astro/src/cli/info/domain/debug-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type DebugInfo = Array<[string, string | Array<string>]>;
Loading