Skip to content

Commit

Permalink
refactor: extract error codes and cleanup (#1974)
Browse files Browse the repository at this point in the history
[skip ci]
  • Loading branch information
bigopon committed May 17, 2024
1 parent e0481d6 commit 63ffdc9
Show file tree
Hide file tree
Showing 20 changed files with 209 additions and 160 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
"@typescript-eslint/eslint-plugin": "6.10.0",
"@typescript-eslint/parser": "6.10.0",
"chalk": "4.1.2",
"chromedriver": "123.0.0",
"chromedriver": "125.0.0",
"codecov": "^3.8.3",
"concurrently": "7.6.0",
"cross-env": "7.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe('3-runtime-html/dialog/dialog-service.spec.ts', function () {
afterStarted: async (_, dialogService) => {
let error: DialogCancelError<unknown>;
await dialogService.open({}).catch(err => error = err);
assert.strictEqual(error.message, 'AUR0903');
assert.includes(error.message, 'AUR0903');
// assert.strictEqual(error.message, 'Invalid Dialog Settings. You must provide "component", "template" or both.');
}
},
Expand Down Expand Up @@ -300,7 +300,7 @@ describe('3-runtime-html/dialog/dialog-service.spec.ts', function () {

assert.notStrictEqual(error, undefined);
assert.strictEqual(error.wasCancelled, true);
assert.strictEqual(error.message, 'Dialog activation rejected');
assert.includes(error.message, 'AUR0905');
assert.strictEqual(canActivateCallCount, 1);
assert.html.textContent(ctx.doc.querySelector('au-dialog-container'), null);
}
Expand Down Expand Up @@ -415,7 +415,7 @@ describe('3-runtime-html/dialog/dialog-service.spec.ts', function () {
errorCaughtCount++;
error = err;
});
assert.deepStrictEqual(error, Object.assign(new Error(), {
assert.deepStrictEqual(error, Object.assign(new Error('AUR0908:'), {
wasCancelled: false,
value: expectedError
}));
Expand Down
6 changes: 3 additions & 3 deletions packages/__tests__/src/fetch-client/fetch-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ describe('fetch-client/fetch-client.spec.ts', function () {
it('rejects invalid configs', function () {
assert.throws(
() => client.configure(1 as RequestInit),
/invalid config/,
/AUR5002/,
`() => client.configure(1 as RequestInit)`
);
});

it('rejects invalid config returned from "configure" call', function () {
assert.throws(
() => client.configure(() => 1 as any),
/The config callback did not return a valid HttpClientConfiguration/,
/AUR5001/,
`() => client.configure(1 as RequestInit)`
);
});
Expand All @@ -90,7 +90,7 @@ describe('fetch-client/fetch-client.spec.ts', function () {
client.configure(config => config.withInterceptor({
request: () => ({ } as any)
}));
return assert.rejects(() => client.fetch('/a'), /An invalid result was returned by the interceptor chain/);
return assert.rejects(() => client.fetch('/a'), /AUR5006/);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { AppTask } from '@aurelia/runtime-html';
import { IDialogGlobalSettings } from './dialog-interfaces';
import { DefaultDialogGlobalSettings, DefaultDialogDomRenderer } from './dialog-default-impl';
import { DialogService } from './dialog-service';
import { singletonRegistration } from '../../utilities-di';
import { createError } from '../../utilities';
import { singletonRegistration } from './utilities-di';
import { ErrorNames, createMappedError } from './errors';

export type DialogConfigurationProvider = (settings: IDialogGlobalSettings) => void | Promise<unknown>;

Expand Down Expand Up @@ -37,14 +37,7 @@ DialogConfiguration.customize(settings => {
```
*/
export const DialogConfiguration = /*@__PURE__*/createDialogConfiguration(() => {
if (__DEV__)
throw createError(`AUR0904: Invalid dialog configuration. ` +
'Specify the implementations for ' +
'<IDialogService>, <IDialogGlobalSettings> and <IDialogDomRenderer>, ' +
'or use the DialogDefaultConfiguration export.'
);
else
throw createError(`AUR0904`);
throw createMappedError(ErrorNames.dialog_no_empty_default_configuration);
}, [class NoopDialogGlobalSettings {
public static register(container: IContainer): void {
container.register(singletonRegistration(IDialogGlobalSettings, this));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import {
DialogCancelError,
DialogCloseError,
} from './dialog-interfaces';
import { createError, isFunction } from '../../utilities';
import { instanceRegistration } from '../../utilities-di';
import { isFunction } from './utilities';
import { instanceRegistration } from './utilities-di';

import type {
DialogDeactivationStatuses,
IDialogComponent,
IDialogLoadedSettings,
} from './dialog-interfaces';
import { ErrorNames, createMappedError } from './errors';

/**
* A controller object for a Dialog instance.
Expand Down Expand Up @@ -108,7 +109,7 @@ export class DialogController implements IDialogController {
if (canActivate !== true) {
dom.dispose();
if (rejectOnCancel) {
throw createDialogCancelError(null, 'Dialog activation rejected');
throw createDialogCancelError(null, ErrorNames.dialog_activation_rejected);
}
return DialogOpenResult.create(true, this);
}
Expand Down Expand Up @@ -155,7 +156,7 @@ export class DialogController implements IDialogController {
deactivating = false;
this._closingPromise = void 0;
if (rejectOnCancel) {
throw createDialogCancelError(null, 'Dialog cancellation rejected');
throw createDialogCancelError(null, ErrorNames.dialog_cancellation_rejected);
}
return DialogCloseResult.create('abort' as T);
}
Expand All @@ -167,7 +168,7 @@ export class DialogController implements IDialogController {
if (!rejectOnCancel && status !== 'error') {
this._resolve(dialogResult);
} else {
this._reject(createDialogCancelError(value, 'Dialog cancelled with a rejection on cancel'));
this._reject(createDialogCancelError(value, ErrorNames.dialog_cancelled_with_cancel_on_rejection_setting));
}
return dialogResult;
}
Expand Down Expand Up @@ -268,15 +269,15 @@ export class DialogController implements IDialogController {

class EmptyComponent {}

function createDialogCancelError<T>(output: T | undefined, msg: string): DialogCancelError<T> {
const error = createError(msg) as DialogCancelError<T>;
function createDialogCancelError<T>(output: T | undefined, code: ErrorNames/* , msg: string */): DialogCancelError<T> {
const error = createMappedError(code) as DialogCancelError<T>;
error.wasCancelled = true;
error.value = output;
return error;
}

function createDialogCloseError<T = unknown>(output: T): DialogCloseError<T> {
const error = createError('') as DialogCloseError<T>;
const error = createMappedError(ErrorNames.dialog_custom_error) as DialogCloseError<T>;
error.wasCancelled = false;
error.value = output;
return error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from './dialog-interfaces';

import { IContainer, resolve } from '@aurelia/kernel';
import { singletonRegistration } from '../../utilities-di';
import { singletonRegistration } from './utilities-di';

export class DefaultDialogGlobalSettings implements IDialogGlobalSettings {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createInterface } from '../../utilities-di';
import { createInterface } from './utilities-di';

import type { Constructable, IContainer, IDisposable } from '@aurelia/kernel';
import type { ICustomElementViewModel } from '@aurelia/runtime-html';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import {
IDialogLoadedSettings,
} from './dialog-interfaces';
import { DialogController } from './dialog-controller';
import { createError, isFunction, isPromise } from '../../utilities';
import { instanceRegistration, singletonRegistration } from '../../utilities-di';
import { isFunction, isPromise } from './utilities';
import { instanceRegistration, singletonRegistration } from './utilities-di';

import type {
DialogOpenPromise,
IDialogSettings,
} from './dialog-interfaces';
import { ErrorNames, createMappedError } from './errors';

/**
* A default implementation for the dialog service allowing for the creation of dialogs.
Expand All @@ -29,13 +30,10 @@ export class DialogService implements IDialogService {
Registration.aliasTo(this, IDialogService),
AppTask.deactivating(IDialogService, dialogService => onResolve(
dialogService.closeAll(),
(openDialogController) => {
if (openDialogController.length > 0) {
(openDialogControllers) => {
if (openDialogControllers.length > 0) {
// todo: what to do?
if (__DEV__)
throw createError(`AUR0901: There are still ${openDialogController.length} open dialog(s).`);
else
throw createError(`AUR0901:${openDialogController.length}`);
throw createMappedError(ErrorNames.dialog_not_all_dialogs_closed, openDialogControllers.length);
}
}
))
Expand Down Expand Up @@ -198,10 +196,7 @@ class DialogSettings<T extends object = object> implements IDialogSettings<T> {
/** @internal */
private _validate(): this {
if (this.component == null && this.template == null) {
if (__DEV__)
throw createError(`AUR0903: Invalid Dialog Settings. You must provide "component", "template" or both.`);
else
throw createError(`AUR0903`);
throw createMappedError(ErrorNames.dialog_settings_invalid);
}
return this;
}
Expand Down
75 changes: 75 additions & 0 deletions packages/dialog/src/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable prefer-template */

/** @internal */
export const createMappedError: CreateError = __DEV__
? (code: ErrorNames, ...details: unknown[]) => new Error(`AUR${String(code).padStart(4, '0')}: ${getMessageByCode(code, ...details)}`)
: (code: ErrorNames, ...details: unknown[]) => new Error(`AUR${String(code).padStart(4, '0')}:${details.map(String)}`);

_START_CONST_ENUM();
/** @internal */
export const enum ErrorNames {
method_not_implemented = 99,

dialog_not_all_dialogs_closed = 901,
dialog_settings_invalid = 903,
dialog_no_empty_default_configuration = 904,
dialog_activation_rejected = 905,
dialog_cancellation_rejected = 906,
dialog_cancelled_with_cancel_on_rejection_setting = 907,
dialog_custom_error = 908,
}
_END_CONST_ENUM();

const errorsMap: Record<ErrorNames, string> = {
[ErrorNames.method_not_implemented]: 'Method {{0}} not implemented',

[ErrorNames.dialog_not_all_dialogs_closed]: `Failured to close all dialogs when deactivating the application, There are still {{0}} open dialog(s).`,
[ErrorNames.dialog_settings_invalid]: `Invalid Dialog Settings. You must provide either "component" or "template" or both.`,
[ErrorNames.dialog_no_empty_default_configuration]: `Invalid dialog configuration. ` +
'Specify the implementations for <IDialogService>, <IDialogGlobalSettings> and <IDialogDomRenderer>, ' +
'or use the DialogDefaultConfiguration export.',
[ErrorNames.dialog_activation_rejected]: 'Dialog activation rejected',
[ErrorNames.dialog_cancellation_rejected]: 'Dialog cancellation rejected',
[ErrorNames.dialog_cancelled_with_cancel_on_rejection_setting]: 'Dialog cancelled with a rejection on cancel',
[ErrorNames.dialog_custom_error]: 'Dialog custom error',
};

const getMessageByCode = (name: ErrorNames, ...details: unknown[]) => {
let cooked: string = errorsMap[name];
for (let i = 0; i < details.length; ++i) {
const regex = new RegExp(`{{${i}(:.*)?}}`, 'g');
let matches = regex.exec(cooked);
while (matches != null) {
const method = matches[1]?.slice(1);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let value = details[i] as any;
if (value != null) {
switch (method) {
case 'join(!=)': value = (value as unknown[]).join('!='); break;
case 'element': value = value === '*' ? 'all elements' : `<${value} />`; break;
default: {
// property access
if (method?.startsWith('.')) {
value = String(value[method.slice(1)]);
} else {
value = String(value);
}
}
}
}
cooked = cooked.slice(0, matches.index) + value + cooked.slice(regex.lastIndex);
matches = regex.exec(cooked);
}
}
return cooked;
};

type CreateError = (code: ErrorNames, ...details: unknown[]) => Error;

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function pleaseHelpCreateAnIssue(title: string, body?: string) {
return `\nThis is likely an issue with Aurelia.\n Please help create an issue by clicking the following link\n`
+ `https://github.com/aurelia/aurelia/issues/new?title=${encodeURIComponent(title)}`
+ (body != null ? `&body=${encodeURIComponent(body)}` : '&template=bug_report.md');
}
10 changes: 5 additions & 5 deletions packages/dialog/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,23 @@ export {
type IDialogComponentCanActivate,
type IDialogComponentDeactivate,
type IDialogComponentCanDeactivate,
} from './plugins/dialog/dialog-interfaces';
} from './dialog-interfaces';

// default impl
export {
DialogController,
} from './plugins/dialog/dialog-controller';
} from './dialog-controller';
export {
DialogService,
} from './plugins/dialog/dialog-service';
} from './dialog-service';
export {
DialogConfiguration,
DialogDefaultConfiguration,
type DialogConfigurationProvider,
} from './plugins/dialog/dialog-configuration';
} from './dialog-configuration';

export {
DefaultDialogDom,
DefaultDialogDomRenderer,
DefaultDialogGlobalSettings,
} from './plugins/dialog/dialog-default-impl';
} from './dialog-default-impl';
23 changes: 0 additions & 23 deletions packages/expression-parser/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,8 @@ const getMessageByCode = (name: ErrorNames, ...details: unknown[]) => {
let value = details[i] as any;
if (value != null) {
switch (method) {
case 'nodeName': value = (value as Node).nodeName.toLowerCase(); break;
case 'name': value = (value as { name: string }).name; break;
case 'typeof': value = typeof value; break;
case 'ctor': value = (value as object).constructor.name; break;
case 'controller': value = value.controller.name; break;
case 'target@property': value = `${value.target}@${value.targetProperty}`; break;
case 'toString': value = Object.prototype.toString.call(value); break;
case 'join(!=)': value = (value as unknown[]).join('!='); break;
case 'bindingCommandHelp': value = getBindingCommandHelp(value); break;
case 'element': value = value === '*' ? 'all elements' : `<${value} />`; break;
default: {
// property access
Expand All @@ -145,19 +138,3 @@ function pleaseHelpCreateAnIssue(title: string, body?: string) {
+ `https://github.com/aurelia/aurelia/issues/new?title=${encodeURIComponent(title)}`
+ (body != null ? `&body=${encodeURIComponent(body)}` : '&template=bug_report.md');
}

function getBindingCommandHelp(name: string) {
switch (name) {
case 'delegate':
return `\nThe ".delegate" binding command has been removed in v2.`
+ ` Binding command ".trigger" should be used instead.`
+ ` If you are migrating v1 application, install compat package`
+ ` to add back the ".delegate" binding command for ease of migration.`;
case 'call':
return `\nThe ".call" binding command has been removed in v2.`
+ ` If you want to pass a callback that preserves the context of the function call,`
+ ` you can use lambda instead. Refer to lambda expression doc for more details.`;
default:
return '';
}
}
Loading

0 comments on commit 63ffdc9

Please sign in to comment.