@poetora/cli 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/bin/cli-builder.js +1 -1
  2. package/bin/services/version.service.d.ts +1 -1
  3. package/bin/services/version.service.js +12 -10
  4. package/package.json +8 -3
  5. package/.turbo/turbo-build.log +0 -4
  6. package/src/accessibility.ts +0 -180
  7. package/src/cli-builder.ts +0 -274
  8. package/src/cli.ts +0 -22
  9. package/src/commands/__tests__/base.command.test.ts +0 -139
  10. package/src/commands/__tests__/dev.command.test.ts +0 -241
  11. package/src/commands/__tests__/init.command.test.ts +0 -281
  12. package/src/commands/__tests__/utils.ts +0 -20
  13. package/src/commands/base.command.ts +0 -97
  14. package/src/commands/check.command.ts +0 -40
  15. package/src/commands/dev.command.ts +0 -63
  16. package/src/commands/index.ts +0 -6
  17. package/src/commands/init.command.ts +0 -125
  18. package/src/commands/link.command.ts +0 -39
  19. package/src/commands/update.command.ts +0 -23
  20. package/src/constants.ts +0 -4
  21. package/src/errors/cli-error.ts +0 -83
  22. package/src/errors/index.ts +0 -1
  23. package/src/index.ts +0 -110
  24. package/src/mdxAccessibility.ts +0 -132
  25. package/src/middlewares.ts +0 -73
  26. package/src/services/__tests__/port.service.test.ts +0 -83
  27. package/src/services/__tests__/template.service.test.ts +0 -234
  28. package/src/services/__tests__/version.service.test.ts +0 -165
  29. package/src/services/accessibility-check.service.ts +0 -226
  30. package/src/services/index.ts +0 -7
  31. package/src/services/link.service.ts +0 -65
  32. package/src/services/openapi-check.service.ts +0 -68
  33. package/src/services/port.service.ts +0 -47
  34. package/src/services/template.service.ts +0 -203
  35. package/src/services/update.service.ts +0 -76
  36. package/src/services/version.service.ts +0 -161
  37. package/src/start.ts +0 -6
  38. package/src/types/common.ts +0 -53
  39. package/src/types/index.ts +0 -2
  40. package/src/types/options.ts +0 -42
  41. package/src/utils/console-logger.ts +0 -123
  42. package/src/utils/index.ts +0 -2
  43. package/src/utils/logger.interface.ts +0 -70
  44. package/tsconfig.build.json +0 -17
  45. package/tsconfig.json +0 -21
  46. package/vitest.config.ts +0 -8
