Skip to content
Open

wip #4797

Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions packages/project/src/ProjectError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import chalk from "chalk";
import util from "util";

export class ProjectError extends Error {
static from(...message: string[]): ProjectError {
const formattedMessage = this.formatMessage(...message);
return new ProjectError(formattedMessage);
}

static formatMessage(...message: string[]): string {
const [text, ...args] = message;
// Replace all placeholders (match with `/%[a-zA-Z]/g` regex) with colorized values.
const messageWithColorizedPlaceholders = text.replace(/%[a-zA-Z]/g, match => {
return chalk.red(match);
});

return util.format(messageWithColorizedPlaceholders, ...args);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type ExtensionDefinitionModel } from "./ExtensionDefinitionModel.js";
import { type z } from "zod";
import { type ProjectModel } from "~/models/index.js";
import { ProjectError } from "~/ProjectError.js";

export interface ExtensionInstanceModelContext {
[key: string]: any;
Expand Down Expand Up @@ -35,8 +36,11 @@ export class ExtensionInstanceModel<TParamsSchema extends z.ZodTypeAny> {

const validationResult = await paramsSchema.safeParseAsync(this.params);
if (!validationResult.success) {
throw new Error(
`Validation failed for extension "${this.definition.type}": ${validationResult.error.message}`
const errorMessages = validationResult.error.errors.map(err => err.message).join("; ");

throw ProjectError.from(
`Validation failed for extension of type %s: ${errorMessages}`,
this.definition.type
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,22 @@ import { z } from "zod";
import path from "path";
import fs from "fs";
import { type IProjectModel } from "~/abstractions/models/index.js";
import { ProjectError } from "~/ProjectError.js";

export const zodPathToAbstraction = (
expectedAbstraction: Abstraction<any>,
project: IProjectModel
) => {
const getTokenName = (token: symbol) => {
const str = token.toString();
return str.replace(/^Symbol\(/, "").replace(/\)$/, "");
};

const tokenName = getTokenName(expectedAbstraction.token);

return z
.string()
.describe(`Path to a file exporting ${expectedAbstraction.token.toString()}`)
.describe(`Path to a file exporting ${tokenName}`)
.superRefine(async (src, ctx) => {
let absoluteSrcPath = src;
if (!path.isAbsolute(src)) {
Expand All @@ -20,7 +28,10 @@ export const zodPathToAbstraction = (
if (!fs.existsSync(absoluteSrcPath)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Source file does not exist: ${absoluteSrcPath}. Please provide a valid path.`
message: ProjectError.formatMessage(
`File not found: %s. Please check the path and try again.`,
src
)
});
return;
}
Expand All @@ -34,7 +45,11 @@ export const zodPathToAbstraction = (
if (!exportedImplementation) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Source file for extension "${src}" (type: ${expectedAbstraction.token.toString()}) must export a class named "${exportName}".`
message: ProjectError.formatMessage(
`The file %s must export a class named %s.`,
src,
exportName
)
});
return;
}
Expand All @@ -47,7 +62,12 @@ export const zodPathToAbstraction = (
if (!isCorrectAbstraction) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Source file for extension "${src}" (type: ${expectedAbstraction.token.toString()}) must export a class that implements the "${expectedAbstraction.token.toString()}" abstraction.`
message: ProjectError.formatMessage(
`The class %s in %s must implement the %s interface.`,
exportName,
src,
tokenName
)
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createImplementation } from "@webiny/di";
import { ValidateProjectConfigService } from "~/abstractions/index.js";
import { ProjectError } from "~/ProjectError.js";

export class DefaultValidateProjectConfigService implements ValidateProjectConfigService.Interface {
async execute(projectConfig: ValidateProjectConfigService.Params): Promise<void> {
Expand All @@ -13,8 +14,9 @@ export class DefaultValidateProjectConfigService implements ValidateProjectConfi
try {
await extension.validate();
} catch (error) {
throw new Error(
`Validation failed for extension of type "${extensionType}": ${error.message}`
throw ProjectError.from(
`Validation failed for extension of type %s: ${error.message}`,
extensionType
);
}
}
Expand Down
Loading