model-vue-presenter
Version:
Model-View-Presenter toolkit for Vue 3 apps
118 lines (117 loc) • 4.32 kB
JavaScript
import { isReadonly, getCurrentScope, onScopeDispose, computed } from "vue";
const ERROR = {
PRESENTER_CONFIG: {
INFO: "PresenterFactory: The configuration returned by the factory function is invalid.",
get VIEW_MODEL_IS_NOT_COMPUTED() {
return `${this.INFO} The 'viewModel' property must be a Vue computed property.`;
},
get ON_CREATED_HOOK_NOT_A_FUNCTION() {
return `${this.INFO} The 'onCreated' property must be a function.`;
},
get ON_DESTROY_HOOK_NOT_A_FUNCTION() {
return `${this.INFO} The 'onDestroy' property must be a function.`;
}
},
VIEW_MODEL_MOCKING: {
INFO: "PresenterFactory: The mocked view model is invalid.",
PROPERTIES_ARE_MISSING(properties) {
return `${this.INFO} Properties ${properties} are missing in the mocked view model.`;
},
HAS_NON_EXISTING_PROPERTIES(properties) {
return `${this.INFO} Mocked view model properties ${properties} doesn't exist in the actual view model.`;
}
},
SPYING: {
INFO: "PresenterFactory: Failed to spy on presenter.",
get NO_PRESENTER_INSTANCE() {
return `${this.INFO} Did you call your presenter hook before calling spy().`;
}
}
};
function validatePresenterConfig(config) {
if (!isReadonly(config.viewModel)) {
throw new Error(ERROR.PRESENTER_CONFIG.VIEW_MODEL_IS_NOT_COMPUTED);
}
if (config.onCreated && typeof config.onCreated !== "function") {
throw new Error(ERROR.PRESENTER_CONFIG.ON_CREATED_HOOK_NOT_A_FUNCTION);
}
if (config.onDestroy && typeof config.onDestroy !== "function") {
throw new Error(ERROR.PRESENTER_CONFIG.ON_DESTROY_HOOK_NOT_A_FUNCTION);
}
}
function validateViewModelOverride(viewModelOverride, actualViewModel) {
const mockedViewModel = viewModelOverride(actualViewModel);
const nonExistingProperties = Object.keys(mockedViewModel).filter((key) => !(key in actualViewModel));
const missingProperties = Object.keys(actualViewModel).filter((key) => !(key in mockedViewModel));
if (nonExistingProperties.length) {
throw new Error(ERROR.VIEW_MODEL_MOCKING.HAS_NON_EXISTING_PROPERTIES(nonExistingProperties));
}
if (missingProperties.length) {
throw new Error(ERROR.VIEW_MODEL_MOCKING.PROPERTIES_ARE_MISSING(missingProperties));
}
}
function tryOnScopeDispose(fn) {
if (getCurrentScope()) onScopeDispose(fn);
}
function presenterFactory(presenterFactoryFunction) {
const cachedPresenterInstance = {
value: void 0,
reset() {
this.value = void 0;
}
};
const viewModelOverride = {
value: void 0,
reset() {
this.value = void 0;
}
};
function createPresenterProxy(presenterOptions, viewModelOverride2) {
return new Proxy(presenterOptions, {
get(target, property) {
if (property === "viewModel") {
return computed(() => viewModelOverride2(target.viewModel.value));
}
return Reflect.get(target, property);
}
});
}
function usePresenterHook(props, view) {
var _a;
const config = presenterFactoryFunction(props, view);
validatePresenterConfig(config);
if (viewModelOverride.value) validateViewModelOverride(viewModelOverride.value, config.viewModel.value);
(_a = config.onCreated) == null ? void 0 : _a.call(config);
tryOnScopeDispose(() => {
var _a2;
return (_a2 = config.onDestroy) == null ? void 0 : _a2.call(config);
});
const presenterOptions = viewModelOverride.value ? createPresenterProxy(config, viewModelOverride.value) : config;
cachedPresenterInstance.value = presenterOptions;
viewModelOverride.reset();
return {
presenter: presenterOptions,
viewModel: presenterOptions.viewModel
};
}
usePresenterHook.spy = function() {
const presenterOptions = cachedPresenterInstance.value;
if (!presenterOptions) throw ERROR.SPYING.NO_PRESENTER_INSTANCE;
cachedPresenterInstance.reset();
return {
presenter: presenterOptions,
viewModel: presenterOptions.viewModel
};
};
usePresenterHook.resetSpy = function() {
cachedPresenterInstance.reset();
};
usePresenterHook.mockViewModel = function(viewModelOverrideFunction) {
viewModelOverride.value = viewModelOverrideFunction;
};
return usePresenterHook;
}
export {
presenterFactory
};
//# sourceMappingURL=main.js.map