@vue-skuilder/cli
Version:
CLI scaffolding tool for vue-skuilder projects
192 lines (170 loc) • 5.75 kB
text/typescript
import chalk from 'chalk';
/**
* Error categories for studio-ui build failures
*/
export enum StudioBuildErrorType {
QUESTIONS_HASH_ERROR = 'questions-hash-error',
BUILD_FAILURE = 'build-failure',
COPY_FAILURE = 'copy-failure',
MISSING_SOURCE = 'missing-source',
MISSING_DEPENDENCIES = 'missing-dependencies',
VITE_BUILD_ERROR = 'vite-build-error',
TYPESCRIPT_ERROR = 'typescript-error',
CRITICAL_ERROR = 'critical-error'
}
/**
* Structured error information for studio-ui build failures
*/
export interface StudioBuildError {
type: StudioBuildErrorType;
message: string;
cause?: Error;
context?: Record<string, unknown>;
recoverable: boolean;
fallbackAvailable: boolean;
}
/**
* Create a structured studio build error
*/
export function createStudioBuildError(
type: StudioBuildErrorType,
message: string,
options: {
cause?: Error;
context?: Record<string, unknown>;
recoverable?: boolean;
fallbackAvailable?: boolean;
} = {}
): StudioBuildError {
return {
type,
message,
cause: options.cause,
context: options.context,
recoverable: options.recoverable ?? true,
fallbackAvailable: options.fallbackAvailable ?? true
};
}
/**
* Report a studio build error with appropriate formatting and guidance
*/
export function reportStudioBuildError(error: StudioBuildError): void {
// Main error message
console.error(chalk.red(`❌ Studio-UI Build Error: ${error.message}`));
// Error type context
console.error(chalk.gray(` Type: ${error.type}`));
// Underlying cause if available
if (error.cause) {
console.error(chalk.gray(` Cause: ${error.cause.message}`));
}
// Context information
if (error.context) {
for (const [key, value] of Object.entries(error.context)) {
console.error(chalk.gray(` ${key}: ${String(value)}`));
}
}
// Recovery guidance
if (error.recoverable) {
if (error.fallbackAvailable) {
console.log(chalk.yellow(` Fallback: Using default studio-ui (without local questions)`));
} else {
console.log(chalk.yellow(` Recovery: Manual intervention required`));
}
} else {
console.error(chalk.red(` Critical: Studio session cannot continue`));
}
// Type-specific guidance
provideBuildErrorGuidance(error.type);
}
/**
* Provide specific guidance based on error type
*/
function provideBuildErrorGuidance(errorType: StudioBuildErrorType): void {
switch (errorType) {
case StudioBuildErrorType.QUESTIONS_HASH_ERROR:
console.log(chalk.cyan(` 💡 Check that src/questions/ directory is accessible`));
console.log(chalk.cyan(` 💡 Verify file permissions and disk space`));
break;
case StudioBuildErrorType.BUILD_FAILURE:
console.log(chalk.cyan(` 💡 Check that Vite build dependencies are installed`));
console.log(chalk.cyan(` 💡 Verify TypeScript configuration is valid`));
break;
case StudioBuildErrorType.COPY_FAILURE:
console.log(chalk.cyan(` 💡 Check write permissions for .skuilder/studio-builds/`));
console.log(chalk.cyan(` 💡 Verify sufficient disk space available`));
break;
case StudioBuildErrorType.MISSING_SOURCE:
console.log(chalk.cyan(` 💡 Rebuild CLI package: yarn workspace @vue-skuilder/cli build`));
console.log(chalk.cyan(` 💡 Verify studio-ui source was embedded during build`));
break;
case StudioBuildErrorType.MISSING_DEPENDENCIES:
console.log(chalk.cyan(` 💡 Install dependencies: yarn install`));
console.log(chalk.cyan(` 💡 Check that required packages are in package.json`));
break;
case StudioBuildErrorType.VITE_BUILD_ERROR:
console.log(chalk.cyan(` 💡 Check question type implementations for syntax errors`));
console.log(chalk.cyan(` 💡 Verify all imports and dependencies are available`));
break;
case StudioBuildErrorType.TYPESCRIPT_ERROR:
console.log(chalk.cyan(` 💡 Check TypeScript configuration in tsconfig.json`));
console.log(chalk.cyan(` 💡 Verify all question types have proper type definitions`));
break;
case StudioBuildErrorType.CRITICAL_ERROR:
console.log(chalk.cyan(` 💡 Report this issue to the vue-skuilder team`));
console.log(chalk.cyan(` 💡 Include the full error output in your report`));
break;
}
}
/**
* Wrap a studio build operation with error handling
*/
export async function withStudioBuildErrorHandling<T>(
operation: () => Promise<T>,
errorType: StudioBuildErrorType,
context?: Record<string, unknown>
): Promise<T> {
try {
return await operation();
} catch (error) {
const buildError = createStudioBuildError(
errorType,
error instanceof Error ? error.message : String(error),
{
cause: error instanceof Error ? error : undefined,
context,
recoverable: true,
fallbackAvailable: true
}
);
reportStudioBuildError(buildError);
throw buildError;
}
}
/**
* Check if error is a recoverable studio build error
*/
export function isRecoverableStudioBuildError(error: unknown): boolean {
return error instanceof Error &&
'type' in error &&
'recoverable' in error &&
Boolean(error.recoverable);
}
/**
* Extract error details for logging
*/
export function extractErrorDetails(error: unknown): {
message: string;
stack?: string;
code?: string;
} {
if (error instanceof Error) {
return {
message: error.message,
stack: error.stack,
code: 'code' in error ? String(error.code) : undefined
};
}
return {
message: String(error)
};
}