mirror of
https://github.com/flarum/framework.git
synced 2025-02-21 08:39:25 +08:00
chore: graceful failure from extend/override errors (#4134)
This commit is contained in:
parent
27087cc713
commit
5d281b9471
@ -279,6 +279,13 @@ export default class Application {
|
||||
|
||||
initialRoute!: string;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public currentInitializerExtension: string | null = null;
|
||||
|
||||
private handledErrors: { extension: null | string; errorId: string; error: any }[] = [];
|
||||
|
||||
public load(payload: Application['data']) {
|
||||
this.data = payload;
|
||||
this.translator.setLocale(payload.locale);
|
||||
@ -288,17 +295,19 @@ export default class Application {
|
||||
const caughtInitializationErrors: CallableFunction[] = [];
|
||||
|
||||
this.initializers.toArray().forEach((initializer) => {
|
||||
this.currentInitializerExtension = initializer.itemName.includes('/')
|
||||
? initializer.itemName.replace(/(\/flarum-ext-)|(\/flarum-)/g, '-')
|
||||
: initializer.itemName;
|
||||
|
||||
try {
|
||||
initializer(this);
|
||||
} catch (e) {
|
||||
const extension = initializer.itemName.includes('/')
|
||||
? initializer.itemName.replace(/(\/flarum-ext-)|(\/flarum-)/g, '-')
|
||||
: initializer.itemName;
|
||||
|
||||
caughtInitializationErrors.push(() =>
|
||||
fireApplicationError(
|
||||
extractText(app.translator.trans('core.lib.error.extension_initialiation_failed_message', { extension })),
|
||||
`${extension} failed to initialize`,
|
||||
extractText(
|
||||
app.translator.trans('core.lib.error.extension_initialiation_failed_message', { extension: this.currentInitializerExtension })
|
||||
),
|
||||
`${this.currentInitializerExtension} failed to initialize`,
|
||||
e
|
||||
)
|
||||
);
|
||||
@ -727,4 +736,12 @@ export default class Application {
|
||||
|
||||
return prefix + url + (queryString ? '?' + queryString : '');
|
||||
}
|
||||
|
||||
public handleErrorOnce(extension: null | string, errorId: string, userTitle: string, consoleTitle: string, error: any) {
|
||||
if (this.handledErrors.some((e) => e.errorId === errorId)) return;
|
||||
|
||||
this.handledErrors.push({ extension, errorId, error });
|
||||
|
||||
fireApplicationError(userTitle, consoleTitle, error);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
import extractText from './utils/extractText';
|
||||
import app from './app';
|
||||
|
||||
/**
|
||||
* Extend an object's method by running its output through a mutating callback
|
||||
* every time it is called.
|
||||
@ -28,6 +31,8 @@ export function extend<T extends Record<string, any>, K extends KeyOfType<T, Fun
|
||||
methods: K | K[],
|
||||
callback: (this: T, val: ReturnType<T[K]>, ...args: Parameters<T[K]>) => void
|
||||
) {
|
||||
const extension = app.currentInitializerExtension;
|
||||
|
||||
// A lazy loaded module, only apply the function after the module is loaded.
|
||||
if (typeof object === 'string') {
|
||||
let [namespace, id] = flarum.reg.namespaceAndIdFromPath(object);
|
||||
@ -45,7 +50,17 @@ export function extend<T extends Record<string, any>, K extends KeyOfType<T, Fun
|
||||
object[method] = function (this: T, ...args: Parameters<T[K]>) {
|
||||
const value = original ? original.apply(this, args) : undefined;
|
||||
|
||||
callback.apply(this, [value, ...args]);
|
||||
try {
|
||||
callback.apply(this, [value, ...args]);
|
||||
} catch (e) {
|
||||
app.handleErrorOnce(
|
||||
extension,
|
||||
`${extension}::extend::${object.constructor.name}::${method.toString()}`,
|
||||
extractText(app.translator.trans('core.lib.error.extension_runtime_failed_message', { extension })),
|
||||
`${extension} failed to extend ${object.constructor.name}::${method.toString()}`,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
} as T[K];
|
||||
@ -86,6 +101,8 @@ export function override<T extends Record<any, any>, K extends KeyOfType<T, Func
|
||||
methods: K | K[],
|
||||
newMethod: (this: T, orig: T[K], ...args: Parameters<T[K]>) => void
|
||||
) {
|
||||
const extension = app.currentInitializerExtension;
|
||||
|
||||
// A lazy loaded module, only apply the function after the module is loaded.
|
||||
if (typeof object === 'string') {
|
||||
let [namespace, id] = flarum.reg.namespaceAndIdFromPath(object);
|
||||
@ -101,7 +118,17 @@ export function override<T extends Record<any, any>, K extends KeyOfType<T, Func
|
||||
const original: Function = object[method];
|
||||
|
||||
object[method] = function (this: T, ...args: Parameters<T[K]>) {
|
||||
return newMethod.apply(this, [original?.bind(this), ...args]);
|
||||
try {
|
||||
return newMethod.apply(this, [original?.bind(this), ...args]);
|
||||
} catch (e) {
|
||||
app.handleErrorOnce(
|
||||
extension,
|
||||
`${extension}::extend::${object.constructor.name}::${method.toString()}`,
|
||||
extractText(app.translator.trans('core.lib.error.extension_runtime_failed_message', { extension })),
|
||||
`${extension} failed to override ${object.constructor.name}::${method.toString()}`,
|
||||
e
|
||||
);
|
||||
}
|
||||
} as T[K];
|
||||
|
||||
Object.assign(object[method], original);
|
||||
|
@ -3,9 +3,9 @@ import app from '../app';
|
||||
/**
|
||||
* Fire a Flarum error which is shown in the JS console for everyone and in an alert for the admin.
|
||||
*
|
||||
* @param userTitle: a user friendly title of the error, should be localized.
|
||||
* @param consoleTitle: an error title that goes in the console, doesn't have to be localized.
|
||||
* @param error: the error.
|
||||
* @param userTitle a user friendly title of the error, should be localized.
|
||||
* @param consoleTitle an error title that goes in the console, doesn't have to be localized.
|
||||
* @param error the error.
|
||||
*/
|
||||
export default function fireApplicationError(userTitle: string, consoleTitle: string, error: any) {
|
||||
console.group(`%c${consoleTitle}`, 'background-color: #d83e3e; color: #ffffff; font-weight: bold;');
|
||||
|
@ -722,6 +722,7 @@ core:
|
||||
db_error_message: "Database query failed. This may be caused by an incompatibility between an extension and your database driver."
|
||||
dependent_extensions_message: "Cannot disable {extension} until the following dependent extensions are disabled: {extensions}"
|
||||
extension_initialiation_failed_message: "{extension} failed to initialize, check the browser console for further information."
|
||||
extension_runtime_failed_message: "{extension} encountered an error while running. Check the browser console for further information."
|
||||
generic_message: "Oops! Something went wrong. Please reload the page and try again."
|
||||
generic_cross_origin_message: "Oops! Something went wrong during a cross-origin request. Please reload the page and try again."
|
||||
missing_dependencies_message: "Cannot enable {extension} until the following dependencies are enabled: {extensions}"
|
||||
|
Loading…
x
Reference in New Issue
Block a user