@@ -1,97 +0,0 @@
1
- import { CliError } from '../errors/index.js';
2
- import type { ILogger } from '../utils/index.js';
3
-
4
- /**
5
- * Base abstract class for all CLI commands
6
- * Implements Template Method pattern for consistent command execution flow
7
- */
8
- export abstract class BaseCommand<TOptions = unknown, TResult = void> {
9
- /**
10
- * Command name (e.g., 'dev', 'init', 'check')
11
- */
12
- abstract readonly name: string;
13
-
14
- /**
15
- * Command description for help text
16
- */
17
- abstract readonly description: string;
18
-
19
- constructor(
20
- protected readonly logger: ILogger,
21
- protected readonly packageName: string = 'poet'
22
- ) {}
23
-
24
- /**
25
- * Template method that orchestrates command execution
26
- * This is the main entry point called by the CLI framework
27
- */
28
- /* prettier-ignore */
29
- async run(options: TOptions): Promise<TResult> {
30
- try {
31
- // 1. Validate input options
32
- await this.validate(options);
33
-
34
- // 2. Execute command logic
35
- const result = await this.execute(options);
36
-
37
- // 3. Return result
38
- return result;
39
- } catch (error) {
40
- // 4. Handle errors consistently
41
- this.handleError(error);
42
- throw error;
43
- }
44
- }
45
-
46
- /**
47
- * Validate command options before execution
48
- * Override this method to add custom validation logic
49
- * @param options - Command options to validate
50
- * @throws {ValidationError} if validation fails
51
- */
52
- // prettier-ignore
53
- protected async validate(_options: TOptions): Promise<void> {
54
- // Default: no validation
55
- // Subclasses can override to add validation
56
- }
57
-
58
- /**
59
- * Execute the main command logic
60
- * This method must be implemented by all command subclasses
61
- * @param options - Validated command options
62
- * @returns Command execution result
63
- */
64
- protected abstract execute(options: TOptions): Promise<TResult>;
65
-
66
- /**
67
- * Handle errors that occur during command execution
68
- * Can be overridden to provide custom error handling
69
- * @param error - The error to handle
70
- */
71
- protected handleError(error: unknown): void {
72
- if (error instanceof CliError) {
73
- // CLI-specific errors with user-friendly messages
74
- this.logger.error(error.message);
75
- } else if (error instanceof Error) {
76
- // Generic errors
77
- this.logger.error(error.message);
78
-
79
- // In debug mode, show stack trace
80
- if (process.env.DEBUG === 'true') {
81
- this.logger.logColor(error.stack ?? '', 'gray');
82
- }
83
- } else {
84
- // Unknown error types
85
- this.logger.error('An unexpected error occurred');
86
- this.logger.logColor(String(error), 'gray');
87
- }
88
- }
89
-
90
- /**
91
- * Exit the process with given code
92
- * Separated for easier testing (can be mocked)
93
- */
94
- protected exit(code: number): never {
95
- process.exit(code);
96
- }
97
- }
@@ -1,40 +0,0 @@
1
- import type { AccessibilityCheckService, OpenApiCheckService } from '../services/index.js';
2
- import type { OpenApiCheckOptions } from '../types/index.js';
3
- import type { ILogger } from '../utils/index.js';
4
- import { BaseCommand } from './base.command.js';
5
-
6
- /**
7
- * Check command - Validate OpenAPI specs and accessibility
8
- */
9
- export class CheckCommand extends BaseCommand {
10
- readonly name = 'check';
11
- readonly description = 'check OpenAPI specs or accessibility';
12
-
13
- constructor(
14
- logger: ILogger,
15
- private readonly openApiCheckService: OpenApiCheckService,
16
- private readonly accessibilityCheckService: AccessibilityCheckService,
17
- packageName: string = 'poet'
18
- ) {
19
- super(logger, packageName);
20
- }
21
-
22
- /**
23
- * Execute OpenAPI check
24
- */
25
- async checkOpenApi(options: OpenApiCheckOptions): Promise<void> {
26
- await this.openApiCheckService.validateSpec(options.filename, options.localSchema);
27
- }
28
-
29
- /**
30
- * Execute accessibility check
31
- */
32
- async checkAccessibility(): Promise<number> {
33
- return await this.accessibilityCheckService.checkAccessibility();
34
- }
35
-
36
- protected override async execute(options: OpenApiCheckOptions): Promise<void> {
37
- // This is for openapi-check command
38
- await this.openApiCheckService.validateSpec(options.filename, options.localSchema);
39
- }
40
- }
@@ -1,63 +0,0 @@
1
- import { dev } from '@poetora/previewing';
2
- import type { ArgumentsCamelCase } from 'yargs';
3
- import { InvalidEnvironmentError } from '../errors/index.js';
4
- import type { PortService, VersionService } from '../services/index.js';
5
- import type { DevOptions } from '../types/index.js';
6
- import type { ILogger } from '../utils/index.js';
7
- import { BaseCommand } from './base.command.js';
8
-
9
- /**
10
- * Dev command - Start local development server
11
- */
12
- export class DevCommand extends BaseCommand {
13
- readonly name = 'dev';
14
- readonly description = 'initialize a local preview environment';
15
-
16
- constructor(
17
- logger: ILogger,
18
- private readonly versionService: VersionService,
19
- private readonly portService: PortService,
20
- packageName: string = 'poet'
21
- ) {
22
- super(logger, packageName);
23
- }
24
-
25
- protected override async validate(_options: DevOptions): Promise<void> {
26
- // Check Node.js version
27
- const versionResult = this.versionService.checkNodeVersion();
28
-
29
- if (!versionResult.isValid) {
30
- throw new InvalidEnvironmentError(versionResult.message ?? 'Unsupported Node.js version');
31
- }
32
-
33
- // Show warning if below recommended version
34
- if (versionResult.hasWarning && versionResult.message) {
35
- this.logger.warn(versionResult.message);
36
- }
37
- }
38
-
39
- protected override async execute(options: DevOptions): Promise<void> {
40
- // Find available port
41
- const port = await this.portService.findAvailablePort(options.port);
42
-
43
- // Get CLI version
44
- const cliVersion = this.versionService.getCliVersion();
45
-
46
- // Start development server (delegate to @poetora/previewing)
47
- // Convert DevOptions to ArgumentsCamelCase format expected by dev()
48
- const devArgs: ArgumentsCamelCase = {
49
- _: [],
50
- $0: this.packageName,
51
- port,
52
- open: options.open ?? true,
53
- localSchema: options.localSchema ?? false,
54
- clientVersion: options.clientVersion,
55
- groups: options.groups,
56
- disableOpenapi: options.disableOpenapi ?? false,
57
- packageName: this.packageName,
58
- cliVersion,
59
- };
60
-
61
- await dev(devArgs);
62
- }
63
- }
@@ -1,6 +0,0 @@
1
- export * from './base.command.js';
2
- export * from './check.command.js';
3
- export * from './dev.command.js';
4
- export * from './init.command.js';
5
- export * from './link.command.js';
6
- export * from './update.command.js';
@@ -1,125 +0,0 @@
1
- import { input, select } from '@inquirer/prompts';
2
- import { ValidationError } from '../errors/index.js';
3
- import type { TemplateService } from '../services/template.service.js';
4
- import type { InitOptions } from '../types/index.js';
5
- import type { ILogger } from '../utils/index.js';
6
- import { BaseCommand } from './base.command.js';
7
-
8
- /**
9
- * Init command - Create a new Poetora documentation site
10
- */
11
- export class InitCommand extends BaseCommand {
12
- readonly name = 'init';
13
- readonly description = 'Create a new Poetora documentation site';
14
-
15
- constructor(
16
- logger: ILogger,
17
- private readonly templateService: TemplateService,
18
- packageName: string = 'poet'
19
- ) {
20
- super(logger, packageName);
21
- }
22
-
23
- protected override async execute(options: InitOptions): Promise<void> {
24
- let installDir = options.directory;
25
-
26
- // Step 1: Handle existing directory
27
- const dirStatus = await this.templateService.checkDirectory(installDir);
28
-
29
- if (dirStatus.exists && dirStatus.hasContents) {
30
- const choice = await this.promptDirectoryChoice(installDir);
31
-
32
- if (choice === 'cancel') {
33
- this.logger.info('Installation cancelled');
34
- return;
35
- }
36
-
37
- if (choice === 'subdir') {
38
- const subdir = await this.promptSubdirectoryName();
39
- installDir = installDir === '.' ? subdir : `${installDir}/${subdir}`;
40
- }
41
- }
42
-
43
- // Step 2: Prompt for project configuration
44
- const projectName = await this.promptProjectName(installDir);
45
- const theme = await this.promptTheme();
46
-
47
- // Step 3: Download and install template
48
- this.logger.info('Setting up documentation project...');
49
-
50
- await this.templateService.installTemplate({
51
- directory: installDir,
52
- projectName,
53
- theme,
54
- });
55
-
56
- // Step 4: Show success message
57
- this.showOnboardingMessage(installDir);
58
- }
59
-
60
- private async promptDirectoryChoice(
61
- directory: string
62
- ): Promise<'subdir' | 'overwrite' | 'cancel'> {
63
- const choice = await select({
64
- message: `Directory ${directory} is not empty. What would you like to do?`,
65
- choices: [
66
- { name: 'Create in a subdirectory', value: 'subdir' },
67
- { name: 'Overwrite current directory (may lose contents)', value: 'overwrite' },
68
- { name: 'Cancel', value: 'cancel' },
69
- ],
70
- });
71
-
72
- return choice as 'subdir' | 'overwrite' | 'cancel';
73
- }
74
-
75
- private async promptSubdirectoryName(): Promise<string> {
76
- const subdir = await input({
77
- message: 'Subdirectory name:',
78
- default: 'docs',
79
- });
80
-
81
- if (!subdir || subdir.trim() === '') {
82
- throw new ValidationError('Subdirectory name cannot be empty');
83
- }
84
-
85
- return subdir.trim();
86
- }
87
-
88
- private async promptProjectName(installDir: string): Promise<string> {
89
- const defaultProject = installDir === '.' ? 'Poetora' : installDir;
90
-
91
- const projectName = await input({
92
- message: 'Project Name',
93
- default: defaultProject,
94
- });
95
-
96
- return projectName || defaultProject;
97
- }
98
-
99
- private async promptTheme(): Promise<string> {
100
- const themes = this.templateService.getAvailableThemes();
101
-
102
- const theme = await select({
103
- message: 'Theme',
104
- choices: themes.map((t) => ({
105
- name: t,
106
- value: t,
107
- })),
108
- });
109
-
110
- return theme;
111
- }
112
-
113
- private showOnboardingMessage(installDir: string): void {
114
- this.logger.log('');
115
- this.logger.success('Documentation Setup!');
116
- this.logger.log('');
117
- this.logger.log('To see your docs run:');
118
- this.logger.log('');
119
- if (installDir !== '.') {
120
- this.logger.log(` cd ${installDir}`);
121
- }
122
- this.logger.log(` ${this.packageName} dev`);
123
- this.logger.log('');
124
- }
125
- }
@@ -1,39 +0,0 @@
1
- import type { LinkService } from '../services/index.js';
2
- import type { RenameOptions } from '../types/index.js';
3
- import type { ILogger } from '../utils/index.js';
4
- import { BaseCommand } from './base.command.js';
5
-
6
- /**
7
- * Link command - Check broken links and rename files
8
- */
9
- export class LinkCommand extends BaseCommand {
10
- readonly name = 'link';
11
- readonly description = 'manage documentation links';
12
-
13
- constructor(
14
- logger: ILogger,
15
- private readonly linkService: LinkService,
16
- packageName: string = 'poet'
17
- ) {
18
- super(logger, packageName);
19
- }
20
-
21
- /**
22
- * Check for broken links
23
- */
24
- async checkBrokenLinks(): Promise<Record<string, string[]>> {
25
- return await this.linkService.checkBrokenLinks();
26
- }
27
-
28
- /**
29
- * Rename file and update references
30
- */
31
- async renameFile(options: RenameOptions): Promise<void> {
32
- await this.linkService.renameFile(options.from, options.to, options.force);
33
- }
34
-
35
- protected override async execute(): Promise<void> {
36
- // Default action: check broken links
37
- await this.linkService.checkBrokenLinks();
38
- }
39
- }
@@ -1,23 +0,0 @@
1
- import type { UpdateService } from '../services/index.js';
2
- import type { ILogger } from '../utils/index.js';
3
- import { BaseCommand } from './base.command.js';
4
-
5
- /**
6
- * Update command - Update CLI to latest version
7
- */
8
- export class UpdateCommand extends BaseCommand {
9
- readonly name = 'update';
10
- readonly description = 'update the CLI to the latest version';
11
-
12
- constructor(
13
- logger: ILogger,
14
- private readonly updateService: UpdateService,
15
- packageName: string = 'poet'
16
- ) {
17
- super(logger, packageName);
18
- }
19
-
20
- protected override async execute(): Promise<void> {
21
- await this.updateService.update();
22
- }
23
- }
package/src/constants.ts DELETED
@@ -1,4 +0,0 @@
1
- import os from 'os';
2
-
3
- export const HOME_DIR = os.homedir();
4
- export const CMD_EXEC_PATH = process.cwd();
@@ -1,83 +0,0 @@
1
- /**
2
- * Base CLI error class for all custom errors
3
- */
4
- export class CliError extends Error {
5
- constructor(
6
- message: string,
7
- public readonly code: string,
8
- public readonly exitCode: number = 1
9
- ) {
10
- super(message);
11
- this.name = 'CliError';
12
- Error.captureStackTrace(this, this.constructor);
13
- }
14
- }
15
-
16
- /**
17
- * Error thrown when environment requirements are not met
18
- */
19
- export class InvalidEnvironmentError extends CliError {
20
- constructor(message: string) {
21
- super(message, 'INVALID_ENVIRONMENT', 1);
22
- this.name = 'InvalidEnvironmentError';
23
- }
24
- }
25
-
26
- /**
27
- * Error thrown when no available port is found
28
- */
29
- export class NoAvailablePortError extends CliError {
30
- constructor(message: string) {
31
- super(message, 'NO_AVAILABLE_PORT', 1);
32
- this.name = 'NoAvailablePortError';
33
- }
34
- }
35
-
36
- /**
37
- * Error thrown when configuration file is not found
38
- */
39
- export class ConfigNotFoundError extends CliError {
40
- constructor(message: string) {
41
- super(message, 'CONFIG_NOT_FOUND', 1);
42
- this.name = 'ConfigNotFoundError';
43
- }
44
- }
45
-
46
- /**
47
- * Error thrown when command validation fails
48
- */
49
- export class ValidationError extends CliError {
50
- constructor(
51
- message: string,
52
- public readonly field?: string
53
- ) {
54
- super(message, 'VALIDATION_ERROR', 1);
55
- this.name = 'ValidationError';
56
- }
57
- }
58
-
59
- /**
60
- * Error thrown when file operations fail
61
- */
62
- export class FileSystemError extends CliError {
63
- constructor(
64
- message: string,
65
- public readonly filePath?: string
66
- ) {
67
- super(message, 'FILE_SYSTEM_ERROR', 1);
68
- this.name = 'FileSystemError';
69
- }
70
- }
71
-
72
- /**
73
- * Error thrown when external service calls fail
74
- */
75
- export class ExternalServiceError extends CliError {
76
- constructor(
77
- message: string,
78
- public readonly service?: string
79
- ) {
80
- super(message, 'EXTERNAL_SERVICE_ERROR', 1);
81
- this.name = 'ExternalServiceError';
82
- }
83
- }
@@ -1 +0,0 @@
1
- export * from './cli-error.js';
package/src/index.ts DELETED
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env node
2
- import { type ChildProcess, spawn } from 'child_process';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
-
9
- const packageName =
10
- path.basename(process.argv[1] ?? '') === 'index'
11
- ? 'poet'
12
- : path.basename(process.argv[1] ?? '') || 'poet';
13
-
14
- let cli: ChildProcess | null = null;
15
- let isShuttingDown = false;
16
- let hasExited = false;
17
-
18
- const cleanup = async (): Promise<void> => {
19
- if (isShuttingDown) return;
20
- isShuttingDown = true;
21
-
22
- if (cli && !cli.killed) {
23
- try {
24
- cli.kill('SIGTERM');
25
-
26
- await new Promise<void>((resolve) => {
27
- const timeout = setTimeout(() => {
28
- if (cli && !cli.killed) {
29
- cli.kill('SIGKILL');
30
- }
31
- resolve();
32
- }, 5000);
33
-
34
- cli?.once('exit', () => {
35
- clearTimeout(timeout);
36
- resolve();
37
- });
38
- });
39
- } catch (_error) {
40
- // ignore
41
- }
42
- }
43
- };
44
-
45
- const exitProcess = (code: number) => {
46
- if (hasExited) return;
47
- hasExited = true;
48
- process.exit(code);
49
- };
50
-
51
- const killSignals = ['SIGINT', 'SIGTERM', 'SIGQUIT', 'SIGHUP'];
52
- killSignals.forEach((signal) => {
53
- process.on(signal, async () => {
54
- await cleanup();
55
- exitProcess(0);
56
- });
57
- });
58
-
59
- process.on('uncaughtException', async () => {
60
- await cleanup();
61
- exitProcess(1);
62
- });
63
-
64
- process.on('unhandledRejection', async () => {
65
- await cleanup();
66
- exitProcess(1);
67
- });
68
-
69
- try {
70
- cli = spawn(
71
- 'node',
72
- ['--no-deprecation', path.join(__dirname, '../bin/start'), ...process.argv.slice(2)],
73
- {
74
- stdio: 'inherit',
75
- env: {
76
- ...process.env,
77
- POETORA_PACKAGE_NAME: packageName,
78
- CLI_TEST_MODE: process.env.CLI_TEST_MODE ?? 'false',
79
- },
80
- shell: process.platform === 'win32',
81
- windowsHide: process.platform === 'win32',
82
- detached: false,
83
- }
84
- );
85
-
86
- cli.on('error', async (error) => {
87
- console.error(`Failed to start ${packageName}: ${error.message}`);
88
- await cleanup();
89
- exitProcess(1);
90
- });
91
-
92
- cli.on('exit', (code) => {
93
- exitProcess(code ?? 0);
94
- });
95
- } catch (error) {
96
- console.error(`Failed to start ${packageName}: ${error}`);
97
- exitProcess(1);
98
- }
99
-
100
- process.on('exit', () => {
101
- if (cli && !cli.killed) {
102
- try {
103
- cli.kill('SIGKILL');
104
- } catch (_error) {
105
- // ignore
106
- }
107
- }
108
- });
109
-
110
- export { cli };